Browse Source

Use UnitConverter to abstract complexity.

af/merge-core
James Jackson-South 8 years ago
parent
commit
285784c5b2
  1. 73
      src/ImageSharp/Common/Helpers/UnitConverter.cs
  2. 2
      src/ImageSharp/Formats/Gif/GifDecoderCore.cs
  3. 2
      src/ImageSharp/Formats/Gif/GifEncoderCore.cs
  4. 5
      src/ImageSharp/Formats/Jpeg/Components/Decoder/JFifMarker.cs
  5. 23
      src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs
  6. 8
      src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs
  7. 6
      src/ImageSharp/Formats/Png/PngConstants.cs
  8. 13
      src/ImageSharp/Formats/Png/PngDecoderCore.cs
  9. 22
      src/ImageSharp/Formats/Png/PngEncoderCore.cs
  10. 2
      src/ImageSharp/MetaData/ImageMetaData.cs
  11. 2
      src/ImageSharp/MetaData/PixelResolutionUnit.cs
  12. 2
      tests/ImageSharp.Tests/Formats/Jpg/JFifMarkerTests.cs

73
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
{
/// <summary>
/// Contains methods for converting values between unit scales.
/// </summary>
internal static class UnitConverter
{
/// <summary>
/// The number of centimeters in a meter.
/// 1 cm is equal to exactly 0.01 meters.
/// </summary>
private const double CmsInMeter = 1 / 0.01D;
/// <summary>
/// The number of inches in a meter.
/// 1 inch is equal to exactly 0.0254 meters.
/// </summary>
private const double InchesInMeter = 1 / 0.0254D;
/// <summary>
/// Scales the value from centimeters to meters.
/// </summary>
/// <param name="x">The value to scale.</param>
/// <returns>The <see cref="double"/>.</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public static double CmToMeter(double x) => x * CmsInMeter;
/// <summary>
/// Scales the value from meters to centimeters.
/// </summary>
/// <param name="x">The value to scale.</param>
/// <returns>The <see cref="double"/>.</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public static double MeterToCm(double x) => x / CmsInMeter;
/// <summary>
/// Scales the value from meters to inches.
/// </summary>
/// <param name="x">The value to scale.</param>
/// <returns>The <see cref="double"/>.</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public static double MeterToInch(double x) => x / InchesInMeter;
/// <summary>
/// Scales the value from inches to meters.
/// </summary>
/// <param name="x">The value to scale.</param>
/// <returns>The <see cref="double"/>.</returns>
[MethodImpl(InliningOptions.ShortMethod)]
public static double InchToMeter(double x) => x * InchesInMeter;
/// <summary>
/// Converts an <see cref="ExifTag.ResolutionUnit"/> to a <see cref="PixelResolutionUnit"/>.
/// </summary>
/// <param name="profile">The EXIF profile containing the value.</param>
/// <returns>The <see cref="PixelResolutionUnit"/></returns>
[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;
}
}
}

2
src/ImageSharp/Formats/Gif/GifDecoderCore.cs

