mirror of https://github.com/SixLabors/ImageSharp
committed by
GitHub
19 changed files with 1187 additions and 593 deletions
@ -1,26 +0,0 @@ |
|||||
// Copyright (c) Six Labors.
|
|
||||
// Licensed under the Apache License, Version 2.0.
|
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Enumeration representing the resolution units defined by the Tiff file-format.
|
|
||||
/// </summary>
|
|
||||
public enum TiffResolutionUnit : ushort |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// No absolute unit of measurement.
|
|
||||
/// </summary>
|
|
||||
None = 1, |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Inch.
|
|
||||
/// </summary>
|
|
||||
Inch = 2, |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Centimeter.
|
|
||||
/// </summary>
|
|
||||
Centimeter = 3 |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,92 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
|
||||
|
using System.Collections.Generic; |
||||
|
|
||||
|
using SixLabors.ImageSharp.Metadata; |
||||
|
using SixLabors.ImageSharp.Metadata.Profiles.Exif; |
||||
|
using SixLabors.ImageSharp.Metadata.Profiles.Icc; |
||||
|
using SixLabors.ImageSharp.Metadata.Profiles.Iptc; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Experimental.Tiff |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// The decoder metadata creator.
|
||||
|
/// </summary>
|
||||
|
internal static class TiffDecoderMetadataCreator |
||||
|
{ |
||||
|
public static ImageMetadata Create(List<TiffFrameMetadata> frames, bool ignoreMetadata, ByteOrder byteOrder) |
||||
|
{ |
||||
|
if (frames.Count < 1) |
||||
|
{ |
||||
|
TiffThrowHelper.ThrowImageFormatException("Expected at least one frame."); |
||||
|
} |
||||
|
|
||||
|
var coreMetadata = new ImageMetadata(); |
||||
|
TiffFrameMetadata rootFrameMetadata = frames[0]; |
||||
|
|
||||
|
coreMetadata.ResolutionUnits = rootFrameMetadata.ResolutionUnit; |
||||
|
if (rootFrameMetadata.HorizontalResolution != null) |
||||
|
{ |
||||
|
coreMetadata.HorizontalResolution = rootFrameMetadata.HorizontalResolution.Value; |
||||
|
} |
||||
|
|
||||
|
if (rootFrameMetadata.VerticalResolution != null) |
||||
|
{ |
||||
|
coreMetadata.VerticalResolution = rootFrameMetadata.VerticalResolution.Value; |
||||
|
} |
||||
|
|
||||
|
TiffMetadata tiffMetadata = coreMetadata.GetTiffMetadata(); |
||||
|
tiffMetadata.ByteOrder = byteOrder; |
||||
|
tiffMetadata.BitsPerPixel = GetBitsPerPixel(rootFrameMetadata); |
||||
|
tiffMetadata.Compression = rootFrameMetadata.Compression; |
||||
|
|
||||
|
if (!ignoreMetadata) |
||||
|
{ |
||||
|
foreach (TiffFrameMetadata frame in frames) |
||||
|
{ |
||||
|
if (tiffMetadata.XmpProfile == null) |
||||
|
{ |
||||
|
byte[] buf = frame.GetArray<byte>(ExifTag.XMP, true); |
||||
|
if (buf != null) |
||||
|
{ |
||||
|
tiffMetadata.XmpProfile = buf; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (coreMetadata.ExifProfile == null) |
||||
|
{ |
||||
|
byte[] buf = frame.GetArray<byte>(ExifTag.SubIFDOffset, true); |
||||
|
if (buf != null) |
||||
|
{ |
||||
|
coreMetadata.ExifProfile = new ExifProfile(buf); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (coreMetadata.IptcProfile == null) |
||||
|
{ |
||||
|
byte[] buf = frame.GetArray<byte>(ExifTag.IPTC, true); |
||||
|
if (buf != null) |
||||
|
{ |
||||
|
coreMetadata.IptcProfile = new IptcProfile(buf); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (coreMetadata.IccProfile == null) |
||||
|
{ |
||||
|
byte[] buf = frame.GetArray<byte>(ExifTag.IccProfile, true); |
||||
|
if (buf != null) |
||||
|
{ |
||||
|
coreMetadata.IccProfile = new IccProfile(buf); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return coreMetadata; |
||||
|
} |
||||
|
|
||||
|
private static TiffBitsPerPixel GetBitsPerPixel(TiffFrameMetadata firstFrameMetaData) |
||||
|
=> (TiffBitsPerPixel)firstFrameMetaData.BitsPerPixel; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,363 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// 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; |
||||
|
using SixLabors.ImageSharp.PixelFormats; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Experimental.Tiff |
||||
|
{ |
||||
|
internal class TiffEncoderEntriesCollector |
||||
|
{ |
||||
|
public List<IExifValue> Entries { get; } = new List<IExifValue>(); |
||||
|
|
||||
|
public void ProcessGeneral<TPixel>(Image<TPixel> image) |
||||
|
where TPixel : unmanaged, IPixel<TPixel> |
||||
|
=> new GeneralProcessor(this).Process(image); |
||||
|
|
||||
|
public void ProcessImageFormat(TiffEncoderCore encoder) |
||||
|
=> new ImageFormatProcessor(this).Process(encoder); |
||||
|
|
||||
|
public void Add(IExifValue entry) |
||||
|
{ |
||||
|
IExifValue exist = this.Entries.Find(t => t.Tag == entry.Tag); |
||||
|
if (exist != null) |
||||
|
{ |
||||
|
this.Entries.Remove(exist); |
||||
|
} |
||||
|
|
||||
|
this.Entries.Add(entry); |
||||
|
} |
||||
|
|
||||
|
private void AddInternal(IExifValue entry) => this.Entries.Add(entry); |
||||
|
|
||||
|
private class GeneralProcessor |
||||
|
{ |
||||
|
private readonly TiffEncoderEntriesCollector collector; |
||||
|
|
||||
|
public GeneralProcessor(TiffEncoderEntriesCollector collector) => this.collector = collector; |
||||
|
|
||||
|
public void Process<TPixel>(Image<TPixel> image) |
||||
|
where TPixel : unmanaged, IPixel<TPixel> |
||||
|
{ |
||||
|
TiffFrameMetadata frameMetadata = image.Frames.RootFrame.Metadata.GetTiffMetadata(); |
||||
|
|
||||
|
var width = new ExifLong(ExifTagValue.ImageWidth) |
||||
|
{ |
||||
|
Value = (uint)image.Width |
||||
|
}; |
||||
|
|
||||
|
var height = new ExifLong(ExifTagValue.ImageLength) |
||||
|
{ |
||||
|
Value = (uint)image.Height |
||||
|
}; |
||||
|
|
||||
|
var software = new ExifString(ExifTagValue.Software) |
||||
|
{ |
||||
|
Value = "ImageSharp" |
||||
|
}; |
||||
|
|
||||
|
this.collector.AddInternal(width); |
||||
|
this.collector.AddInternal(height); |
||||
|
this.collector.AddInternal(software); |
||||
|
|
||||
|
this.ProcessResolution(image.Metadata, frameMetadata); |
||||
|
|
||||
|
this.ProcessProfiles(image.Metadata, frameMetadata); |
||||
|
this.ProcessMetadata(frameMetadata); |
||||
|
} |
||||
|
|
||||
|
private static bool IsMetadata(ExifTag tag) |
||||
|
{ |
||||
|
switch ((ExifTagValue)(ushort)tag) |
||||
|
{ |
||||
|
case ExifTagValue.DocumentName: |
||||
|
case ExifTagValue.ImageDescription: |
||||
|
case ExifTagValue.Make: |
||||
|
case ExifTagValue.Model: |
||||
|
case ExifTagValue.Software: |
||||
|
case ExifTagValue.DateTime: |
||||
|
case ExifTagValue.Artist: |
||||
|
case ExifTagValue.HostComputer: |
||||
|
case ExifTagValue.TargetPrinter: |
||||
|
case ExifTagValue.XMP: |
||||
|
case ExifTagValue.Rating: |
||||
|
case ExifTagValue.RatingPercent: |
||||
|
case ExifTagValue.ImageID: |
||||
|
case ExifTagValue.Copyright: |
||||
|
case ExifTagValue.MDLabName: |
||||
|
case ExifTagValue.MDSampleInfo: |
||||
|
case ExifTagValue.MDPrepDate: |
||||
|
case ExifTagValue.MDPrepTime: |
||||
|
case ExifTagValue.MDFileUnits: |
||||
|
case ExifTagValue.SEMInfo: |
||||
|
case ExifTagValue.XPTitle: |
||||
|
case ExifTagValue.XPComment: |
||||
|
case ExifTagValue.XPAuthor: |
||||
|
case ExifTagValue.XPKeywords: |
||||
|
case ExifTagValue.XPSubject: |
||||
|
return true; |
||||
|
default: |
||||
|
return false; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void ProcessResolution(ImageMetadata imageMetadata, TiffFrameMetadata frameMetadata) |
||||
|
{ |
||||
|
frameMetadata.SetResolutions( |
||||
|
imageMetadata.ResolutionUnits, |
||||
|
imageMetadata.HorizontalResolution, |
||||
|
imageMetadata.VerticalResolution); |
||||
|
|
||||
|
var xResolution = new ExifRational(ExifTagValue.XResolution) |
||||
|
{ |
||||
|
Value = frameMetadata.GetSingle<Rational>(ExifTag.XResolution) |
||||
|
}; |
||||
|
|
||||
|
var yResolution = new ExifRational(ExifTagValue.YResolution) |
||||
|
{ |
||||
|
Value = frameMetadata.GetSingle<Rational>(ExifTag.YResolution) |
||||
|
}; |
||||
|
|
||||
|
var resolutionUnit = new ExifShort(ExifTagValue.ResolutionUnit) |
||||
|
{ |
||||
|
Value = frameMetadata.GetSingle<ushort>(ExifTag.ResolutionUnit) |
||||
|
}; |
||||
|
|
||||
|
this.collector.AddInternal(xResolution); |
||||
|
this.collector.AddInternal(yResolution); |
||||
|
this.collector.AddInternal(resolutionUnit); |
||||
|
} |
||||
|
|
||||
|
private void ProcessMetadata(TiffFrameMetadata frameMetadata) |
||||
|
{ |
||||
|
foreach (IExifValue entry in frameMetadata.FrameTags) |
||||
|
{ |
||||
|
// todo: skip subIfd
|
||||
|
if (entry.DataType == ExifDataType.Ifd) |
||||
|
{ |
||||
|
continue; |
||||
|
} |
||||
|
|
||||
|
switch ((ExifTagValue)(ushort)entry.Tag) |
||||
|
{ |
||||
|
case ExifTagValue.SubIFDOffset: |
||||
|
case ExifTagValue.GPSIFDOffset: |
||||
|
case ExifTagValue.SubIFDs: |
||||
|
case ExifTagValue.XMP: |
||||
|
case ExifTagValue.IPTC: |
||||
|
case ExifTagValue.IccProfile: |
||||
|
continue; |
||||
|
} |
||||
|
|
||||
|
switch (ExifTags.GetPart(entry.Tag)) |
||||
|
{ |
||||
|
case ExifParts.ExifTags: |
||||
|
case ExifParts.GpsTags: |
||||
|
break; |
||||
|
|
||||
|
case ExifParts.IfdTags: |
||||
|
if (!IsMetadata(entry.Tag)) |
||||
|
{ |
||||
|
continue; |
||||
|
} |
||||
|
|
||||
|
break; |
||||
|
} |
||||
|
|
||||
|
if (!this.collector.Entries.Exists(t => t.Tag == entry.Tag)) |
||||
|
{ |
||||
|
this.collector.AddInternal(entry.DeepClone()); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void ProcessProfiles(ImageMetadata imageMetadata, TiffFrameMetadata tiffFrameMetadata) |
||||
|
{ |
||||
|
if (imageMetadata.ExifProfile != null) |
||||
|
{ |
||||
|
// todo: implement processing exif profile
|
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
tiffFrameMetadata.Remove(ExifTag.SubIFDOffset); |
||||
|
} |
||||
|
|
||||
|
if (imageMetadata.IptcProfile != null) |
||||
|
{ |
||||
|
imageMetadata.IptcProfile.UpdateData(); |
||||
|
var iptc = new ExifByteArray(ExifTagValue.IPTC, ExifDataType.Byte) |
||||
|
{ |
||||
|
Value = imageMetadata.IptcProfile.Data |
||||
|
}; |
||||
|
|
||||
|
this.collector.AddInternal(iptc); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
tiffFrameMetadata.Remove(ExifTag.IPTC); |
||||
|
} |
||||
|
|
||||
|
if (imageMetadata.IccProfile != null) |
||||
|
{ |
||||
|
var icc = new ExifByteArray(ExifTagValue.IccProfile, ExifDataType.Undefined) |
||||
|
{ |
||||
|
Value = imageMetadata.IccProfile.ToByteArray() |
||||
|
}; |
||||
|
|
||||
|
this.collector.AddInternal(icc); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
tiffFrameMetadata.Remove(ExifTag.IccProfile); |
||||
|
} |
||||
|
|
||||
|
TiffMetadata tiffMetadata = imageMetadata.GetTiffMetadata(); |
||||
|
if (tiffMetadata.XmpProfile != null) |
||||
|
{ |
||||
|
var xmp = new ExifByteArray(ExifTagValue.XMP, ExifDataType.Byte) |
||||
|
{ |
||||
|
Value = tiffMetadata.XmpProfile |
||||
|
}; |
||||
|
|
||||
|
this.collector.AddInternal(xmp); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
tiffFrameMetadata.Remove(ExifTag.XMP); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private class ImageFormatProcessor |
||||
|
{ |
||||
|
private readonly TiffEncoderEntriesCollector collector; |
||||
|
|
||||
|
public ImageFormatProcessor(TiffEncoderEntriesCollector collector) => this.collector = collector; |
||||
|
|
||||
|
public void Process(TiffEncoderCore encoder) |
||||
|
{ |
||||
|
var samplesPerPixel = new ExifLong(ExifTagValue.SamplesPerPixel) |
||||
|
{ |
||||
|
Value = GetSamplesPerPixel(encoder) |
||||
|
}; |
||||
|
|
||||
|
ushort[] bitsPerSampleValue = GetBitsPerSampleValue(encoder); |
||||
|
var bitPerSample = new ExifShortArray(ExifTagValue.BitsPerSample) |
||||
|
{ |
||||
|
Value = bitsPerSampleValue |
||||
|
}; |
||||
|
|
||||
|
ushort compressionType = GetCompressionType(encoder); |
||||
|
var compression = new ExifShort(ExifTagValue.Compression) |
||||
|
{ |
||||
|
Value = compressionType |
||||
|
}; |
||||
|
|
||||
|
var photometricInterpretation = new ExifShort(ExifTagValue.PhotometricInterpretation) |
||||
|
{ |
||||
|
Value = (ushort)encoder.PhotometricInterpretation |
||||
|
}; |
||||
|
|
||||
|
this.collector.Add(samplesPerPixel); |
||||
|
this.collector.Add(bitPerSample); |
||||
|
this.collector.Add(compression); |
||||
|
this.collector.Add(photometricInterpretation); |
||||
|
|
||||
|
if (encoder.UseHorizontalPredictor) |
||||
|
{ |
||||
|
if (encoder.Mode == TiffEncodingMode.Rgb || encoder.Mode == TiffEncodingMode.Gray || encoder.Mode == TiffEncodingMode.ColorPalette) |
||||
|
{ |
||||
|
var predictor = new ExifShort(ExifTagValue.Predictor) { Value = (ushort)TiffPredictor.Horizontal }; |
||||
|
|
||||
|
this.collector.Add(predictor); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private static uint GetSamplesPerPixel(TiffEncoderCore encoder) |
||||
|
{ |
||||
|
switch (encoder.PhotometricInterpretation) |
||||
|
{ |
||||
|
case TiffPhotometricInterpretation.Rgb: |
||||
|
return 3; |
||||
|
case TiffPhotometricInterpretation.PaletteColor: |
||||
|
case TiffPhotometricInterpretation.BlackIsZero: |
||||
|
case TiffPhotometricInterpretation.WhiteIsZero: |
||||
|
return 1; |
||||
|
default: |
||||
|
return 3; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private static ushort[] GetBitsPerSampleValue(TiffEncoderCore encoder) |
||||
|
{ |
||||
|
switch (encoder.PhotometricInterpretation) |
||||
|
{ |
||||
|
case TiffPhotometricInterpretation.PaletteColor: |
||||
|
return new ushort[] { 8 }; |
||||
|
case TiffPhotometricInterpretation.Rgb: |
||||
|
return new ushort[] { 8, 8, 8 }; |
||||
|
case TiffPhotometricInterpretation.WhiteIsZero: |
||||
|
if (encoder.Mode == TiffEncodingMode.BiColor) |
||||
|
{ |
||||
|
return new ushort[] { 1 }; |
||||
|
} |
||||
|
|
||||
|
return new ushort[] { 8 }; |
||||
|
case TiffPhotometricInterpretation.BlackIsZero: |
||||
|
if (encoder.Mode == TiffEncodingMode.BiColor) |
||||
|
{ |
||||
|
return new ushort[] { 1 }; |
||||
|
} |
||||
|
|
||||
|
return new ushort[] { 8 }; |
||||
|
default: |
||||
|
return new ushort[] { 8, 8, 8 }; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private static ushort GetCompressionType(TiffEncoderCore encoder) |
||||
|
{ |
||||
|
switch (encoder.CompressionType) |
||||
|
{ |
||||
|
case TiffEncoderCompression.Deflate: |
||||
|
// Deflate is allowed for all modes.
|
||||
|
return (ushort)TiffCompression.Deflate; |
||||
|
case TiffEncoderCompression.PackBits: |
||||
|
// PackBits is allowed for all modes.
|
||||
|
return (ushort)TiffCompression.PackBits; |
||||
|
case TiffEncoderCompression.Lzw: |
||||
|
if (encoder.Mode == TiffEncodingMode.Rgb || encoder.Mode == TiffEncodingMode.Gray || encoder.Mode == TiffEncodingMode.ColorPalette) |
||||
|
{ |
||||
|
return (ushort)TiffCompression.Lzw; |
||||
|
} |
||||
|
|
||||
|
break; |
||||
|
|
||||
|
case TiffEncoderCompression.CcittGroup3Fax: |
||||
|
if (encoder.Mode == TiffEncodingMode.BiColor) |
||||
|
{ |
||||
|
return (ushort)TiffCompression.CcittGroup3Fax; |
||||
|
} |
||||
|
|
||||
|
break; |
||||
|
|
||||
|
case TiffEncoderCompression.ModifiedHuffman: |
||||
|
if (encoder.Mode == TiffEncodingMode.BiColor) |
||||
|
{ |
||||
|
return (ushort)TiffCompression.Ccitt1D; |
||||
|
} |
||||
|
|
||||
|
break; |
||||
|
} |
||||
|
|
||||
|
return (ushort)TiffCompression.None; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,200 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
|
||||
|
using System.Linq; |
||||
|
|
||||
|
using SixLabors.ImageSharp.Metadata.Profiles.Exif; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Experimental.Tiff |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// The tiff metadata extensions
|
||||
|
/// </summary>
|
||||
|
internal static class TiffFrameMetadataExtensions |
||||
|
{ |
||||
|
public static T[] GetArray<T>(this TiffFrameMetadata meta, ExifTag tag, bool optional = false) |
||||
|
where T : struct |
||||
|
{ |
||||
|
if (meta.TryGetArray(tag, out T[] result)) |
||||
|
{ |
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
if (!optional) |
||||
|
{ |
||||
|
TiffThrowHelper.ThrowTagNotFound(nameof(tag)); |
||||
|
} |
||||
|
|
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
public static bool TryGetArray<T>(this TiffFrameMetadata meta, ExifTag tag, out T[] result) |
||||
|
where T : struct |
||||
|
{ |
||||
|
foreach (IExifValue entry in meta.FrameTags) |
||||
|
{ |
||||
|
if (entry.Tag == tag) |
||||
|
{ |
||||
|
DebugGuard.IsTrue(entry.IsArray, "Expected array entry"); |
||||
|
|
||||
|
result = (T[])entry.GetValue(); |
||||
|
return true; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
result = null; |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
public static TEnum[] GetEnumArray<TEnum, TTagValue>(this TiffFrameMetadata meta, ExifTag tag, bool optional = false) |
||||
|
where TEnum : struct |
||||
|
where TTagValue : struct |
||||
|
{ |
||||
|
if (meta.TryGetArray(tag, out TTagValue[] result)) |
||||
|
{ |
||||
|
return result.Select(a => (TEnum)(object)a).ToArray(); |
||||
|
} |
||||
|
|
||||
|
if (!optional) |
||||
|
{ |
||||
|
TiffThrowHelper.ThrowTagNotFound(nameof(tag)); |
||||
|
} |
||||
|
|
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
public static string GetString(this TiffFrameMetadata meta, ExifTag tag) |
||||
|
{ |
||||
|
foreach (IExifValue entry in meta.FrameTags) |
||||
|
{ |
||||
|
if (entry.Tag == tag) |
||||
|
{ |
||||
|
DebugGuard.IsTrue(entry.DataType == ExifDataType.Ascii, "Expected string entry"); |
||||
|
object value = entry.GetValue(); |
||||
|
DebugGuard.IsTrue(value is string, "Expected string entry"); |
||||
|
|
||||
|
return (string)value; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
public static bool SetString(this TiffFrameMetadata meta, ExifTag tag, string value) |
||||
|
{ |
||||
|
IExifValue obj = FindOrCreate(meta, tag); |
||||
|
DebugGuard.IsTrue(obj.DataType == ExifDataType.Ascii, "Expected string entry"); |
||||
|
|
||||
|
return obj.TrySetValue(value); |
||||
|
} |
||||
|
|
||||
|
public static TEnum? GetSingleEnumNullable<TEnum, TTagValue>(this TiffFrameMetadata meta, ExifTag tag) |
||||
|
where TEnum : struct |
||||
|
where TTagValue : struct |
||||
|
{ |
||||
|
if (!meta.TryGetSingle(tag, out TTagValue value)) |
||||
|
{ |
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
return (TEnum)(object)value; |
||||
|
} |
||||
|
|
||||
|
public static TEnum GetSingleEnum<TEnum, TTagValue>(this TiffFrameMetadata meta, ExifTag tag, TEnum? defaultValue = null) |
||||
|
where TEnum : struct |
||||
|
where TTagValue : struct |
||||
|
=> meta.GetSingleEnumNullable<TEnum, TTagValue>(tag) ?? (defaultValue != null ? defaultValue.Value : throw TiffThrowHelper.TagNotFound(nameof(tag))); |
||||
|
|
||||
|
public static bool SetSingleEnum<TEnum, TTagValue>(this TiffFrameMetadata meta, ExifTag tag, TEnum value) |
||||
|
where TEnum : struct |
||||
|
where TTagValue : struct |
||||
|
{ |
||||
|
IExifValue obj = FindOrCreate(meta, tag); |
||||
|
|
||||
|
object val = (TTagValue)(object)value; |
||||
|
return obj.TrySetValue(val); |
||||
|
} |
||||
|
|
||||
|
public static T GetSingle<T>(this TiffFrameMetadata meta, ExifTag tag) |
||||
|
where T : struct |
||||
|
{ |
||||
|
if (meta.TryGetSingle(tag, out T result)) |
||||
|
{ |
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
throw TiffThrowHelper.TagNotFound(nameof(tag)); |
||||
|
} |
||||
|
|
||||
|
public static bool TryGetSingle<T>(this TiffFrameMetadata meta, ExifTag tag, out T result) |
||||
|
where T : struct |
||||
|
{ |
||||
|
foreach (IExifValue entry in meta.FrameTags) |
||||
|
{ |
||||
|
if (entry.Tag == tag) |
||||
|
{ |
||||
|
DebugGuard.IsTrue(!entry.IsArray, "Expected non array entry"); |
||||
|
|
||||
|
object value = entry.GetValue(); |
||||
|
|
||||
|
result = (T)value; |
||||
|
return true; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
result = default; |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
public static bool SetSingle<T>(this TiffFrameMetadata meta, ExifTag tag, T value) |
||||
|
where T : struct |
||||
|
{ |
||||
|
IExifValue obj = FindOrCreate(meta, tag); |
||||
|
DebugGuard.IsTrue(!obj.IsArray, "Expected non array entry"); |
||||
|
|
||||
|
object val = (T)(object)value; |
||||
|
return obj.TrySetValue(val); |
||||
|
} |
||||
|
|
||||
|
public static bool Remove(this TiffFrameMetadata meta, ExifTag tag) |
||||
|
{ |
||||
|
IExifValue obj = null; |
||||
|
foreach (IExifValue entry in meta.FrameTags) |
||||
|
{ |
||||
|
if (entry.Tag == tag) |
||||
|
{ |
||||
|
obj = entry; |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (obj != null) |
||||
|
{ |
||||
|
return meta.FrameTags.Remove(obj); |
||||
|
} |
||||
|
|
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
private static IExifValue FindOrCreate(TiffFrameMetadata meta, ExifTag tag) |
||||
|
{ |
||||
|
IExifValue obj = null; |
||||
|
foreach (IExifValue entry in meta.FrameTags) |
||||
|
{ |
||||
|
if (entry.Tag == tag) |
||||
|
{ |
||||
|
obj = entry; |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (obj == null) |
||||
|
{ |
||||
|
obj = ExifValues.Create(tag); |
||||
|
meta.FrameTags.Add(obj); |
||||
|
} |
||||
|
|
||||
|
return obj; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,101 @@ |
|||||
|
// 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.SetSingle(ExifTag.ResolutionUnit, (ushort)unit + 1); |
||||
|
meta.SetResolution(ExifTag.XResolution, horizontal); |
||||
|
meta.SetResolution(ExifTag.YResolution, vertical); |
||||
|
} |
||||
|
|
||||
|
public static PixelResolutionUnit GetResolutionUnit(this TiffFrameMetadata meta) |
||||
|
{ |
||||
|
if (!meta.TryGetSingle(ExifTag.ResolutionUnit, out ushort res)) |
||||
|
{ |
||||
|
res = TiffFrameMetadata.DefaultResolutionUnit; |
||||
|
} |
||||
|
|
||||
|
return (PixelResolutionUnit)(res - 1); |
||||
|
} |
||||
|
|
||||
|
public static double? GetResolution(this TiffFrameMetadata meta, ExifTag tag) |
||||
|
{ |
||||
|
if (!meta.TryGetSingle(tag, out Rational resolution)) |
||||
|
{ |
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
double res = resolution.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.Remove(tag); |
||||
|
return; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
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.SetSingle(tag, new Rational(res)); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue