diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs
index 20175613e..d690a3a75 100644
--- a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs
+++ b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs
@@ -59,6 +59,11 @@ namespace SixLabors.ImageSharp.Formats.Bmp
///
private Stream stream;
+ ///
+ /// The metadata
+ ///
+ private ImageMetaData metaData;
+
///
/// The file header containing general information.
/// TODO: Why is this not used? We advance the stream but do not use the values parsed.
@@ -103,7 +108,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
{
this.ReadImageHeaders(stream, out bool inverted, out byte[] palette);
- var image = new Image(this.configuration, this.infoHeader.Width, this.infoHeader.Height);
+ var image = new Image(this.configuration, this.infoHeader.Width, this.infoHeader.Height, this.metaData);
Buffer2D pixels = image.GetRootFramePixelBuffer();
@@ -157,7 +162,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
public IImageInfo Identify(Stream stream)
{
this.ReadImageHeaders(stream, out _, out _);
- return new ImageInfo(new PixelTypeInfo(this.infoHeader.BitsPerPixel), this.infoHeader.Width, this.infoHeader.Height, new ImageMetaData());
+ return new ImageInfo(new PixelTypeInfo(this.infoHeader.BitsPerPixel), this.infoHeader.Width, this.infoHeader.Height, this.metaData);
}
///
@@ -518,6 +523,17 @@ namespace SixLabors.ImageSharp.Formats.Bmp
throw new NotSupportedException($"ImageSharp does not support this BMP file. HeaderSize: {headerSize}.");
}
+ // Resolution is stored in PPM.
+ ImageMetaData meta = new ImageMetaData();
+ if (this.infoHeader.XPelsPerMeter > 0 && this.infoHeader.YPelsPerMeter > 0)
+ {
+ meta.ResolutionUnits = PixelResolutionUnit.PixelsPerMeter;
+ meta.HorizontalResolution = this.infoHeader.XPelsPerMeter;
+ meta.VerticalResolution = this.infoHeader.YPelsPerMeter;
+ }
+
+ this.metaData = meta;
+
// skip the remaining header because we can't read those parts
this.stream.Skip(skipAmount);
}
diff --git a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs
index a4e61f910..80fc6330a 100644
--- a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs
+++ b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs
@@ -3,6 +3,8 @@
using System;
using System.IO;
+using SixLabors.ImageSharp.Common.Helpers;
+using SixLabors.ImageSharp.MetaData;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Memory;
@@ -50,6 +52,38 @@ namespace SixLabors.ImageSharp.Formats.Bmp
int bytesPerLine = 4 * (((image.Width * bpp) + 31) / 32);
this.padding = bytesPerLine - (image.Width * (int)this.bitsPerPixel);
+ // Set Resolution.
+ ImageMetaData meta = image.MetaData;
+ int hResolution = 0;
+ int vResolution = 0;
+
+ if (meta.ResolutionUnits != PixelResolutionUnit.AspectRatio)
+ {
+ if (meta.HorizontalResolution > 0 && meta.VerticalResolution > 0)
+ {
+ switch (meta.ResolutionUnits)
+ {
+ case PixelResolutionUnit.PixelsPerInch:
+
+ hResolution = (int)Math.Round(UnitConverter.InchToMeter(meta.HorizontalResolution));
+ vResolution = (int)Math.Round(UnitConverter.InchToMeter(meta.VerticalResolution));
+ break;
+
+ case PixelResolutionUnit.PixelsPerCentimeter:
+
+ hResolution = (int)Math.Round(UnitConverter.CmToMeter(meta.HorizontalResolution));
+ vResolution = (int)Math.Round(UnitConverter.CmToMeter(meta.VerticalResolution));
+ break;
+
+ case PixelResolutionUnit.PixelsPerMeter:
+ hResolution = (int)Math.Round(meta.HorizontalResolution);
+ vResolution = (int)Math.Round(meta.VerticalResolution);
+
+ break;
+ }
+ }
+ }
+
var infoHeader = new BmpInfoHeader(
headerSize: BmpInfoHeader.Size,
height: image.Height,
@@ -58,7 +92,9 @@ namespace SixLabors.ImageSharp.Formats.Bmp
planes: 1,
imageSize: image.Height * bytesPerLine,
clrUsed: 0,
- clrImportant: 0);
+ clrImportant: 0,
+ xPelsPerMeter: hResolution,
+ yPelsPerMeter: vResolution);
var fileHeader = new BmpFileHeader(
type: 19778, // BM
diff --git a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs
index 80f21c65d..ada33f2b8 100644
--- a/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs
+++ b/src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs
@@ -5,7 +5,7 @@ using System;
using System.Buffers.Binary;
using System.IO;
using System.Runtime.CompilerServices;
-
+using SixLabors.ImageSharp.Common.Helpers;
using SixLabors.ImageSharp.Formats.Jpeg.Components;
using SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder;
using SixLabors.ImageSharp.MetaData;
@@ -448,11 +448,23 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
this.buffer[12] = 0x01; // versionlo
// Resolution. Big Endian
- 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));
+
+ if (meta.ResolutionUnits == PixelResolutionUnit.PixelsPerMeter)
+ {
+ // Scale down to PPI
+ this.buffer[13] = (byte)PixelResolutionUnit.PixelsPerInch; // xyunits
+ BinaryPrimitives.WriteInt16BigEndian(hResolution, (short)Math.Round(UnitConverter.MeterToInch(meta.HorizontalResolution)));
+ BinaryPrimitives.WriteInt16BigEndian(vResolution, (short)Math.Round(UnitConverter.MeterToInch(meta.VerticalResolution)));
+ }
+ else
+ {
+ // We can simply pass the value.
+ this.buffer[13] = (byte)meta.ResolutionUnits; // xyunits
+ 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/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs
index fd2c636bb..38c550344 100644
--- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs
+++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs
@@ -411,18 +411,12 @@ namespace SixLabors.ImageSharp.Formats.Png
int vResolution = BinaryPrimitives.ReadInt32BigEndian(data.Slice(4, 4));
byte unit = data[8];
- if (unit == byte.MinValue)
- {
- metadata.HorizontalResolution = hResolution;
- metadata.VerticalResolution = vResolution;
- metadata.ResolutionUnits = PixelResolutionUnit.AspectRatio;
- return;
- }
+ metadata.ResolutionUnits = unit == byte.MinValue
+ ? PixelResolutionUnit.AspectRatio
+ : PixelResolutionUnit.PixelsPerMeter;
- // Use PPC since original is in meters.
- metadata.HorizontalResolution = UnitConverter.MeterToCm(hResolution);
- metadata.VerticalResolution = UnitConverter.MeterToCm(vResolution);
- metadata.ResolutionUnits = PixelResolutionUnit.PixelsPerCentimeter;
+ metadata.HorizontalResolution = hResolution;
+ metadata.VerticalResolution = vResolution;
}
///
diff --git a/src/ImageSharp/Formats/Png/PngEncoderCore.cs b/src/ImageSharp/Formats/Png/PngEncoderCore.cs
index b8a5c22bb..df0dffa49 100644
--- a/src/ImageSharp/Formats/Png/PngEncoderCore.cs
+++ b/src/ImageSharp/Formats/Png/PngEncoderCore.cs
@@ -623,6 +623,13 @@ namespace SixLabors.ImageSharp.Formats.Png
BinaryPrimitives.WriteInt32BigEndian(vResolution, (int)Math.Round(meta.VerticalResolution));
break;
+ case PixelResolutionUnit.PixelsPerInch:
+
+ 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;
+
case PixelResolutionUnit.PixelsPerCentimeter:
this.chunkDataBuffer[8] = 1; // Per meter
@@ -633,8 +640,8 @@ namespace SixLabors.ImageSharp.Formats.Png
default:
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)));
+ BinaryPrimitives.WriteInt32BigEndian(hResolution, (int)Math.Round(meta.HorizontalResolution));
+ BinaryPrimitives.WriteInt32BigEndian(vResolution, (int)Math.Round(meta.VerticalResolution));
break;
}
diff --git a/src/ImageSharp/MetaData/PixelResolutionUnit.cs b/src/ImageSharp/MetaData/PixelResolutionUnit.cs
index 899dedeb8..ce848004f 100644
--- a/src/ImageSharp/MetaData/PixelResolutionUnit.cs
+++ b/src/ImageSharp/MetaData/PixelResolutionUnit.cs
@@ -9,18 +9,23 @@ namespace SixLabors.ImageSharp.MetaData
public enum PixelResolutionUnit : byte
{
///
- /// No units; width:height pixel aspect ratio = Ydensity:Xdensity
+ /// No units; width:height pixel aspect ratio.
///
AspectRatio = 0,
///
- /// Pixels per inch (2.54 cm)
+ /// Pixels per inch (2.54 cm).
///
- PixelsPerInch = 1, // Other image formats would default to this.
+ PixelsPerInch = 1,
///
/// Pixels per centimeter.
///
- PixelsPerCentimeter = 2
+ PixelsPerCentimeter = 2,
+
+ ///
+ /// Pixels per meter (100 cm).
+ ///
+ PixelsPerMeter = 3
}
}