Browse Source

Read/Write bmp resolution

pull/649/head
James Jackson-South 8 years ago
parent
commit
965bc1037d
  1. 20
      src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs
  2. 38
      src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs
  3. 20
      src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs
  4. 16
      src/ImageSharp/Formats/Png/PngDecoderCore.cs
  5. 11
      src/ImageSharp/Formats/Png/PngEncoderCore.cs
  6. 13
      src/ImageSharp/MetaData/PixelResolutionUnit.cs

20
src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs

@ -59,6 +59,11 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// </summary>
private Stream stream;
/// <summary>
/// The metadata
/// </summary>
private ImageMetaData metaData;
/// <summary>
/// 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<TPixel>(this.configuration, this.infoHeader.Width, this.infoHeader.Height);
var image = new Image<TPixel>(this.configuration, this.infoHeader.Width, this.infoHeader.Height, this.metaData);
Buffer2D<TPixel> 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);
}
/// <summary>
@ -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);
}

38
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

20
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<byte> hResolution = this.buffer.AsSpan(14, 2);
Span<byte> 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

16
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;
}
/// <summary>

11
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;
}

13
src/ImageSharp/MetaData/PixelResolutionUnit.cs

@ -9,18 +9,23 @@ namespace SixLabors.ImageSharp.MetaData
public enum PixelResolutionUnit : byte
{
/// <summary>
/// No units; width:height pixel aspect ratio = Ydensity:Xdensity
/// No units; width:height pixel aspect ratio.
/// </summary>
AspectRatio = 0,
/// <summary>
/// Pixels per inch (2.54 cm)
/// Pixels per inch (2.54 cm).
/// </summary>
PixelsPerInch = 1, // Other image formats would default to this.
PixelsPerInch = 1,
/// <summary>
/// Pixels per centimeter.
/// </summary>
PixelsPerCentimeter = 2
PixelsPerCentimeter = 2,
/// <summary>
/// Pixels per meter (100 cm).
/// </summary>
PixelsPerMeter = 3
}
}

Loading…
Cancel
Save