Browse Source

Merge pull request #1570 from IldarKhayrutdinov/tiff-format

Remove TiffFrameMetadataResolutionExtensions class
pull/1553/head
James Jackson-South 5 years ago
committed by GitHub
parent
commit
ecd63982a7
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 50
      src/ImageSharp/Common/Helpers/UnitConverter.cs
  2. 78
      src/ImageSharp/Formats/Tiff/TiffEncoderEntriesCollector.cs
  3. 48
      src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs
  4. 97
      src/ImageSharp/Formats/Tiff/TiffFrameMetadataResolutionExtensions.cs
  5. 6
      src/ImageSharp/Formats/Tiff/Writers/TiffBaseColorWriter.cs
  6. 2
      src/ImageSharp/Formats/Tiff/Writers/TiffPaletteWriter.cs
  7. 2
      tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs

50
src/ImageSharp/Common/Helpers/UnitConverter.cs

@ -30,6 +30,11 @@ namespace SixLabors.ImageSharp.Common.Helpers
/// </summary> /// </summary>
private const double InchesInMeter = 1 / 0.0254D; private const double InchesInMeter = 1 / 0.0254D;
/// <summary>
/// The default resolution unit value.
/// </summary>
private const PixelResolutionUnit DefaultResolutionUnit = PixelResolutionUnit.PixelsPerInch;
/// <summary> /// <summary>
/// Scales the value from centimeters to meters. /// Scales the value from centimeters to meters.
/// </summary> /// </summary>
@ -89,7 +94,50 @@ namespace SixLabors.ImageSharp.Common.Helpers
IExifValue<ushort> resolution = profile.GetValue(ExifTag.ResolutionUnit); IExifValue<ushort> resolution = profile.GetValue(ExifTag.ResolutionUnit);
// EXIF is 1, 2, 3 so we minus "1" off the result. // EXIF is 1, 2, 3 so we minus "1" off the result.
return resolution is null ? default : (PixelResolutionUnit)(byte)(resolution.Value - 1); return resolution is null ? DefaultResolutionUnit : (PixelResolutionUnit)(byte)(resolution.Value - 1);
}
/// <summary>
/// Sets the exif profile resolution values.
/// </summary>
/// <param name="exifProfile">The exif profile.</param>
/// <param name="unit">The resolution unit.</param>
/// <param name="horizontal">The horizontal resolution value.</param>
/// <param name="vertical">The vertical resolution value.</param>
[MethodImpl(InliningOptions.ShortMethod)]
public static void SetResolutionValues(ExifProfile exifProfile, PixelResolutionUnit unit, double horizontal, double vertical)
{
switch (unit)
{
case PixelResolutionUnit.AspectRatio:
case PixelResolutionUnit.PixelsPerInch:
case PixelResolutionUnit.PixelsPerCentimeter:
break;
case PixelResolutionUnit.PixelsPerMeter:
{
unit = PixelResolutionUnit.PixelsPerCentimeter;
horizontal = UnitConverter.MeterToCm(horizontal);
vertical = UnitConverter.MeterToCm(vertical);
}
break;
default:
unit = PixelResolutionUnit.PixelsPerInch;
break;
}
exifProfile.SetValue(ExifTag.ResolutionUnit, (ushort)(unit + 1));
if (unit == PixelResolutionUnit.AspectRatio)
{
exifProfile.RemoveValue(ExifTag.XResolution);
exifProfile.RemoveValue(ExifTag.YResolution);
}
else
{
exifProfile.SetValue(ExifTag.XResolution, new Rational(horizontal));
exifProfile.SetValue(ExifTag.YResolution, new Rational(vertical));
}
} }
} }
} }

78
src/ImageSharp/Formats/Tiff/TiffEncoderEntriesCollector.cs