@ -535,7 +535,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
// Aspect Ratio = (Pixel Aspect Ratio + 15) / 64 // Aspect Ratio = (Pixel Aspect Ratio + 15) / 64
if (this.logicalScreenDescriptor.PixelAspectRatio > 0) if (this.logicalScreenDescriptor.PixelAspectRatio > 0)
{ {
meta.ResolutionUnits = ResolutionUnits.AspectRatio; meta.ResolutionUnits = PixelResolutionUnit.AspectRatio;
float ratio = (this.logicalScreenDescriptor.PixelAspectRatio + 15) / 64F; float ratio = (this.logicalScreenDescriptor.PixelAspectRatio + 15) / 64F;
if (ratio > 1) if (ratio > 1)

2
src/ImageSharp/Formats/Gif/GifEncoderCore.cs

@ -238,7 +238,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
ImageMetaData meta = image.MetaData; ImageMetaData meta = image.MetaData;
byte ratio = 0; byte ratio = 0;
if (meta.ResolutionUnits == ResolutionUnits.AspectRatio) if (meta.ResolutionUnits == PixelResolutionUnit.AspectRatio)
{ {
double hr = meta.HorizontalResolution; double hr = meta.HorizontalResolution;
double vr = meta.VerticalResolution; double vr = meta.VerticalResolution;

5
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(xDensity, 0, nameof(xDensity));
Guard.MustBeGreaterThan(yDensity, 0, nameof(yDensity)); Guard.MustBeGreaterThan(yDensity, 0, nameof(yDensity));
Guard.MustBeBetweenOrEqualTo(densityUnits, 0, 2, nameof(densityUnits));
this.MajorVersion = majorVersion; this.MajorVersion = majorVersion;
this.MinorVersion = minorVersion; this.MinorVersion = minorVersion;
this.DensityUnits = (ResolutionUnits)densityUnits; this.DensityUnits = (PixelResolutionUnit)densityUnits;
this.XDensity = xDensity; this.XDensity = xDensity;
this.YDensity = yDensity; this.YDensity = yDensity;
} }
@ -53,7 +54,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
/// 01 : Pixels per inch (2.54 cm) /// 01 : Pixels per inch (2.54 cm)
/// 02 : Pixels per centimeter /// 02 : Pixels per centimeter
/// </summary> /// </summary>
public ResolutionUnits DensityUnits { get; } public PixelResolutionUnit DensityUnits { get; }
/// <summary> /// <summary>
/// Gets the horizontal pixel density. Must not be zero. /// Gets the horizontal pixel density. Must not be zero.

23
src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs

@ -2,11 +2,13 @@
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System; using System;
using System.Buffers.Binary;
using System.IO; using System.IO;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Formats.Jpeg.Components; using SixLabors.ImageSharp.Formats.Jpeg.Components;
using SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder; using SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder;
using SixLabors.ImageSharp.MetaData;
using SixLabors.ImageSharp.MetaData.Profiles.Exif; using SixLabors.ImageSharp.MetaData.Profiles.Exif;
using SixLabors.ImageSharp.MetaData.Profiles.Icc; using SixLabors.ImageSharp.MetaData.Profiles.Icc;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
@ -210,10 +212,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
int componentCount = 3; int componentCount = 3;
// Write the Start Of Image marker. // Write the Start Of Image marker.
this.WriteApplicationHeader( this.WriteApplicationHeader(image.MetaData);
(byte)image.MetaData.ResolutionUnits,
(short)image.MetaData.HorizontalResolution,
(short)image.MetaData.VerticalResolution);
this.WriteProfiles(image); this.WriteProfiles(image);
@ -428,10 +427,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
/// <summary> /// <summary>
/// Writes the application header containing the JFIF identifier plus extra data. /// Writes the application header containing the JFIF identifier plus extra data.
/// </summary> /// </summary>
/// <param name="resolutionUnits">The resolution unit of measurement.</param> /// <param name="meta">The image meta data.</param>
/// <param name="horizontalResolution">The resolution of the image in the x- direction.</param> private void WriteApplicationHeader(ImageMetaData meta)
/// <param name="verticalResolution">The resolution of the image in the y- direction.</param>
private void WriteApplicationHeader(byte resolutionUnits, short horizontalResolution, short verticalResolution)
{ {
// Write the start of image marker. Markers are always prefixed with with 0xff. // Write the start of image marker. Markers are always prefixed with with 0xff.
this.buffer[0] = JpegConstants.Markers.XFF; this.buffer[0] = JpegConstants.Markers.XFF;
@ -449,13 +446,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
this.buffer[10] = 0x00; // = "JFIF",'\0' this.buffer[10] = 0x00; // = "JFIF",'\0'
this.buffer[11] = 0x01; // versionhi this.buffer[11] = 0x01; // versionhi
this.buffer[12] = 0x01; // versionlo this.buffer[12] = 0x01; // versionlo
this.buffer[13] = resolutionUnits; // xyunits
// Resolution. Big Endian // Resolution. Big Endian
this.buffer[14] = (byte)(horizontalResolution >> 8); this.buffer[13] = (byte)meta.ResolutionUnits; // xyunits
this.buffer[15] = (byte)horizontalResolution; Span<byte> hResolution = this.buffer.AsSpan(14, 2);
this.buffer[16] = (byte)(verticalResolution >> 8); Span<byte> vResolution = this.buffer.AsSpan(16, 2);
this.buffer[17] = (byte)verticalResolution; BinaryPrimitives.WriteInt16BigEndian(hResolution, (short)Math.Round(meta.HorizontalResolution));
BinaryPrimitives.WriteInt16BigEndian(vResolution, (short)Math.Round(meta.VerticalResolution));
// No thumbnail // No thumbnail
this.buffer[18] = 0x00; // Thumbnail width this.buffer[18] = 0x00; // Thumbnail width

8
src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs

@ -7,7 +7,7 @@ using System.Collections.Generic;
using System.IO; using System.IO;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Common.Helpers;
using SixLabors.ImageSharp.Formats.Jpeg.Components; using SixLabors.ImageSharp.Formats.Jpeg.Components;
using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder; using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder;
using SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components; using SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort.Components;
@ -424,15 +424,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.PdfJsPort
? ((Rational)verticalTag.Value).ToDouble() ? ((Rational)verticalTag.Value).ToDouble()
: 0; : 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) if (horizontalValue > 0 && verticalValue > 0)
{ {
this.MetaData.HorizontalResolution = horizontalValue; this.MetaData.HorizontalResolution = horizontalValue;
this.MetaData.VerticalResolution = verticalValue; this.MetaData.VerticalResolution = verticalValue;
this.MetaData.ResolutionUnits = (ResolutionUnits)units; this.MetaData.ResolutionUnits = UnitConverter.ExifProfileToResolutionUnit(this.MetaData.ExifProfile);
} }
} }

6
src/ImageSharp/Formats/Png/PngConstants.cs

@ -41,11 +41,5 @@ namespace SixLabors.ImageSharp.Formats.Png
/// The header bytes as a big endian coded ulong. /// The header bytes as a big endian coded ulong.
/// </summary> /// </summary>
public const ulong HeaderValue = 0x89504E470D0A1A0AUL; public const ulong HeaderValue = 0x89504E470D0A1A0AUL;
/// <summary>
/// The number of inches in a meter. Used for converting PPM to PPI in <see cref="PngChunkType.Physical"/>.
/// One inch is equal to exactly 0.0254 meters.
/// </summary>
public const double InchesInMeter = 1 / 0.0254D;
} }
} }

