diff --git a/.gitattributes b/.gitattributes
index 435f0bb91b..0f550f4074 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -89,13 +89,22 @@
###############################################################################
*.bmp binary
*.dll binary
+*.eot binary
*.exe binary
*.gif binary
*.jpg binary
+*.pdf binary
*.png binary
-*.tga binary
+*.ppt binary
+*.pptx binary
*.ttf binary
*.snk binary
+*.ttf binary
+*.woff binary
+*.woff2 binary
+*.xls binary
+*.xlsx binary
+
###############################################################################
# Set explicit file behavior to:
@@ -107,3 +116,4 @@
*.pdf diff=astextplain
*.pptx diff=astextplain
*.rtf diff=astextplain
+*.svg diff=astextplain
diff --git a/Directory.Build.props b/Directory.Build.props
index e865a83f85..95cadcd57f 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -29,10 +29,42 @@
true
+
+
+
+
+ $(DefineConstants);SUPPORTS_MATHF;SUPPORTS_HASHCODE;SUPPORTS_EXTENDED_INTRINSICS;SUPPORTS_SPAN_STREAM;SUPPORTS_ENCODING_STRING
+
+
+ $(DefineConstants);SUPPORTS_MATHF;SUPPORTS_HASHCODE;SUPPORTS_EXTENDED_INTRINSICS;SUPPORTS_SPAN_STREAM;SUPPORTS_ENCODING_STRING
+
+
+ $(DefineConstants);SUPPORTS_MATHF;
+
+
+ $(DefineConstants);SUPPORTS_MATHF;SUPPORTS_SPAN_STREAM;SUPPORTS_ENCODING_STRING
+
+
+ $(DefineConstants);SUPPORTS_EXTENDED_INTRINSICS
+
+
true
-
+
false
diff --git a/Directory.Build.targets b/Directory.Build.targets
index 01c1f10397..f6523fee03 100644
--- a/Directory.Build.targets
+++ b/Directory.Build.targets
@@ -22,18 +22,19 @@
-
-
+
+
+
-
+
diff --git a/README.md b/README.md
index 28c3770373..a1c9bca997 100644
--- a/README.md
+++ b/README.md
@@ -155,6 +155,7 @@ Core Team
- [Dirk Lemstra](https://github.com/dlemstra)
- [Anton Firsov](https://github.com/antonfirsov)
- [Scott Williams](https://github.com/tocsoft)
+- [Brian Popow](https://github.com/brianpopow)
### Backers
diff --git a/shared-infrastructure b/shared-infrastructure
index faf84e44ec..c2e689abe9 160000
--- a/shared-infrastructure
+++ b/shared-infrastructure
@@ -1 +1 @@
-Subproject commit faf84e44ec90e8a42a7271bcd04fea76279efb08
+Subproject commit c2e689abe9227209e6d5bc4bf56255d92b4a5d62
diff --git a/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj b/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj
index 5a53d3e78b..c4e3224bbe 100644
--- a/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj
+++ b/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj
@@ -8,18 +8,10 @@
SixLabors.ImageSharp.Drawing
Image Draw Shape Path Font
SixLabors.ImageSharp
- netcoreapp2.1;netstandard1.3;netstandard2.0
-
-
-
- $(DefineConstants);SUPPORTS_MATHF
+ netcoreapp2.1;netstandard2.0;netstandard1.3
-
- $(DefineConstants);SUPPORTS_HASHCODE
-
-
diff --git a/src/ImageSharp.Drawing/Processing/BrushApplicator.cs b/src/ImageSharp.Drawing/Processing/BrushApplicator.cs
index a9df07ced3..058c6d0ebb 100644
--- a/src/ImageSharp.Drawing/Processing/BrushApplicator.cs
+++ b/src/ImageSharp.Drawing/Processing/BrushApplicator.cs
@@ -90,19 +90,23 @@ namespace SixLabors.ImageSharp.Processing
{
Span amountSpan = amountBuffer.Memory.Span;
Span overlaySpan = overlay.Memory.Span;
+ float blendPercentage = this.Options.BlendPercentage;
- for (int i = 0; i < scanline.Length; i++)
+ if (blendPercentage < 1)
{
- if (this.Options.BlendPercentage < 1)
+ for (int i = 0; i < scanline.Length; i++)
{
- amountSpan[i] = scanline[i] * this.Options.BlendPercentage;
+ amountSpan[i] = scanline[i] * blendPercentage;
+ overlaySpan[i] = this[x + i, y];
}
- else
+ }
+ else
+ {
+ for (int i = 0; i < scanline.Length; i++)
{
amountSpan[i] = scanline[i];
+ overlaySpan[i] = this[x + i, y];
}
-
- overlaySpan[i] = this[x + i, y];
}
Span destinationRow = this.Target.GetPixelRowSpan(y).Slice(x, scanline.Length);
diff --git a/src/ImageSharp.Drawing/Processing/PathGradientBrush.cs b/src/ImageSharp.Drawing/Processing/PathGradientBrush.cs
index 9e354120e6..1265678991 100644
--- a/src/ImageSharp.Drawing/Processing/PathGradientBrush.cs
+++ b/src/ImageSharp.Drawing/Processing/PathGradientBrush.cs
@@ -2,11 +2,13 @@
// Licensed under the Apache License, Version 2.0.
using System;
+using System.Buffers;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using SixLabors.ImageSharp.PixelFormats;
+using SixLabors.Memory;
using SixLabors.Primitives;
using SixLabors.Shapes;
@@ -47,7 +49,7 @@ namespace SixLabors.ImageSharp.Processing
throw new ArgumentNullException(nameof(colors));
}
- if (!colors.Any())
+ if (colors.Length == 0)
{
throw new ArgumentOutOfRangeException(
nameof(colors),
@@ -99,7 +101,7 @@ namespace SixLabors.ImageSharp.Processing
throw new ArgumentNullException(nameof(colors));
}
- if (!colors.Any())
+ if (colors.Length == 0)
{
throw new ArgumentOutOfRangeException(
nameof(colors),
@@ -133,22 +135,19 @@ namespace SixLabors.ImageSharp.Processing
private readonly float length;
- private readonly PointF[] buffer;
-
public Edge(Path path, Color startColor, Color endColor)
{
this.path = path;
Vector2[] points = path.LineSegments.SelectMany(s => s.Flatten()).Select(p => (Vector2)p).ToArray();
- this.Start = points.First();
+ this.Start = points[0];
this.StartColor = (Vector4)startColor;
this.End = points.Last();
this.EndColor = (Vector4)endColor;
this.length = DistanceBetween(this.End, this.Start);
- this.buffer = new PointF[this.path.MaxIntersections];
}
public PointF Start { get; }
@@ -159,18 +158,38 @@ namespace SixLabors.ImageSharp.Processing
public Vector4 EndColor { get; }
- public Intersection? FindIntersection(PointF start, PointF end)
+ public Intersection? FindIntersection(PointF start, PointF end, MemoryAllocator allocator)
{
- int intersections = this.path.FindIntersections(start, end, this.buffer);
-
- if (intersections == 0)
+ // TODO: The number of max intersections is upper bound to the number of nodes of the path.
+ // Normally these numbers would be small and could potentially be stackalloc rather than pooled.
+ // Investigate performance beifit of checking length and choosing approach.
+ using (IMemoryOwner memory = allocator.Allocate(this.path.MaxIntersections))
{
- return null;
- }
+ Span buffer = memory.Memory.Span;
+ int intersections = this.path.FindIntersections(start, end, buffer);
+
+ if (intersections == 0)
+ {
+ return null;
+ }
+
+ buffer = buffer.Slice(0, intersections);
+
+ PointF minPoint = buffer[0];
+ var min = new Intersection(minPoint, ((Vector2)(minPoint - start)).LengthSquared());
+ for (int i = 1; i < buffer.Length; i++)
+ {
+ PointF point = buffer[i];
+ var current = new Intersection(point, ((Vector2)(point - start)).LengthSquared());
- return this.buffer.Take(intersections)
- .Select(p => new Intersection(point: p, distance: ((Vector2)(p - start)).LengthSquared()))
- .Aggregate((min, current) => min.Distance > current.Distance ? current : min);
+ if (min.Distance > current.Distance)
+ {
+ min = current;
+ }
+ }
+
+ return min;
+ }
}
public Vector4 ColorAt(float distance)
@@ -197,6 +216,10 @@ namespace SixLabors.ImageSharp.Processing
private readonly IList edges;
+ private readonly TPixel centerPixel;
+
+ private readonly TPixel transparentPixel;
+
///
/// Initializes a new instance of the class.
///
@@ -214,13 +237,15 @@ namespace SixLabors.ImageSharp.Processing
: base(configuration, options, source)
{
this.edges = edges;
-
PointF[] points = edges.Select(s => s.Start).ToArray();
this.center = points.Aggregate((p1, p2) => p1 + p2) / edges.Count;
this.centerColor = (Vector4)centerColor;
+ this.centerPixel = centerColor.ToPixel();
+
+ this.maxDistance = points.Select(p => (Vector2)(p - this.center)).Max(d => d.Length());
- this.maxDistance = points.Select(p => (Vector2)(p - this.center)).Select(d => d.Length()).Max();
+ this.transparentPixel = Color.Transparent.ToPixel();
}
///
@@ -232,22 +257,20 @@ namespace SixLabors.ImageSharp.Processing
if (point == this.center)
{
- return new Color(this.centerColor).ToPixel();
+ return this.centerPixel;
}
var direction = Vector2.Normalize(point - this.center);
-
PointF end = point + (PointF)(direction * this.maxDistance);
(Edge edge, Intersection? info) = this.FindIntersection(point, end);
if (!info.HasValue)
{
- return Color.Transparent.ToPixel();
+ return this.transparentPixel;
}
PointF intersection = info.Value.Point;
-
Vector4 edgeColor = edge.ColorAt(intersection);
float length = DistanceBetween(intersection, this.center);
@@ -263,9 +286,10 @@ namespace SixLabors.ImageSharp.Processing
{
(Edge edge, Intersection? info) closest = default;
+ MemoryAllocator allocator = this.Target.MemoryAllocator;
foreach (Edge edge in this.edges)
{
- Intersection? intersection = edge.FindIntersection(start, end);
+ Intersection? intersection = edge.FindIntersection(start, end, allocator);
if (!intersection.HasValue)
{
diff --git a/src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor.cs b/src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor.cs
index e217fd9a6c..e1dbefdb66 100644
--- a/src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor.cs
+++ b/src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor.cs
@@ -60,10 +60,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing
public float Opacity { get; }
///
- public IImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle)
+ public IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle)
where TPixelBg : struct, IPixel
{
- var visitor = new ProcessorFactoryVisitor(this, source, sourceRectangle);
+ var visitor = new ProcessorFactoryVisitor(configuration, this, source, sourceRectangle);
this.Image.AcceptVisitor(visitor);
return visitor.Result;
}
@@ -71,12 +71,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing
private class ProcessorFactoryVisitor : IImageVisitor
where TPixelBg : struct, IPixel
{
+ private readonly Configuration configuration;
private readonly DrawImageProcessor definition;
private readonly Image source;
private readonly Rectangle sourceRectangle;
- public ProcessorFactoryVisitor(DrawImageProcessor definition, Image source, Rectangle sourceRectangle)
+ public ProcessorFactoryVisitor(Configuration configuration, DrawImageProcessor definition, Image source, Rectangle sourceRectangle)
{
+ this.configuration = configuration;
this.definition = definition;
this.source = source;
this.sourceRectangle = sourceRectangle;
@@ -88,6 +90,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing
where TPixelFg : struct, IPixel
{
this.Result = new DrawImageProcessor(
+ this.configuration,
image,
this.source,
this.sourceRectangle,
diff --git a/src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor{TPixelBg,TPixelFg}.cs b/src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor{TPixelBg,TPixelFg}.cs
index eab6b2f4a1..55f72c7fc9 100644
--- a/src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor{TPixelBg,TPixelFg}.cs
+++ b/src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor{TPixelBg,TPixelFg}.cs
@@ -22,6 +22,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing
///
/// Initializes a new instance of the class.
///
+ /// The configuration which allows altering default behaviour or extending the library.
/// The foreground to blend with the currently processing image.
/// The source for the current processor instance.
/// The source area to process for the current processor instance.
@@ -30,6 +31,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing
/// The Alpha blending mode to use when drawing the image.
/// The opacity of the image to blend. Must be between 0 and 1.
public DrawImageProcessor(
+ Configuration configuration,
Image image,
Image source,
Rectangle sourceRectangle,
@@ -37,7 +39,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing
PixelColorBlendingMode colorBlendingMode,
PixelAlphaCompositionMode alphaCompositionMode,
float opacity)
- : base(source, sourceRectangle)
+ : base(configuration, source, sourceRectangle)
{
Guard.MustBeBetweenOrEqualTo(opacity, 0, 1, nameof(opacity));
diff --git a/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillProcessor.cs b/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillProcessor.cs
index 3963f99a5c..c94e0c179b 100644
--- a/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillProcessor.cs
+++ b/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillProcessor.cs
@@ -34,10 +34,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing
public GraphicsOptions Options { get; }
///
- public IImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle)
+ public IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle)
where TPixel : struct, IPixel
- {
- return new FillProcessor(this, source, sourceRectangle);
- }
+ => new FillProcessor(configuration, this, source, sourceRectangle);
}
}
diff --git a/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillProcessor{TPixel}.cs b/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillProcessor{TPixel}.cs
index fc94826187..ca639cd142 100644
--- a/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillProcessor{TPixel}.cs
+++ b/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillProcessor{TPixel}.cs
@@ -6,7 +6,6 @@ using System.Buffers;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Advanced.ParallelUtils;
-using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Primitives;
@@ -21,8 +20,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing
{
private readonly FillProcessor definition;
- public FillProcessor(FillProcessor definition, Image source, Rectangle sourceRectangle)
- : base(source, sourceRectangle)
+ public FillProcessor(Configuration configuration, FillProcessor definition, Image source, Rectangle sourceRectangle)
+ : base(configuration, source, sourceRectangle)
{
this.definition = definition;
}
@@ -89,7 +88,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing
source,
sourceRectangle))
{
- amount.Memory.Span.Fill(1f);
+ amount.Memory.Span.Fill(1F);
ParallelHelper.IterateRows(
workingRect,
@@ -112,7 +111,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing
{
solidBrush = this.definition.Brush as SolidBrush;
- if (solidBrush == null)
+ if (solidBrush is null)
{
return false;
}
diff --git a/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillRegionProcessor.cs b/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillRegionProcessor.cs
index 7d51be1c51..6cfeb785ce 100644
--- a/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillRegionProcessor.cs
+++ b/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillRegionProcessor.cs
@@ -42,10 +42,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing
public GraphicsOptions Options { get; }
///
- public IImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle)
+ public IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle)
where TPixel : struct, IPixel
- {
- return new FillRegionProcessor(this, source, sourceRectangle);
- }
+ => new FillRegionProcessor(configuration, this, source, sourceRectangle);
}
}
diff --git a/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillRegionProcessor{TPixel}.cs b/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillRegionProcessor{TPixel}.cs
index 4744a4e920..7d632ad23d 100644
--- a/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillRegionProcessor{TPixel}.cs
+++ b/src/ImageSharp.Drawing/Processing/Processors/Drawing/FillRegionProcessor{TPixel}.cs
@@ -23,8 +23,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing
{
private readonly FillRegionProcessor definition;
- public FillRegionProcessor(FillRegionProcessor definition, Image source, Rectangle sourceRectangle)
- : base(source, sourceRectangle)
+ public FillRegionProcessor(Configuration configuration, FillRegionProcessor definition, Image source, Rectangle sourceRectangle)
+ : base(configuration, source, sourceRectangle)
{
this.definition = definition;
}
diff --git a/src/ImageSharp.Drawing/Processing/Processors/Text/DrawTextProcessor.cs b/src/ImageSharp.Drawing/Processing/Processors/Text/DrawTextProcessor.cs
index 775cf55abf..75774a6285 100644
--- a/src/ImageSharp.Drawing/Processing/Processors/Text/DrawTextProcessor.cs
+++ b/src/ImageSharp.Drawing/Processing/Processors/Text/DrawTextProcessor.cs
@@ -72,10 +72,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Text
public PointF Location { get; }
///
- public IImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle)
+ public IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle)
where TPixel : struct, IPixel
- {
- return new DrawTextProcessor(this, source, sourceRectangle);
- }
+ => new DrawTextProcessor(configuration, this, source, sourceRectangle);
}
}
diff --git a/src/ImageSharp.Drawing/Processing/Processors/Text/DrawTextProcessor{TPixel}.cs b/src/ImageSharp.Drawing/Processing/Processors/Text/DrawTextProcessor{TPixel}.cs
index 64d32efb80..c47f764a29 100644
--- a/src/ImageSharp.Drawing/Processing/Processors/Text/DrawTextProcessor{TPixel}.cs
+++ b/src/ImageSharp.Drawing/Processing/Processors/Text/DrawTextProcessor{TPixel}.cs
@@ -27,8 +27,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Text
private readonly DrawTextProcessor definition;
- public DrawTextProcessor(DrawTextProcessor definition, Image source, Rectangle sourceRectangle)
- : base(source, sourceRectangle)
+ public DrawTextProcessor(Configuration configuration, DrawTextProcessor definition, Image source, Rectangle sourceRectangle)
+ : base(configuration, source, sourceRectangle)
{
this.definition = definition;
}
diff --git a/src/ImageSharp/Advanced/AotCompilerTools.cs b/src/ImageSharp/Advanced/AotCompilerTools.cs
index 5d172d93f7..60c1f4178a 100644
--- a/src/ImageSharp/Advanced/AotCompilerTools.cs
+++ b/src/ImageSharp/Advanced/AotCompilerTools.cs
@@ -40,7 +40,7 @@ namespace SixLabors.ImageSharp.Advanced
///
private static void SeedEverything()
{
- Seed();
+ Seed();
Seed();
Seed();
Seed();
@@ -48,8 +48,10 @@ namespace SixLabors.ImageSharp.Advanced
Seed();
Seed();
Seed();
- Seed();
- Seed();
+ Seed();
+ Seed();
+ Seed();
+ Seed();
Seed();
Seed();
Seed();
diff --git a/src/ImageSharp/Common/Extensions/EncoderExtensions.cs b/src/ImageSharp/Common/Extensions/EncoderExtensions.cs
index 59c878485d..87aaa93a9f 100644
--- a/src/ImageSharp/Common/Extensions/EncoderExtensions.cs
+++ b/src/ImageSharp/Common/Extensions/EncoderExtensions.cs
@@ -1,7 +1,7 @@
-// Copyright (c) Six Labors and contributors.
+// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
-#if !NETCOREAPP2_1
+#if !SUPPORTS_ENCODING_STRING
using System;
using System.Text;
@@ -32,4 +32,4 @@ namespace SixLabors.ImageSharp
}
}
}
-#endif
\ No newline at end of file
+#endif
diff --git a/src/ImageSharp/Common/Extensions/StreamExtensions.cs b/src/ImageSharp/Common/Extensions/StreamExtensions.cs
index 6af09b220a..cee3e24145 100644
--- a/src/ImageSharp/Common/Extensions/StreamExtensions.cs
+++ b/src/ImageSharp/Common/Extensions/StreamExtensions.cs
@@ -4,7 +4,6 @@
using System;
using System.Buffers;
using System.IO;
-
using SixLabors.ImageSharp.Memory;
using SixLabors.Memory;
@@ -15,7 +14,6 @@ namespace SixLabors.ImageSharp
///
internal static class StreamExtensions
{
-#if NETCOREAPP2_1
///
/// Writes data from a stream into the provided buffer.
///
@@ -24,23 +22,18 @@ namespace SixLabors.ImageSharp
/// The offset within the buffer to begin writing.
/// The number of bytes to write to the stream.
public static void Write(this Stream stream, Span buffer, int offset, int count)
- {
- stream.Write(buffer.Slice(offset, count));
- }
+ => stream.Write(buffer.Slice(offset, count));
///
/// Reads data from a stream into the provided buffer.
///
/// The stream.
- /// The buffer..
+ /// The buffer.
/// The offset within the buffer where the bytes are read into.
/// The number of bytes, if available, to read.
/// The actual number of bytes read.
public static int Read(this Stream stream, Span buffer, int offset, int count)
- {
- return stream.Read(buffer.Slice(offset, count));
- }
-#endif
+ => stream.Read(buffer.Slice(offset, count));
///
/// Skips the number of bytes in the given stream.
@@ -75,17 +68,39 @@ namespace SixLabors.ImageSharp
}
public static void Read(this Stream stream, IManagedByteBuffer buffer)
- {
- stream.Read(buffer.Array, 0, buffer.Length());
- }
+ => stream.Read(buffer.Array, 0, buffer.Length());
public static void Write(this Stream stream, IManagedByteBuffer buffer)
+ => stream.Write(buffer.Array, 0, buffer.Length());
+
+#if !SUPPORTS_SPAN_STREAM
+ // This is a port of the CoreFX implementation and is MIT Licensed:
+ // https://github.com/dotnet/corefx/blob/17300169760c61a90cab8d913636c1058a30a8c1/src/Common/src/CoreLib/System/IO/Stream.cs#L742
+ public static int Read(this Stream stream, Span buffer)
{
- stream.Write(buffer.Array, 0, buffer.Length());
+ // This uses ArrayPool.Shared, rather than taking a MemoryAllocator,
+ // in order to match the signature of the framework method that exists in
+ // .NET Core.
+ byte[] sharedBuffer = ArrayPool.Shared.Rent(buffer.Length);
+ try
+ {
+ int numRead = stream.Read(sharedBuffer, 0, buffer.Length);
+ if ((uint)numRead > (uint)buffer.Length)
+ {
+ throw new IOException("Stream was too long.");
+ }
+
+ new Span(sharedBuffer, 0, numRead).CopyTo(buffer);
+ return numRead;
+ }
+ finally
+ {
+ ArrayPool.Shared.Return(sharedBuffer);
+ }
}
-#if NET472 || NETSTANDARD1_3 || NETSTANDARD2_0
- // This is a port of the CoreFX implementation and is MIT Licensed: https://github.com/dotnet/coreclr/blob/c4dca1072d15bdda64c754ad1ea474b1580fa554/src/System.Private.CoreLib/shared/System/IO/Stream.cs#L770
+ // This is a port of the CoreFX implementation and is MIT Licensed:
+ // https://github.com/dotnet/corefx/blob/17300169760c61a90cab8d913636c1058a30a8c1/src/Common/src/CoreLib/System/IO/Stream.cs#L775
public static void Write(this Stream stream, ReadOnlySpan buffer)
{
// This uses ArrayPool.Shared, rather than taking a MemoryAllocator,
diff --git a/src/ImageSharp/Common/Helpers/EnumUtils.cs b/src/ImageSharp/Common/Helpers/EnumUtils.cs
new file mode 100644
index 0000000000..a98b7f84c6
--- /dev/null
+++ b/src/ImageSharp/Common/Helpers/EnumUtils.cs
@@ -0,0 +1,50 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using System;
+using System.Runtime.CompilerServices;
+
+namespace SixLabors.ImageSharp
+{
+ ///
+ /// Common utility methods for working with enums.
+ ///
+ internal static class EnumUtils
+ {
+ ///
+ /// Converts the numeric representation of the enumerated constants to an equivalent enumerated object.
+ ///
+ /// The type of enum
+ /// The value to parse
+ /// The default value to return.
+ /// The .
+ public static TEnum Parse(int value, TEnum defaultValue)
+ where TEnum : Enum
+ {
+ foreach (TEnum enumValue in Enum.GetValues(typeof(TEnum)))
+ {
+ TEnum current = enumValue;
+ if (value == Unsafe.As(ref current))
+ {
+ return enumValue;
+ }
+ }
+
+ return defaultValue;
+ }
+
+ ///
+ /// Returns a value indicating whether the given enum has a flag of the given value.
+ ///
+ /// The type of enum.
+ /// The value.
+ /// The flag.
+ /// The .
+ public static bool HasFlag(TEnum value, TEnum flag)
+ where TEnum : Enum
+ {
+ uint flagValue = Unsafe.As(ref flag);
+ return (Unsafe.As(ref value) & flagValue) == flagValue;
+ }
+ }
+}
diff --git a/src/ImageSharp/Common/Helpers/ImageMaths.cs b/src/ImageSharp/Common/Helpers/ImageMaths.cs
index c51a54a40b..122952caeb 100644
--- a/src/ImageSharp/Common/Helpers/ImageMaths.cs
+++ b/src/ImageSharp/Common/Helpers/ImageMaths.cs
@@ -38,7 +38,7 @@ namespace SixLabors.ImageSharp
/// The .
[MethodImpl(InliningOptions.ShortMethod)]
public static byte Get8BitBT709Luminance(byte r, byte g, byte b) =>
- (byte)((r * .2126F) + (g * .7152F) + (b * .0722F) + 0.5f);
+ (byte)((r * .2126F) + (g * .7152F) + (b * .0722F) + 0.5F);
///
/// Gets the luminance from the rgb components using the formula as specified by ITU-R Recommendation BT.709.
@@ -49,7 +49,7 @@ namespace SixLabors.ImageSharp
/// The .
[MethodImpl(InliningOptions.ShortMethod)]
public static ushort Get16BitBT709Luminance(ushort r, ushort g, ushort b) =>
- (ushort)((r * .2126F) + (g * .7152F) + (b * .0722F));
+ (ushort)((r * .2126F) + (g * .7152F) + (b * .0722F) + 0.5F);
///
/// Gets the luminance from the rgb components using the formula as specified by ITU-R Recommendation BT.709.
@@ -60,7 +60,7 @@ namespace SixLabors.ImageSharp
/// The .
[MethodImpl(InliningOptions.ShortMethod)]
public static ushort Get16BitBT709Luminance(float r, float g, float b) =>
- (ushort)((r * .2126F) + (g * .7152F) + (b * .0722F));
+ (ushort)((r * .2126F) + (g * .7152F) + (b * .0722F) + 0.5F);
///
/// Scales a value from a 16 bit to it's 8 bit equivalent.
diff --git a/src/ImageSharp/Common/Helpers/TolerantMath.cs b/src/ImageSharp/Common/Helpers/TolerantMath.cs
index bef7eb1610..62b5644725 100644
--- a/src/ImageSharp/Common/Helpers/TolerantMath.cs
+++ b/src/ImageSharp/Common/Helpers/TolerantMath.cs
@@ -1,4 +1,4 @@
-// Copyright (c) Six Labors and contributors.
+// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
@@ -103,4 +103,4 @@ namespace SixLabors.ImageSharp
return Math.Floor(a);
}
}
-}
\ No newline at end of file
+}
diff --git a/src/ImageSharp/Common/Helpers/UnitConverter.cs b/src/ImageSharp/Common/Helpers/UnitConverter.cs
index 75dbb032c5..9e43061702 100644
--- a/src/ImageSharp/Common/Helpers/UnitConverter.cs
+++ b/src/ImageSharp/Common/Helpers/UnitConverter.cs
@@ -1,4 +1,4 @@
-// Copyright (c) Six Labors and contributors.
+// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Runtime.CompilerServices;
@@ -86,9 +86,10 @@ namespace SixLabors.ImageSharp.Common.Helpers
[MethodImpl(InliningOptions.ShortMethod)]
public static PixelResolutionUnit ExifProfileToResolutionUnit(ExifProfile profile)
{
- return profile.TryGetValue(ExifTag.ResolutionUnit, out ExifValue resolution)
- ? (PixelResolutionUnit)(byte)(((ushort)resolution.Value) - 1) // EXIF is 1, 2, 3
- : default;
+ IExifValue resolution = profile.GetValue(ExifTag.ResolutionUnit);
+
+ // EXIF is 1, 2, 3 so we minus "1" off the result.
+ return resolution is null ? default : (PixelResolutionUnit)(byte)(resolution.Value - 1);
}
}
}
diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs
index 03e082cce0..eda5f1f784 100644
--- a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs
+++ b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs
@@ -445,11 +445,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// Keeps track of rows, which have undefined pixels.
private void UncompressRle4(int w, Span buffer, Span undefinedPixels, Span rowsWithUndefinedPixels)
{
-#if NETCOREAPP2_1
Span cmd = stackalloc byte[2];
-#else
- var cmd = new byte[2];
-#endif
int count = 0;
while (count < buffer.Length)
@@ -556,11 +552,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// Keeps track of rows, which have undefined pixels.
private void UncompressRle8(int w, Span buffer, Span undefinedPixels, Span rowsWithUndefinedPixels)
{
-#if NETCOREAPP2_1
Span cmd = stackalloc byte[2];
-#else
- var cmd = new byte[2];
-#endif
int count = 0;
while (count < buffer.Length)
@@ -639,11 +631,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// Keeps track of rows, which have undefined pixels.
private void UncompressRle24(int w, Span buffer, Span undefinedPixels, Span rowsWithUndefinedPixels)
{
-#if NETCOREAPP2_1
Span cmd = stackalloc byte[2];
-#else
- var cmd = new byte[2];
-#endif
int uncompressedPixels = 0;
while (uncompressedPixels < buffer.Length)
@@ -1213,11 +1201,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
///
private void ReadInfoHeader()
{
-#if NETCOREAPP2_1
Span buffer = stackalloc byte[BmpInfoHeader.MaxHeaderSize];
-#else
- var buffer = new byte[BmpInfoHeader.MaxHeaderSize];
-#endif
// Read the header size.
this.stream.Read(buffer, 0, BmpInfoHeader.HeaderSizeSize);
@@ -1321,7 +1305,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
this.metadata = meta;
short bitsPerPixel = this.infoHeader.BitsPerPixel;
- this.bmpMetadata = this.metadata.GetFormatMetadata(BmpFormat.Instance);
+ this.bmpMetadata = this.metadata.GetBmpMetadata();
this.bmpMetadata.InfoHeaderType = infoHeaderType;
// We can only encode at these bit rates so far (1 bit and 4 bit are still missing).
@@ -1339,11 +1323,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
///
private void ReadFileHeader()
{
-#if NETCOREAPP2_1
Span buffer = stackalloc byte[BmpFileHeader.Size];
-#else
- var buffer = new byte[BmpFileHeader.Size];
-#endif
this.stream.Read(buffer, 0, BmpFileHeader.Size);
short fileTypeMarker = BinaryPrimitives.ReadInt16LittleEndian(buffer);
diff --git a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs
index a5e1ee5dbc..41be71d2b3 100644
--- a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs
+++ b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs
@@ -105,7 +105,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
this.configuration = image.GetConfiguration();
ImageMetadata metadata = image.Metadata;
- BmpMetadata bmpMetadata = metadata.GetFormatMetadata(BmpFormat.Instance);
+ BmpMetadata bmpMetadata = metadata.GetBmpMetadata();
this.bitsPerPixel = this.bitsPerPixel ?? bmpMetadata.BitsPerPixel;
short bpp = (short)this.bitsPerPixel;
@@ -173,11 +173,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
reserved: 0,
offset: BmpFileHeader.Size + infoHeaderSize + colorPaletteSize);
-#if NETCOREAPP2_1
Span buffer = stackalloc byte[infoHeaderSize];
-#else
- var buffer = new byte[infoHeaderSize];
-#endif
fileHeader.WriteTo(buffer);
stream.Write(buffer, 0, BmpFileHeader.Size);
@@ -315,11 +311,11 @@ namespace SixLabors.ImageSharp.Formats.Bmp
private void Write8Bit(Stream stream, ImageFrame image)
where TPixel : struct, IPixel
{
- bool isGray8 = typeof(TPixel) == typeof(Gray8);
+ bool isL8 = typeof(TPixel) == typeof(L8);
using (IMemoryOwner colorPaletteBuffer = this.memoryAllocator.AllocateManagedByteBuffer(ColorPaletteSize8Bit, AllocationOptions.Clean))
{
Span colorPalette = colorPaletteBuffer.GetSpan();
- if (isGray8)
+ if (isL8)
{
this.Write8BitGray(stream, image, colorPalette);
}
diff --git a/src/ImageSharp/Formats/Bmp/BmpMetaData.cs b/src/ImageSharp/Formats/Bmp/BmpMetadata.cs
similarity index 100%
rename from src/ImageSharp/Formats/Bmp/BmpMetaData.cs
rename to src/ImageSharp/Formats/Bmp/BmpMetadata.cs
diff --git a/src/ImageSharp/Formats/Bmp/MetadataExtensions.cs b/src/ImageSharp/Formats/Bmp/MetadataExtensions.cs
new file mode 100644
index 0000000000..0315b3c768
--- /dev/null
+++ b/src/ImageSharp/Formats/Bmp/MetadataExtensions.cs
@@ -0,0 +1,21 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using SixLabors.ImageSharp.Formats.Bmp;
+using SixLabors.ImageSharp.Metadata;
+
+namespace SixLabors.ImageSharp
+{
+ ///
+ /// Extension methods for the type.
+ ///
+ public static partial class MetadataExtensions
+ {
+ ///
+ /// Gets the bmp format specific metadata for the image.
+ ///
+ /// The metadata this method extends.
+ /// The .
+ public static BmpMetadata GetBmpMetadata(this ImageMetadata metadata) => metadata.GetFormatMetadata(BmpFormat.Instance);
+ }
+}
diff --git a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs
index c11e93a93a..b4d92b15dd 100644
--- a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs
+++ b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs
@@ -553,7 +553,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void SetFrameMetadata(ImageFrameMetadata meta)
{
- GifFrameMetadata gifMeta = meta.GetFormatMetadata(GifFormat.Instance);
+ GifFrameMetadata gifMeta = meta.GetGifMetadata();
if (this.graphicsControlExtension.DelayTime > 0)
{
gifMeta.FrameDelay = this.graphicsControlExtension.DelayTime;
@@ -615,7 +615,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
}
this.metadata = meta;
- this.gifMetadata = meta.GetFormatMetadata(GifFormat.Instance);
+ this.gifMetadata = meta.GetGifMetadata();
this.gifMetadata.ColorTableMode = this.logicalScreenDescriptor.GlobalColorTableFlag
? GifColorTableMode.Global
: GifColorTableMode.Local;
diff --git a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs
index c8fc448125..f8b40306b8 100644
--- a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs
+++ b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs
@@ -78,7 +78,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
this.configuration = image.GetConfiguration();
ImageMetadata metadata = image.Metadata;
- GifMetadata gifMetadata = metadata.GetFormatMetadata(GifFormat.Instance);
+ GifMetadata gifMetadata = metadata.GetGifMetadata();
this.colorTableMode = this.colorTableMode ?? gifMetadata.ColorTableMode;
bool useGlobalTable = this.colorTableMode == GifColorTableMode.Global;
@@ -136,7 +136,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
{
ImageFrame frame = image.Frames[i];
ImageFrameMetadata metadata = frame.Metadata;
- GifFrameMetadata frameMetadata = metadata.GetFormatMetadata(GifFormat.Instance);
+ GifFrameMetadata frameMetadata = metadata.GetGifMetadata();
this.WriteGraphicalControlExtension(frameMetadata, transparencyIndex, stream);
this.WriteImageDescriptor(frame, false, stream);
@@ -166,7 +166,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
foreach (ImageFrame frame in image.Frames)
{
ImageFrameMetadata metadata = frame.Metadata;
- GifFrameMetadata frameMetadata = metadata.GetFormatMetadata(GifFormat.Instance);
+ GifFrameMetadata frameMetadata = metadata.GetGifMetadata();
if (quantized is null)
{
// Allow each frame to be encoded at whatever color depth the frame designates if set.
diff --git a/src/ImageSharp/Formats/Gif/GifFrameMetaData.cs b/src/ImageSharp/Formats/Gif/GifFrameMetadata.cs
similarity index 100%
rename from src/ImageSharp/Formats/Gif/GifFrameMetaData.cs
rename to src/ImageSharp/Formats/Gif/GifFrameMetadata.cs
diff --git a/src/ImageSharp/Formats/Gif/GifMetaData.cs b/src/ImageSharp/Formats/Gif/GifMetadata.cs
similarity index 100%
rename from src/ImageSharp/Formats/Gif/GifMetaData.cs
rename to src/ImageSharp/Formats/Gif/GifMetadata.cs
diff --git a/src/ImageSharp/Formats/Gif/LzwDecoder.cs b/src/ImageSharp/Formats/Gif/LzwDecoder.cs
index af390e9545..2ae8a834ef 100644
--- a/src/ImageSharp/Formats/Gif/LzwDecoder.cs
+++ b/src/ImageSharp/Formats/Gif/LzwDecoder.cs
@@ -113,11 +113,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
Unsafe.Add(ref suffixRef, code) = (byte)code;
}
-#if NETCOREAPP2_1
Span buffer = stackalloc byte[255];
-#else
- var buffer = new byte[255];
-#endif
while (xyz < length)
{
@@ -227,11 +223,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// The .
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
-#if NETCOREAPP2_1
private int ReadBlock(Span buffer)
-#else
- private int ReadBlock(byte[] buffer)
-#endif
{
int bufferSize = this.stream.ReadByte();
diff --git a/src/ImageSharp/Formats/Gif/MetadataExtensions.cs b/src/ImageSharp/Formats/Gif/MetadataExtensions.cs
new file mode 100644
index 0000000000..7c432d26fe
--- /dev/null
+++ b/src/ImageSharp/Formats/Gif/MetadataExtensions.cs
@@ -0,0 +1,28 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using SixLabors.ImageSharp.Formats.Gif;
+using SixLabors.ImageSharp.Metadata;
+
+namespace SixLabors.ImageSharp
+{
+ ///
+ /// Extension methods for the type.
+ ///
+ public static partial class MetadataExtensions
+ {
+ ///
+ /// Gets the gif format specific metadata for the image.
+ ///
+ /// The metadata this method extends.
+ /// The .
+ public static GifMetadata GetGifMetadata(this ImageMetadata metadata) => metadata.GetFormatMetadata(GifFormat.Instance);
+
+ ///
+ /// Gets the gif format specific metadata for the image frame.
+ ///
+ /// The metadata this method extends.
+ /// The .
+ public static GifFrameMetadata GetGifMetadata(this ImageFrameMetadata metadata) => metadata.GetFormatMetadata(GifFormat.Instance);
+ }
+}
diff --git a/src/ImageSharp/Formats/IImageDecoder.cs b/src/ImageSharp/Formats/IImageDecoder.cs
index 8dafdac795..e8e84de7d8 100644
--- a/src/ImageSharp/Formats/IImageDecoder.cs
+++ b/src/ImageSharp/Formats/IImageDecoder.cs
@@ -1,4 +1,4 @@
-// Copyright (c) Six Labors and contributors.
+// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.IO;
@@ -17,17 +17,16 @@ namespace SixLabors.ImageSharp.Formats
/// The pixel format.
/// The configuration for the image.
/// The containing image data.
- /// The decoded image of a given pixel type.
+ /// The .
Image Decode(Configuration configuration, Stream stream)
where TPixel : struct, IPixel;
///
/// Decodes the image from the specified stream to an .
- /// The decoder is free to choose the pixel type.
///
/// The configuration for the image.
/// The containing image data.
- /// The decoded image of a pixel type chosen by the decoder.
+ /// The .
Image Decode(Configuration configuration, Stream stream);
}
}
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponent.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponent.cs
index 5353303947..9fa4ce6d85 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponent.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegComponent.cs
@@ -1,4 +1,4 @@
-// Copyright (c) Six Labors and contributors.
+// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
@@ -22,11 +22,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
this.Frame = frame;
this.Id = id;
- // Valid sampling factors are 1..2
- if (horizontalFactor == 0
- || verticalFactor == 0
- || horizontalFactor > 2
- || verticalFactor > 2)
+ // Validate sampling factors.
+ if (horizontalFactor == 0 || verticalFactor == 0)
{
JpegThrowHelper.ThrowBadSampling();
}
@@ -138,4 +135,4 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
this.SpectralBlocks = this.memoryAllocator.Allocate2D(width, height, AllocationOptions.Clean);
}
}
-}
\ No newline at end of file
+}
diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs
index a1bf048521..4e1c0c1beb 100644
--- a/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs
+++ b/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs
@@ -1,4 +1,4 @@
-// Copyright (c) Six Labors and contributors.
+// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.IO;
@@ -28,6 +28,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
}
}
+ ///
+ public Image Decode(Configuration configuration, Stream stream)
+ => this.Decode(configuration, stream);
+
///
public IImageInfo Identify(Configuration configuration, Stream stream)
{
@@ -38,8 +42,5 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
return decoder.Identify(stream);
}
}
-
- ///
- public Image Decode(Configuration configuration, Stream stream) => this.Decode(configuration, stream);
}
-}
\ No newline at end of file
+}
diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs
index c15fe5e274..62765a8843 100644
--- a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs
+++ b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs
@@ -465,24 +465,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
}
}
- private double GetExifResolutionValue(ExifTag tag)
+ private double GetExifResolutionValue(ExifTag tag)
{
- if (!this.Metadata.ExifProfile.TryGetValue(tag, out ExifValue exifValue))
- {
- return 0;
- }
+ IExifValue resolution = this.Metadata.ExifProfile.GetValue(tag);
- switch (exifValue.DataType)
- {
- case ExifDataType.Rational:
- return ((Rational)exifValue.Value).ToDouble();
- case ExifDataType.Long:
- return (uint)exifValue.Value;
- case ExifDataType.DoubleFloat:
- return (double)exifValue.Value;
- default:
- return 0;
- }
+ return resolution is null ? 0 : resolution.Value.ToDouble();
}
///
@@ -649,48 +636,51 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
switch (quantizationTableSpec >> 4)
{
case 0:
+ {
+ // 8 bit values
+ if (remaining < 64)
{
- // 8 bit values
- if (remaining < 64)
- {
- done = true;
- break;
- }
+ done = true;
+ break;
+ }
- this.InputStream.Read(this.temp, 0, 64);
- remaining -= 64;
+ this.InputStream.Read(this.temp, 0, 64);
+ remaining -= 64;
- ref Block8x8F table = ref this.QuantizationTables[tableIndex];
- for (int j = 0; j < 64; j++)
- {
- table[j] = this.temp[j];
- }
+ ref Block8x8F table = ref this.QuantizationTables[tableIndex];
+ for (int j = 0; j < 64; j++)
+ {
+ table[j] = this.temp[j];
}
+ }
- break;
+ break;
case 1:
+ {
+ // 16 bit values
+ if (remaining < 128)
{
- // 16 bit values
- if (remaining < 128)
- {
- done = true;
- break;
- }
+ done = true;
+ break;
+ }
- this.InputStream.Read(this.temp, 0, 128);
- remaining -= 128;
+ this.InputStream.Read(this.temp, 0, 128);
+ remaining -= 128;
- ref Block8x8F table = ref this.QuantizationTables[tableIndex];
- for (int j = 0; j < 64; j++)
- {
- table[j] = (this.temp[2 * j] << 8) | this.temp[(2 * j) + 1];
- }
+ ref Block8x8F table = ref this.QuantizationTables[tableIndex];
+ for (int j = 0; j < 64; j++)
+ {
+ table[j] = (this.temp[2 * j] << 8) | this.temp[(2 * j) + 1];
}
+ }
+
+ break;
- break;
default:
+ {
JpegThrowHelper.ThrowBadQuantizationTable();
break;
+ }
}
if (done)
@@ -774,7 +764,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
for (int i = 0; i < this.ComponentCount; i++)
{
byte hv = this.temp[index + 1];
- int h = hv >> 4;
+ int h = (hv >> 4) & 15;
int v = hv & 15;
if (maxH < h)
diff --git a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs
index 0b9eeb609b..cd3c19aa3e 100644
--- a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs
+++ b/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs
@@ -1,4 +1,4 @@
-// Copyright (c) Six Labors and contributors.
+// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
@@ -206,7 +206,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
ImageMetadata metadata = image.Metadata;
// System.Drawing produces identical output for jpegs with a quality parameter of 0 and 1.
- int qlty = (this.quality ?? metadata.GetFormatMetadata(JpegFormat.Instance).Quality).Clamp(1, 100);
+ int qlty = (this.quality ?? metadata.GetJpegMetadata().Quality).Clamp(1, 100);
this.subsample = this.subsample ?? (qlty >= 91 ? JpegSubsample.Ratio444 : JpegSubsample.Ratio420);
// Convert from a quality rating to a scaling factor.
@@ -647,7 +647,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
///
private void WriteExifProfile(ExifProfile exifProfile)
{
- if (exifProfile is null)
+ if (exifProfile is null || exifProfile.Values.Count == 0)
{
return;
}
@@ -655,9 +655,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
const int MaxBytesApp1 = 65533; // 64k - 2 padding bytes
const int MaxBytesWithExifId = 65527; // Max - 6 bytes for EXIF header.
- byte[] data = exifProfile?.ToByteArray();
+ byte[] data = exifProfile.ToByteArray();
- if (data is null || data.Length == 0)
+ if (data.Length == 0)
{
return;
}
@@ -998,4 +998,4 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
this.outputStream.Write(this.buffer, 0, 4);
}
}
-}
\ No newline at end of file
+}
diff --git a/src/ImageSharp/Formats/Jpeg/JpegMetaData.cs b/src/ImageSharp/Formats/Jpeg/JpegMetadata.cs
similarity index 100%
rename from src/ImageSharp/Formats/Jpeg/JpegMetaData.cs
rename to src/ImageSharp/Formats/Jpeg/JpegMetadata.cs
diff --git a/src/ImageSharp/Formats/Jpeg/MetadataExtensions.cs b/src/ImageSharp/Formats/Jpeg/MetadataExtensions.cs
new file mode 100644
index 0000000000..53a9d2a35d
--- /dev/null
+++ b/src/ImageSharp/Formats/Jpeg/MetadataExtensions.cs
@@ -0,0 +1,21 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using SixLabors.ImageSharp.Formats.Jpeg;
+using SixLabors.ImageSharp.Metadata;
+
+namespace SixLabors.ImageSharp
+{
+ ///
+ /// Extension methods for the type.
+ ///
+ public static partial class MetadataExtensions
+ {
+ ///
+ /// Gets the jpeg format specific metadata for the image.
+ ///
+ /// The metadata this method extends.
+ /// The .
+ public static JpegMetadata GetJpegMetadata(this ImageMetadata metadata) => metadata.GetFormatMetadata(JpegFormat.Instance);
+ }
+}
diff --git a/src/ImageSharp/Formats/Png/MetadataExtensions.cs b/src/ImageSharp/Formats/Png/MetadataExtensions.cs
new file mode 100644
index 0000000000..762a6c40cb
--- /dev/null
+++ b/src/ImageSharp/Formats/Png/MetadataExtensions.cs
@@ -0,0 +1,21 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using SixLabors.ImageSharp.Formats.Png;
+using SixLabors.ImageSharp.Metadata;
+
+namespace SixLabors.ImageSharp
+{
+ ///
+ /// Extension methods for the type.
+ ///
+ public static partial class MetadataExtensions
+ {
+ ///
+ /// Gets the png format specific metadata for the image.
+ ///
+ /// The metadata this method extends.
+ /// The .
+ public static PngMetadata GetPngMetadata(this ImageMetadata metadata) => metadata.GetFormatMetadata(PngFormat.Instance);
+ }
+}
diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs
index b24a5eabda..0161797128 100644
--- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs
+++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs
@@ -149,7 +149,7 @@ namespace SixLabors.ImageSharp.Formats.Png
where TPixel : struct, IPixel
{
var metadata = new ImageMetadata();
- PngMetadata pngMetadata = metadata.GetFormatMetadata(PngFormat.Instance);
+ PngMetadata pngMetadata = metadata.GetPngMetadata();
this.currentStream = stream;
this.currentStream.Skip(8);
Image image = null;
@@ -240,7 +240,7 @@ namespace SixLabors.ImageSharp.Formats.Png
public IImageInfo Identify(Stream stream)
{
var metadata = new ImageMetadata();
- PngMetadata pngMetadata = metadata.GetFormatMetadata(PngFormat.Instance);
+ PngMetadata pngMetadata = metadata.GetPngMetadata();
this.currentStream = stream;
this.currentStream.Skip(8);
try
@@ -658,8 +658,8 @@ namespace SixLabors.ImageSharp.Formats.Png
scanlineSpan,
rowSpan,
pngMetadata.HasTransparency,
- pngMetadata.TransparentGray16.GetValueOrDefault(),
- pngMetadata.TransparentGray8.GetValueOrDefault());
+ pngMetadata.TransparentL16.GetValueOrDefault(),
+ pngMetadata.TransparentL8.GetValueOrDefault());
break;
@@ -742,8 +742,8 @@ namespace SixLabors.ImageSharp.Formats.Png
pixelOffset,
increment,
pngMetadata.HasTransparency,
- pngMetadata.TransparentGray16.GetValueOrDefault(),
- pngMetadata.TransparentGray8.GetValueOrDefault());
+ pngMetadata.TransparentL16.GetValueOrDefault(),
+ pngMetadata.TransparentL8.GetValueOrDefault());
break;
@@ -837,11 +837,11 @@ namespace SixLabors.ImageSharp.Formats.Png
{
if (this.header.BitDepth == 16)
{
- pngMetadata.TransparentGray16 = new Gray16(BinaryPrimitives.ReadUInt16LittleEndian(alpha.Slice(0, 2)));
+ pngMetadata.TransparentL16 = new L16(BinaryPrimitives.ReadUInt16LittleEndian(alpha.Slice(0, 2)));
}
else
{
- pngMetadata.TransparentGray8 = new Gray8(ReadByteLittleEndian(alpha, 0));
+ pngMetadata.TransparentL8 = new L8(ReadByteLittleEndian(alpha, 0));
}
pngMetadata.HasTransparency = true;
diff --git a/src/ImageSharp/Formats/Png/PngEncoderCore.cs b/src/ImageSharp/Formats/Png/PngEncoderCore.cs
index 19c6af27f7..22e3f252df 100644
--- a/src/ImageSharp/Formats/Png/PngEncoderCore.cs
+++ b/src/ImageSharp/Formats/Png/PngEncoderCore.cs
@@ -199,16 +199,16 @@ namespace SixLabors.ImageSharp.Formats.Png
if (this.use16Bit)
{
// 16 bit grayscale
- using (IMemoryOwner luminanceBuffer = this.memoryAllocator.Allocate(rowSpan.Length))
+ using (IMemoryOwner luminanceBuffer = this.memoryAllocator.Allocate(rowSpan.Length))
{
- Span luminanceSpan = luminanceBuffer.GetSpan();
- ref Gray16 luminanceRef = ref MemoryMarshal.GetReference(luminanceSpan);
- PixelOperations.Instance.ToGray16(this.configuration, rowSpan, luminanceSpan);
+ Span luminanceSpan = luminanceBuffer.GetSpan();
+ ref L16 luminanceRef = ref MemoryMarshal.GetReference(luminanceSpan);
+ PixelOperations.Instance.ToL16(this.configuration, rowSpan, luminanceSpan);
// Can't map directly to byte array as it's big-endian.
for (int x = 0, o = 0; x < luminanceSpan.Length; x++, o += 2)
{
- Gray16 luminance = Unsafe.Add(ref luminanceRef, x);
+ L16 luminance = Unsafe.Add(ref luminanceRef, x);
BinaryPrimitives.WriteUInt16BigEndian(rawScanlineSpan.Slice(o, 2), luminance.PackedValue);
}
}
@@ -218,7 +218,7 @@ namespace SixLabors.ImageSharp.Formats.Png
if (this.bitDepth == 8)
{
// 8 bit grayscale
- PixelOperations.Instance.ToGray8Bytes(
+ PixelOperations.Instance.ToL8Bytes(
this.configuration,
rowSpan,
rawScanlineSpan,
@@ -235,7 +235,7 @@ namespace SixLabors.ImageSharp.Formats.Png
Span tempSpan = temp.GetSpan();
// We need to first create an array of luminance bytes then scale them down to the correct bit depth.
- PixelOperations.Instance.ToGray8Bytes(
+ PixelOperations.Instance.ToL8Bytes(
this.configuration,
rowSpan,
tempSpan,
@@ -622,11 +622,13 @@ namespace SixLabors.ImageSharp.Formats.Png
/// The image metadata.
private void WriteExifChunk(Stream stream, ImageMetadata meta)
{
- if (meta.ExifProfile?.Values.Count > 0)
+ if (meta.ExifProfile is null || meta.ExifProfile.Values.Count == 0)
{
- meta.SyncProfiles();
- this.WriteChunk(stream, PngChunkType.Exif, meta.ExifProfile.ToByteArray());
+ return;
}
+
+ meta.SyncProfiles();
+ this.WriteChunk(stream, PngChunkType.Exif, meta.ExifProfile.ToByteArray());
}
///
@@ -761,15 +763,15 @@ namespace SixLabors.ImageSharp.Formats.Png
}
else if (pngMetadata.ColorType == PngColorType.Grayscale)
{
- if (pngMetadata.TransparentGray16.HasValue && this.use16Bit)
+ if (pngMetadata.TransparentL16.HasValue && this.use16Bit)
{
- BinaryPrimitives.WriteUInt16LittleEndian(alpha, pngMetadata.TransparentGray16.Value.PackedValue);
+ BinaryPrimitives.WriteUInt16LittleEndian(alpha, pngMetadata.TransparentL16.Value.PackedValue);
this.WriteChunk(stream, PngChunkType.Transparency, this.chunkDataBuffer, 0, 2);
}
- else if (pngMetadata.TransparentGray8.HasValue)
+ else if (pngMetadata.TransparentL8.HasValue)
{
alpha.Clear();
- alpha[1] = pngMetadata.TransparentGray8.Value.PackedValue;
+ alpha[1] = pngMetadata.TransparentL8.Value.PackedValue;
this.WriteChunk(stream, PngChunkType.Transparency, this.chunkDataBuffer, 0, 2);
}
}
diff --git a/src/ImageSharp/Formats/Png/PngMetaData.cs b/src/ImageSharp/Formats/Png/PngMetadata.cs
similarity index 94%
rename from src/ImageSharp/Formats/Png/PngMetaData.cs
rename to src/ImageSharp/Formats/Png/PngMetadata.cs
index ec8779a59a..87a2080f0f 100644
--- a/src/ImageSharp/Formats/Png/PngMetaData.cs
+++ b/src/ImageSharp/Formats/Png/PngMetadata.cs
@@ -29,8 +29,8 @@ namespace SixLabors.ImageSharp.Formats.Png
this.Gamma = other.Gamma;
this.InterlaceMethod = other.InterlaceMethod;
this.HasTransparency = other.HasTransparency;
- this.TransparentGray8 = other.TransparentGray8;
- this.TransparentGray16 = other.TransparentGray16;
+ this.TransparentL8 = other.TransparentL8;
+ this.TransparentL16 = other.TransparentL16;
this.TransparentRgb24 = other.TransparentRgb24;
this.TransparentRgb48 = other.TransparentRgb48;
@@ -77,13 +77,13 @@ namespace SixLabors.ImageSharp.Formats.Png
/// Gets or sets the 8 bit grayscale transparent color.
/// This represents any color in an 8 bit grayscale encoded png that should be transparent.
///
- public Gray8? TransparentGray8 { get; set; }
+ public L8? TransparentL8 { get; set; }
///
/// Gets or sets the 16 bit grayscale transparent color.
/// This represents any color in a 16 bit grayscale encoded png that should be transparent.
///
- public Gray16? TransparentGray16 { get; set; }
+ public L16? TransparentL16 { get; set; }
///
/// Gets or sets a value indicating whether the image contains a transparency chunk and markers were decoded.
diff --git a/src/ImageSharp/Formats/Png/PngScanlineProcessor.cs b/src/ImageSharp/Formats/Png/PngScanlineProcessor.cs
index c23694951d..5f9d1de9c5 100644
--- a/src/ImageSharp/Formats/Png/PngScanlineProcessor.cs
+++ b/src/ImageSharp/Formats/Png/PngScanlineProcessor.cs
@@ -1,4 +1,4 @@
-// Copyright (c) Six Labors and contributors.
+// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
@@ -20,8 +20,8 @@ namespace SixLabors.ImageSharp.Formats.Png
ReadOnlySpan scanlineSpan,
Span rowSpan,
bool hasTrans,
- Gray16 luminance16Trans,
- Gray8 luminanceTrans)
+ L16 luminance16Trans,
+ L8 luminanceTrans)
where TPixel : struct, IPixel
{
TPixel pixel = default;
@@ -36,7 +36,7 @@ namespace SixLabors.ImageSharp.Formats.Png
for (int x = 0, o = 0; x < header.Width; x++, o += 2)
{
ushort luminance = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, 2));
- pixel.FromGray16(new Gray16(luminance));
+ pixel.FromL16(Unsafe.As(ref luminance));
Unsafe.Add(ref rowSpanRef, x) = pixel;
}
}
@@ -45,7 +45,7 @@ namespace SixLabors.ImageSharp.Formats.Png
for (int x = 0; x < header.Width; x++)
{
byte luminance = (byte)(Unsafe.Add(ref scanlineSpanRef, x) * scaleFactor);
- pixel.FromGray8(new Gray8(luminance));
+ pixel.FromL8(Unsafe.As(ref luminance));
Unsafe.Add(ref rowSpanRef, x) = pixel;
}
}
@@ -55,32 +55,28 @@ namespace SixLabors.ImageSharp.Formats.Png
if (header.BitDepth == 16)
{
- Rgba64 rgba64 = default;
+ La32 source = default;
for (int x = 0, o = 0; x < header.Width; x++, o += 2)
{
ushort luminance = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, 2));
- rgba64.R = luminance;
- rgba64.G = luminance;
- rgba64.B = luminance;
- rgba64.A = luminance.Equals(luminance16Trans.PackedValue) ? ushort.MinValue : ushort.MaxValue;
+ source.L = luminance;
+ source.A = luminance.Equals(luminance16Trans.PackedValue) ? ushort.MinValue : ushort.MaxValue;
- pixel.FromRgba64(rgba64);
+ pixel.FromLa32(source);
Unsafe.Add(ref rowSpanRef, x) = pixel;
}
}
else
{
+ La16 source = default;
byte scaledLuminanceTrans = (byte)(luminanceTrans.PackedValue * scaleFactor);
- Rgba32 rgba32 = default;
for (int x = 0; x < header.Width; x++)
{
byte luminance = (byte)(Unsafe.Add(ref scanlineSpanRef, x) * scaleFactor);
- rgba32.R = luminance;
- rgba32.G = luminance;
- rgba32.B = luminance;
- rgba32.A = luminance.Equals(scaledLuminanceTrans) ? byte.MinValue : byte.MaxValue;
+ source.L = luminance;
+ source.A = luminance.Equals(scaledLuminanceTrans) ? byte.MinValue : byte.MaxValue;
- pixel.FromRgba32(rgba32);
+ pixel.FromLa16(source);
Unsafe.Add(ref rowSpanRef, x) = pixel;
}
}
@@ -93,8 +89,8 @@ namespace SixLabors.ImageSharp.Formats.Png
int pixelOffset,
int increment,
bool hasTrans,
- Gray16 luminance16Trans,
- Gray8 luminanceTrans)
+ L16 luminance16Trans,
+ L8 luminanceTrans)
where TPixel : struct, IPixel
{
TPixel pixel = default;
@@ -109,7 +105,7 @@ namespace SixLabors.ImageSharp.Formats.Png
for (int x = pixelOffset, o = 0; x < header.Width; x += increment, o += 2)
{
ushort luminance = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, 2));
- pixel.FromGray16(new Gray16(luminance));
+ pixel.FromL16(Unsafe.As(ref luminance));
Unsafe.Add(ref rowSpanRef, x) = pixel;
}
}
@@ -118,7 +114,7 @@ namespace SixLabors.ImageSharp.Formats.Png
for (int x = pixelOffset, o = 0; x < header.Width; x += increment, o++)
{
byte luminance = (byte)(Unsafe.Add(ref scanlineSpanRef, o) * scaleFactor);
- pixel.FromGray8(new Gray8(luminance));
+ pixel.FromL8(Unsafe.As(ref luminance));
Unsafe.Add(ref rowSpanRef, x) = pixel;
}
}
@@ -128,32 +124,28 @@ namespace SixLabors.ImageSharp.Formats.Png
if (header.BitDepth == 16)
{
- Rgba64 rgba64 = default;
+ La32 source = default;
for (int x = pixelOffset, o = 0; x < header.Width; x += increment, o += 2)
{
ushort luminance = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, 2));
- rgba64.R = luminance;
- rgba64.G = luminance;
- rgba64.B = luminance;
- rgba64.A = luminance.Equals(luminance16Trans.PackedValue) ? ushort.MinValue : ushort.MaxValue;
+ source.L = luminance;
+ source.A = luminance.Equals(luminance16Trans.PackedValue) ? ushort.MinValue : ushort.MaxValue;
- pixel.FromRgba64(rgba64);
+ pixel.FromLa32(source);
Unsafe.Add(ref rowSpanRef, x) = pixel;
}
}
else
{
+ La16 source = default;
byte scaledLuminanceTrans = (byte)(luminanceTrans.PackedValue * scaleFactor);
- Rgba32 rgba32 = default;
for (int x = pixelOffset, o = 0; x < header.Width; x += increment, o++)
{
byte luminance = (byte)(Unsafe.Add(ref scanlineSpanRef, o) * scaleFactor);
- rgba32.R = luminance;
- rgba32.G = luminance;
- rgba32.B = luminance;
- rgba32.A = luminance.Equals(scaledLuminanceTrans) ? byte.MinValue : byte.MaxValue;
+ source.L = luminance;
+ source.A = luminance.Equals(scaledLuminanceTrans) ? byte.MinValue : byte.MaxValue;
- pixel.FromRgba32(rgba32);
+ pixel.FromLa16(source);
Unsafe.Add(ref rowSpanRef, x) = pixel;
}
}
@@ -173,35 +165,26 @@ namespace SixLabors.ImageSharp.Formats.Png
if (header.BitDepth == 16)
{
- Rgba64 rgba64 = default;
+ La32 source = default;
for (int x = 0, o = 0; x < header.Width; x++, o += 4)
{
- ushort luminance = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, 2));
- ushort alpha = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + 2, 2));
- rgba64.R = luminance;
- rgba64.G = luminance;
- rgba64.B = luminance;
- rgba64.A = alpha;
+ source.L = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, 2));
+ source.A = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + 2, 2));
- pixel.FromRgba64(rgba64);
+ pixel.FromLa32(source);
Unsafe.Add(ref rowSpanRef, x) = pixel;
}
}
else
{
- Rgba32 rgba32 = default;
+ La16 source = default;
for (int x = 0; x < header.Width; x++)
{
int offset = x * bytesPerPixel;
- byte luminance = Unsafe.Add(ref scanlineSpanRef, offset);
- byte alpha = Unsafe.Add(ref scanlineSpanRef, offset + bytesPerSample);
-
- rgba32.R = luminance;
- rgba32.G = luminance;
- rgba32.B = luminance;
- rgba32.A = alpha;
+ source.L = Unsafe.Add(ref scanlineSpanRef, offset);
+ source.A = Unsafe.Add(ref scanlineSpanRef, offset + bytesPerSample);
- pixel.FromRgba32(rgba32);
+ pixel.FromLa16(source);
Unsafe.Add(ref rowSpanRef, x) = pixel;
}
}
@@ -223,34 +206,26 @@ namespace SixLabors.ImageSharp.Formats.Png
if (header.BitDepth == 16)
{
- Rgba64 rgba64 = default;
+ La32 source = default;
for (int x = pixelOffset, o = 0; x < header.Width; x += increment, o += 4)
{
- ushort luminance = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, 2));
- ushort alpha = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + 2, 2));
- rgba64.R = luminance;
- rgba64.G = luminance;
- rgba64.B = luminance;
- rgba64.A = alpha;
+ source.L = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o, 2));
+ source.A = BinaryPrimitives.ReadUInt16BigEndian(scanlineSpan.Slice(o + 2, 2));
- pixel.FromRgba64(rgba64);
+ pixel.FromLa32(source);
Unsafe.Add(ref rowSpanRef, x) = pixel;
}
}
else
{
- Rgba32 rgba32 = default;
int offset = 0;
+ La16 source = default;
for (int x = pixelOffset; x < header.Width; x += increment)
{
- byte luminance = Unsafe.Add(ref scanlineSpanRef, offset);
- byte alpha = Unsafe.Add(ref scanlineSpanRef, offset + bytesPerSample);
- rgba32.R = luminance;
- rgba32.G = luminance;
- rgba32.B = luminance;
- rgba32.A = alpha;
+ source.L = Unsafe.Add(ref scanlineSpanRef, offset);
+ source.A = Unsafe.Add(ref scanlineSpanRef, offset + bytesPerSample);
- pixel.FromRgba32(rgba32);
+ pixel.FromLa16(source);
Unsafe.Add(ref rowSpanRef, x) = pixel;
offset += bytesPerPixel;
}
@@ -403,12 +378,12 @@ namespace SixLabors.ImageSharp.Formats.Png
}
else
{
+ Rgba32 rgba32 = default;
ReadOnlySpan rgb24Span = MemoryMarshal.Cast(scanlineSpan);
ref Rgb24 rgb24SpanRef = ref MemoryMarshal.GetReference(rgb24Span);
for (int x = 0; x < header.Width; x++)
{
ref readonly Rgb24 rgb24 = ref Unsafe.Add(ref rgb24SpanRef, x);
- Rgba32 rgba32 = default;
rgba32.Rgb = rgb24;
rgba32.A = rgb24.Equals(rgb24Trans) ? byte.MinValue : byte.MaxValue;
@@ -576,4 +551,4 @@ namespace SixLabors.ImageSharp.Formats.Png
}
}
}
-}
\ No newline at end of file
+}
diff --git a/src/ImageSharp/Formats/Tga/MetadataExtensions.cs b/src/ImageSharp/Formats/Tga/MetadataExtensions.cs
new file mode 100644
index 0000000000..2b0e405e75
--- /dev/null
+++ b/src/ImageSharp/Formats/Tga/MetadataExtensions.cs
@@ -0,0 +1,21 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using SixLabors.ImageSharp.Formats.Tga;
+using SixLabors.ImageSharp.Metadata;
+
+namespace SixLabors.ImageSharp
+{
+ ///
+ /// Extension methods for the type.
+ ///
+ public static partial class MetadataExtensions
+ {
+ ///
+ /// Gets the tga format specific metadata for the image.
+ ///
+ /// The metadata this method extends.
+ /// The .
+ public static TgaMetadata GetTgaMetadata(this ImageMetadata metadata) => metadata.GetFormatMetadata(TgaFormat.Instance);
+ }
+}
diff --git a/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs b/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs
index d861450e04..d4f42a6c36 100644
--- a/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs
+++ b/src/ImageSharp/Formats/Tga/TgaDecoderCore.cs
@@ -90,7 +90,7 @@ namespace SixLabors.ImageSharp.Formats.Tga
throw new UnknownImageFormatException("Width or height cannot be 0");
}
- var image = new Image(this.configuration, this.fileHeader.Width, this.fileHeader.Height, this.metadata);
+ var image = Image.CreateUninitialized(this.configuration, this.fileHeader.Width, this.fileHeader.Height, this.metadata);
Buffer2D pixels = image.GetRootFramePixelBuffer();
if (this.fileHeader.ColorMapType is 1)
@@ -301,7 +301,7 @@ namespace SixLabors.ImageSharp.Formats.Tga
switch (colorMapPixelSizeInBytes)
{
case 1:
- color.FromGray8(Unsafe.As(ref palette[bufferSpan[idx] * colorMapPixelSizeInBytes]));
+ color.FromL8(Unsafe.As(ref palette[bufferSpan[idx] * colorMapPixelSizeInBytes]));
break;
case 2:
// Set alpha value to 1, to treat it as opaque for Bgra5551.
@@ -341,7 +341,7 @@ namespace SixLabors.ImageSharp.Formats.Tga
this.currentStream.Read(row);
int newY = Invert(y, height, inverted);
Span pixelSpan = pixels.GetRowSpan(newY);
- PixelOperations.Instance.FromGray8Bytes(
+ PixelOperations.Instance.FromL8Bytes(
this.configuration,
row.GetSpan(),
pixelSpan,
@@ -467,7 +467,7 @@ namespace SixLabors.ImageSharp.Formats.Tga
switch (bytesPerPixel)
{
case 1:
- color.FromGray8(Unsafe.As(ref bufferSpan[idx]));
+ color.FromL8(Unsafe.As(ref bufferSpan[idx]));
break;
case 2:
// Set alpha value to 1, to treat it as opaque for Bgra5551.
@@ -565,15 +565,12 @@ namespace SixLabors.ImageSharp.Formats.Tga
{
this.currentStream = stream;
-#if NETCOREAPP2_1
Span buffer = stackalloc byte[TgaFileHeader.Size];
-#else
- var buffer = new byte[TgaFileHeader.Size];
-#endif
+
this.currentStream.Read(buffer, 0, TgaFileHeader.Size);
this.fileHeader = TgaFileHeader.Parse(buffer);
this.metadata = new ImageMetadata();
- this.tgaMetadata = this.metadata.GetFormatMetadata(TgaFormat.Instance);
+ this.tgaMetadata = this.metadata.GetTgaMetadata();
this.tgaMetadata.BitsPerPixel = (TgaBitsPerPixel)this.fileHeader.PixelDepth;
// Bit at position 5 of the descriptor indicates, that the origin is top left instead of bottom right.
diff --git a/src/ImageSharp/Formats/Tga/TgaEncoderCore.cs b/src/ImageSharp/Formats/Tga/TgaEncoderCore.cs
index 28b87e9857..9dcea142f5 100644
--- a/src/ImageSharp/Formats/Tga/TgaEncoderCore.cs
+++ b/src/ImageSharp/Formats/Tga/TgaEncoderCore.cs
@@ -71,7 +71,7 @@ namespace SixLabors.ImageSharp.Formats.Tga
this.configuration = image.GetConfiguration();
ImageMetadata metadata = image.Metadata;
- TgaMetadata tgaMetadata = metadata.GetFormatMetadata(TgaFormat.Instance);
+ TgaMetadata tgaMetadata = metadata.GetTgaMetadata();
this.bitsPerPixel = this.bitsPerPixel ?? tgaMetadata.BitsPerPixel;
TgaImageType imageType = this.compression is TgaCompression.RunLength ? TgaImageType.RleTrueColor : TgaImageType.TrueColor;
@@ -97,11 +97,7 @@ namespace SixLabors.ImageSharp.Formats.Tga
pixelDepth: (byte)this.bitsPerPixel.Value,
imageDescriptor: imageDescriptor);
-#if NETCOREAPP2_1
Span buffer = stackalloc byte[TgaFileHeader.Size];
-#else
- byte[] buffer = new byte[TgaFileHeader.Size];
-#endif
fileHeader.WriteTo(buffer);
stream.Write(buffer, 0, TgaFileHeader.Size);
@@ -251,7 +247,7 @@ namespace SixLabors.ImageSharp.Formats.Tga
for (int y = pixels.Height - 1; y >= 0; y--)
{
Span pixelSpan = pixels.GetRowSpan(y);
- PixelOperations.Instance.ToGray8Bytes(
+ PixelOperations.Instance.ToL8Bytes(
this.configuration,
pixelSpan,
row.GetSpan(),
diff --git a/src/ImageSharp/Image.FromBytes.cs b/src/ImageSharp/Image.FromBytes.cs
index 465139b2e5..389fbba6d5 100644
--- a/src/ImageSharp/Image.FromBytes.cs
+++ b/src/ImageSharp/Image.FromBytes.cs
@@ -75,7 +75,7 @@ namespace SixLabors.ImageSharp
public static Image Load(Configuration config, byte[] data)
where TPixel : struct, IPixel
{
- using (var stream = new MemoryStream(data))
+ using (var stream = new MemoryStream(data, 0, data.Length, false, true))
{
return Load(config, stream);
}
@@ -92,7 +92,7 @@ namespace SixLabors.ImageSharp
public static Image Load(Configuration config, byte[] data, out IImageFormat format)
where TPixel : struct, IPixel
{
- using (var stream = new MemoryStream(data))
+ using (var stream = new MemoryStream(data, 0, data.Length, false, true))
{
return Load(config, stream, out format);
}
@@ -108,7 +108,7 @@ namespace SixLabors.ImageSharp
public static Image Load(byte[] data, IImageDecoder decoder)
where TPixel : struct, IPixel
{
- using (var stream = new MemoryStream(data))
+ using (var stream = new MemoryStream(data, 0, data.Length, false, true))
{
return Load(stream, decoder);
}
@@ -125,9 +125,9 @@ namespace SixLabors.ImageSharp
public static Image Load(Configuration config, byte[] data, IImageDecoder decoder)
where TPixel : struct, IPixel
{
- using (var memoryStream = new MemoryStream(data))
+ using (var stream = new MemoryStream(data, 0, data.Length, false, true))
{
- return Load(config, memoryStream, decoder);
+ return Load(config, stream, decoder);
}
}
@@ -270,7 +270,7 @@ namespace SixLabors.ImageSharp
///
/// The byte array containing image data.
/// The detected format.
- /// A new .
+ /// The .
public static Image Load(byte[] data, out IImageFormat format) =>
Load(Configuration.Default, data, out format);
@@ -279,52 +279,52 @@ namespace SixLabors.ImageSharp
///
/// The byte array containing encoded image data.
/// The decoder.
- /// A new .
+ /// The .
public static Image Load(byte[] data, IImageDecoder decoder) => Load(Configuration.Default, data, decoder);
///
- /// Load a new instance of from the given encoded byte array.
+ /// Load a new instance of from the given encoded byte array.
///
/// The config for the decoder.
/// The byte array containing encoded image data.
- /// A new .
+ /// The .
public static Image Load(Configuration config, byte[] data) => Load(config, data, out _);
///
- /// Load a new instance of from the given encoded byte array.
+ /// Load a new instance of from the given encoded byte array.
///
/// The config for the decoder.
/// The byte array containing image data.
/// The decoder.
- /// A new .
+ /// The .
public static Image Load(Configuration config, byte[] data, IImageDecoder decoder)
{
- using (var stream = new MemoryStream(data))
+ using (var stream = new MemoryStream(data, 0, data.Length, false, true))
{
return Load(config, stream, decoder);
}
}
///
- /// Load a new instance of from the given encoded byte array.
+ /// Load a new instance of from the given encoded byte array.
///
/// The config for the decoder.
/// The byte array containing image data.
/// The mime type of the decoded image.
- /// A new .
+ /// The .
public static Image Load(Configuration config, byte[] data, out IImageFormat format)
{
- using (var stream = new MemoryStream(data))
+ using (var stream = new MemoryStream(data, 0, data.Length, false, true))
{
return Load(config, stream, out format);
}
}
///
- /// Load a new instance of from the given encoded byte span.
+ /// Load a new instance of from the given encoded byte span.
///
/// The byte span containing image data.
- /// A new .
+ /// The .
public static Image Load(ReadOnlySpan data) => Load(Configuration.Default, data);
///
@@ -332,7 +332,7 @@ namespace SixLabors.ImageSharp
///
/// The byte span containing image data.
/// The decoder.
- /// A new .
+ /// The .
public static Image Load(ReadOnlySpan data, IImageDecoder decoder) =>
Load(Configuration.Default, data, decoder);
@@ -341,7 +341,7 @@ namespace SixLabors.ImageSharp
///
/// The byte span containing image data.
/// The detected format.
- /// A new .
+ /// The .
public static Image Load(ReadOnlySpan data, out IImageFormat format) =>
Load(Configuration.Default, data, out format);
@@ -350,7 +350,7 @@ namespace SixLabors.ImageSharp
///
/// The configuration options.
/// The byte span containing image data.
- /// A new .
+ /// The .
public static Image Load(Configuration config, ReadOnlySpan data) => Load(config, data, out _);
///
@@ -359,7 +359,7 @@ namespace SixLabors.ImageSharp
/// The Configuration.
/// The byte span containing image data.
/// The decoder.
- /// A new .
+ /// The .
public static unsafe Image Load(
Configuration config,
ReadOnlySpan data,
@@ -380,7 +380,7 @@ namespace SixLabors.ImageSharp
/// The configuration options.
/// The byte span containing image data.
/// The of the decoded image.>
- /// A new .
+ /// The .
public static unsafe Image Load(
Configuration config,
ReadOnlySpan data,
diff --git a/src/ImageSharp/Image.FromFile.cs b/src/ImageSharp/Image.FromFile.cs
index 08ed381c5a..95b4b9790e 100644
--- a/src/ImageSharp/Image.FromFile.cs
+++ b/src/ImageSharp/Image.FromFile.cs
@@ -1,4 +1,4 @@
-// Copyright (c) Six Labors and contributors.
+// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
@@ -39,17 +39,17 @@ namespace SixLabors.ImageSharp
}
///
- /// Create a new instance of the class from the given file.
+ /// Create a new instance of the class from the given file.
///
/// The file path to the image.
///
/// Thrown if the stream is not readable nor seekable.
///
- /// A new .
+ /// The .
public static Image Load(string path) => Load(Configuration.Default, path);
///
- /// Create a new instance of the class from the given file.
+ /// Create a new instance of the class from the given file.
///
/// The file path to the image.
/// The mime type of the decoded image.
@@ -60,18 +60,18 @@ namespace SixLabors.ImageSharp
public static Image Load(string path, out IImageFormat format) => Load(Configuration.Default, path, out format);
///
- /// Create a new instance of the class from the given file.
+ /// Create a new instance of the class from the given file.
///
/// The config for the decoder.
/// The file path to the image.
///
/// Thrown if the stream is not readable nor seekable.
///
- /// A new .
+ /// The .
public static Image Load(Configuration config, string path) => Load(config, path, out _);
///
- /// Create a new instance of the class from the given file.
+ /// Create a new instance of the class from the given file.
///
/// The Configuration.
/// The file path to the image.
@@ -79,7 +79,7 @@ namespace SixLabors.ImageSharp
///
/// Thrown if the stream is not readable nor seekable.
///
- /// A new .
+ /// The .
public static Image Load(Configuration config, string path, IImageDecoder decoder)
{
using (Stream stream = config.FileSystem.OpenRead(path))
@@ -89,14 +89,14 @@ namespace SixLabors.ImageSharp
}
///
- /// Create a new instance of the class from the given file.
+ /// Create a new instance of the class from the given file.
///
/// The file path to the image.
/// The decoder.
///
/// Thrown if the stream is not readable nor seekable.
///
- /// A new .
+ /// The .
public static Image Load(string path, IImageDecoder decoder) => Load(Configuration.Default, path, decoder);
///
@@ -224,4 +224,4 @@ namespace SixLabors.ImageSharp
}
}
}
-}
\ No newline at end of file
+}
diff --git a/src/ImageSharp/Image.FromStream.cs b/src/ImageSharp/Image.FromStream.cs
index 60db45f215..db2cc2fd19 100644
--- a/src/ImageSharp/Image.FromStream.cs
+++ b/src/ImageSharp/Image.FromStream.cs
@@ -80,7 +80,7 @@ namespace SixLabors.ImageSharp
/// The format type of the decoded image.
/// Thrown if the stream is not readable.
/// Image cannot be loaded.
- /// A new .>
+ /// The .
public static Image Load(Stream stream, out IImageFormat format) => Load(Configuration.Default, stream, out format);
///
@@ -90,7 +90,7 @@ namespace SixLabors.ImageSharp
/// The stream containing image information.
/// Thrown if the stream is not readable.
/// Image cannot be loaded.
- /// A new .>
+ /// The .
public static Image Load(Stream stream) => Load(Configuration.Default, stream);
///
@@ -101,7 +101,7 @@ namespace SixLabors.ImageSharp
/// The decoder.
/// Thrown if the stream is not readable.
/// Image cannot be loaded.
- /// A new .>
+ /// The .
public static Image Load(Stream stream, IImageDecoder decoder) => Load(Configuration.Default, stream, decoder);
///
diff --git a/src/ImageSharp/ImageFrameCollection.cs b/src/ImageSharp/ImageFrameCollection.cs
index c5bd02c79d..c584d2d193 100644
--- a/src/ImageSharp/ImageFrameCollection.cs
+++ b/src/ImageSharp/ImageFrameCollection.cs
@@ -1,4 +1,4 @@
-// Copyright (c) Six Labors and contributors.
+// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
@@ -122,7 +122,7 @@ namespace SixLabors.ImageSharp
public IEnumerator GetEnumerator() => this.NonGenericGetEnumerator();
///
- IEnumerator IEnumerable.GetEnumerator() => this.NonGenericGetEnumerator();
+ IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator();
///
/// Implements .
diff --git a/src/ImageSharp/ImageSharp.csproj b/src/ImageSharp/ImageSharp.csproj
index e0d70193fe..28343eaaa5 100644
--- a/src/ImageSharp/ImageSharp.csproj
+++ b/src/ImageSharp/ImageSharp.csproj
@@ -12,6 +12,7 @@
netcoreapp2.1;netstandard1.3;netstandard2.0
$(TargetFrameworks);net472
+ netcoreapp2.1;netstandard2.0;netstandard1.3;net472
true
true
@@ -20,23 +21,6 @@
SixLabors.ImageSharp
-
-
- $(DefineConstants);SUPPORTS_MATHF
-
-
-
- $(DefineConstants);SUPPORTS_HASHCODE
-
-
-
- $(DefineConstants);SUPPORTS_EXTENDED_INTRINSICS
-
-
-
-
-
-
@@ -77,15 +61,30 @@
True
Bgra32.PixelOperations.Generated.tt
-
+
+ True
+ True
+ Bgra5551.PixelOperations.Generated.tt
+
+
True
True
- Gray8.PixelOperations.Generated.tt
+ L8.PixelOperations.Generated.tt
-
+
True
True
- Gray16.PixelOperations.Generated.tt
+ L16.PixelOperations.Generated.tt
+
+
+ True
+ True
+ La16.PixelOperations.Generated.tt
+
+
+ True
+ True
+ La32.PixelOperations.Generated.tt
True
@@ -152,13 +151,25 @@
TextTemplatingFileGenerator
Bgra32.PixelOperations.Generated.cs
-
+
+ TextTemplatingFileGenerator
+ Bgra5551.PixelOperations.Generated.cs
+
+
+ TextTemplatingFileGenerator
+ L8.PixelOperations.Generated.cs
+
+
+ TextTemplatingFileGenerator
+ L16.PixelOperations.Generated.cs
+
+
TextTemplatingFileGenerator
- Gray8.PixelOperations.Generated.cs
+ La16.PixelOperations.Generated.cs
-
+
TextTemplatingFileGenerator
- Gray16.PixelOperations.Generated.cs
+ La32.PixelOperations.Generated.cs
TextTemplatingFileGenerator
diff --git a/src/ImageSharp/Image{TPixel}.cs b/src/ImageSharp/Image{TPixel}.cs
index 3f733479dc..29184522f9 100644
--- a/src/ImageSharp/Image{TPixel}.cs
+++ b/src/ImageSharp/Image{TPixel}.cs
@@ -130,7 +130,7 @@ namespace SixLabors.ImageSharp
protected override ImageFrameCollection NonGenericFrameCollection => this.Frames;
///
- /// Gets the frames.
+ /// Gets the collection of image frames.
///
public new ImageFrameCollection Frames { get; }
@@ -166,8 +166,12 @@ namespace SixLabors.ImageSharp
{
this.EnsureNotDisposed();
- IEnumerable> clonedFrames =
- this.Frames.Select, ImageFrame>(x => x.Clone(configuration));
+ var clonedFrames = new ImageFrame[this.Frames.Count];
+ for (int i = 0; i < clonedFrames.Length; i++)
+ {
+ clonedFrames[i] = this.Frames[i].Clone(configuration);
+ }
+
return new Image(configuration, this.Metadata.DeepClone(), clonedFrames);
}
@@ -181,8 +185,12 @@ namespace SixLabors.ImageSharp
{
this.EnsureNotDisposed();
- IEnumerable> clonedFrames =
- this.Frames.Select, ImageFrame>(x => x.CloneAs(configuration));
+ var clonedFrames = new ImageFrame[this.Frames.Count];
+ for (int i = 0; i < clonedFrames.Length; i++)
+ {
+ clonedFrames[i] = this.Frames[i].CloneAs(configuration);
+ }
+
return new Image(configuration, this.Metadata.DeepClone(), clonedFrames);
}
diff --git a/src/ImageSharp/MetaData/Profiles/Exif/ExifTags.cs b/src/ImageSharp/MetaData/Profiles/Exif/ExifTags.cs
deleted file mode 100644
index 0ed3a43b70..0000000000
--- a/src/ImageSharp/MetaData/Profiles/Exif/ExifTags.cs
+++ /dev/null
@@ -1,281 +0,0 @@
-// Copyright (c) Six Labors and contributors.
-// Licensed under the Apache License, Version 2.0.
-
-using static SixLabors.ImageSharp.Metadata.Profiles.Exif.ExifTag;
-
-namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
-{
- internal static class ExifTags
- {
- ///
- /// The collection if Image File Directory tags
- ///
- public static readonly ExifTag[] Ifd =
- {
- SubfileType,
- OldSubfileType,
- ImageWidth,
- ImageLength,
- BitsPerSample,
- Compression,
- PhotometricInterpretation,
- Thresholding,
- CellWidth,
- CellLength,
- FillOrder,
- DocumentName,
- ImageDescription,
- Make,
- Model,
- StripOffsets,
- Orientation,
- SamplesPerPixel,
- RowsPerStrip,
- StripByteCounts,
- MinSampleValue,
- MaxSampleValue,
- XResolution,
- YResolution,
- PlanarConfiguration,
- PageName,
- XPosition,
- YPosition,
- FreeOffsets,
- FreeByteCounts,
- GrayResponseUnit,
- GrayResponseCurve,
- T4Options,
- T6Options,
- ResolutionUnit,
- PageNumber,
- ColorResponseUnit,
- TransferFunction,
- Software,
- DateTime,
- Artist,
- HostComputer,
- Predictor,
- WhitePoint,
- PrimaryChromaticities,
- ColorMap,
- HalftoneHints,
- TileWidth,
- TileLength,
- TileOffsets,
- TileByteCounts,
- BadFaxLines,
- CleanFaxData,
- ConsecutiveBadFaxLines,
- InkSet,
- InkNames,
- NumberOfInks,
- DotRange,
- TargetPrinter,
- ExtraSamples,
- SampleFormat,
- SMinSampleValue,
- SMaxSampleValue,
- TransferRange,
- ClipPath,
- XClipPathUnits,
- YClipPathUnits,
- Indexed,
- JPEGTables,
- OPIProxy,
- ProfileType,
- FaxProfile,
- CodingMethods,
- VersionYear,
- ModeNumber,
- Decode,
- DefaultImageColor,
- T82ptions,
- JPEGProc,
- JPEGInterchangeFormat,
- JPEGInterchangeFormatLength,
- JPEGRestartInterval,
- JPEGLosslessPredictors,
- JPEGPointTransforms,
- JPEGQTables,
- JPEGDCTables,
- JPEGACTables,
- YCbCrCoefficients,
- YCbCrSubsampling,
- YCbCrSubsampling,
- YCbCrPositioning,
- ReferenceBlackWhite,
- StripRowCounts,
- XMP,
- Rating,
- RatingPercent,
- ImageID,
- CFARepeatPatternDim,
- CFAPattern2,
- BatteryLevel,
- Copyright,
- MDFileTag,
- MDScalePixel,
- MDLabName,
- MDSampleInfo,
- MDPrepDate,
- MDPrepTime,
- MDFileUnits,
- PixelScale,
- IntergraphPacketData,
- IntergraphRegisters,
- IntergraphMatrix,
- ModelTiePoint,
- SEMInfo,
- ModelTransform,
- ImageLayer,
- FaxRecvParams,
- FaxSubaddress,
- FaxRecvTime,
- ImageSourceData,
- XPTitle,
- XPComment,
- XPAuthor,
- XPKeywords,
- XPSubject,
- GDALMetadata,
- GDALNoData
- };
-
- ///
- /// The collection of Exif tags
- ///
- public static readonly ExifTag[] Exif =
- {
- ExposureTime,
- FNumber,
- ExposureProgram,
- SpectralSensitivity,
- ISOSpeedRatings,
- OECF,
- Interlace,
- TimeZoneOffset,
- SelfTimerMode,
- SensitivityType,
- StandardOutputSensitivity,
- RecommendedExposureIndex,
- ISOSpeed,
- ISOSpeedLatitudeyyy,
- ISOSpeedLatitudezzz,
- ExifVersion,
- DateTimeOriginal,
- DateTimeDigitized,
- OffsetTime,
- OffsetTimeOriginal,
- OffsetTimeDigitized,
- ComponentsConfiguration,
- CompressedBitsPerPixel,
- ShutterSpeedValue,
- ApertureValue,
- BrightnessValue,
- ExposureBiasValue,
- MaxApertureValue,
- SubjectDistance,
- MeteringMode,
- LightSource,
- Flash,
- FocalLength,
- FlashEnergy2,
- SpatialFrequencyResponse2,
- Noise,
- FocalPlaneXResolution2,
- FocalPlaneYResolution2,
- FocalPlaneResolutionUnit2,
- ImageNumber,
- SecurityClassification,
- ImageHistory,
- SubjectArea,
- ExposureIndex2,
- TIFFEPStandardID,
- SensingMethod2,
- MakerNote,
- UserComment,
- SubsecTime,
- SubsecTimeOriginal,
- SubsecTimeDigitized,
- AmbientTemperature,
- Humidity,
- Pressure,
- WaterDepth,
- Acceleration,
- CameraElevationAngle,
- FlashpixVersion,
- ColorSpace,
- PixelXDimension,
- PixelYDimension,
- RelatedSoundFile,
- FlashEnergy,
- SpatialFrequencyResponse,
- FocalPlaneXResolution,
- FocalPlaneYResolution,
- FocalPlaneResolutionUnit,
- SubjectLocation,
- ExposureIndex,
- SensingMethod,
- FileSource,
- SceneType,
- CFAPattern,
- CustomRendered,
- ExposureMode,
- WhiteBalance,
- DigitalZoomRatio,
- FocalLengthIn35mmFilm,
- SceneCaptureType,
- GainControl,
- Contrast,
- Saturation,
- Sharpness,
- DeviceSettingDescription,
- SubjectDistanceRange,
- ImageUniqueID,
- OwnerName,
- SerialNumber,
- LensInfo,
- LensMake,
- LensModel,
- LensSerialNumber
- };
-
- ///
- /// The collection of GPS tags
- ///
- public static readonly ExifTag[] Gps =
- {
- GPSVersionID,
- GPSLatitudeRef,
- GPSLatitude,
- GPSLongitudeRef,
- GPSLongitude,
- GPSAltitudeRef,
- GPSAltitude,
- GPSTimestamp,
- GPSSatellites,
- GPSStatus,
- GPSMeasureMode,
- GPSDOP,
- GPSSpeedRef,
- GPSSpeed,
- GPSTrackRef,
- GPSTrack,
- GPSImgDirectionRef,
- GPSImgDirection,
- GPSMapDatum,
- GPSDestLatitudeRef,
- GPSDestLatitude,
- GPSDestLongitudeRef,
- GPSDestLongitude,
- GPSDestBearingRef,
- GPSDestBearing,
- GPSDestDistanceRef,
- GPSDestDistance,
- GPSProcessingMethod,
- GPSAreaInformation,
- GPSDateStamp,
- GPSDifferential
- };
- }
-}
\ No newline at end of file
diff --git a/src/ImageSharp/MetaData/Profiles/Exif/ExifValue.cs b/src/ImageSharp/MetaData/Profiles/Exif/ExifValue.cs
deleted file mode 100644
index 05a9f35c9b..0000000000
--- a/src/ImageSharp/MetaData/Profiles/Exif/ExifValue.cs
+++ /dev/null
@@ -1,721 +0,0 @@
-// Copyright (c) Six Labors and contributors.
-// Licensed under the Apache License, Version 2.0.
-
-using System;
-using System.Globalization;
-using System.Text;
-using SixLabors.ImageSharp.Primitives;
-
-namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
-{
- ///
- /// Represent the value of the EXIF profile.
- ///
- public sealed class ExifValue : IEquatable, IDeepCloneable
- {
- ///
- /// Initializes a new instance of the class.
- ///
- /// The tag.
- /// The data type.
- /// The value.
- /// Whether the value is an array.
- internal ExifValue(ExifTag tag, ExifDataType dataType, object value, bool isArray)
- {
- this.Tag = tag;
- this.DataType = dataType;
- this.IsArray = isArray && dataType != ExifDataType.Ascii;
- this.Value = value;
- }
-
- ///
- /// Initializes a new instance of the class
- /// by making a copy from another exif value.
- ///
- /// The other exif value, where the clone should be made from.
- /// is null.
- private ExifValue(ExifValue other)
- {
- Guard.NotNull(other, nameof(other));
-
- this.DataType = other.DataType;
- this.IsArray = other.IsArray;
- this.Tag = other.Tag;
-
- if (!other.IsArray)
- {
- // All types are value types except for string which is immutable so safe to simply assign.
- this.Value = other.Value;
- }
- else
- {
- // All array types are value types so Clone() is sufficient here.
- var array = (Array)other.Value;
- this.Value = array.Clone();
- }
- }
-
- ///
- /// Gets the data type of the exif value.
- ///
- public ExifDataType DataType { get; }
-
- ///
- /// Gets a value indicating whether the value is an array.
- ///
- public bool IsArray { get; }
-
- ///
- /// Gets the tag of the exif value.
- ///
- public ExifTag Tag { get; }
-
- ///
- /// Gets the value.
- ///
- public object Value { get; }
-
- ///
- /// Gets a value indicating whether the EXIF value has a value.
- ///
- internal bool HasValue
- {
- get
- {
- if (this.Value is null)
- {
- return false;
- }
-
- if (this.DataType == ExifDataType.Ascii)
- {
- return ((string)this.Value).Length > 0;
- }
-
- return true;
- }
- }
-
- ///
- /// Gets the length of the EXIF value
- ///
- internal int Length
- {
- get
- {
- if (this.Value is null)
- {
- return 4;
- }
-
- int size = (int)(GetSize(this.DataType) * this.NumberOfComponents);
-
- return size < 4 ? 4 : size;
- }
- }
-
- ///
- /// Gets the number of components.
- ///
- internal int NumberOfComponents
- {
- get
- {
- if (this.DataType == ExifDataType.Ascii)
- {
- return Encoding.UTF8.GetBytes((string)this.Value).Length;
- }
-
- if (this.IsArray)
- {
- return ((Array)this.Value).Length;
- }
-
- return 1;
- }
- }
-
- ///
- /// Compares two objects for equality.
- ///
- ///
- /// The on the left side of the operand.
- ///
- ///
- /// The on the right side of the operand.
- ///
- ///
- /// True if the parameter is equal to the parameter; otherwise, false.
- ///
- public static bool operator ==(ExifValue left, ExifValue right) => ReferenceEquals(left, right) || left.Equals(right);
-
- ///
- /// Compares two objects for equality.
- ///
- ///
- /// The on the left side of the operand.
- ///
- ///
- /// The on the right side of the operand.
- ///
- ///
- /// True if the parameter is not equal to the parameter; otherwise, false.
- ///
- public static bool operator !=(ExifValue left, ExifValue right) => !(left == right);
-
- ///
- public override bool Equals(object obj) => obj is ExifValue other && this.Equals(other);
-
- ///
- public bool Equals(ExifValue other)
- {
- if (other is null)
- {
- return false;
- }
-
- if (ReferenceEquals(this, other))
- {
- return true;
- }
-
- return
- this.Tag == other.Tag
- && this.DataType == other.DataType
- && object.Equals(this.Value, other.Value);
- }
-
- ///
- /// Clones the current value, overwriting the value.
- ///
- /// The value to overwrite.
- ///
- public ExifValue WithValue(object value)
- {
- this.CheckValue(value);
-
- return new ExifValue(this.Tag, this.DataType, value, this.IsArray);
- }
-
- ///
- public override int GetHashCode()
- {
- return HashCode.Combine(this.Tag, this.DataType, this.Value);
- }
-
- ///
- public override string ToString()
- {
- if (this.Value is null)
- {
- return null;
- }
-
- if (this.DataType == ExifDataType.Ascii)
- {
- return (string)this.Value;
- }
-
- if (!this.IsArray)
- {
- return this.ToString(this.Value);
- }
-
- var sb = new StringBuilder();
- foreach (object value in (Array)this.Value)
- {
- sb.Append(this.ToString(value));
- sb.Append(' ');
- }
-
- return sb.ToString();
- }
-
- ///
- public ExifValue DeepClone() => new ExifValue(this);
-
- ///
- /// Creates a new
- ///
- /// The tag.
- /// The value.
- ///
- /// The .
- ///
- ///
- /// Thrown if the tag is not supported.
- ///
- internal static ExifValue Create(ExifTag tag, object value)
- {
- Guard.IsFalse(tag == ExifTag.Unknown, nameof(tag), "Invalid Tag");
-
- switch (tag)
- {
- case ExifTag.ImageDescription:
- case ExifTag.Make:
- case ExifTag.Model:
- case ExifTag.Software:
- case ExifTag.DateTime:
- case ExifTag.Artist:
- case ExifTag.HostComputer:
- case ExifTag.Copyright:
- case ExifTag.DocumentName:
- case ExifTag.PageName:
- case ExifTag.InkNames:
- case ExifTag.TargetPrinter:
- case ExifTag.ImageID:
- case ExifTag.MDLabName:
- case ExifTag.MDSampleInfo:
- case ExifTag.MDPrepDate:
- case ExifTag.MDPrepTime:
- case ExifTag.MDFileUnits:
- case ExifTag.SEMInfo:
- case ExifTag.SpectralSensitivity:
- case ExifTag.DateTimeOriginal:
- case ExifTag.DateTimeDigitized:
- case ExifTag.SubsecTime:
- case ExifTag.SubsecTimeOriginal:
- case ExifTag.SubsecTimeDigitized:
- case ExifTag.FaxSubaddress:
- case ExifTag.OffsetTime:
- case ExifTag.OffsetTimeOriginal:
- case ExifTag.OffsetTimeDigitized:
- case ExifTag.SecurityClassification:
- case ExifTag.ImageHistory:
- case ExifTag.ImageUniqueID:
- case ExifTag.OwnerName:
- case ExifTag.SerialNumber:
- case ExifTag.LensMake:
- case ExifTag.LensModel:
- case ExifTag.LensSerialNumber:
- case ExifTag.GDALMetadata:
- case ExifTag.GDALNoData:
- case ExifTag.GPSLatitudeRef:
- case ExifTag.GPSLongitudeRef:
- case ExifTag.GPSSatellites:
- case ExifTag.GPSStatus:
- case ExifTag.GPSMeasureMode:
- case ExifTag.GPSSpeedRef:
- case ExifTag.GPSTrackRef:
- case ExifTag.GPSImgDirectionRef:
- case ExifTag.GPSMapDatum:
- case ExifTag.GPSDestLatitudeRef:
- case ExifTag.GPSDestLongitudeRef:
- case ExifTag.GPSDestBearingRef:
- case ExifTag.GPSDestDistanceRef:
- case ExifTag.GPSDateStamp:
- return new ExifValue(tag, ExifDataType.Ascii, value, true);
-
- case ExifTag.ClipPath:
- case ExifTag.VersionYear:
- case ExifTag.XMP:
- case ExifTag.CFAPattern2:
- case ExifTag.TIFFEPStandardID:
- case ExifTag.XPTitle:
- case ExifTag.XPComment:
- case ExifTag.XPAuthor:
- case ExifTag.XPKeywords:
- case ExifTag.XPSubject:
- case ExifTag.GPSVersionID:
- return new ExifValue(tag, ExifDataType.Byte, value, true);
-
- case ExifTag.FaxProfile:
- case ExifTag.ModeNumber:
- case ExifTag.GPSAltitudeRef:
- return new ExifValue(tag, ExifDataType.Byte, value, false);
-
- case ExifTag.FreeOffsets:
- case ExifTag.FreeByteCounts:
- case ExifTag.ColorResponseUnit:
- case ExifTag.TileOffsets:
- case ExifTag.SMinSampleValue:
- case ExifTag.SMaxSampleValue:
- case ExifTag.JPEGQTables:
- case ExifTag.JPEGDCTables:
- case ExifTag.JPEGACTables:
- case ExifTag.StripRowCounts:
- case ExifTag.IntergraphRegisters:
- case ExifTag.TimeZoneOffset:
- return new ExifValue(tag, ExifDataType.Long, value, true);
- case ExifTag.SubfileType:
- case ExifTag.SubIFDOffset:
- case ExifTag.GPSIFDOffset:
- case ExifTag.T4Options:
- case ExifTag.T6Options:
- case ExifTag.XClipPathUnits:
- case ExifTag.YClipPathUnits:
- case ExifTag.ProfileType:
- case ExifTag.CodingMethods:
- case ExifTag.T82ptions:
- case ExifTag.JPEGInterchangeFormat:
- case ExifTag.JPEGInterchangeFormatLength:
- case ExifTag.MDFileTag:
- case ExifTag.StandardOutputSensitivity:
- case ExifTag.RecommendedExposureIndex:
- case ExifTag.ISOSpeed:
- case ExifTag.ISOSpeedLatitudeyyy:
- case ExifTag.ISOSpeedLatitudezzz:
- case ExifTag.FaxRecvParams:
- case ExifTag.FaxRecvTime:
- case ExifTag.ImageNumber:
- return new ExifValue(tag, ExifDataType.Long, value, false);
-
- case ExifTag.WhitePoint:
- case ExifTag.PrimaryChromaticities:
- case ExifTag.YCbCrCoefficients:
- case ExifTag.ReferenceBlackWhite:
- case ExifTag.PixelScale:
- case ExifTag.IntergraphMatrix:
- case ExifTag.ModelTiePoint:
- case ExifTag.ModelTransform:
- case ExifTag.GPSLatitude:
- case ExifTag.GPSLongitude:
- case ExifTag.GPSTimestamp:
- case ExifTag.GPSDestLatitude:
- case ExifTag.GPSDestLongitude:
- return new ExifValue(tag, ExifDataType.Rational, value, true);
-
- case ExifTag.XPosition:
- case ExifTag.YPosition:
- case ExifTag.XResolution:
- case ExifTag.YResolution:
- case ExifTag.BatteryLevel:
- case ExifTag.ExposureTime:
- case ExifTag.FNumber:
- case ExifTag.MDScalePixel:
- case ExifTag.CompressedBitsPerPixel:
- case ExifTag.ApertureValue:
- case ExifTag.MaxApertureValue:
- case ExifTag.SubjectDistance:
- case ExifTag.FocalLength:
- case ExifTag.FlashEnergy2:
- case ExifTag.FocalPlaneXResolution2:
- case ExifTag.FocalPlaneYResolution2:
- case ExifTag.ExposureIndex2:
- case ExifTag.Humidity:
- case ExifTag.Pressure:
- case ExifTag.Acceleration:
- case ExifTag.FlashEnergy:
- case ExifTag.FocalPlaneXResolution:
- case ExifTag.FocalPlaneYResolution:
- case ExifTag.ExposureIndex:
- case ExifTag.DigitalZoomRatio:
- case ExifTag.LensInfo:
- case ExifTag.GPSAltitude:
- case ExifTag.GPSDOP:
- case ExifTag.GPSSpeed:
- case ExifTag.GPSTrack:
- case ExifTag.GPSImgDirection:
- case ExifTag.GPSDestBearing:
- case ExifTag.GPSDestDistance:
- return new ExifValue(tag, ExifDataType.Rational, value, false);
-
- case ExifTag.BitsPerSample:
- case ExifTag.MinSampleValue:
- case ExifTag.MaxSampleValue:
- case ExifTag.GrayResponseCurve:
- case ExifTag.ColorMap:
- case ExifTag.ExtraSamples:
- case ExifTag.PageNumber:
- case ExifTag.TransferFunction:
- case ExifTag.Predictor:
- case ExifTag.HalftoneHints:
- case ExifTag.SampleFormat:
- case ExifTag.TransferRange:
- case ExifTag.DefaultImageColor:
- case ExifTag.JPEGLosslessPredictors:
- case ExifTag.JPEGPointTransforms:
- case ExifTag.YCbCrSubsampling:
- case ExifTag.CFARepeatPatternDim:
- case ExifTag.IntergraphPacketData:
- case ExifTag.ISOSpeedRatings:
- case ExifTag.SubjectArea:
- case ExifTag.SubjectLocation:
- return new ExifValue(tag, ExifDataType.Short, value, true);
-
- case ExifTag.OldSubfileType:
- case ExifTag.Compression:
- case ExifTag.PhotometricInterpretation:
- case ExifTag.Thresholding:
- case ExifTag.CellWidth:
- case ExifTag.CellLength:
- case ExifTag.FillOrder:
- case ExifTag.Orientation:
- case ExifTag.SamplesPerPixel:
- case ExifTag.PlanarConfiguration:
- case ExifTag.GrayResponseUnit:
- case ExifTag.ResolutionUnit:
- case ExifTag.CleanFaxData:
- case ExifTag.InkSet:
- case ExifTag.NumberOfInks:
- case ExifTag.DotRange:
- case ExifTag.Indexed:
- case ExifTag.OPIProxy:
- case ExifTag.JPEGProc:
- case ExifTag.JPEGRestartInterval:
- case ExifTag.YCbCrPositioning:
- case ExifTag.Rating:
- case ExifTag.RatingPercent:
- case ExifTag.ExposureProgram:
- case ExifTag.Interlace:
- case ExifTag.SelfTimerMode:
- case ExifTag.SensitivityType:
- case ExifTag.MeteringMode:
- case ExifTag.LightSource:
- case ExifTag.FocalPlaneResolutionUnit2:
- case ExifTag.SensingMethod2:
- case ExifTag.Flash:
- case ExifTag.ColorSpace:
- case ExifTag.FocalPlaneResolutionUnit:
- case ExifTag.SensingMethod:
- case ExifTag.CustomRendered:
- case ExifTag.ExposureMode:
- case ExifTag.WhiteBalance:
- case ExifTag.FocalLengthIn35mmFilm:
- case ExifTag.SceneCaptureType:
- case ExifTag.GainControl:
- case ExifTag.Contrast:
- case ExifTag.Saturation:
- case ExifTag.Sharpness:
- case ExifTag.SubjectDistanceRange:
- case ExifTag.GPSDifferential:
- return new ExifValue(tag, ExifDataType.Short, value, false);
-
- case ExifTag.Decode:
- return new ExifValue(tag, ExifDataType.SignedRational, value, true);
-
- case ExifTag.ShutterSpeedValue:
- case ExifTag.BrightnessValue:
- case ExifTag.ExposureBiasValue:
- case ExifTag.AmbientTemperature:
- case ExifTag.WaterDepth:
- case ExifTag.CameraElevationAngle:
- return new ExifValue(tag, ExifDataType.SignedRational, value, false);
-
- case ExifTag.JPEGTables:
- case ExifTag.OECF:
- case ExifTag.ExifVersion:
- case ExifTag.ComponentsConfiguration:
- case ExifTag.MakerNote:
- case ExifTag.UserComment:
- case ExifTag.FlashpixVersion:
- case ExifTag.SpatialFrequencyResponse:
- case ExifTag.SpatialFrequencyResponse2:
- case ExifTag.Noise:
- case ExifTag.CFAPattern:
- case ExifTag.DeviceSettingDescription:
- case ExifTag.ImageSourceData:
- case ExifTag.GPSProcessingMethod:
- case ExifTag.GPSAreaInformation:
- return new ExifValue(tag, ExifDataType.Undefined, value, true);
-
- case ExifTag.FileSource:
- case ExifTag.SceneType:
- return new ExifValue(tag, ExifDataType.Undefined, value, false);
-
- case ExifTag.StripOffsets:
- case ExifTag.TileByteCounts:
- case ExifTag.ImageLayer:
- return CreateNumber(tag, value, true);
-
- case ExifTag.ImageWidth:
- case ExifTag.ImageLength:
- case ExifTag.TileWidth:
- case ExifTag.TileLength:
- case ExifTag.BadFaxLines:
- case ExifTag.ConsecutiveBadFaxLines:
- case ExifTag.PixelXDimension:
- case ExifTag.PixelYDimension:
- return CreateNumber(tag, value, false);
-
- default:
- throw new NotSupportedException();
- }
- }
-
- ///
- /// Gets the size in bytes of the given data type.
- ///
- /// The data type.
- ///
- /// The .
- ///
- ///
- /// Thrown if the type is unsupported.
- ///
- internal static uint GetSize(ExifDataType dataType)
- {
- switch (dataType)
- {
- case ExifDataType.Ascii:
- case ExifDataType.Byte:
- case ExifDataType.SignedByte:
- case ExifDataType.Undefined:
- return 1;
- case ExifDataType.Short:
- case ExifDataType.SignedShort:
- return 2;
- case ExifDataType.Long:
- case ExifDataType.SignedLong:
- case ExifDataType.SingleFloat:
- return 4;
- case ExifDataType.DoubleFloat:
- case ExifDataType.Rational:
- case ExifDataType.SignedRational:
- return 8;
- default:
- throw new NotSupportedException(dataType.ToString());
- }
- }
-
- ///
- /// Returns an EXIF value with a numeric type for the given tag.
- ///
- /// The tag.
- /// The value.
- /// Whether the value is an array.
- ///
- /// The .
- ///
- private static ExifValue CreateNumber(ExifTag tag, object value, bool isArray)
- {
- Type type = value?.GetType();
- if (type?.IsArray == true)
- {
- type = type.GetElementType();
- }
-
- if (type is null || type == typeof(ushort))
- {
- return new ExifValue(tag, ExifDataType.Short, value, isArray);
- }
-
- if (type == typeof(short))
- {
- return new ExifValue(tag, ExifDataType.SignedShort, value, isArray);
- }
-
- if (type == typeof(uint))
- {
- return new ExifValue(tag, ExifDataType.Long, value, isArray);
- }
-
- return new ExifValue(tag, ExifDataType.SignedLong, value, isArray);
- }
-
- ///
- /// Checks the value type of the given object.
- ///
- /// The value to check.
- ///
- /// Thrown if the object type is not supported.
- ///
- private void CheckValue(object value)
- {
- if (value is null)
- {
- return;
- }
-
- Type type = value.GetType();
-
- if (this.DataType == ExifDataType.Ascii)
- {
- Guard.IsTrue(type == typeof(string), nameof(value), "Value should be a string.");
- return;
- }
-
- if (type.IsArray)
- {
- Guard.IsTrue(this.IsArray, nameof(value), "Value should not be an array.");
- type = type.GetElementType();
- }
- else
- {
- Guard.IsFalse(this.IsArray, nameof(value), "Value should not be an array.");
- }
-
- switch (this.DataType)
- {
- case ExifDataType.Byte:
- Guard.IsTrue(type == typeof(byte), nameof(value), $"Value should be a byte{(this.IsArray ? " array." : ".")}");
- break;
- case ExifDataType.DoubleFloat:
- Guard.IsTrue(type == typeof(double), nameof(value), $"Value should be a double{(this.IsArray ? " array." : ".")}");
- break;
- case ExifDataType.Long:
- Guard.IsTrue(type == typeof(uint), nameof(value), $"Value should be an unsigned int{(this.IsArray ? " array." : ".")}");
- break;
- case ExifDataType.Rational:
- Guard.IsTrue(type == typeof(Rational), nameof(value), $"Value should be a Rational{(this.IsArray ? " array." : ".")}");
- break;
- case ExifDataType.Short:
- Guard.IsTrue(type == typeof(ushort), nameof(value), $"Value should be an unsigned short{(this.IsArray ? " array." : ".")}");
- break;
- case ExifDataType.SignedByte:
- Guard.IsTrue(type == typeof(sbyte), nameof(value), $"Value should be a signed byte{(this.IsArray ? " array." : ".")}");
- break;
- case ExifDataType.SignedLong:
- Guard.IsTrue(type == typeof(int), nameof(value), $"Value should be an int{(this.IsArray ? " array." : ".")}");
- break;
- case ExifDataType.SignedRational:
- Guard.IsTrue(type == typeof(SignedRational), nameof(value), $"Value should be a SignedRational{(this.IsArray ? " array." : ".")}");
- break;
- case ExifDataType.SignedShort:
- Guard.IsTrue(type == typeof(short), nameof(value), $"Value should be a short{(this.IsArray ? " array." : ".")}");
- break;
- case ExifDataType.SingleFloat:
- Guard.IsTrue(type == typeof(float), nameof(value), $"Value should be a float{(this.IsArray ? " array." : ".")}");
- break;
- case ExifDataType.Undefined:
- Guard.IsTrue(type == typeof(byte), nameof(value), "Value should be a byte array.");
- break;
- default:
- throw new NotSupportedException();
- }
- }
-
- ///
- /// Converts the object value of this instance to its equivalent string representation
- ///
- /// The value
- /// The
- private string ToString(object value)
- {
- if (ExifTagDescriptionAttribute.GetDescription(this.Tag, value) is string description)
- {
- return description;
- }
-
- switch (this.DataType)
- {
- case ExifDataType.Ascii:
- return (string)value;
- case ExifDataType.Byte:
- return ((byte)value).ToString("X2", CultureInfo.InvariantCulture);
- case ExifDataType.DoubleFloat:
- return ((double)value).ToString(CultureInfo.InvariantCulture);
- case ExifDataType.Long:
- return ((uint)value).ToString(CultureInfo.InvariantCulture);
- case ExifDataType.Rational:
- return ((Rational)value).ToString(CultureInfo.InvariantCulture);
- case ExifDataType.Short:
- return ((ushort)value).ToString(CultureInfo.InvariantCulture);
- case ExifDataType.SignedByte:
- return ((sbyte)value).ToString("X2", CultureInfo.InvariantCulture);
- case ExifDataType.SignedLong:
- return ((int)value).ToString(CultureInfo.InvariantCulture);
- case ExifDataType.SignedRational:
- return ((Rational)value).ToString(CultureInfo.InvariantCulture);
- case ExifDataType.SignedShort:
- return ((short)value).ToString(CultureInfo.InvariantCulture);
- case ExifDataType.SingleFloat:
- return ((float)value).ToString(CultureInfo.InvariantCulture);
- case ExifDataType.Undefined:
- return ((byte)value).ToString("X2", CultureInfo.InvariantCulture);
- default:
- throw new NotSupportedException();
- }
- }
- }
-}
\ No newline at end of file
diff --git a/src/ImageSharp/MetaData/Profiles/Exif/README.md b/src/ImageSharp/MetaData/Profiles/Exif/README.md
deleted file mode 100644
index b6e27b70c5..0000000000
--- a/src/ImageSharp/MetaData/Profiles/Exif/README.md
+++ /dev/null
@@ -1,3 +0,0 @@
-Adapted from Magick.NET:
-
-https://github.com/dlemstra/Magick.NET/tree/784e23b1f5c824fc03d4b95d3387b3efe1ed510b/Magick.NET/Core/Profiles/Exif
\ No newline at end of file
diff --git a/src/ImageSharp/MetaData/FrameDecodingMode.cs b/src/ImageSharp/Metadata/FrameDecodingMode.cs
similarity index 100%
rename from src/ImageSharp/MetaData/FrameDecodingMode.cs
rename to src/ImageSharp/Metadata/FrameDecodingMode.cs
diff --git a/src/ImageSharp/MetaData/ImageFrameMetaData.cs b/src/ImageSharp/Metadata/ImageFrameMetadata.cs
similarity index 100%
rename from src/ImageSharp/MetaData/ImageFrameMetaData.cs
rename to src/ImageSharp/Metadata/ImageFrameMetadata.cs
diff --git a/src/ImageSharp/MetaData/ImageMetaData.cs b/src/ImageSharp/Metadata/ImageMetadata.cs
similarity index 100%
rename from src/ImageSharp/MetaData/ImageMetaData.cs
rename to src/ImageSharp/Metadata/ImageMetadata.cs
diff --git a/src/ImageSharp/MetaData/PixelResolutionUnit.cs b/src/ImageSharp/Metadata/PixelResolutionUnit.cs
similarity index 100%
rename from src/ImageSharp/MetaData/PixelResolutionUnit.cs
rename to src/ImageSharp/Metadata/PixelResolutionUnit.cs
diff --git a/src/ImageSharp/Metadata/Profiles/Exif/DC-008-Translation-2019-E.pdf b/src/ImageSharp/Metadata/Profiles/Exif/DC-008-Translation-2019-E.pdf
new file mode 100644
index 0000000000..9be0c8402b
Binary files /dev/null and b/src/ImageSharp/Metadata/Profiles/Exif/DC-008-Translation-2019-E.pdf differ
diff --git a/src/ImageSharp/MetaData/Profiles/Exif/ExifConstants.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifConstants.cs
similarity index 100%
rename from src/ImageSharp/MetaData/Profiles/Exif/ExifConstants.cs
rename to src/ImageSharp/Metadata/Profiles/Exif/ExifConstants.cs
diff --git a/src/ImageSharp/MetaData/Profiles/Exif/ExifDataType.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifDataType.cs
similarity index 83%
rename from src/ImageSharp/MetaData/Profiles/Exif/ExifDataType.cs
rename to src/ImageSharp/Metadata/Profiles/Exif/ExifDataType.cs
index 83e7f7fe8b..74c86f7214 100644
--- a/src/ImageSharp/MetaData/Profiles/Exif/ExifDataType.cs
+++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifDataType.cs
@@ -1,4 +1,4 @@
-// Copyright (c) Six Labors and contributors.
+// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
@@ -20,6 +20,10 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
///
/// An 8-bit byte containing one 7-bit ASCII code. The final byte is terminated with NULL.
+ ///
+ /// Although the standard defines ASCII this has commonly been ignored as
+ /// ASCII cannot properly encode text in many languages.
+ ///
///
Ascii = 2,
@@ -64,13 +68,13 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
SignedRational = 10,
///
- /// A 32-bit floating point value.
+ /// A 32-bit single precision floating point value.
///
SingleFloat = 11,
///
- /// A 64-bit floating point value.
+ /// A 64-bit double precision floating point value.
///
DoubleFloat = 12
}
-}
\ No newline at end of file
+}
diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifDataTypes.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifDataTypes.cs
new file mode 100644
index 0000000000..d94dc56401
--- /dev/null
+++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifDataTypes.cs
@@ -0,0 +1,45 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using System;
+
+namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
+{
+ internal static class ExifDataTypes
+ {
+ ///
+ /// Gets the size in bytes of the given data type.
+ ///
+ /// The data type.
+ ///
+ /// The .
+ ///
+ ///
+ /// Thrown if the type is unsupported.
+ ///
+ public static uint GetSize(ExifDataType dataType)
+ {
+ switch (dataType)
+ {
+ case ExifDataType.Ascii:
+ case ExifDataType.Byte:
+ case ExifDataType.SignedByte:
+ case ExifDataType.Undefined:
+ return 1;
+ case ExifDataType.Short:
+ case ExifDataType.SignedShort:
+ return 2;
+ case ExifDataType.Long:
+ case ExifDataType.SignedLong:
+ case ExifDataType.SingleFloat:
+ return 4;
+ case ExifDataType.DoubleFloat:
+ case ExifDataType.Rational:
+ case ExifDataType.SignedRational:
+ return 8;
+ default:
+ throw new NotSupportedException(dataType.ToString());
+ }
+ }
+ }
+}
diff --git a/src/ImageSharp/MetaData/Profiles/Exif/ExifParts.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifParts.cs
similarity index 85%
rename from src/ImageSharp/MetaData/Profiles/Exif/ExifParts.cs
rename to src/ImageSharp/Metadata/Profiles/Exif/ExifParts.cs
index d22dc730f9..6405a7ff2b 100644
--- a/src/ImageSharp/MetaData/Profiles/Exif/ExifParts.cs
+++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifParts.cs
@@ -1,4 +1,4 @@
-// Copyright (c) Six Labors and contributors.
+// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
@@ -29,11 +29,11 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
///
/// GPSTags
///
- GPSTags = 8,
+ GpsTags = 8,
///
/// All
///
- All = IfdTags | ExifTags | GPSTags
+ All = IfdTags | ExifTags | GpsTags
}
-}
\ No newline at end of file
+}
diff --git a/src/ImageSharp/MetaData/Profiles/Exif/ExifProfile.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifProfile.cs
similarity index 78%
rename from src/ImageSharp/MetaData/Profiles/Exif/ExifProfile.cs
rename to src/ImageSharp/Metadata/Profiles/Exif/ExifProfile.cs
index 3d90cb3a9e..f98a1f3c74 100644
--- a/src/ImageSharp/MetaData/Profiles/Exif/ExifProfile.cs
+++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifProfile.cs
@@ -1,4 +1,4 @@
-// Copyright (c) Six Labors and contributors.
+// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
@@ -22,7 +22,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
///
/// The collection of EXIF values
///
- private List values;
+ private List values;
///
/// The thumbnail offset position in the byte stream
@@ -70,9 +70,9 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
if (other.values != null)
{
- this.values = new List(other.Values.Count);
+ this.values = new List(other.Values.Count);
- foreach (ExifValue value in other.Values)
+ foreach (IExifValue value in other.Values)
{
this.values.Add(value.DeepClone());
}
@@ -98,7 +98,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
///
/// Gets the values of this EXIF profile.
///
- public IReadOnlyList Values
+ public IReadOnlyList Values
{
get
{
@@ -138,46 +138,13 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
///
/// Returns the value with the specified tag.
///
- /// The tag of the EXIF value.
- ///
- /// The .
- ///
- public ExifValue GetValue(ExifTag tag)
- {
- foreach (ExifValue exifValue in this.Values)
- {
- if (exifValue.Tag == tag)
- {
- return exifValue;
- }
- }
-
- return null;
- }
-
- ///
- /// Conditionally returns the value of the tag if it exists.
- ///
- /// The tag of the EXIF value.
- /// The value of the tag, if found.
- ///
- /// The .
- ///
- public bool TryGetValue(ExifTag tag, out ExifValue value)
+ /// The tag of the exif value.
+ /// The value with the specified tag.
+ /// The data type of the tag.
+ public IExifValue GetValue(ExifTag tag)
{
- foreach (ExifValue exifValue in this.Values)
- {
- if (exifValue.Tag == tag)
- {
- value = exifValue;
-
- return true;
- }
- }
-
- value = default;
-
- return false;
+ IExifValue value = this.GetValueInternal(tag);
+ return value is null ? null : (IExifValue)value;
}
///
@@ -206,24 +173,11 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
///
/// Sets the value of the specified tag.
///
- /// The tag of the EXIF value.
+ /// The tag of the exif value.
/// The value.
- public void SetValue(ExifTag tag, object value)
- {
- for (int i = 0; i < this.Values.Count; i++)
- {
- if (this.values[i].Tag == tag)
- {
- this.values[i] = this.values[i].WithValue(value);
-
- return;
- }
- }
-
- var newExifValue = ExifValue.Create(tag, value);
-
- this.values.Add(newExifValue);
- }
+ /// The data type of the tag.
+ public void SetValue(ExifTag tag, TValueType value)
+ => this.SetValueInternal(tag, value);
///
/// Converts this instance to a byte array.
@@ -238,7 +192,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
if (this.values.Count == 0)
{
- return null;
+ return Array.Empty();
}
var writer = new ExifWriter(this.values, this.Parts);
@@ -248,6 +202,50 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
///
public ExifProfile DeepClone() => new ExifProfile(this);
+ ///
+ /// Returns the value with the specified tag.
+ ///
+ /// The tag of the exif value.
+ /// The value with the specified tag.
+ internal IExifValue GetValueInternal(ExifTag tag)
+ {
+ foreach (IExifValue exifValue in this.Values)
+ {
+ if (exifValue.Tag == tag)
+ {
+ return exifValue;
+ }
+ }
+
+ return null;
+ }
+
+ ///
+ /// Sets the value of the specified tag.
+ ///
+ /// The tag of the exif value.
+ /// The value.
+ internal void SetValueInternal(ExifTag tag, object value)
+ {
+ foreach (IExifValue exifValue in this.Values)
+ {
+ if (exifValue.Tag == tag)
+ {
+ exifValue.TrySetValue(value);
+ return;
+ }
+ }
+
+ ExifValue newExifValue = ExifValues.Create(tag);
+ if (newExifValue is null)
+ {
+ throw new NotSupportedException();
+ }
+
+ newExifValue.TrySetValue(value);
+ this.values.Add(newExifValue);
+ }
+
///
/// Synchronizes the profiles with the specified metadata.
///
@@ -258,9 +256,9 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
this.SyncResolution(ExifTag.YResolution, metadata.VerticalResolution);
}
- private void SyncResolution(ExifTag tag, double resolution)
+ private void SyncResolution(ExifTag tag, double resolution)
{
- ExifValue value = this.GetValue(tag);
+ IExifValue value = this.GetValue(tag);
if (value is null)
{
@@ -285,7 +283,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
if (this.data is null)
{
- this.values = new List();
+ this.values = new List();
return;
}
@@ -301,4 +299,4 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
this.thumbnailLength = (int)reader.ThumbnailLength;
}
}
-}
\ No newline at end of file
+}
diff --git a/src/ImageSharp/MetaData/Profiles/Exif/ExifReader.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs
similarity index 83%
rename from src/ImageSharp/MetaData/Profiles/Exif/ExifReader.cs
rename to src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs
index 77c1cf2eab..00410fb597 100644
--- a/src/ImageSharp/MetaData/Profiles/Exif/ExifReader.cs
+++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs
@@ -5,7 +5,6 @@ using System;
using System.Buffers.Binary;
using System.Collections.Generic;
using System.Collections.ObjectModel;
-using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using SixLabors.ImageSharp.Primitives;
@@ -68,12 +67,12 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
///
/// The .
///
- public List ReadValues()
+ public List ReadValues()
{
- var values = new List();
+ var values = new List();
// II == 0x4949
- this.isBigEndian = !(this.ReadUInt16() == 0x4949);
+ this.isBigEndian = this.ReadUInt16() != 0x4949;
if (this.ReadUInt16() != 0x002A)
{
@@ -101,7 +100,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
private static TDataType[] ToArray(ExifDataType dataType, ReadOnlySpan data, ConverterMethod converter)
{
- int dataTypeSize = (int)ExifValue.GetSize(dataType);
+ int dataTypeSize = (int)ExifDataTypes.GetSize(dataType);
int length = data.Length / dataTypeSize;
var result = new TDataType[length];
@@ -135,7 +134,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
///
/// The values.
/// The index.
- private void AddValues(List values, uint index)
+ private void AddValues(List values, uint index)
{
if (index > (uint)this.exifData.Length)
{
@@ -153,9 +152,9 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
}
bool duplicate = false;
- foreach (ExifValue val in values)
+ foreach (IExifValue val in values)
{
- if (val.Tag == value.Tag)
+ if (val == value)
{
duplicate = true;
break;
@@ -167,19 +166,13 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
continue;
}
- if (value.Tag == ExifTag.SubIFDOffset)
+ if (value == ExifTag.SubIFDOffset)
{
- if (value.DataType == ExifDataType.Long)
- {
- this.exifOffset = (uint)value.Value;
- }
+ this.exifOffset = ((ExifLong)value).Value;
}
- else if (value.Tag == ExifTag.GPSIFDOffset)
+ else if (value == ExifTag.GPSIFDOffset)
{
- if (value.DataType == ExifDataType.Long)
- {
- this.gpsOffset = (uint)value.Value;
- }
+ this.gpsOffset = ((ExifLong)value).Value;
}
else
{
@@ -285,30 +278,24 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
private bool TryReadValue(out ExifValue exifValue)
{
+ exifValue = default;
+
// 2 | 2 | 4 | 4
// tag | type | count | value offset
if (this.RemainingLength < 12)
{
- exifValue = default;
-
return false;
}
- ExifTag tag = this.ToEnum(this.ReadUInt16(), ExifTag.Unknown);
- uint type = this.ReadUInt16();
+ var tag = (ExifTagValue)this.ReadUInt16();
+ ExifDataType dataType = EnumUtils.Parse(this.ReadUInt16(), ExifDataType.Unknown);
// Ensure that the data type is valid
- if (type == 0 || type > 12)
+ if (dataType == ExifDataType.Unknown)
{
- exifValue = new ExifValue(tag, ExifDataType.Unknown, null, false);
-
- return true;
+ return false;
}
- var dataType = (ExifDataType)type;
-
- object value;
-
uint numberOfComponents = this.ReadUInt32();
// Issue #132: ExifDataType == Undefined is treated like a byte array.
@@ -318,23 +305,20 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
numberOfComponents = 4;
}
- uint size = numberOfComponents * ExifValue.GetSize(dataType);
+ uint size = numberOfComponents * ExifDataTypes.GetSize(dataType);
this.TryReadSpan(4, out ReadOnlySpan offsetBuffer);
+ object value;
if (size > 4)
{
int oldIndex = this.position;
-
uint newIndex = this.ConvertToUInt32(offsetBuffer);
// Ensure that the new index does not overrun the data
if (newIndex > int.MaxValue)
{
- this.AddInvalidTag(tag);
-
- exifValue = default;
-
+ this.AddInvalidTag(new UnkownExifTag(tag));
return false;
}
@@ -342,12 +326,9 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
if (this.RemainingLength < size)
{
- this.AddInvalidTag(tag);
+ this.AddInvalidTag(new UnkownExifTag(tag));
this.position = oldIndex;
-
- exifValue = default;
-
return false;
}
@@ -361,33 +342,25 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
value = this.ConvertValue(dataType, offsetBuffer, numberOfComponents);
}
- exifValue = new ExifValue(tag, dataType, value, isArray: value != null && numberOfComponents != 1);
-
- return true;
- }
+ exifValue = ExifValues.Create(tag) ?? ExifValues.Create(tag, dataType, numberOfComponents);
- private void AddInvalidTag(ExifTag tag)
- {
- if (this.invalidTags is null)
+ if (exifValue is null)
{
- this.invalidTags = new List();
+ this.AddInvalidTag(new UnkownExifTag(tag));
+ return false;
}
- this.invalidTags.Add(tag);
- }
-
- [MethodImpl(InliningOptions.ShortMethod)]
- private TEnum ToEnum(int value, TEnum defaultValue)
- where TEnum : struct, Enum
- {
- if (EnumHelper.IsDefined(value))
+ if (!exifValue.TrySetValue(value))
{
- return Unsafe.As(ref value);
+ return false;
}
- return defaultValue;
+ return true;
}
+ private void AddInvalidTag(ExifTag tag)
+ => (this.invalidTags ?? (this.invalidTags = new List())).Add(tag);
+
private bool TryReadSpan(int length, out ReadOnlySpan span)
{
if (this.RemainingLength < length)
@@ -421,18 +394,18 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
private void GetThumbnail(uint offset)
{
- var values = new List();
+ var values = new List();
this.AddValues(values, offset);
foreach (ExifValue value in values)
{
- if (value.Tag == ExifTag.JPEGInterchangeFormat && (value.DataType == ExifDataType.Long))
+ if (value == ExifTag.JPEGInterchangeFormat)
{
- this.ThumbnailOffset = (uint)value.Value;
+ this.ThumbnailOffset = ((ExifLong)value).Value;
}
- else if (value.Tag == ExifTag.JPEGInterchangeFormatLength && value.DataType == ExifDataType.Long)
+ else if (value == ExifTag.JPEGInterchangeFormatLength)
{
- this.ThumbnailLength = (uint)value.Value;
+ this.ThumbnailLength = ((ExifLong)value).Value;
}
}
}
@@ -541,18 +514,5 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
? BinaryPrimitives.ReadInt16BigEndian(buffer)
: BinaryPrimitives.ReadInt16LittleEndian(buffer);
}
-
- private sealed class EnumHelper
- where TEnum : struct, Enum
- {
- private static readonly int[] Values = Enum.GetValues(typeof(TEnum)).Cast()
- .Select(e => Convert.ToInt32(e)).OrderBy(e => e).ToArray();
-
- [MethodImpl(InliningOptions.ShortMethod)]
- public static bool IsDefined(int value)
- {
- return Array.BinarySearch(Values, value) >= 0;
- }
- }
}
}
diff --git a/src/ImageSharp/MetaData/Profiles/Exif/ExifTagDescriptionAttribute.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifTagDescriptionAttribute.cs
similarity index 86%
rename from src/ImageSharp/MetaData/Profiles/Exif/ExifTagDescriptionAttribute.cs
rename to src/ImageSharp/Metadata/Profiles/Exif/ExifTagDescriptionAttribute.cs
index 845e4ee734..b9bb2ee056 100644
--- a/src/ImageSharp/MetaData/Profiles/Exif/ExifTagDescriptionAttribute.cs
+++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifTagDescriptionAttribute.cs
@@ -1,4 +1,4 @@
-// Copyright (c) Six Labors and contributors.
+// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
@@ -31,7 +31,8 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
///
public static string GetDescription(ExifTag tag, object value)
{
- FieldInfo field = tag.GetType().GetTypeInfo().GetDeclaredField(tag.ToString());
+ var tagValue = (ExifTagValue)(ushort)tag;
+ FieldInfo field = tagValue.GetType().GetTypeInfo().GetDeclaredField(tagValue.ToString());
if (field is null)
{
@@ -42,7 +43,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
{
object attributeValue = customAttribute.ConstructorArguments[0].Value;
- if (object.Equals(attributeValue, value))
+ if (Equals(attributeValue, value))
{
return (string)customAttribute.ConstructorArguments[1].Value;
}
diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifTags.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifTags.cs
new file mode 100644
index 0000000000..13fff5b6ad
--- /dev/null
+++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifTags.cs
@@ -0,0 +1,275 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
+{
+ internal static class ExifTags
+ {
+ public static ExifParts GetPart(ExifTag tag)
+ {
+ switch ((ExifTagValue)(ushort)tag)
+ {
+ case ExifTagValue.SubfileType:
+ case ExifTagValue.OldSubfileType:
+ case ExifTagValue.ImageWidth:
+ case ExifTagValue.ImageLength:
+ case ExifTagValue.BitsPerSample:
+ case ExifTagValue.Compression:
+ case ExifTagValue.PhotometricInterpretation:
+ case ExifTagValue.Thresholding:
+ case ExifTagValue.CellWidth:
+ case ExifTagValue.CellLength:
+ case ExifTagValue.FillOrder:
+ case ExifTagValue.DocumentName:
+ case ExifTagValue.ImageDescription:
+ case ExifTagValue.Make:
+ case ExifTagValue.Model:
+ case ExifTagValue.StripOffsets:
+ case ExifTagValue.Orientation:
+ case ExifTagValue.SamplesPerPixel:
+ case ExifTagValue.RowsPerStrip:
+ case ExifTagValue.StripByteCounts:
+ case ExifTagValue.MinSampleValue:
+ case ExifTagValue.MaxSampleValue:
+ case ExifTagValue.XResolution:
+ case ExifTagValue.YResolution:
+ case ExifTagValue.PlanarConfiguration:
+ case ExifTagValue.PageName:
+ case ExifTagValue.XPosition:
+ case ExifTagValue.YPosition:
+ case ExifTagValue.FreeOffsets:
+ case ExifTagValue.FreeByteCounts:
+ case ExifTagValue.GrayResponseUnit:
+ case ExifTagValue.GrayResponseCurve:
+ case ExifTagValue.T4Options:
+ case ExifTagValue.T6Options:
+ case ExifTagValue.ResolutionUnit:
+ case ExifTagValue.PageNumber:
+ case ExifTagValue.ColorResponseUnit:
+ case ExifTagValue.TransferFunction:
+ case ExifTagValue.Software:
+ case ExifTagValue.DateTime:
+ case ExifTagValue.Artist:
+ case ExifTagValue.HostComputer:
+ case ExifTagValue.Predictor:
+ case ExifTagValue.WhitePoint:
+ case ExifTagValue.PrimaryChromaticities:
+ case ExifTagValue.ColorMap:
+ case ExifTagValue.HalftoneHints:
+ case ExifTagValue.TileWidth:
+ case ExifTagValue.TileLength:
+ case ExifTagValue.TileOffsets:
+ case ExifTagValue.TileByteCounts:
+ case ExifTagValue.BadFaxLines:
+ case ExifTagValue.CleanFaxData:
+ case ExifTagValue.ConsecutiveBadFaxLines:
+ case ExifTagValue.InkSet:
+ case ExifTagValue.InkNames:
+ case ExifTagValue.NumberOfInks:
+ case ExifTagValue.DotRange:
+ case ExifTagValue.TargetPrinter:
+ case ExifTagValue.ExtraSamples:
+ case ExifTagValue.SampleFormat:
+ case ExifTagValue.SMinSampleValue:
+ case ExifTagValue.SMaxSampleValue:
+ case ExifTagValue.TransferRange:
+ case ExifTagValue.ClipPath:
+ case ExifTagValue.XClipPathUnits:
+ case ExifTagValue.YClipPathUnits:
+ case ExifTagValue.Indexed:
+ case ExifTagValue.JPEGTables:
+ case ExifTagValue.OPIProxy:
+ case ExifTagValue.ProfileType:
+ case ExifTagValue.FaxProfile:
+ case ExifTagValue.CodingMethods:
+ case ExifTagValue.VersionYear:
+ case ExifTagValue.ModeNumber:
+ case ExifTagValue.Decode:
+ case ExifTagValue.DefaultImageColor:
+ case ExifTagValue.T82ptions:
+ case ExifTagValue.JPEGProc:
+ case ExifTagValue.JPEGInterchangeFormat:
+ case ExifTagValue.JPEGInterchangeFormatLength:
+ case ExifTagValue.JPEGRestartInterval:
+ case ExifTagValue.JPEGLosslessPredictors:
+ case ExifTagValue.JPEGPointTransforms:
+ case ExifTagValue.JPEGQTables:
+ case ExifTagValue.JPEGDCTables:
+ case ExifTagValue.JPEGACTables:
+ case ExifTagValue.YCbCrCoefficients:
+ case ExifTagValue.YCbCrPositioning:
+ case ExifTagValue.YCbCrSubsampling:
+ case ExifTagValue.ReferenceBlackWhite:
+ case ExifTagValue.StripRowCounts:
+ case ExifTagValue.XMP:
+ case ExifTagValue.Rating:
+ case ExifTagValue.RatingPercent:
+ case ExifTagValue.ImageID:
+ case ExifTagValue.CFARepeatPatternDim:
+ case ExifTagValue.CFAPattern2:
+ case ExifTagValue.BatteryLevel:
+ case ExifTagValue.Copyright:
+ case ExifTagValue.MDFileTag:
+ case ExifTagValue.MDScalePixel:
+ case ExifTagValue.MDLabName:
+ case ExifTagValue.MDSampleInfo:
+ case ExifTagValue.MDPrepDate:
+ case ExifTagValue.MDPrepTime:
+ case ExifTagValue.MDFileUnits:
+ case ExifTagValue.PixelScale:
+ case ExifTagValue.IntergraphPacketData:
+ case ExifTagValue.IntergraphRegisters:
+ case ExifTagValue.IntergraphMatrix:
+ case ExifTagValue.ModelTiePoint:
+ case ExifTagValue.SEMInfo:
+ case ExifTagValue.ModelTransform:
+ case ExifTagValue.ImageLayer:
+ case ExifTagValue.FaxRecvParams:
+ case ExifTagValue.FaxSubaddress:
+ case ExifTagValue.FaxRecvTime:
+ case ExifTagValue.ImageSourceData:
+ case ExifTagValue.XPTitle:
+ case ExifTagValue.XPComment:
+ case ExifTagValue.XPAuthor:
+ case ExifTagValue.XPKeywords:
+ case ExifTagValue.XPSubject:
+ case ExifTagValue.GDALMetadata:
+ case ExifTagValue.GDALNoData:
+ return ExifParts.IfdTags;
+
+ case ExifTagValue.ExposureTime:
+ case ExifTagValue.FNumber:
+ case ExifTagValue.ExposureProgram:
+ case ExifTagValue.SpectralSensitivity:
+ case ExifTagValue.ISOSpeedRatings:
+ case ExifTagValue.OECF:
+ case ExifTagValue.Interlace:
+ case ExifTagValue.TimeZoneOffset:
+ case ExifTagValue.SelfTimerMode:
+ case ExifTagValue.SensitivityType:
+ case ExifTagValue.StandardOutputSensitivity:
+ case ExifTagValue.RecommendedExposureIndex:
+ case ExifTagValue.ISOSpeed:
+ case ExifTagValue.ISOSpeedLatitudeyyy:
+ case ExifTagValue.ISOSpeedLatitudezzz:
+ case ExifTagValue.ExifVersion:
+ case ExifTagValue.DateTimeOriginal:
+ case ExifTagValue.DateTimeDigitized:
+ case ExifTagValue.OffsetTime:
+ case ExifTagValue.OffsetTimeOriginal:
+ case ExifTagValue.OffsetTimeDigitized:
+ case ExifTagValue.ComponentsConfiguration:
+ case ExifTagValue.CompressedBitsPerPixel:
+ case ExifTagValue.ShutterSpeedValue:
+ case ExifTagValue.ApertureValue:
+ case ExifTagValue.BrightnessValue:
+ case ExifTagValue.ExposureBiasValue:
+ case ExifTagValue.MaxApertureValue:
+ case ExifTagValue.SubjectDistance:
+ case ExifTagValue.MeteringMode:
+ case ExifTagValue.LightSource:
+ case ExifTagValue.Flash:
+ case ExifTagValue.FocalLength:
+ case ExifTagValue.FlashEnergy2:
+ case ExifTagValue.SpatialFrequencyResponse2:
+ case ExifTagValue.Noise:
+ case ExifTagValue.FocalPlaneXResolution2:
+ case ExifTagValue.FocalPlaneYResolution2:
+ case ExifTagValue.FocalPlaneResolutionUnit2:
+ case ExifTagValue.ImageNumber:
+ case ExifTagValue.SecurityClassification:
+ case ExifTagValue.ImageHistory:
+ case ExifTagValue.SubjectArea:
+ case ExifTagValue.ExposureIndex2:
+ case ExifTagValue.TIFFEPStandardID:
+ case ExifTagValue.SensingMethod2:
+ case ExifTagValue.MakerNote:
+ case ExifTagValue.UserComment:
+ case ExifTagValue.SubsecTime:
+ case ExifTagValue.SubsecTimeOriginal:
+ case ExifTagValue.SubsecTimeDigitized:
+ case ExifTagValue.AmbientTemperature:
+ case ExifTagValue.Humidity:
+ case ExifTagValue.Pressure:
+ case ExifTagValue.WaterDepth:
+ case ExifTagValue.Acceleration:
+ case ExifTagValue.CameraElevationAngle:
+ case ExifTagValue.FlashpixVersion:
+ case ExifTagValue.ColorSpace:
+ case ExifTagValue.PixelXDimension:
+ case ExifTagValue.PixelYDimension:
+ case ExifTagValue.RelatedSoundFile:
+ case ExifTagValue.FlashEnergy:
+ case ExifTagValue.SpatialFrequencyResponse:
+ case ExifTagValue.FocalPlaneXResolution:
+ case ExifTagValue.FocalPlaneYResolution:
+ case ExifTagValue.FocalPlaneResolutionUnit:
+ case ExifTagValue.SubjectLocation:
+ case ExifTagValue.ExposureIndex:
+ case ExifTagValue.SensingMethod:
+ case ExifTagValue.FileSource:
+ case ExifTagValue.SceneType:
+ case ExifTagValue.CFAPattern:
+ case ExifTagValue.CustomRendered:
+ case ExifTagValue.ExposureMode:
+ case ExifTagValue.WhiteBalance:
+ case ExifTagValue.DigitalZoomRatio:
+ case ExifTagValue.FocalLengthIn35mmFilm:
+ case ExifTagValue.SceneCaptureType:
+ case ExifTagValue.GainControl:
+ case ExifTagValue.Contrast:
+ case ExifTagValue.Saturation:
+ case ExifTagValue.Sharpness:
+ case ExifTagValue.DeviceSettingDescription:
+ case ExifTagValue.SubjectDistanceRange:
+ case ExifTagValue.ImageUniqueID:
+ case ExifTagValue.OwnerName:
+ case ExifTagValue.SerialNumber:
+ case ExifTagValue.LensInfo:
+ case ExifTagValue.LensMake:
+ case ExifTagValue.LensModel:
+ case ExifTagValue.LensSerialNumber:
+ return ExifParts.ExifTags;
+
+ case ExifTagValue.GPSVersionID:
+ case ExifTagValue.GPSLatitudeRef:
+ case ExifTagValue.GPSLatitude:
+ case ExifTagValue.GPSLongitudeRef:
+ case ExifTagValue.GPSLongitude:
+ case ExifTagValue.GPSAltitudeRef:
+ case ExifTagValue.GPSAltitude:
+ case ExifTagValue.GPSTimestamp:
+ case ExifTagValue.GPSSatellites:
+ case ExifTagValue.GPSStatus:
+ case ExifTagValue.GPSMeasureMode:
+ case ExifTagValue.GPSDOP:
+ case ExifTagValue.GPSSpeedRef:
+ case ExifTagValue.GPSSpeed:
+ case ExifTagValue.GPSTrackRef:
+ case ExifTagValue.GPSTrack:
+ case ExifTagValue.GPSImgDirectionRef:
+ case ExifTagValue.GPSImgDirection:
+ case ExifTagValue.GPSMapDatum:
+ case ExifTagValue.GPSDestLatitudeRef:
+ case ExifTagValue.GPSDestLatitude:
+ case ExifTagValue.GPSDestLongitudeRef:
+ case ExifTagValue.GPSDestLongitude:
+ case ExifTagValue.GPSDestBearingRef:
+ case ExifTagValue.GPSDestBearing:
+ case ExifTagValue.GPSDestDistanceRef:
+ case ExifTagValue.GPSDestDistance:
+ case ExifTagValue.GPSProcessingMethod:
+ case ExifTagValue.GPSAreaInformation:
+ case ExifTagValue.GPSDateStamp:
+ case ExifTagValue.GPSDifferential:
+ return ExifParts.GpsTags;
+
+ case ExifTagValue.Unknown:
+ case ExifTagValue.SubIFDOffset:
+ case ExifTagValue.GPSIFDOffset:
+ default:
+ return ExifParts.None;
+ }
+ }
+ }
+}
diff --git a/src/ImageSharp/MetaData/Profiles/Exif/ExifWriter.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifWriter.cs
similarity index 58%
rename from src/ImageSharp/MetaData/Profiles/Exif/ExifWriter.cs
rename to src/ImageSharp/Metadata/Profiles/Exif/ExifWriter.cs
index 67c1b2b65e..48b0fddca7 100644
--- a/src/ImageSharp/MetaData/Profiles/Exif/ExifWriter.cs
+++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifWriter.cs
@@ -18,24 +18,24 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
/// Which parts will be written.
///
private readonly ExifParts allowedParts;
- private readonly IList values;
+ private readonly IList values;
private List dataOffsets;
- private readonly List ifdIndexes;
- private readonly List exifIndexes;
- private readonly List gpsIndexes;
+ private readonly List ifdValues;
+ private readonly List exifValues;
+ private readonly List gpsValues;
///
/// Initializes a new instance of the class.
///
/// The values.
/// The allowed parts.
- public ExifWriter(IList values, ExifParts allowedParts)
+ public ExifWriter(IList values, ExifParts allowedParts)
{
this.values = values;
this.allowedParts = allowedParts;
- this.ifdIndexes = this.GetIndexes(ExifParts.IfdTags, ExifTags.Ifd);
- this.exifIndexes = this.GetIndexes(ExifParts.ExifTags, ExifTags.Exif);
- this.gpsIndexes = this.GetIndexes(ExifParts.GPSTags, ExifTags.Gps);
+ this.ifdValues = this.GetPartValues(ExifParts.IfdTags);
+ this.exifValues = this.GetPartValues(ExifParts.ExifTags);
+ this.gpsValues = this.GetPartValues(ExifParts.GpsTags);
}
///
@@ -46,43 +46,29 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
///
public byte[] GetData()
{
- uint startIndex = 0;
+ const uint startIndex = 0;
uint length;
- int exifIndex = -1;
- int gpsIndex = -1;
- if (this.exifIndexes.Count > 0)
- {
- exifIndex = this.GetIndex(this.ifdIndexes, ExifTag.SubIFDOffset);
- }
-
- if (this.gpsIndexes.Count > 0)
- {
- gpsIndex = this.GetIndex(this.ifdIndexes, ExifTag.GPSIFDOffset);
- }
-
- uint ifdLength = 2 + this.GetLength(this.ifdIndexes) + 4;
- uint exifLength = this.GetLength(this.exifIndexes);
- uint gpsLength = this.GetLength(this.gpsIndexes);
+ IExifValue exifOffset = GetOffsetValue(this.ifdValues, this.exifValues, ExifTag.SubIFDOffset);
+ IExifValue gpsOffset = GetOffsetValue(this.ifdValues, this.gpsValues, ExifTag.GPSIFDOffset);
- if (exifLength > 0)
+ if (this.ifdValues.Count == 0 && this.exifValues.Count == 0 && this.gpsValues.Count == 0)
{
- exifLength += 2;
+ return Array.Empty();
}
- if (gpsLength > 0)
- {
- gpsLength += 2;
- }
+ uint ifdLength = this.GetLength(this.ifdValues) + 4U;
+ uint exifLength = this.GetLength(this.exifValues);
+ uint gpsLength = this.GetLength(this.gpsValues);
length = ifdLength + exifLength + gpsLength;
- if (length == 6)
+ if (length == 4U)
{
- return null;
+ return Array.Empty();
}
- // two bytes for the byte Order marker 'II', followed by the number 42 (0x2A) and a 0, making 4 bytes total
+ // two bytes for the byte Order marker 'II' or 'MM', followed by the number 42 (0x2A) and a 0, making 4 bytes total
length += (uint)ExifConstants.LittleEndianByteOrderMarker.Length;
length += 4 + 2;
@@ -91,38 +77,31 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
int i = 0;
- // the byte order marker for little-endian, followed by the number 42 and a 0
+ // The byte order marker for little-endian, followed by the number 42 and a 0
ExifConstants.LittleEndianByteOrderMarker.AsSpan().CopyTo(result.AsSpan(start: i));
i += ExifConstants.LittleEndianByteOrderMarker.Length;
- uint ifdOffset = ((uint)i - startIndex) + 4;
+ uint ifdOffset = ((uint)i - startIndex) + 4U;
uint thumbnailOffset = ifdOffset + ifdLength + exifLength + gpsLength;
- if (exifLength > 0)
- {
- this.values[exifIndex] = this.values[exifIndex].WithValue(ifdOffset + ifdLength);
- }
-
- if (gpsLength > 0)
- {
- this.values[gpsIndex] = this.values[gpsIndex].WithValue(ifdOffset + ifdLength + exifLength);
- }
+ exifOffset?.TrySetValue(ifdOffset + ifdLength);
+ gpsOffset?.TrySetValue(ifdOffset + ifdLength + exifLength);
i = WriteUInt32(ifdOffset, result, i);
- i = this.WriteHeaders(this.ifdIndexes, result, i);
+ i = this.WriteHeaders(this.ifdValues, result, i);
i = WriteUInt32(thumbnailOffset, result, i);
- i = this.WriteData(startIndex, this.ifdIndexes, result, i);
+ i = this.WriteData(startIndex, this.ifdValues, result, i);
if (exifLength > 0)
{
- i = this.WriteHeaders(this.exifIndexes, result, i);
- i = this.WriteData(startIndex, this.exifIndexes, result, i);
+ i = this.WriteHeaders(this.exifValues, result, i);
+ i = this.WriteData(startIndex, this.exifValues, result, i);
}
if (gpsLength > 0)
{
- i = this.WriteHeaders(this.gpsIndexes, result, i);
- i = this.WriteData(startIndex, this.gpsIndexes, result, i);
+ i = this.WriteHeaders(this.gpsValues, result, i);
+ i = this.WriteData(startIndex, this.gpsValues, result, i);
}
WriteUInt16(0, result, i);
@@ -179,79 +158,137 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
return offset + 4;
}
- private int GetIndex(IList indexes, ExifTag tag)
+ private static IExifValue GetOffsetValue(List ifdValues, List values, ExifTag offset)
{
- foreach (int index in indexes)
+ int index = -1;
+
+ for (int i = 0; i < ifdValues.Count; i++)
{
- if (this.values[index].Tag == tag)
+ if (ifdValues[i].Tag == offset)
{
- return index;
+ index = i;
}
}
- int newIndex = this.values.Count;
- indexes.Add(newIndex);
- this.values.Add(ExifValue.Create(tag, null));
- return newIndex;
+ if (values.Count > 0)
+ {
+ if (index != -1)
+ {
+ return ifdValues[index];
+ }
+
+ ExifValue result = ExifValues.Create(offset);
+ ifdValues.Add(result);
+
+ return result;
+ }
+ else if (index != -1)
+ {
+ ifdValues.RemoveAt(index);
+ }
+
+ return null;
}
- private List GetIndexes(ExifParts part, ExifTag[] tags)
+ private List GetPartValues(ExifParts part)
{
- if (((int)this.allowedParts & (int)part) == 0)
+ var result = new List();
+
+ if (!EnumUtils.HasFlag(this.allowedParts, part))
{
- return new List();
+ return result;
}
- var result = new List();
- for (int i = 0; i < this.values.Count; i++)
+ foreach (IExifValue value in this.values)
{
- ExifValue value = this.values[i];
-
- if (!value.HasValue)
+ if (!HasValue(value))
{
continue;
}
- int index = Array.IndexOf(tags, value.Tag);
- if (index > -1)
+ if (ExifTags.GetPart(value.Tag) == part)
{
- result.Add(i);
+ result.Add(value);
}
}
return result;
}
- private uint GetLength(IList indexes)
+ private static bool HasValue(IExifValue exifValue)
+ {
+ object value = exifValue.GetValue();
+ if (value is null)
+ {
+ return false;
+ }
+
+ if (exifValue.DataType == ExifDataType.Ascii)
+ {
+ string stringValue = (string)value;
+ return stringValue.Length > 0;
+ }
+
+ if (value is Array arrayValue)
+ {
+ return arrayValue.Length > 0;
+ }
+
+ return true;
+ }
+
+ private uint GetLength(IList values)
{
- uint length = 0;
+ if (values.Count == 0)
+ {
+ return 0;
+ }
- foreach (int index in indexes)
+ uint length = 2;
+
+ foreach (IExifValue value in values)
{
- uint valueLength = (uint)this.values[index].Length;
+ uint valueLength = GetLength(value);
+
+ length += 2 + 2 + 4 + 4;
if (valueLength > 4)
{
- length += 12 + valueLength;
- }
- else
- {
- length += 12;
+ length += valueLength;
}
}
return length;
}
- private int WriteArray(ExifValue value, Span destination, int offset)
+ private static uint GetLength(IExifValue value) => GetNumberOfComponents(value) * ExifDataTypes.GetSize(value.DataType);
+
+ private static uint GetNumberOfComponents(IExifValue exifValue)
+ {
+ object value = exifValue.GetValue();
+
+ if (exifValue.DataType == ExifDataType.Ascii)
+ {
+ return (uint)Encoding.UTF8.GetBytes((string)value).Length + 1;
+ }
+
+ if (value is Array arrayValue)
+ {
+ return (uint)arrayValue.Length;
+ }
+
+ return 1;
+ }
+
+ private int WriteArray(IExifValue value, Span destination, int offset)
{
if (value.DataType == ExifDataType.Ascii)
{
- return this.WriteValue(ExifDataType.Ascii, value.Value, destination, offset);
+ return this.WriteValue(ExifDataType.Ascii, value.GetValue(), destination, offset);
}
int newOffset = offset;
- foreach (object obj in (Array)value.Value)
+ foreach (object obj in (Array)value.GetValue())
{
newOffset = this.WriteValue(value.DataType, obj, destination, newOffset);
}
@@ -259,7 +296,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
return newOffset;
}
- private int WriteData(uint startIndex, List indexes, Span destination, int offset)
+ private int WriteData(uint startIndex, List values, Span destination, int offset)
{
if (this.dataOffsets.Count == 0)
{
@@ -269,10 +306,9 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
int newOffset = offset;
int i = 0;
- foreach (int index in indexes)
+ foreach (IExifValue value in values)
{
- ExifValue value = this.values[index];
- if (value.Length > 4)
+ if (GetLength(value) > 4)
{
WriteUInt32((uint)(newOffset - startIndex), destination, this.dataOffsets[i++]);
newOffset = this.WriteValue(value, destination, newOffset);
@@ -282,25 +318,25 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
return newOffset;
}
- private int WriteHeaders(List indexes, Span destination, int offset)
+ private int WriteHeaders(List values, Span destination, int offset)
{
this.dataOffsets = new List();
- int newOffset = WriteUInt16((ushort)indexes.Count, destination, offset);
+ int newOffset = WriteUInt16((ushort)values.Count, destination, offset);
- if (indexes.Count == 0)
+ if (values.Count == 0)
{
return newOffset;
}
- foreach (int index in indexes)
+ foreach (IExifValue value in values)
{
- ExifValue value = this.values[index];
newOffset = WriteUInt16((ushort)value.Tag, destination, newOffset);
newOffset = WriteUInt16((ushort)value.DataType, destination, newOffset);
- newOffset = WriteUInt32((uint)value.NumberOfComponents, destination, newOffset);
+ newOffset = WriteUInt32(GetNumberOfComponents(value), destination, newOffset);
- if (value.Length > 4)
+ uint length = GetLength(value);
+ if (length > 4)
{
this.dataOffsets.Add(newOffset);
}
@@ -332,7 +368,9 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
switch (dataType)
{
case ExifDataType.Ascii:
- return Write(Encoding.UTF8.GetBytes((string)value), destination, offset);
+ offset = Write(Encoding.UTF8.GetBytes((string)value), destination, offset);
+ destination[offset] = 0;
+ return offset + 1;
case ExifDataType.Byte:
case ExifDataType.Undefined:
destination[offset] = (byte)value;
@@ -340,8 +378,18 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
case ExifDataType.DoubleFloat:
return WriteDouble((double)value, destination, offset);
case ExifDataType.Short:
+ if (value is Number shortNumber)
+ {
+ return WriteUInt16((ushort)shortNumber, destination, offset);
+ }
+
return WriteUInt16((ushort)value, destination, offset);
case ExifDataType.Long:
+ if (value is Number longNumber)
+ {
+ return WriteUInt32((uint)longNumber, destination, offset);
+ }
+
return WriteUInt32((uint)value, destination, offset);
case ExifDataType.Rational:
WriteRational(destination.Slice(offset, 8), (Rational)value);
@@ -363,14 +411,14 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
}
}
- private int WriteValue(ExifValue value, Span destination, int offset)
+ private int WriteValue(IExifValue value, Span destination, int offset)
{
if (value.IsArray && value.DataType != ExifDataType.Ascii)
{
return this.WriteArray(value, destination, offset);
}
- return this.WriteValue(value.DataType, value.Value, destination, offset);
+ return this.WriteValue(value.DataType, value.GetValue(), destination, offset);
}
}
}
diff --git a/src/ImageSharp/Metadata/Profiles/Exif/README.md b/src/ImageSharp/Metadata/Profiles/Exif/README.md
new file mode 100644
index 0000000000..7901527e1a
--- /dev/null
+++ b/src/ImageSharp/Metadata/Profiles/Exif/README.md
@@ -0,0 +1,3 @@
+Adapted from Magick.NET:
+
+https://github.com/dlemstra/Magick.NET
diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.Byte.cs b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.Byte.cs
new file mode 100644
index 0000000000..dc33fd8b0a
--- /dev/null
+++ b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.Byte.cs
@@ -0,0 +1,24 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
+{
+ ///
+ public abstract partial class ExifTag
+ {
+ ///
+ /// Gets the FaxProfile exif tag.
+ ///
+ public static ExifTag FaxProfile { get; } = new ExifTag(ExifTagValue.FaxProfile);
+
+ ///
+ /// Gets the ModeNumber exif tag.
+ ///
+ public static ExifTag ModeNumber { get; } = new ExifTag(ExifTagValue.ModeNumber);
+
+ ///
+ /// Gets the GPSAltitudeRef exif tag.
+ ///
+ public static ExifTag GPSAltitudeRef { get; } = new ExifTag(ExifTagValue.GPSAltitudeRef);
+ }
+}
diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.ByteArray.cs b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.ByteArray.cs
new file mode 100644
index 0000000000..2bfa8ff213
--- /dev/null
+++ b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.ByteArray.cs
@@ -0,0 +1,64 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
+{
+ ///
+ public abstract partial class ExifTag
+ {
+ ///
+ /// Gets the ClipPath exif tag.
+ ///
+ public static ExifTag ClipPath => new ExifTag(ExifTagValue.ClipPath);
+
+ ///
+ /// Gets the VersionYear exif tag.
+ ///
+ public static ExifTag VersionYear => new ExifTag(ExifTagValue.VersionYear);
+
+ ///
+ /// Gets the XMP exif tag.
+ ///
+ public static ExifTag XMP => new ExifTag(ExifTagValue.XMP);
+
+ ///
+ /// Gets the CFAPattern2 exif tag.
+ ///
+ public static ExifTag CFAPattern2 => new ExifTag(ExifTagValue.CFAPattern2);
+
+ ///
+ /// Gets the TIFFEPStandardID exif tag.
+ ///
+ public static ExifTag TIFFEPStandardID => new ExifTag(ExifTagValue.TIFFEPStandardID);
+
+ ///
+ /// Gets the XPTitle exif tag.
+ ///
+ public static ExifTag XPTitle => new ExifTag(ExifTagValue.XPTitle);
+
+ ///
+ /// Gets the XPComment exif tag.
+ ///
+ public static ExifTag XPComment => new ExifTag(ExifTagValue.XPComment);
+
+ ///
+ /// Gets the XPAuthor exif tag.
+ ///
+ public static ExifTag XPAuthor => new ExifTag(ExifTagValue.XPAuthor);
+
+ ///
+ /// Gets the XPKeywords exif tag.
+ ///
+ public static ExifTag XPKeywords => new ExifTag(ExifTagValue.XPKeywords);
+
+ ///
+ /// Gets the XPSubject exif tag.
+ ///
+ public static ExifTag XPSubject => new ExifTag(ExifTagValue.XPSubject);
+
+ ///
+ /// Gets the GPSVersionID exif tag.
+ ///
+ public static ExifTag GPSVersionID => new ExifTag(ExifTagValue.GPSVersionID);
+ }
+}
diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.DoubleArray.cs b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.DoubleArray.cs
new file mode 100644
index 0000000000..6cbae4c558
--- /dev/null
+++ b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.DoubleArray.cs
@@ -0,0 +1,29 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
+{
+ ///
+ public abstract partial class ExifTag
+ {
+ ///
+ /// Gets the PixelScale exif tag.
+ ///
+ public static ExifTag PixelScale { get; } = new ExifTag(ExifTagValue.PixelScale);
+
+ ///
+ /// Gets the IntergraphMatrix exif tag.
+ ///
+ public static ExifTag IntergraphMatrix { get; } = new ExifTag(ExifTagValue.IntergraphMatrix);
+
+ ///
+ /// Gets the ModelTiePoint exif tag.
+ ///
+ public static ExifTag ModelTiePoint { get; } = new ExifTag(ExifTagValue.ModelTiePoint);
+
+ ///
+ /// Gets the ModelTransform exif tag.
+ ///
+ public static ExifTag ModelTransform { get; } = new ExifTag(ExifTagValue.ModelTransform);
+ }
+}
diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.Long.cs b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.Long.cs
new file mode 100644
index 0000000000..571b50efb6
--- /dev/null
+++ b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.Long.cs
@@ -0,0 +1,114 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
+{
+ ///
+ public abstract partial class ExifTag
+ {
+ ///
+ /// Gets the SubfileType exif tag.
+ ///
+ public static ExifTag SubfileType { get; } = new ExifTag(ExifTagValue.SubfileType);
+
+ ///
+ /// Gets the SubIFDOffset exif tag.
+ ///
+ public static ExifTag SubIFDOffset { get; } = new ExifTag(ExifTagValue.SubIFDOffset);
+
+ ///
+ /// Gets the GPSIFDOffset exif tag.
+ ///
+ public static ExifTag GPSIFDOffset { get; } = new ExifTag(ExifTagValue.GPSIFDOffset);
+
+ ///
+ /// Gets the T4Options exif tag.
+ ///
+ public static ExifTag T4Options { get; } = new ExifTag(ExifTagValue.T4Options);
+
+ ///
+ /// Gets the T6Options exif tag.
+ ///
+ public static ExifTag T6Options { get; } = new ExifTag(ExifTagValue.T6Options);
+
+ ///
+ /// Gets the XClipPathUnits exif tag.
+ ///
+ public static ExifTag XClipPathUnits { get; } = new ExifTag(ExifTagValue.XClipPathUnits);
+
+ ///
+ /// Gets the YClipPathUnits exif tag.
+ ///
+ public static ExifTag YClipPathUnits { get; } = new ExifTag(ExifTagValue.YClipPathUnits);
+
+ ///
+ /// Gets the ProfileType exif tag.
+ ///
+ public static ExifTag ProfileType { get; } = new ExifTag(ExifTagValue.ProfileType);
+
+ ///
+ /// Gets the CodingMethods exif tag.
+ ///
+ public static ExifTag CodingMethods { get; } = new ExifTag(ExifTagValue.CodingMethods);
+
+ ///
+ /// Gets the T82ptions exif tag.
+ ///
+ public static ExifTag T82ptions { get; } = new ExifTag(ExifTagValue.T82ptions);
+
+ ///
+ /// Gets the JPEGInterchangeFormat exif tag.
+ ///
+ public static ExifTag JPEGInterchangeFormat { get; } = new ExifTag(ExifTagValue.JPEGInterchangeFormat);
+
+ ///
+ /// Gets the JPEGInterchangeFormatLength exif tag.
+ ///
+ public static ExifTag JPEGInterchangeFormatLength { get; } = new ExifTag(ExifTagValue.JPEGInterchangeFormatLength);
+
+ ///
+ /// Gets the MDFileTag exif tag.
+ ///
+ public static ExifTag MDFileTag { get; } = new ExifTag(ExifTagValue.MDFileTag);
+
+ ///
+ /// Gets the StandardOutputSensitivity exif tag.
+ ///
+ public static ExifTag StandardOutputSensitivity { get; } = new ExifTag(ExifTagValue.StandardOutputSensitivity);
+
+ ///
+ /// Gets the RecommendedExposureIndex exif tag.
+ ///
+ public static ExifTag RecommendedExposureIndex { get; } = new ExifTag(ExifTagValue.RecommendedExposureIndex);
+
+ ///
+ /// Gets the ISOSpeed exif tag.
+ ///
+ public static ExifTag ISOSpeed { get; } = new ExifTag(ExifTagValue.ISOSpeed);
+
+ ///
+ /// Gets the ISOSpeedLatitudeyyy exif tag.
+ ///
+ public static ExifTag ISOSpeedLatitudeyyy { get; } = new ExifTag(ExifTagValue.ISOSpeedLatitudeyyy);
+
+ ///
+ /// Gets the ISOSpeedLatitudezzz exif tag.
+ ///
+ public static ExifTag ISOSpeedLatitudezzz { get; } = new ExifTag(ExifTagValue.ISOSpeedLatitudezzz);
+
+ ///
+ /// Gets the FaxRecvParams exif tag.
+ ///
+ public static ExifTag FaxRecvParams { get; } = new ExifTag(ExifTagValue.FaxRecvParams);
+
+ ///
+ /// Gets the FaxRecvTime exif tag.
+ ///
+ public static ExifTag FaxRecvTime { get; } = new ExifTag(ExifTagValue.FaxRecvTime);
+
+ ///
+ /// Gets the ImageNumber exif tag.
+ ///
+ public static ExifTag ImageNumber { get; } = new ExifTag(ExifTagValue.ImageNumber);
+ }
+}
diff --git a/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.LongArray.cs b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.LongArray.cs
new file mode 100644
index 0000000000..120f2dab0f
--- /dev/null
+++ b/src/ImageSharp/Metadata/Profiles/Exif/Tags/ExifTag.LongArray.cs
@@ -0,0 +1,69 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
+{
+ ///
+ public abstract partial class ExifTag
+ {
+ ///
+ /// Gets the FreeOffsets exif tag.
+ ///
+ public static ExifTag FreeOffsets { get; } = new ExifTag(ExifTagValue.FreeOffsets);
+
+ ///
+ /// Gets the FreeByteCounts exif tag.
+ ///
+ public static ExifTag FreeByteCounts { get; } = new ExifTag(ExifTagValue.FreeByteCounts);
+
+ ///
+ /// Gets the ColorResponseUnit exif tag.
+ ///
+ public static ExifTag ColorResponseUnit { get; } = new ExifTag(ExifTagValue.ColorResponseUnit);
+
+ ///
+ /// Gets the TileOffsets exif tag.
+ ///
+ public static ExifTag TileOffsets { get; } = new ExifTag(ExifTagValue.TileOffsets);
+
+ ///
+ /// Gets the SMinSampleValue exif tag.
+ ///
+ public static ExifTag SMinSampleValue { get; } = new ExifTag(ExifTagValue.SMinSampleValue);
+
+ ///
+ /// Gets the SMaxSampleValue exif tag.
+ ///
+ public static ExifTag SMaxSampleValue { get; } = new ExifTag(ExifTagValue.SMaxSampleValue);
+
+ ///
+ /// Gets the JPEGQTables exif tag.
+ ///
+ public static ExifTag JPEGQTables { get; } = new ExifTag(ExifTagValue.JPEGQTables);
+
+ ///
+ /// Gets the JPEGDCTables exif tag.
+ ///
+ public static ExifTag JPEGDCTables { get; } = new ExifTag