@ -2,7 +2,7 @@
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System.Collections.Generic; using System.Collections.Generic;
using SixLabors.ImageSharp.Common.Helpers;
using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants;
using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.Metadata;
using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.Metadata.Profiles.Exif;
@ -12,6 +12,8 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff
{ {
internal class TiffEncoderEntriesCollector internal class TiffEncoderEntriesCollector
{ {
private const string SoftwareValue = "ImageSharp";
public List<IExifValue> Entries { get; } = new List<IExifValue>(); public List<IExifValue> Entries { get; } = new List<IExifValue>();
public void ProcessGeneral<TPixel>(Image<TPixel> image) public void ProcessGeneral<TPixel>(Image<TPixel> image)
@ -21,18 +23,20 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff
public void ProcessImageFormat(TiffEncoderCore encoder) public void ProcessImageFormat(TiffEncoderCore encoder)
=> new ImageFormatProcessor(this).Process(encoder); => new ImageFormatProcessor(this).Process(encoder);
public void Add(IExifValue entry) public void AddOrReplace(IExifValue entry)
{ {
IExifValue exist = this.Entries.Find(t => t.Tag == entry.Tag); int index = this.Entries.FindIndex(t => t.Tag == entry.Tag);
if (exist != null) if (index >= 0)
{ {
this.Entries.Remove(exist); this.Entries[index] = entry;
}
else
{
this.Entries.Add(entry);
} }
this.Entries.Add(entry);
} }
private void AddInternal(IExifValue entry) => this.Entries.Add(entry); private void Add(IExifValue entry) => this.Entries.Add(entry);
private class GeneralProcessor private class GeneralProcessor
{ {
@ -57,12 +61,12 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff
var software = new ExifString(ExifTagValue.Software) var software = new ExifString(ExifTagValue.Software)
{ {
Value = "ImageSharp" Value = SoftwareValue
}; };
this.collector.AddInternal(width); this.collector.Add(width);
this.collector.AddInternal(height); this.collector.Add(height);
this.collector.AddInternal(software); this.collector.Add(software);
this.ProcessResolution(image.Metadata, frameMetadata); this.ProcessResolution(image.Metadata, frameMetadata);
@ -70,7 +74,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff
this.ProcessMetadata(frameMetadata); this.ProcessMetadata(frameMetadata);
} }
private static bool IsMetadata(ExifTag tag) private static bool IsPureMetadata(ExifTag tag)
{ {
switch ((ExifTagValue)(ushort)tag) switch ((ExifTagValue)(ushort)tag)
{ {
@ -107,29 +111,22 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff
private void ProcessResolution(ImageMetadata imageMetadata, TiffFrameMetadata frameMetadata) private void ProcessResolution(ImageMetadata imageMetadata, TiffFrameMetadata frameMetadata)
{ {
frameMetadata.SetResolutions( UnitConverter.SetResolutionValues(
frameMetadata.ExifProfile,
imageMetadata.ResolutionUnits, imageMetadata.ResolutionUnits,
imageMetadata.HorizontalResolution, imageMetadata.HorizontalResolution,
imageMetadata.VerticalResolution); imageMetadata.VerticalResolution);
var xResolution = new ExifRational(ExifTagValue.XResolution) this.collector.Add(frameMetadata.ExifProfile.GetValue(ExifTag.ResolutionUnit).DeepClone());
{
Value = frameMetadata.ExifProfile.GetValue(ExifTag.XResolution).Value
};
var yResolution = new ExifRational(ExifTagValue.YResolution) IExifValue xResolution = frameMetadata.ExifProfile.GetValue(ExifTag.XResolution)?.DeepClone();
{ IExifValue yResolution = frameMetadata.ExifProfile.GetValue(ExifTag.YResolution)?.DeepClone();
Value = frameMetadata.ExifProfile.GetValue(ExifTag.YResolution).Value
};
var resolutionUnit = new ExifShort(ExifTagValue.ResolutionUnit) if (xResolution != null && yResolution != null)
{ {
Value = frameMetadata.ExifProfile.GetValue(ExifTag.ResolutionUnit).Value this.collector.Add(xResolution);
}; this.collector.Add(yResolution);
}
this.collector.AddInternal(xResolution);
this.collector.AddInternal(yResolution);
this.collector.AddInternal(resolutionUnit);
} }
private void ProcessMetadata(TiffFrameMetadata frameMetadata) private void ProcessMetadata(TiffFrameMetadata frameMetadata)
@ -160,7 +157,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff
break; break;
case ExifParts.IfdTags: case ExifParts.IfdTags:
if (!IsMetadata(entry.Tag)) if (!IsPureMetadata(entry.Tag))
{ {
continue; continue;
} }
@ -170,7 +167,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff
if (!this.collector.Entries.Exists(t => t.Tag == entry.Tag)) if (!this.collector.Entries.Exists(t => t.Tag == entry.Tag))
{ {
this.collector.AddInternal(entry.DeepClone()); this.collector.AddOrReplace(entry.DeepClone());
} }
} }
} }
@ -194,7 +191,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff
Value = imageMetadata.IptcProfile.Data Value = imageMetadata.IptcProfile.Data
}; };
this.collector.AddInternal(iptc); this.collector.Add(iptc);
} }
else else
{ {
@ -208,7 +205,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff
Value = imageMetadata.IccProfile.ToByteArray() Value = imageMetadata.IccProfile.ToByteArray()
}; };
this.collector.AddInternal(icc); this.collector.Add(icc);
} }
else else
{ {
@ -223,7 +220,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff
Value = tiffMetadata.XmpProfile Value = tiffMetadata.XmpProfile
}; };
this.collector.AddInternal(xmp); this.collector.Add(xmp);
} }
else else
{ {
@ -262,10 +259,10 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff
Value = (ushort)encoder.PhotometricInterpretation Value = (ushort)encoder.PhotometricInterpretation
}; };
this.collector.Add(samplesPerPixel); this.collector.AddOrReplace(samplesPerPixel);
this.collector.Add(bitPerSample); this.collector.AddOrReplace(bitPerSample);
this.collector.Add(compression); this.collector.AddOrReplace(compression);
this.collector.Add(photometricInterpretation); this.collector.AddOrReplace(photometricInterpretation);
if (encoder.UseHorizontalPredictor) if (encoder.UseHorizontalPredictor)
{ {
@ -273,7 +270,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff
{ {
var predictor = new ExifShort(ExifTagValue.Predictor) { Value = (ushort)TiffPredictor.Horizontal }; var predictor = new ExifShort(ExifTagValue.Predictor) { Value = (ushort)TiffPredictor.Horizontal };
this.collector.Add(predictor); this.collector.AddOrReplace(predictor);
} }
} }
} }
@ -282,12 +279,11 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff
{ {
switch (encoder.PhotometricInterpretation) switch (encoder.PhotometricInterpretation)
{ {
case TiffPhotometricInterpretation.Rgb:
return 3;
case TiffPhotometricInterpretation.PaletteColor: case TiffPhotometricInterpretation.PaletteColor:
case TiffPhotometricInterpretation.BlackIsZero: case TiffPhotometricInterpretation.BlackIsZero:
case TiffPhotometricInterpretation.WhiteIsZero: case TiffPhotometricInterpretation.WhiteIsZero:
return 1; return 1;
case TiffPhotometricInterpretation.Rgb:
default: default:
return 3; return 3;
} }

48
src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs

@ -3,6 +3,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using SixLabors.ImageSharp.Common.Helpers;
using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants;
using SixLabors.ImageSharp.Formats.Tiff; using SixLabors.ImageSharp.Formats.Tiff;
using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.Metadata;
@ -15,9 +16,6 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff
/// </summary> /// </summary>
public class TiffFrameMetadata : IDeepCloneable public class TiffFrameMetadata : IDeepCloneable
{ {
// 2 (Inch)
internal const ushort DefaultResolutionUnit = 2;
private const TiffPlanarConfiguration DefaultPlanarConfiguration = TiffPlanarConfiguration.Chunky; private const TiffPlanarConfiguration DefaultPlanarConfiguration = TiffPlanarConfiguration.Chunky;
private const TiffPredictor DefaultPredictor = TiffPredictor.None; private const TiffPredictor DefaultPredictor = TiffPredictor.None;
@ -212,13 +210,13 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff
/// Gets the resolution of the image in x- direction. /// Gets the resolution of the image in x- direction.
/// </summary> /// </summary>
/// <value>The density of the image in x- direction.</value> /// <value>The density of the image in x- direction.</value>
public double? HorizontalResolution => this.GetResolution(ExifTag.XResolution); public double? HorizontalResolution => this.ExifProfile.GetValue(ExifTag.XResolution)?.Value.ToDouble();
/// <summary> /// <summary>
/// Gets the resolution of the image in y- direction. /// Gets the resolution of the image in y- direction.
/// </summary> /// </summary>
/// <value>The density of the image in y- direction.</value> /// <value>The density of the image in y- direction.</value>
public double? VerticalResolution => this.GetResolution(ExifTag.YResolution); public double? VerticalResolution => this.ExifProfile.GetValue(ExifTag.YResolution)?.Value.ToDouble();
/// <summary> /// <summary>
/// Gets how the components of each pixel are stored. /// Gets how the components of each pixel are stored.
@ -228,7 +226,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff
/// <summary> /// <summary>
/// Gets the unit of measurement for XResolution and YResolution. /// Gets the unit of measurement for XResolution and YResolution.
/// </summary> /// </summary>
public PixelResolutionUnit ResolutionUnit => this.GetResolutionUnit(); public PixelResolutionUnit ResolutionUnit => UnitConverter.ExifProfileToResolutionUnit(this.ExifProfile);
/// <summary> /// <summary>
/// Gets a color map for palette color images. /// Gets a color map for palette color images.
@ -252,28 +250,16 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff
public TiffSampleFormat[] SampleFormat => this.ExifProfile.GetValue(ExifTag.SampleFormat)?.Value?.Select(a => (TiffSampleFormat)a).ToArray(); public TiffSampleFormat[] SampleFormat => this.ExifProfile.GetValue(ExifTag.SampleFormat)?.Value?.Select(a => (TiffSampleFormat)a).ToArray();
/// <summary> /// <summary>
/// Clears the metadata. /// Clears the pure metadata.
/// </summary> /// </summary>
public void ClearMetadata() public void ClearMetadata()
{ {
var tags = new List<IExifValue>(); var tags = new List<IExifValue>();
foreach (IExifValue entry in this.ExifProfile.Values) foreach (IExifValue entry in this.ExifProfile.Values)
{ {
switch ((ExifTagValue)(ushort)entry.Tag) if (IsFormatTag((ExifTagValue)(ushort)entry.Tag))
{ {
case ExifTagValue.ImageWidth: tags.Add(entry);
case ExifTagValue.ImageLength:
case ExifTagValue.ResolutionUnit:
case ExifTagValue.XResolution:
case ExifTagValue.YResolution:
//// image format tags
case ExifTagValue.Predictor:
case ExifTagValue.PlanarConfiguration:
case ExifTagValue.PhotometricInterpretation:
case ExifTagValue.BitsPerSample:
case ExifTagValue.ColorMap:
tags.Add(entry);
break;
} }
} }
@ -282,5 +268,25 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff
/// <inheritdoc/> /// <inheritdoc/>
public IDeepCloneable DeepClone() => new TiffFrameMetadata() { ExifProfile = this.ExifProfile.DeepClone() }; public IDeepCloneable DeepClone() => new TiffFrameMetadata() { ExifProfile = this.ExifProfile.DeepClone() };
private static bool IsFormatTag(ExifTagValue tag)
{
switch (tag)
{
case ExifTagValue.ImageWidth:
case ExifTagValue.ImageLength:
case ExifTagValue.ResolutionUnit:
case ExifTagValue.XResolution:
case ExifTagValue.YResolution:
case ExifTagValue.Predictor:
case ExifTagValue.PlanarConfiguration:
case ExifTagValue.PhotometricInterpretation:
case ExifTagValue.BitsPerSample:
case ExifTagValue.ColorMap:
return true;
}
return false;
}
} }
} }

97
src/ImageSharp/Formats/Tiff/TiffFrameMetadataResolutionExtensions.cs

@ -1,97 +0,0 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.Common.Helpers;
using SixLabors.ImageSharp.Metadata;
using SixLabors.ImageSharp.Metadata.Profiles.Exif;
namespace SixLabors.ImageSharp.Formats.Experimental.Tiff
{
internal static class TiffFrameMetadataResolutionExtensions
{
public static void SetResolutions(this TiffFrameMetadata meta, PixelResolutionUnit unit, double horizontal, double vertical)
{
switch (unit)
{
case PixelResolutionUnit.AspectRatio:
case PixelResolutionUnit.PixelsPerInch:
case PixelResolutionUnit.PixelsPerCentimeter:
break;
case PixelResolutionUnit.PixelsPerMeter:
{
unit = PixelResolutionUnit.PixelsPerCentimeter;
horizontal = UnitConverter.MeterToCm(horizontal);
vertical = UnitConverter.MeterToCm(vertical);
}
break;
default:
unit = PixelResolutionUnit.PixelsPerInch;
break;
}
meta.ExifProfile.SetValue(ExifTag.ResolutionUnit, (ushort)(unit + 1));
meta.SetResolution(ExifTag.XResolution, horizontal);
meta.SetResolution(ExifTag.YResolution, vertical);
}
public static PixelResolutionUnit GetResolutionUnit(this TiffFrameMetadata meta)
{
ushort res = meta.ExifProfile.GetValue(ExifTag.ResolutionUnit)?.Value ?? TiffFrameMetadata.DefaultResolutionUnit;
return (PixelResolutionUnit)(res - 1);
}
public static double? GetResolution(this TiffFrameMetadata meta, ExifTag<Rational> tag)
{
IExifValue<Rational> resolution = meta.ExifProfile.GetValue(tag);
if (resolution == null)
{
return null;
}
double res = resolution.Value.ToDouble();
switch (meta.ResolutionUnit)
{
case PixelResolutionUnit.AspectRatio:
return 0;
case PixelResolutionUnit.PixelsPerCentimeter:
return UnitConverter.CmToInch(res);
case PixelResolutionUnit.PixelsPerMeter:
return UnitConverter.MeterToInch(res);
case PixelResolutionUnit.PixelsPerInch:
default:
// DefaultResolutionUnit is Inch
return res;
}
}
private static void SetResolution(this TiffFrameMetadata meta, ExifTag<Rational> tag, double? value)
{
if (value == null)
{
meta.ExifProfile.RemoveValue(tag);
return;
}
double res = value.Value;
switch (meta.ResolutionUnit)
{
case PixelResolutionUnit.AspectRatio:
res = 0;
break;
case PixelResolutionUnit.PixelsPerCentimeter:
res = UnitConverter.InchToCm(res);
break;
case PixelResolutionUnit.PixelsPerMeter:
res = UnitConverter.InchToMeter(res);
break;
case PixelResolutionUnit.PixelsPerInch:
default:
break;
}
meta.ExifProfile.SetValue(tag, new Rational(res));
}
}
}

6
src/ImageSharp/Formats/Tiff/Writers/TiffBaseColorWriter.cs

@ -87,17 +87,17 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Writers
/// <param name="stripByteCounts">The strip byte counts.</param> /// <param name="stripByteCounts">The strip byte counts.</param>
private void AddStripTags(int rowsPerStrip, uint[] stripOffsets, uint[] stripByteCounts) private void AddStripTags(int rowsPerStrip, uint[] stripOffsets, uint[] stripByteCounts)
{ {
this.EntriesCollector.Add(new ExifLong(ExifTagValue.RowsPerStrip) this.EntriesCollector.AddOrReplace(new ExifLong(ExifTagValue.RowsPerStrip)
{ {
Value = (uint)rowsPerStrip Value = (uint)rowsPerStrip
}); });
this.EntriesCollector.Add(new ExifLongArray(ExifTagValue.StripOffsets) this.EntriesCollector.AddOrReplace(new ExifLongArray(ExifTagValue.StripOffsets)
{ {
Value = stripOffsets Value = stripOffsets
}); });
this.EntriesCollector.Add(new ExifLongArray(ExifTagValue.StripByteCounts) this.EntriesCollector.AddOrReplace(new ExifLongArray(ExifTagValue.StripByteCounts)
{ {
Value = stripByteCounts Value = stripByteCounts
}); });

2
src/ImageSharp/Formats/Tiff/Writers/TiffPaletteWriter.cs

@ -121,7 +121,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Writers
Value = palette Value = palette
}; };
this.EntriesCollector.Add(colorMap); this.EntriesCollector.AddOrReplace(colorMap);
} }
} }
} }