13
src/ImageSharp/Formats/Png/PngDecoderCore.cs

@ -10,6 +10,7 @@ using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text; using System.Text;
using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Common.Helpers;
using SixLabors.ImageSharp.Formats.Png.Filters; using SixLabors.ImageSharp.Formats.Png.Filters;
using SixLabors.ImageSharp.Formats.Png.Zlib; using SixLabors.ImageSharp.Formats.Png.Zlib;
using SixLabors.ImageSharp.MetaData; using SixLabors.ImageSharp.MetaData;
@ -406,7 +407,6 @@ namespace SixLabors.ImageSharp.Formats.Png
// 1: unit is the meter // 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. // 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 hResolution = BinaryPrimitives.ReadInt32BigEndian(data.Slice(0, 4));
int vResolution = BinaryPrimitives.ReadInt32BigEndian(data.Slice(4, 4)); int vResolution = BinaryPrimitives.ReadInt32BigEndian(data.Slice(4, 4));
byte unit = data[8]; byte unit = data[8];
@ -415,15 +415,14 @@ namespace SixLabors.ImageSharp.Formats.Png
{ {
metadata.HorizontalResolution = hResolution; metadata.HorizontalResolution = hResolution;
metadata.VerticalResolution = vResolution; metadata.VerticalResolution = vResolution;
metadata.ResolutionUnits = ResolutionUnits.AspectRatio; metadata.ResolutionUnits = PixelResolutionUnit.AspectRatio;
return; return;
} }
// Use PPI for its commonality. // Use PPC since original is in meters.
const double inchesInMeter = PngConstants.InchesInMeter; metadata.HorizontalResolution = UnitConverter.MeterToCm(hResolution);
metadata.HorizontalResolution = hResolution / inchesInMeter; metadata.VerticalResolution = UnitConverter.MeterToCm(vResolution);
metadata.VerticalResolution = vResolution / inchesInMeter; metadata.ResolutionUnits = PixelResolutionUnit.PixelsPerCentimeter;
metadata.ResolutionUnits = ResolutionUnits.PixelsPerInch;
} }
/// <summary> /// <summary>

22
src/ImageSharp/Formats/Png/PngEncoderCore.cs

