diff --git a/src/ImageSharp/Common/Helpers/UnitConverter.cs b/src/ImageSharp/Common/Helpers/UnitConverter.cs
index 4a6e6abcb6..efc0e0e152 100644
--- a/src/ImageSharp/Common/Helpers/UnitConverter.cs
+++ b/src/ImageSharp/Common/Helpers/UnitConverter.cs
@@ -30,6 +30,11 @@ namespace SixLabors.ImageSharp.Common.Helpers
///
private const double InchesInMeter = 1 / 0.0254D;
+ ///
+ /// The default resolution unit value.
+ ///
+ private const PixelResolutionUnit DefaultResolutionUnit = PixelResolutionUnit.PixelsPerInch;
+
///
/// Scales the value from centimeters to meters.
///
@@ -89,7 +94,50 @@ namespace SixLabors.ImageSharp.Common.Helpers
IExifValue resolution = profile.GetValue(ExifTag.ResolutionUnit);
// 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);
+ }
+
+ ///
+ /// Sets the exif profile resolution values.
+ ///
+ /// The exif profile.
+ /// The resolution unit.
+ /// The horizontal resolution value.
+ /// The vertical resolution value.
+ [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));
+ }
}
}
}
diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderEntriesCollector.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderEntriesCollector.cs
index 0707ee321a..f1d6114f8f 100644
--- a/src/ImageSharp/Formats/Tiff/TiffEncoderEntriesCollector.cs
+++ b/src/ImageSharp/Formats/Tiff/TiffEncoderEntriesCollector.cs
@@ -2,7 +2,7 @@
// Licensed under the Apache License, Version 2.0.
using System.Collections.Generic;
-
+using SixLabors.ImageSharp.Common.Helpers;
using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants;
using SixLabors.ImageSharp.Metadata;
using SixLabors.ImageSharp.Metadata.Profiles.Exif;
@@ -12,6 +12,8 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff
{
internal class TiffEncoderEntriesCollector
{
+ private const string SoftwareValue = "ImageSharp";
+
public List Entries { get; } = new List();
public void ProcessGeneral(Image image)
@@ -21,18 +23,20 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff
public void ProcessImageFormat(TiffEncoderCore 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);
- if (exist != null)
+ int index = this.Entries.FindIndex(t => t.Tag == entry.Tag);
+ 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
{
@@ -57,12 +61,12 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff
var software = new ExifString(ExifTagValue.Software)
{
- Value = "ImageSharp"
+ Value = SoftwareValue
};
- this.collector.AddInternal(width);
- this.collector.AddInternal(height);
- this.collector.AddInternal(software);
+ this.collector.Add(width);
+ this.collector.Add(height);
+ this.collector.Add(software);
this.ProcessResolution(image.Metadata, frameMetadata);
@@ -70,7 +74,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff
this.ProcessMetadata(frameMetadata);
}
- private static bool IsMetadata(ExifTag tag)
+ private static bool IsPureMetadata(ExifTag tag)
{
switch ((ExifTagValue)(ushort)tag)
{
@@ -107,29 +111,22 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff
private void ProcessResolution(ImageMetadata imageMetadata, TiffFrameMetadata frameMetadata)
{
- frameMetadata.SetResolutions(
+ UnitConverter.SetResolutionValues(
+ frameMetadata.ExifProfile,
imageMetadata.ResolutionUnits,
imageMetadata.HorizontalResolution,
imageMetadata.VerticalResolution);
- var xResolution = new ExifRational(ExifTagValue.XResolution)
- {
- Value = frameMetadata.ExifProfile.GetValue(ExifTag.XResolution).Value
- };
+ this.collector.Add(frameMetadata.ExifProfile.GetValue(ExifTag.ResolutionUnit).DeepClone());
- var yResolution = new ExifRational(ExifTagValue.YResolution)
- {
- Value = frameMetadata.ExifProfile.GetValue(ExifTag.YResolution).Value
- };
+ IExifValue xResolution = frameMetadata.ExifProfile.GetValue(ExifTag.XResolution)?.DeepClone();
+ IExifValue yResolution = frameMetadata.ExifProfile.GetValue(ExifTag.YResolution)?.DeepClone();
- var resolutionUnit = new ExifShort(ExifTagValue.ResolutionUnit)
+ if (xResolution != null && yResolution != null)
{
- Value = frameMetadata.ExifProfile.GetValue(ExifTag.ResolutionUnit).Value
- };
-
- this.collector.AddInternal(xResolution);
- this.collector.AddInternal(yResolution);
- this.collector.AddInternal(resolutionUnit);
+ this.collector.Add(xResolution);
+ this.collector.Add(yResolution);
+ }
}
private void ProcessMetadata(TiffFrameMetadata frameMetadata)
@@ -160,7 +157,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff
break;
case ExifParts.IfdTags:
- if (!IsMetadata(entry.Tag))
+ if (!IsPureMetadata(entry.Tag))
{
continue;
}
@@ -170,7 +167,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff
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
};
- this.collector.AddInternal(iptc);
+ this.collector.Add(iptc);
}
else
{
@@ -208,7 +205,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff
Value = imageMetadata.IccProfile.ToByteArray()
};
- this.collector.AddInternal(icc);
+ this.collector.Add(icc);
}
else
{
@@ -223,7 +220,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff
Value = tiffMetadata.XmpProfile
};
- this.collector.AddInternal(xmp);
+ this.collector.Add(xmp);
}
else
{
@@ -262,10 +259,10 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff
Value = (ushort)encoder.PhotometricInterpretation
};
- this.collector.Add(samplesPerPixel);
- this.collector.Add(bitPerSample);
- this.collector.Add(compression);
- this.collector.Add(photometricInterpretation);
+ this.collector.AddOrReplace(samplesPerPixel);
+ this.collector.AddOrReplace(bitPerSample);
+ this.collector.AddOrReplace(compression);
+ this.collector.AddOrReplace(photometricInterpretation);
if (encoder.UseHorizontalPredictor)
{
@@ -273,7 +270,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff
{
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)
{
- case TiffPhotometricInterpretation.Rgb:
- return 3;
case TiffPhotometricInterpretation.PaletteColor:
case TiffPhotometricInterpretation.BlackIsZero:
case TiffPhotometricInterpretation.WhiteIsZero:
return 1;
+ case TiffPhotometricInterpretation.Rgb:
default:
return 3;
}
diff --git a/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs b/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs
index 5217650da9..242c60974a 100644
--- a/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs
+++ b/src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs
@@ -3,6 +3,7 @@
using System.Collections.Generic;
using System.Linq;
+using SixLabors.ImageSharp.Common.Helpers;
using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants;
using SixLabors.ImageSharp.Formats.Tiff;
using SixLabors.ImageSharp.Metadata;
@@ -15,9 +16,6 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff
///
public class TiffFrameMetadata : IDeepCloneable
{
- // 2 (Inch)
- internal const ushort DefaultResolutionUnit = 2;
-
private const TiffPlanarConfiguration DefaultPlanarConfiguration = TiffPlanarConfiguration.Chunky;
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.
///
/// The density of the image in x- direction.
- public double? HorizontalResolution => this.GetResolution(ExifTag.XResolution);
+ public double? HorizontalResolution => this.ExifProfile.GetValue(ExifTag.XResolution)?.Value.ToDouble();
///
/// Gets the resolution of the image in y- direction.
///
/// The density of the image in y- direction.
- public double? VerticalResolution => this.GetResolution(ExifTag.YResolution);
+ public double? VerticalResolution => this.ExifProfile.GetValue(ExifTag.YResolution)?.Value.ToDouble();
///
/// Gets how the components of each pixel are stored.
@@ -228,7 +226,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff
///
/// Gets the unit of measurement for XResolution and YResolution.
///
- public PixelResolutionUnit ResolutionUnit => this.GetResolutionUnit();
+ public PixelResolutionUnit ResolutionUnit => UnitConverter.ExifProfileToResolutionUnit(this.ExifProfile);
///
/// 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();
///
- /// Clears the metadata.
+ /// Clears the pure metadata.
///
public void ClearMetadata()
{
var tags = new List();
foreach (IExifValue entry in this.ExifProfile.Values)
{
- switch ((ExifTagValue)(ushort)entry.Tag)
+ if (IsFormatTag((ExifTagValue)(ushort)entry.Tag))
{
- case ExifTagValue.ImageWidth:
- 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;
+ tags.Add(entry);
}
}
@@ -282,5 +268,25 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff
///
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;
+ }
}
}
diff --git a/src/ImageSharp/Formats/Tiff/TiffFrameMetadataResolutionExtensions.cs b/src/ImageSharp/Formats/Tiff/TiffFrameMetadataResolutionExtensions.cs
deleted file mode 100644
index 86a128ca3a..0000000000
--- a/src/ImageSharp/Formats/Tiff/TiffFrameMetadataResolutionExtensions.cs
+++ /dev/null
@@ -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 tag)
- {
- IExifValue 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 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));
- }
- }
-}
diff --git a/src/ImageSharp/Formats/Tiff/Writers/TiffBaseColorWriter.cs b/src/ImageSharp/Formats/Tiff/Writers/TiffBaseColorWriter.cs
index 70c91fa892..32adf95c0e 100644
--- a/src/ImageSharp/Formats/Tiff/Writers/TiffBaseColorWriter.cs
+++ b/src/ImageSharp/Formats/Tiff/Writers/TiffBaseColorWriter.cs
@@ -87,17 +87,17 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Writers
/// The strip byte counts.
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
});
- this.EntriesCollector.Add(new ExifLongArray(ExifTagValue.StripOffsets)
+ this.EntriesCollector.AddOrReplace(new ExifLongArray(ExifTagValue.StripOffsets)
{
Value = stripOffsets
});
- this.EntriesCollector.Add(new ExifLongArray(ExifTagValue.StripByteCounts)
+ this.EntriesCollector.AddOrReplace(new ExifLongArray(ExifTagValue.StripByteCounts)
{
Value = stripByteCounts
});
diff --git a/src/ImageSharp/Formats/Tiff/Writers/TiffPaletteWriter.cs b/src/ImageSharp/Formats/Tiff/Writers/TiffPaletteWriter.cs
index b094c22fc0..00f4687207 100644
--- a/src/ImageSharp/Formats/Tiff/Writers/TiffPaletteWriter.cs
+++ b/src/ImageSharp/Formats/Tiff/Writers/TiffPaletteWriter.cs
@@ -121,7 +121,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Writers
Value = palette
};
- this.EntriesCollector.Add(colorMap);
+ this.EntriesCollector.AddOrReplace(colorMap);
}
}
}
diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs
index ceb40d7452..ae26bf6263 100644
--- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs
@@ -93,7 +93,6 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
Assert.Equal(expectedBitsPerPixel, meta.BitsPerPixel);
}
-
[Theory]
[WithFile(RgbUncompressed, PixelTypes.Rgba32, TiffEncoderCompression.CcittGroup3Fax, TiffCompression.CcittGroup3Fax)]
[WithFile(RgbUncompressed, PixelTypes.Rgba32, TiffEncoderCompression.ModifiedHuffman, TiffCompression.Ccitt1D)]
diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs
index dd5fc1a280..54b46cd3ef 100644
--- a/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffMetadataTests.cs
@@ -198,10 +198,10 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
Assert.Equal(1, frame.SamplesPerPixel);
Assert.Equal(32u, frame.RowsPerStrip);
Assert.Equal(new Number[] { 297u }, frame.StripByteCounts, new NumberComparer());
+ Assert.Equal(PixelResolutionUnit.PixelsPerInch, frame.ResolutionUnit);
Assert.Equal(10, frame.HorizontalResolution);
Assert.Equal(10, frame.VerticalResolution);
Assert.Equal(TiffPlanarConfiguration.Chunky, frame.PlanarConfiguration);
- Assert.Equal(PixelResolutionUnit.PixelsPerInch, frame.ResolutionUnit);
Assert.Equal("IrfanView", frame.ExifProfile.GetValue(ExifTag.Software).Value);
Assert.Null(frame.ExifProfile.GetValue(ExifTag.DateTime)?.Value);
Assert.Equal("This is author1;Author2", frame.ExifProfile.GetValue(ExifTag.Artist).Value);