Browse Source

Can now preserve correct resolution for jpeg and gif.

pull/649/head
James Jackson-South 8 years ago
parent
commit
9210d4cc35
  1. 36
      src/ImageSharp/Formats/Gif/GifDecoderCore.cs
  2. 32
      src/ImageSharp/Formats/Gif/GifEncoderCore.cs
  3. 5
      src/ImageSharp/Formats/Jpeg/Components/Decoder/JFifMarker.cs
  4. 10
      src/ImageSharp/Formats/Jpeg/JpegEncoderCore.cs
  5. 6
      src/ImageSharp/Formats/Jpeg/PdfJsPort/PdfJsJpegDecoderCore.cs
  6. 6
      src/ImageSharp/MetaData/ImageMetaData.cs
  7. 4
      src/ImageSharp/MetaData/ResolutionUnits.cs
  8. 4
      tests/ImageSharp.Tests/Formats/Jpg/JFifMarkerTests.cs

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

@ -450,8 +450,8 @@ namespace SixLabors.ImageSharp.Formats.Gif
{ {
int index = Unsafe.Add(ref indicesRef, i); int index = Unsafe.Add(ref indicesRef, i);
if (this.graphicsControlExtension.TransparencyFlag == false || if (!this.graphicsControlExtension.TransparencyFlag
this.graphicsControlExtension.TransparencyIndex != index) || this.graphicsControlExtension.TransparencyIndex != index)
{ {
ref TPixel pixel = ref Unsafe.Add(ref rowRef, x); ref TPixel pixel = ref Unsafe.Add(ref rowRef, x);
rgba.Rgb = colorTable[index]; rgba.Rgb = colorTable[index];
@ -516,14 +516,42 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// <param name="stream">The stream containing image data. </param> /// <param name="stream">The stream containing image data. </param>
private void ReadLogicalScreenDescriptorAndGlobalColorTable(Stream stream) private void ReadLogicalScreenDescriptorAndGlobalColorTable(Stream stream)
{ {
this.metaData = new ImageMetaData();
this.stream = stream; this.stream = stream;
// Skip the identifier // Skip the identifier
this.stream.Skip(6); this.stream.Skip(6);
this.ReadLogicalScreenDescriptor(); 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) if (this.logicalScreenDescriptor.GlobalColorTableFlag)
{ {
int globalColorTableLength = this.logicalScreenDescriptor.GlobalColorTableSize * 3; int globalColorTableLength = this.logicalScreenDescriptor.GlobalColorTableSize * 3;

32
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); 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( var descriptor = new GifLogicalScreenDescriptor(
width: (ushort)image.Width, width: (ushort)image.Width,
height: (ushort)image.Height, height: (ushort)image.Height,
packed: packedValue, packed: packedValue,
backgroundColorIndex: unchecked((byte)transparencyIndex)); backgroundColorIndex: unchecked((byte)transparencyIndex),
ratio);
descriptor.WriteTo(this.buffer); descriptor.WriteTo(this.buffer);

5
src/ImageSharp/Formats/Jpeg/Components/Decoder/JFifMarker.cs

@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System; using System;
using SixLabors.ImageSharp.MetaData;
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
{ {
@ -31,7 +32,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
this.MajorVersion = majorVersion; this.MajorVersion = majorVersion;
this.MinorVersion = minorVersion; this.MinorVersion = minorVersion;
this.DensityUnits = densityUnits; this.DensityUnits = (ResolutionUnits)densityUnits;
this.XDensity = xDensity; this.XDensity = xDensity;
this.YDensity = yDensity; this.YDensity = yDensity;
} }
@ -52,7 +53,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 byte DensityUnits { get; } public ResolutionUnits DensityUnits { get; }
/// <summary> /// <summary>
/// Gets the horizontal pixel density. Must not be zero. /// Gets the horizontal pixel density. Must not be zero.

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

@ -210,7 +210,10 @@ 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((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); this.WriteProfiles(image);
@ -425,9 +428,10 @@ 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="horizontalResolution">The resolution of the image in the x- direction.</param> /// <param name="horizontalResolution">The resolution of the image in the x- direction.</param>
/// <param name="verticalResolution">The resolution of the image in the y- direction.</param> /// <param name="verticalResolution">The resolution of the image in the y- direction.</param>
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. // 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;
@ -445,7 +449,7 @@ 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] = 0x01; // xyunits as dpi this.buffer[13] = resolutionUnits; // xyunits
// Resolution. Big Endian // Resolution. Big Endian
this.buffer[14] = (byte)(horizontalResolution >> 8); this.buffer[14] = (byte)(horizontalResolution >> 8);

6
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.HorizontalResolution = this.jFif.XDensity;
this.MetaData.VerticalResolution = this.jFif.YDensity; this.MetaData.VerticalResolution = this.jFif.YDensity;
this.MetaData.ResolutionUnits = this.jFif.DensityUnits;
} }
else if (this.isExif) else if (this.isExif)
{ {
@ -423,10 +424,15 @@ 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) // EXIF is 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;
} }
} }

6
src/ImageSharp/MetaData/ImageMetaData.cs

@ -47,6 +47,7 @@ namespace SixLabors.ImageSharp.MetaData
{ {
this.HorizontalResolution = other.HorizontalResolution; this.HorizontalResolution = other.HorizontalResolution;
this.VerticalResolution = other.VerticalResolution; this.VerticalResolution = other.VerticalResolution;
this.ResolutionUnits = other.ResolutionUnits;
this.RepeatCount = other.RepeatCount; this.RepeatCount = other.RepeatCount;
foreach (ImageProperty property in other.Properties) foreach (ImageProperty property in other.Properties)
@ -99,6 +100,11 @@ namespace SixLabors.ImageSharp.MetaData
} }
} }
/// <summary>
/// Gets or sets unit of measure used when reporting resolution.
/// </summary>
public ResolutionUnits ResolutionUnits { get; set; } = ResolutionUnits.PixelsPerInch;
/// <summary> /// <summary>
/// Gets or sets the Exif profile. /// Gets or sets the Exif profile.
/// </summary> /// </summary>

4
src/ImageSharp/Formats/DensityUnits.cs → src/ImageSharp/MetaData/ResolutionUnits.cs

@ -1,12 +1,12 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Formats namespace SixLabors.ImageSharp.MetaData
{ {
/// <summary> /// <summary>
/// Provides enumeration of available pixel density units. /// Provides enumeration of available pixel density units.
/// </summary> /// </summary>
public enum DensityUnits : byte public enum ResolutionUnits : byte
{ {
/// <summary> /// <summary>
/// No units; width:height pixel aspect ratio = Ydensity:Xdensity /// No units; width:height pixel aspect ratio = Ydensity:Xdensity

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

@ -2,7 +2,7 @@
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder; using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder;
using SixLabors.ImageSharp.MetaData;
using Xunit; using Xunit;
namespace SixLabors.ImageSharp.Tests.Formats.Jpg namespace SixLabors.ImageSharp.Tests.Formats.Jpg
@ -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(1, marker.DensityUnits); Assert.Equal(ResolutionUnits.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