@ -6,6 +6,7 @@ using System.Buffers.Binary;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Common.Helpers;
using SixLabors.ImageSharp.Formats.Png.Filters; using SixLabors.ImageSharp.Formats.Png.Filters;
using SixLabors.ImageSharp.Formats.Png.Zlib; using SixLabors.ImageSharp.Formats.Png.Zlib;
using SixLabors.ImageSharp.MetaData; using SixLabors.ImageSharp.MetaData;
@ -609,32 +610,31 @@ namespace SixLabors.ImageSharp.Formats.Png
// 1: unit is the meter // 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. // 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; ImageMetaData meta = image.MetaData;
Span<byte> hResolution = this.chunkDataBuffer.AsSpan(0, 4); Span<byte> hResolution = this.chunkDataBuffer.AsSpan(0, 4);
Span<byte> vResolution = this.chunkDataBuffer.AsSpan(4, 4); Span<byte> vResolution = this.chunkDataBuffer.AsSpan(4, 4);
switch (meta.ResolutionUnits) switch (meta.ResolutionUnits)
{ {
case ResolutionUnits.AspectRatio: case PixelResolutionUnit.AspectRatio:
this.chunkDataBuffer[8] = 0; this.chunkDataBuffer[8] = 0;
BinaryPrimitives.WriteInt32BigEndian(hResolution, (int)Math.Round(meta.HorizontalResolution)); 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; break;
case ResolutionUnits.PixelsPerCentimeter: case PixelResolutionUnit.PixelsPerCentimeter:
this.chunkDataBuffer[8] = 1; this.chunkDataBuffer[8] = 1; // Per meter
const int CmInMeter = 100; BinaryPrimitives.WriteInt32BigEndian(hResolution, (int)Math.Round(UnitConverter.CmToMeter(meta.HorizontalResolution)));
BinaryPrimitives.WriteInt32BigEndian(hResolution, (int)Math.Round(meta.HorizontalResolution * CmInMeter)); BinaryPrimitives.WriteInt32BigEndian(vResolution, (int)Math.Round(UnitConverter.CmToMeter(meta.VerticalResolution)));
BinaryPrimitives.WriteInt32BigEndian(vResolution, (int)Math.Round(meta.HorizontalResolution * CmInMeter));
break; break;
default: default:
this.chunkDataBuffer[8] = 1; this.chunkDataBuffer[8] = 1; // Per meter
BinaryPrimitives.WriteInt32BigEndian(hResolution, (int)Math.Round(meta.HorizontalResolution * PngConstants.InchesInMeter)); BinaryPrimitives.WriteInt32BigEndian(hResolution, (int)Math.Round(UnitConverter.InchToMeter(meta.HorizontalResolution)));
BinaryPrimitives.WriteInt32BigEndian(vResolution, (int)Math.Round(meta.HorizontalResolution * PngConstants.InchesInMeter)); BinaryPrimitives.WriteInt32BigEndian(vResolution, (int)Math.Round(UnitConverter.InchToMeter(meta.VerticalResolution)));
break; break;
} }

2
src/ImageSharp/MetaData/ImageMetaData.cs

@ -103,7 +103,7 @@ namespace SixLabors.ImageSharp.MetaData
/// <summary> /// <summary>
/// Gets or sets unit of measure used when reporting resolution. /// Gets or sets unit of measure used when reporting resolution.
/// </summary> /// </summary>
public ResolutionUnits ResolutionUnits { get; set; } = ResolutionUnits.PixelsPerInch; public PixelResolutionUnit ResolutionUnits { get; set; } = PixelResolutionUnit.PixelsPerInch;
/// <summary> /// <summary>
/// Gets or sets the Exif profile. /// Gets or sets the Exif profile.

2
src/ImageSharp/MetaData/ResolutionUnits.cs → src/ImageSharp/MetaData/PixelResolutionUnit.cs

@ -6,7 +6,7 @@ namespace SixLabors.ImageSharp.MetaData
/// <summary> /// <summary>
/// Provides enumeration of available pixel density units. /// Provides enumeration of available pixel density units.
/// </summary> /// </summary>
public enum ResolutionUnits : byte public enum PixelResolutionUnit : byte
{ {
/// <summary> /// <summary>
/// No units; width:height pixel aspect ratio = Ydensity:Xdensity /// No units; width:height pixel aspect ratio = Ydensity:Xdensity

2
tests/ImageSharp.Tests/Formats/Jpg/JFifMarkerTests.cs

@ -32,7 +32,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
Assert.True(isJFif); Assert.True(isJFif);
Assert.Equal(1, marker.MajorVersion); Assert.Equal(1, marker.MajorVersion);
Assert.Equal(1, marker.MinorVersion); 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.XDensity);
Assert.Equal(96, marker.YDensity); Assert.Equal(96, marker.YDensity);
} }

Loading…
Cancel
Save