2
tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs

@ -198,10 +198,10 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
Assert.Equal(1, frame.SamplesPerPixel); Assert.Equal(1, frame.SamplesPerPixel);
Assert.Equal(32u, frame.RowsPerStrip); Assert.Equal(32u, frame.RowsPerStrip);
Assert.Equal(new Number[] { 297u }, frame.StripByteCounts, new NumberComparer()); Assert.Equal(new Number[] { 297u }, frame.StripByteCounts, new NumberComparer());
Assert.Equal(PixelResolutionUnit.PixelsPerInch, frame.ResolutionUnit);
Assert.Equal(10, frame.HorizontalResolution); Assert.Equal(10, frame.HorizontalResolution);
Assert.Equal(10, frame.VerticalResolution); Assert.Equal(10, frame.VerticalResolution);
Assert.Equal(TiffPlanarConfiguration.Chunky, frame.PlanarConfiguration); Assert.Equal(TiffPlanarConfiguration.Chunky, frame.PlanarConfiguration);
Assert.Equal(PixelResolutionUnit.PixelsPerInch, frame.ResolutionUnit);
Assert.Equal("IrfanView", frame.ExifProfile.GetValue(ExifTag.Software).Value); Assert.Equal("IrfanView", frame.ExifProfile.GetValue(ExifTag.Software).Value);
Assert.Null(frame.ExifProfile.GetValue(ExifTag.DateTime)?.Value); Assert.Null(frame.ExifProfile.GetValue(ExifTag.DateTime)?.Value);
Assert.Equal("This is author1;Author2", frame.ExifProfile.GetValue(ExifTag.Artist).Value); Assert.Equal("This is author1;Author2", frame.ExifProfile.GetValue(ExifTag.Artist).Value);

Loading…
Cancel
Save