diff --git a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs
index fc73f55a1e..553748f955 100644
--- a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs
+++ b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs
@@ -450,8 +450,8 @@ namespace SixLabors.ImageSharp.Formats.Gif
{
int index = Unsafe.Add(ref indicesRef, i);
- if (this.graphicsControlExtension.TransparencyFlag == false ||
- this.graphicsControlExtension.TransparencyIndex != index)
+ if (!this.graphicsControlExtension.TransparencyFlag
+ || this.graphicsControlExtension.TransparencyIndex != index)
{
ref TPixel pixel = ref Unsafe.Add(ref rowRef, x);
rgba.Rgb = colorTable[index];
@@ -516,14 +516,42 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// The stream containing image data.
private void ReadLogicalScreenDescriptorAndGlobalColorTable(Stream stream)
{
- this.metaData = new ImageMetaData();
-
this.stream = stream;
// Skip the identifier
this.stream.Skip(6);
this.ReadLogicalScreenDescriptor();
+ var meta = new ImageMetaData();
+
+ // The Pixel Aspect Ratio is defined to be the quotient of the pixel's
+ // width over its height. The value range in this field allows
+ // specification of the widest pixel of 4:1 to the tallest pixel of
+ // 1:4 in increments of 1/64th.
+ //
+ // Values : 0 - No aspect ratio information is given.
+ // 1..255 - Value used in the computation.
+ //
+ // Aspect Ratio = (Pixel Aspect Ratio + 15) / 64
+ if (this.logicalScreenDescriptor.PixelAspectRatio > 0)
+ {
+ meta.ResolutionUnits = ResolutionUnits.AspectRatio;
+ float ratio = (this.logicalScreenDescriptor.PixelAspectRatio + 15) / 64F;
+
+ if (ratio > 1)
+ {
+ meta.HorizontalResolution = ratio;
+ meta.VerticalResolution = 1;
+ }
+ else
+ {
+ meta.VerticalResolution = 1 / ratio;
+ meta.HorizontalResolution = 1;
+ }
+ }
+
+ this.metaData = meta;
+
if (this.logicalScreenDescriptor.GlobalColorTableFlag)
{
int globalColorTableLength = this.logicalScreenDescriptor.GlobalColorTableSize * 3;
diff --git a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs
index e4737f3bc5..f5c6ac3531 100644
--- a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs
+++ b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs
@@ -226,11 +226,41 @@ namespace SixLabors.ImageSharp.Formats.Gif
{
byte packedValue = GifLogicalScreenDescriptor.GetPackedValue(useGlobalTable, this.bitDepth - 1, false, this.bitDepth - 1);
+ // The Pixel Aspect Ratio is defined to be the quotient of the pixel's
+ // width over its height. The value range in this field allows
+ // specification of the widest pixel of 4:1 to the tallest pixel of
+ // 1:4 in increments of 1/64th.
+ //
+ // Values : 0 - No aspect ratio information is given.
+ // 1..255 - Value used in the computation.
+ //
+ // Aspect Ratio = (Pixel Aspect Ratio + 15) / 64
+ ImageMetaData meta = image.MetaData;
+ byte ratio = 0;
+
+ if (meta.ResolutionUnits == ResolutionUnits.AspectRatio)
+ {
+ double hr = meta.HorizontalResolution;
+ double vr = meta.VerticalResolution;
+ if (hr != vr)
+ {
+ if (hr > vr)
+ {
+ ratio = (byte)((hr * 64) - 15);
+ }
+ else
+ {
+ ratio = (byte)(((1 / vr) * 64) - 15);
+ }
+ }
+ }
+
var descriptor = new GifLogicalScreenDescriptor(
width: (ushort)image.Width,
height: (ushort)image.Height,
packed: packedValue,
- backgroundColorIndex: unchecked((byte)transparencyIndex));
+ backgroundColorIndex: unchecked((byte)transparencyIndex),
+ ratio);
descriptor.WriteTo(this.buffer);
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JFifMarker.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JFifMarker.cs
index 591af63442..af0ceea10a 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JFifMarker.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JFifMarker.cs
@@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0.
using System;
+using SixLabors.ImageSharp.MetaData;
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
{
@@ -31,7 +32,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
this.MajorVersion = majorVersion;
this.MinorVersion = minorVersion;
- this.DensityUnits = densityUnits;
+ this.DensityUnits = (ResolutionUnits)densityUnits;
this.XDensity = xDensity;
this.YDensity = yDensity;
}
@@ -52,7 +53,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
/// 01 : Pixels per inch (2.54 cm)
/// 02 : Pixels per centimeter
///
- public byte DensityUnits { get; }
+ public ResolutionUnits 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 1310d90d26..aee5fed8f4 100644
--- a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs
+++ b/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs
@@ -210,7 +210,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
int componentCount = 3;
// Write the Start Of Image marker.
- this.WriteApplicationHeader((short)image.MetaData.HorizontalResolution, (short)image.MetaData.VerticalResolution);
+ this.WriteApplicationHeader(
+ (byte)image.MetaData.ResolutionUnits,
+ (short)image.MetaData.HorizontalResolution,
+ (short)image.MetaData.VerticalResolution);
this.WriteProfiles(image);
@@ -425,9 +428,10 @@ 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(short horizontalResolution, short verticalResolution)
+ private void WriteApplicationHeader(byte resolutionUnits, short horizontalResolution, short verticalResolution)
{
// Write the start of image marker. Markers are always prefixed with with 0xff.
this.buffer[0] = JpegConstants.Markers.XFF;
@@ -445,7 +449,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
this.buffer[10] = 0x00; // = "JFIF",'\0'
this.buffer[11] = 0x01; // versionhi
this.buffer[12] = 0x01; // versionlo
- this.buffer[13] = 0x01; // xyunits as dpi
+ this.buffer[13] = resolutionUnits; // xyunits
// Resolution. Big Endian
this.buffer[14] = (byte)(horizontalResolution >> 8);
diff --git a/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs
index a360d54771..be2af7a3a7 100644
--- a/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs
+++ b/src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs
@@ -412,6 +412,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
{
this.MetaData.HorizontalResolution = this.jFif.XDensity;
this.MetaData.VerticalResolution = this.jFif.YDensity;
+ this.MetaData.ResolutionUnits = this.jFif.DensityUnits;
}
else if (this.isExif)
{
@@ -423,10 +424,15 @@ 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) // EXIF is 1,2,3
+ : byte.MinValue;
+
if (horizontalValue > 0 && verticalValue > 0)
{
this.MetaData.HorizontalResolution = horizontalValue;
this.MetaData.VerticalResolution = verticalValue;
+ this.MetaData.ResolutionUnits = (ResolutionUnits)units;
}
}
diff --git a/src/ImageSharp/MetaData/ImageMetaData.cs b/src/ImageSharp/MetaData/ImageMetaData.cs
index af3cc5f5fd..9a7e2d179e 100644
--- a/src/ImageSharp/MetaData/ImageMetaData.cs
+++ b/src/ImageSharp/MetaData/ImageMetaData.cs
@@ -47,6 +47,7 @@ namespace SixLabors.ImageSharp.MetaData
{
this.HorizontalResolution = other.HorizontalResolution;
this.VerticalResolution = other.VerticalResolution;
+ this.ResolutionUnits = other.ResolutionUnits;
this.RepeatCount = other.RepeatCount;
foreach (ImageProperty property in other.Properties)
@@ -99,6 +100,11 @@ namespace SixLabors.ImageSharp.MetaData
}
}
+ ///
+ /// Gets or sets unit of measure used when reporting resolution.
+ ///
+ public ResolutionUnits ResolutionUnits { get; set; } = ResolutionUnits.PixelsPerInch;
+
///
/// Gets or sets the Exif profile.
///
diff --git a/src/ImageSharp/Formats/DensityUnits.cs b/src/ImageSharp/MetaData/ResolutionUnits.cs
similarity index 88%
rename from src/ImageSharp/Formats/DensityUnits.cs
rename to src/ImageSharp/MetaData/ResolutionUnits.cs
index ce30bd06cd..7dfd62de83 100644
--- a/src/ImageSharp/Formats/DensityUnits.cs
+++ b/src/ImageSharp/MetaData/ResolutionUnits.cs
@@ -1,12 +1,12 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
-namespace SixLabors.ImageSharp.Formats
+namespace SixLabors.ImageSharp.MetaData
{
///
/// Provides enumeration of available pixel density units.
///
- public enum DensityUnits : byte
+ public enum ResolutionUnits : 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 332899e8df..e7d8845b4a 100644
--- a/tests/ImageSharp.Tests/Formats/Jpg/JFifMarkerTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Jpg/JFifMarkerTests.cs
@@ -2,7 +2,7 @@
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder;
-
+using SixLabors.ImageSharp.MetaData;
using Xunit;
namespace SixLabors.ImageSharp.Tests.Formats.Jpg
@@ -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(1, marker.DensityUnits);
+ Assert.Equal(ResolutionUnits.PixelsPerInch, marker.DensityUnits);
Assert.Equal(96, marker.XDensity);
Assert.Equal(96, marker.YDensity);
}