diff --git a/src/ImageSharp/Common/Helpers/UnitConverter.cs b/src/ImageSharp/Common/Helpers/UnitConverter.cs
new file mode 100644
index 0000000000..9a7d25c559
--- /dev/null
+++ b/src/ImageSharp/Common/Helpers/UnitConverter.cs
@@ -0,0 +1,73 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using System;
+using System.Runtime.CompilerServices;
+using SixLabors.ImageSharp.MetaData;
+using SixLabors.ImageSharp.MetaData.Profiles.Exif;
+
+namespace SixLabors.ImageSharp.Common.Helpers
+{
+ ///
+ /// Contains methods for converting values between unit scales.
+ ///
+ internal static class UnitConverter
+ {
+ ///
+ /// The number of centimeters in a meter.
+ /// 1 cm is equal to exactly 0.01 meters.
+ ///
+ private const double CmsInMeter = 1 / 0.01D;
+
+ ///
+ /// The number of inches in a meter.
+ /// 1 inch is equal to exactly 0.0254 meters.
+ ///
+ private const double InchesInMeter = 1 / 0.0254D;
+
+ ///
+ /// Scales the value from centimeters to meters.
+ ///
+ /// The value to scale.
+ /// The .
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public static double CmToMeter(double x) => x * CmsInMeter;
+
+ ///
+ /// Scales the value from meters to centimeters.
+ ///
+ /// The value to scale.
+ /// The .
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public static double MeterToCm(double x) => x / CmsInMeter;
+
+ ///
+ /// Scales the value from meters to inches.
+ ///
+ /// The value to scale.
+ /// The .
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public static double MeterToInch(double x) => x / InchesInMeter;
+
+ ///
+ /// Scales the value from inches to meters.
+ ///
+ /// The value to scale.
+ /// The .
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public static double InchToMeter(double x) => x * InchesInMeter;
+
+ ///
+ /// Converts an to a .
+ ///
+ /// The EXIF profile containing the value.
+ /// The
+ [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;
+ }
+ }
+}
diff --git a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs
index 553748f955..462f09897c 100644
--- a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs
+++ b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs
@@ -535,7 +535,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
// Aspect Ratio = (Pixel Aspect Ratio + 15) / 64
if (this.logicalScreenDescriptor.PixelAspectRatio > 0)
{
- meta.ResolutionUnits = ResolutionUnits.AspectRatio;
+ meta.ResolutionUnits = PixelResolutionUnit.AspectRatio;
float ratio = (this.logicalScreenDescriptor.PixelAspectRatio + 15) / 64F;
if (ratio > 1)
diff --git a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs
index f5c6ac3531..ea507c7811 100644
--- a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs
+++ b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs
@@ -238,7 +238,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
ImageMetaData meta = image.MetaData;
byte ratio = 0;
- if (meta.ResolutionUnits == ResolutionUnits.AspectRatio)
+ if (meta.ResolutionUnits == PixelResolutionUnit.AspectRatio)
{
double hr = meta.HorizontalResolution;
double vr = meta.VerticalResolution;
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JFifMarker.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JFifMarker.cs
index af0ceea10a..f153ce062a 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JFifMarker.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JFifMarker.cs
@@ -29,10 +29,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
{
Guard.MustBeGreaterThan(xDensity, 0, nameof(xDensity));
Guard.MustBeGreaterThan(yDensity, 0, nameof(yDensity));
+ Guard.MustBeBetweenOrEqualTo(densityUnits, 0, 2, nameof(densityUnits));
this.MajorVersion = majorVersion;
this.MinorVersion = minorVersion;
- this.DensityUnits = (ResolutionUnits)densityUnits;
+ this.DensityUnits = (PixelResolutionUnit)densityUnits;
this.XDensity = xDensity;
this.YDensity = yDensity;
}
@@ -53,7 +54,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
/// 01 : Pixels per inch (2.54 cm)
/// 02 : Pixels per centimeter
///
- public ResolutionUnits DensityUnits { get; }
+ public PixelResolutionUnit DensityUnits { get; }
///
/// Gets the horizontal pixel density. Must not be zero.
diff --git a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs
index aee5fed8f4..80f21c65df 100644
--- a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs
+++ b/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs
@@ -2,11 +2,13 @@
// Licensed under the Apache License, Version 2.0.
using System;
+using System.Buffers.Binary;
using System.IO;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Formats.Jpeg.Components;
using SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder;
+using SixLabors.ImageSharp.MetaData;
using SixLabors.ImageSharp.MetaData.Profiles.Exif;
using SixLabors.ImageSharp.MetaData.Profiles.Icc;
using SixLabors.ImageSharp.PixelFormats;
@@ -210,10 +212,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
int componentCount = 3;
// Write the Start Of Image marker.
- this.WriteApplicationHeader(
- (byte)image.MetaData.ResolutionUnits,
- (short)image.MetaData.HorizontalResolution,
- (short)image.MetaData.VerticalResolution);
+ this.WriteApplicationHeader(image.MetaData);
this.WriteProfiles(image);
@@ -428,10 +427,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
///
/// Writes the application header containing the JFIF identifier plus extra data.
///
- /// The resolution unit of measurement.
- /// The resolution of the image in the x- direction.
- /// The resolution of the image in the y- direction.
- private void WriteApplicationHeader(byte resolutionUnits, short horizontalResolution, short verticalResolution)
+ /// The image meta data.
+ private void WriteApplicationHeader(ImageMetaData meta)
{
// Write the start of image marker. Markers are always prefixed with with 0xff.
this.buffer[0] = JpegConstants.Markers.XFF;
@@ -449,13 +446,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
this.buffer[10] = 0x00; // = "JFIF",'\0'
this.buffer[11] = 0x01; // versionhi
this.buffer[12] = 0x01; // versionlo
- this.buffer[13] = resolutionUnits; // xyunits
// Resolution. Big Endian
- this.buffer[14] = (byte)(horizontalResolution >> 8);
- this.buffer[15] = (byte)horizontalResolution;
- this.buffer[16] = (byte)(verticalResolution >> 8);
- this.buffer[17] = (byte)verticalResolution;
+ this.buffer[13] = (byte)meta.ResolutionUnits; // xyunits
+ Span hResolution = this.buffer.AsSpan(14, 2);
+ Span vResolution = this.buffer.AsSpan(16, 2);
+ BinaryPrimitives.WriteInt16BigEndian(hResolution, (short)Math.Round(meta.HorizontalResolution));
+ BinaryPrimitives.WriteInt16BigEndian(vResolution, (short)Math.Round(meta.VerticalResolution));
// No thumbnail
this.buffer[18] = 0x00; // Thumbnail width
diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs
index 8e2db4e8e9..409908eac0 100644
--- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs
+++ b/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs
@@ -7,7 +7,7 @@ using System.Collections.Generic;
using System.IO;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
-
+using SixLabors.ImageSharp.Common.Helpers;
using SixLabors.ImageSharp.Formats.Jpeg.Components;
using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder;
using SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components;
@@ -424,15 +424,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
? ((Rational)verticalTag.Value).ToDouble()
: 0;
- byte units = this.MetaData.ExifProfile.TryGetValue(ExifTag.ResolutionUnit, out ExifValue resolutionTag)
- ? (byte)(((ushort)resolutionTag.Value) - 1) // ExifTag.ResolutionUnit values are 1, 2, 3
- : byte.MinValue;
-
if (horizontalValue > 0 && verticalValue > 0)
{
this.MetaData.HorizontalResolution = horizontalValue;
this.MetaData.VerticalResolution = verticalValue;
- this.MetaData.ResolutionUnits = (ResolutionUnits)units;
+ this.MetaData.ResolutionUnits = UnitConverter.ExifProfileToResolutionUnit(this.MetaData.ExifProfile);
}
}
diff --git a/src/ImageSharp/Formats/Png/PngConstants.cs b/src/ImageSharp/Formats/Png/PngConstants.cs
index df0e16a17b..ff25e26b7a 100644
--- a/src/ImageSharp/Formats/Png/PngConstants.cs
+++ b/src/ImageSharp/Formats/Png/PngConstants.cs
@@ -41,11 +41,5 @@ namespace SixLabors.ImageSharp.Formats.Png
/// The header bytes as a big endian coded ulong.
///
public const ulong HeaderValue = 0x89504E470D0A1A0AUL;
-
- ///
- /// The number of inches in a meter. Used for converting PPM to PPI in .
- /// One inch is equal to exactly 0.0254 meters.
- ///
- public const double InchesInMeter = 1 / 0.0254D;
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs
index f95b9970ad..fd2c636bbc 100644
--- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs
+++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs
@@ -10,6 +10,7 @@ using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using SixLabors.ImageSharp.Advanced;
+using SixLabors.ImageSharp.Common.Helpers;
using SixLabors.ImageSharp.Formats.Png.Filters;
using SixLabors.ImageSharp.Formats.Png.Zlib;
using SixLabors.ImageSharp.MetaData;
@@ -406,7 +407,6 @@ namespace SixLabors.ImageSharp.Formats.Png
// 1: unit is the meter
//
// When the unit specifier is 0, the pHYs chunk defines pixel aspect ratio only; the actual size of the pixels remains unspecified.
- // Conversion note: one inch is equal to exactly 0.0254 meters.
int hResolution = BinaryPrimitives.ReadInt32BigEndian(data.Slice(0, 4));
int vResolution = BinaryPrimitives.ReadInt32BigEndian(data.Slice(4, 4));
byte unit = data[8];
@@ -415,15 +415,14 @@ namespace SixLabors.ImageSharp.Formats.Png
{
metadata.HorizontalResolution = hResolution;
metadata.VerticalResolution = vResolution;
- metadata.ResolutionUnits = ResolutionUnits.AspectRatio;
+ metadata.ResolutionUnits = PixelResolutionUnit.AspectRatio;
return;
}
- // Use PPI for its commonality.
- const double inchesInMeter = PngConstants.InchesInMeter;
- metadata.HorizontalResolution = hResolution / inchesInMeter;
- metadata.VerticalResolution = vResolution / inchesInMeter;
- metadata.ResolutionUnits = ResolutionUnits.PixelsPerInch;
+ // Use PPC since original is in meters.
+ metadata.HorizontalResolution = UnitConverter.MeterToCm(hResolution);
+ metadata.VerticalResolution = UnitConverter.MeterToCm(vResolution);
+ metadata.ResolutionUnits = PixelResolutionUnit.PixelsPerCentimeter;
}
///
diff --git a/src/ImageSharp/Formats/Png/PngEncoderCore.cs b/src/ImageSharp/Formats/Png/PngEncoderCore.cs
index 6aef2a4d9e..b8a5c22bbc 100644
--- a/src/ImageSharp/Formats/Png/PngEncoderCore.cs
+++ b/src/ImageSharp/Formats/Png/PngEncoderCore.cs
@@ -6,6 +6,7 @@ using System.Buffers.Binary;
using System.IO;
using System.Linq;
using SixLabors.ImageSharp.Advanced;
+using SixLabors.ImageSharp.Common.Helpers;
using SixLabors.ImageSharp.Formats.Png.Filters;
using SixLabors.ImageSharp.Formats.Png.Zlib;
using SixLabors.ImageSharp.MetaData;
@@ -609,32 +610,31 @@ namespace SixLabors.ImageSharp.Formats.Png
// 1: unit is the meter
//
// When the unit specifier is 0, the pHYs chunk defines pixel aspect ratio only; the actual size of the pixels remains unspecified.
- // Conversion note: one inch is equal to exactly 0.0254 meters.
ImageMetaData meta = image.MetaData;
Span hResolution = this.chunkDataBuffer.AsSpan(0, 4);
Span vResolution = this.chunkDataBuffer.AsSpan(4, 4);
+
switch (meta.ResolutionUnits)
{
- case ResolutionUnits.AspectRatio:
+ case PixelResolutionUnit.AspectRatio:
this.chunkDataBuffer[8] = 0;
BinaryPrimitives.WriteInt32BigEndian(hResolution, (int)Math.Round(meta.HorizontalResolution));
- BinaryPrimitives.WriteInt32BigEndian(vResolution, (int)Math.Round(meta.HorizontalResolution));
+ BinaryPrimitives.WriteInt32BigEndian(vResolution, (int)Math.Round(meta.VerticalResolution));
break;
- case ResolutionUnits.PixelsPerCentimeter:
+ case PixelResolutionUnit.PixelsPerCentimeter:
- this.chunkDataBuffer[8] = 1;
- const int CmInMeter = 100;
- BinaryPrimitives.WriteInt32BigEndian(hResolution, (int)Math.Round(meta.HorizontalResolution * CmInMeter));
- BinaryPrimitives.WriteInt32BigEndian(vResolution, (int)Math.Round(meta.HorizontalResolution * CmInMeter));
+ this.chunkDataBuffer[8] = 1; // Per meter
+ BinaryPrimitives.WriteInt32BigEndian(hResolution, (int)Math.Round(UnitConverter.CmToMeter(meta.HorizontalResolution)));
+ BinaryPrimitives.WriteInt32BigEndian(vResolution, (int)Math.Round(UnitConverter.CmToMeter(meta.VerticalResolution)));
break;
default:
- this.chunkDataBuffer[8] = 1;
- BinaryPrimitives.WriteInt32BigEndian(hResolution, (int)Math.Round(meta.HorizontalResolution * PngConstants.InchesInMeter));
- BinaryPrimitives.WriteInt32BigEndian(vResolution, (int)Math.Round(meta.HorizontalResolution * PngConstants.InchesInMeter));
+ this.chunkDataBuffer[8] = 1; // Per meter
+ BinaryPrimitives.WriteInt32BigEndian(hResolution, (int)Math.Round(UnitConverter.InchToMeter(meta.HorizontalResolution)));
+ BinaryPrimitives.WriteInt32BigEndian(vResolution, (int)Math.Round(UnitConverter.InchToMeter(meta.VerticalResolution)));
break;
}
diff --git a/src/ImageSharp/MetaData/ImageMetaData.cs b/src/ImageSharp/MetaData/ImageMetaData.cs
index 9a7e2d179e..af5cc61915 100644
--- a/src/ImageSharp/MetaData/ImageMetaData.cs
+++ b/src/ImageSharp/MetaData/ImageMetaData.cs
@@ -103,7 +103,7 @@ namespace SixLabors.ImageSharp.MetaData
///
/// Gets or sets unit of measure used when reporting resolution.
///
- public ResolutionUnits ResolutionUnits { get; set; } = ResolutionUnits.PixelsPerInch;
+ public PixelResolutionUnit ResolutionUnits { get; set; } = PixelResolutionUnit.PixelsPerInch;
///
/// Gets or sets the Exif profile.
diff --git a/src/ImageSharp/MetaData/ResolutionUnits.cs b/src/ImageSharp/MetaData/PixelResolutionUnit.cs
similarity index 93%
rename from src/ImageSharp/MetaData/ResolutionUnits.cs
rename to src/ImageSharp/MetaData/PixelResolutionUnit.cs
index 7dfd62de83..899dedeb82 100644
--- a/src/ImageSharp/MetaData/ResolutionUnits.cs
+++ b/src/ImageSharp/MetaData/PixelResolutionUnit.cs
@@ -6,7 +6,7 @@ namespace SixLabors.ImageSharp.MetaData
///
/// Provides enumeration of available pixel density units.
///
- public enum ResolutionUnits : byte
+ public enum PixelResolutionUnit : byte
{
///
/// No units; width:height pixel aspect ratio = Ydensity:Xdensity
diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JFifMarkerTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JFifMarkerTests.cs
index e7d8845b4a..b2dc3534d1 100644
--- a/tests/ImageSharp.Tests/Formats/Jpg/JFifMarkerTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Jpg/JFifMarkerTests.cs
@@ -32,7 +32,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
Assert.True(isJFif);
Assert.Equal(1, marker.MajorVersion);
Assert.Equal(1, marker.MinorVersion);
- Assert.Equal(ResolutionUnits.PixelsPerInch, marker.DensityUnits);
+ Assert.Equal(PixelResolutionUnit.PixelsPerInch, marker.DensityUnits);
Assert.Equal(96, marker.XDensity);
Assert.Equal(96, marker.YDensity);
}