Skip to content

Commit 11a4e20

Browse files
committed
Add Compression, PhotometricInterpretation and Predictor to TiffFrameMetadata
1 parent d22692e commit 11a4e20

File tree

6 files changed

+134
-59
lines changed

6 files changed

+134
-59
lines changed

src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs

Lines changed: 11 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -14,17 +14,15 @@ namespace SixLabors.ImageSharp.Formats.Tiff
1414
/// </summary>
1515
internal static class TiffDecoderOptionsParser
1616
{
17-
private const TiffPredictor DefaultPredictor = TiffPredictor.None;
18-
1917
private const TiffPlanarConfiguration DefaultPlanarConfiguration = TiffPlanarConfiguration.Chunky;
2018

2119
/// <summary>
2220
/// Determines the TIFF compression and color types, and reads any associated parameters.
2321
/// </summary>
2422
/// <param name="options">The options.</param>
2523
/// <param name="exifProfile">The exif profile of the frame to decode.</param>
26-
/// <param name="entries">The IFD entries container to read the image format information for.</param>
27-
public static void VerifyAndParse(this TiffDecoderCore options, ExifProfile exifProfile, TiffFrameMetadata entries)
24+
/// <param name="frameMetadata">The IFD entries container to read the image format information for current frame.</param>
25+
public static void VerifyAndParse(this TiffDecoderCore options, ExifProfile exifProfile, TiffFrameMetadata frameMetadata)
2826
{
2927
if (exifProfile.GetValue(ExifTag.TileOffsets)?.Value != null)
3028
{
@@ -42,8 +40,7 @@ public static void VerifyAndParse(this TiffDecoderCore options, ExifProfile exif
4240
TiffThrowHelper.ThrowNotSupported("The lower-order bits of the byte FillOrder is not supported.");
4341
}
4442

45-
TiffPredictor predictor = (TiffPredictor?)exifProfile.GetValue(ExifTag.Predictor)?.Value ?? DefaultPredictor;
46-
if (predictor == TiffPredictor.FloatingPoint)
43+
if (frameMetadata.Predictor == TiffPredictor.FloatingPoint)
4744
{
4845
TiffThrowHelper.ThrowNotSupported("TIFF images with FloatingPoint horizontal predictor are not supported.");
4946
}
@@ -68,14 +65,13 @@ public static void VerifyAndParse(this TiffDecoderCore options, ExifProfile exif
6865
VerifyRequiredFieldsArePresent(exifProfile);
6966

7067
options.PlanarConfiguration = (TiffPlanarConfiguration?)exifProfile.GetValue(ExifTag.PlanarConfiguration)?.Value ?? DefaultPlanarConfiguration;
71-
options.Predictor = predictor;
72-
options.PhotometricInterpretation = exifProfile.GetValue(ExifTag.PhotometricInterpretation) != null ?
73-
(TiffPhotometricInterpretation)exifProfile.GetValue(ExifTag.PhotometricInterpretation).Value : TiffPhotometricInterpretation.BlackIsZero;
74-
options.BitsPerPixel = entries.BitsPerPixel != null ? (int)entries.BitsPerPixel.Value : (int)TiffBitsPerPixel.Bit24;
75-
options.BitsPerSample = GetBitsPerSample(entries.BitsPerPixel);
76-
77-
ParseColorType(options, exifProfile);
78-
ParseCompression(options, exifProfile);
68+
options.Predictor = frameMetadata.Predictor ?? TiffFrameMetadata.DefaultPredictor;
69+
options.PhotometricInterpretation = frameMetadata.PhotometricInterpretation ?? TiffFrameMetadata.DefaultPhotometricInterpretation;
70+
options.BitsPerPixel = frameMetadata.BitsPerPixel != null ? (int)frameMetadata.BitsPerPixel.Value : (int)TiffFrameMetadata.DefaultBitsPerPixel;
71+
options.BitsPerSample = GetBitsPerSample(frameMetadata.BitsPerPixel);
72+
73+
options.ParseColorType(exifProfile);
74+
options.ParseCompression(frameMetadata.Compression, exifProfile);
7975
}
8076

8177
private static void VerifyRequiredFieldsArePresent(ExifProfile exifProfile)
@@ -222,9 +218,8 @@ private static void ParseColorType(this TiffDecoderCore options, ExifProfile exi
222218
}
223219
}
224220

225-
private static void ParseCompression(this TiffDecoderCore options, ExifProfile exifProfile)
221+
private static void ParseCompression(this TiffDecoderCore options, TiffCompression? compression, ExifProfile exifProfile)
226222
{
227-
TiffCompression compression = exifProfile.GetValue(ExifTag.Compression) != null ? (TiffCompression)exifProfile.GetValue(ExifTag.Compression).Value : TiffCompression.None;
228223
switch (compression)
229224
{
230225
case TiffCompression.None:

src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -111,19 +111,12 @@ public void Encode<TPixel>(Image<TPixel> image, Stream stream, CancellationToken
111111
: rootFramePhotometricInterpretation;
112112

113113
ImageFrameMetadata rootFrameMetaData = image.Frames.RootFrame.Metadata;
114-
ExifProfile rootFrameExifProfile = image.Frames.RootFrame.Metadata.ExifProfile;
115-
TiffBitsPerPixel? rootFrameBitsPerPixel = rootFrameMetaData.GetTiffMetadata().BitsPerPixel;
114+
TiffFrameMetadata rootFrameTiffMetaData = rootFrameMetaData.GetTiffMetadata();
115+
TiffBitsPerPixel? rootFrameBitsPerPixel = rootFrameTiffMetaData.BitsPerPixel;
116116

117117
// If the user has not chosen a predictor or compression, set the values from the decoded image, if present.
118-
if (!this.HorizontalPredictor.HasValue && rootFrameExifProfile?.GetValue(ExifTag.Predictor) != null)
119-
{
120-
this.HorizontalPredictor = (TiffPredictor)rootFrameExifProfile?.GetValue(ExifTag.Predictor).Value;
121-
}
122-
123-
if (!this.CompressionType.HasValue && rootFrameExifProfile?.GetValue(ExifTag.Compression) != null)
124-
{
125-
this.CompressionType = (TiffCompression)rootFrameExifProfile?.GetValue(ExifTag.Compression).Value;
126-
}
118+
this.HorizontalPredictor ??= rootFrameTiffMetaData.Predictor;
119+
this.CompressionType ??= rootFrameTiffMetaData.Compression;
127120

128121
this.SetBitsPerPixel(rootFrameBitsPerPixel, image.PixelType.BitsPerPixel, photometricInterpretation);
129122
this.SetPhotometricInterpretation(photometricInterpretation);

src/ImageSharp/Formats/Tiff/TiffFrameMetadata.cs

Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Copyright (c) Six Labors.
22
// Licensed under the Apache License, Version 2.0.
33

4+
using SixLabors.ImageSharp.Formats.Tiff.Constants;
45
using SixLabors.ImageSharp.Metadata.Profiles.Exif;
56

67
namespace SixLabors.ImageSharp.Formats.Tiff
@@ -10,6 +11,26 @@ namespace SixLabors.ImageSharp.Formats.Tiff
1011
/// </summary>
1112
public class TiffFrameMetadata : IDeepCloneable
1213
{
14+
/// <summary>
15+
/// The default predictor is None.
16+
/// </summary>
17+
public const TiffPredictor DefaultPredictor = TiffPredictor.None;
18+
19+
/// <summary>
20+
/// The default bits per pixel is Bit24.
21+
/// </summary>
22+
public const TiffBitsPerPixel DefaultBitsPerPixel = TiffBitsPerPixel.Bit24;
23+
24+
/// <summary>
25+
/// The default compression is None.
26+
/// </summary>
27+
public const TiffCompression DefaultCompression = TiffCompression.None;
28+
29+
/// <summary>
30+
/// The default photometric interpretation is BlackIsZero.
31+
/// </summary>
32+
public const TiffPhotometricInterpretation DefaultPhotometricInterpretation = TiffPhotometricInterpretation.BlackIsZero;
33+
1334
/// <summary>
1435
/// Initializes a new instance of the <see cref="TiffFrameMetadata"/> class.
1536
/// </summary>
@@ -28,6 +49,21 @@ public TiffFrameMetadata()
2849
/// </summary>
2950
public TiffBitsPerPixel? BitsPerPixel { get; set; }
3051

52+
/// <summary>
53+
/// Gets or sets the compression scheme used on the image data.
54+
/// </summary>
55+
public TiffCompression? Compression { get; set; }
56+
57+
/// <summary>
58+
/// Gets or sets the color space of the image data.
59+
/// </summary>
60+
public TiffPhotometricInterpretation? PhotometricInterpretation { get; set; }
61+
62+
/// <summary>
63+
/// Gets or sets a mathematical operator that is applied to the image data before an encoding scheme is applied.
64+
/// </summary>
65+
public TiffPredictor? Predictor { get; set; }
66+
3167
/// <summary>
3268
/// Returns a new <see cref="TiffFrameMetadata"/> instance parsed from the given Exif profile.
3369
/// </summary>
@@ -52,6 +88,13 @@ internal static void Parse(TiffFrameMetadata meta, ExifProfile profile)
5288

5389
ushort[] bitsPerSample = profile.GetValue(ExifTag.BitsPerSample)?.Value;
5490
meta.BitsPerPixel = BitsPerPixelFromBitsPerSample(bitsPerSample);
91+
meta.Compression = (TiffCompression?)profile.GetValue(ExifTag.Compression)?.Value ?? DefaultCompression;
92+
meta.PhotometricInterpretation = (TiffPhotometricInterpretation?)profile.GetValue(ExifTag.PhotometricInterpretation)?.Value ?? DefaultPhotometricInterpretation;
93+
meta.Predictor = (TiffPredictor?)profile.GetValue(ExifTag.Predictor)?.Value ?? DefaultPredictor;
94+
95+
profile.RemoveValue(ExifTag.Compression);
96+
profile.RemoveValue(ExifTag.PhotometricInterpretation);
97+
profile.RemoveValue(ExifTag.Predictor);
5598
}
5699

57100
/// <summary>
@@ -63,7 +106,7 @@ private static TiffBitsPerPixel BitsPerPixelFromBitsPerSample(ushort[] bitsPerSa
63106
{
64107
if (bitsPerSample == null)
65108
{
66-
return TiffBitsPerPixel.Bit24;
109+
return DefaultBitsPerPixel;
67110
}
68111

69112
int bitsPerPixel = 0;
@@ -76,6 +119,17 @@ private static TiffBitsPerPixel BitsPerPixelFromBitsPerSample(ushort[] bitsPerSa
76119
}
77120

78121
/// <inheritdoc/>
79-
public IDeepCloneable DeepClone() => new TiffFrameMetadata(this);
122+
public IDeepCloneable DeepClone()
123+
{
124+
var clone = new TiffFrameMetadata
125+
{
126+
BitsPerPixel = this.BitsPerPixel,
127+
Compression = this.Compression,
128+
PhotometricInterpretation = this.PhotometricInterpretation,
129+
Predictor = this.Predictor
130+
};
131+
132+
return clone;
133+
}
80134
}
81135
}

src/ImageSharp/Metadata/Profiles/Exif/ExifProfile.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ public IExifValue<TValueType> GetValue<TValueType>(ExifTag<TValueType> tag)
167167
/// </summary>
168168
/// <param name="tag">The tag of the EXIF value.</param>
169169
/// <returns>
170-
/// The <see cref="bool"/>.
170+
/// True, if the value was removed, otherwise false.
171171
/// </returns>
172172
public bool RemoveValue(ExifTag tag)
173173
{

tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs

Lines changed: 14 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -48,15 +48,14 @@ public void EncoderOptions_SetPhotometricInterpretation_Works(TiffPhotometricInt
4848
// assert
4949
memStream.Position = 0;
5050
using var output = Image.Load<Rgba32>(Configuration, memStream);
51-
ExifProfile exifProfile = output.Frames.RootFrame.Metadata.ExifProfile;
52-
var frameMetaData = TiffFrameMetadata.Parse(exifProfile);
51+
TiffFrameMetadata frameMetaData = output.Frames.RootFrame.Metadata.GetTiffMetadata();
5352
Assert.Equal(expectedBitsPerPixel, frameMetaData.BitsPerPixel);
54-
Assert.Equal(TiffCompression.None, (TiffCompression)exifProfile.GetValue(ExifTag.Compression).Value);
53+
Assert.Equal(TiffCompression.None, frameMetaData.Compression);
5554
}
5655

5756
[Theory]
5857
[InlineData(TiffBitsPerPixel.Bit24)]
59-
[InlineData(TiffBitsPerPixel.Bit8)]
58+
[InlineData(TiffBitsPerPixel.Bit8)]
6059
[InlineData(TiffBitsPerPixel.Bit4)]
6160
[InlineData(TiffBitsPerPixel.Bit1)]
6261
public void EncoderOptions_SetBitPerPixel_Works(TiffBitsPerPixel bitsPerPixel)
@@ -73,10 +72,9 @@ public void EncoderOptions_SetBitPerPixel_Works(TiffBitsPerPixel bitsPerPixel)
7372
memStream.Position = 0;
7473
using var output = Image.Load<Rgba32>(Configuration, memStream);
7574

76-
ExifProfile exifProfile = output.Frames.RootFrame.Metadata.ExifProfile;
7775
TiffFrameMetadata frameMetaData = output.Frames.RootFrame.Metadata.GetTiffMetadata();
7876
Assert.Equal(bitsPerPixel, frameMetaData.BitsPerPixel);
79-
Assert.Equal(TiffCompression.None, (TiffCompression)exifProfile.GetValue(ExifTag.Compression).Value);
77+
Assert.Equal(TiffCompression.None, frameMetaData.Compression);
8078
}
8179

8280
[Theory]
@@ -112,10 +110,9 @@ public void EncoderOptions_SetPhotometricInterpretationAndCompression_Works(Tiff
112110
// assert
113111
memStream.Position = 0;
114112
using var output = Image.Load<Rgba32>(Configuration, memStream);
115-
ExifProfile exifProfile = output.Frames.RootFrame.Metadata.ExifProfile;
116113
TiffFrameMetadata rootFrameMetaData = output.Frames.RootFrame.Metadata.GetTiffMetadata();
117114
Assert.Equal(expectedBitsPerPixel, rootFrameMetaData.BitsPerPixel);
118-
Assert.Equal(expectedCompression, (TiffCompression)exifProfile.GetValue(ExifTag.Compression).Value);
115+
Assert.Equal(expectedCompression, rootFrameMetaData.Compression);
119116
}
120117

121118
[Theory]
@@ -151,7 +148,7 @@ public void TiffEncoder_PreservesBitsPerPixel_WhenInputIsL8()
151148
var tiffEncoder = new TiffEncoder();
152149
using Image input = new Image<L8>(10, 10);
153150
using var memStream = new MemoryStream();
154-
var expectedBitsPerPixel = TiffBitsPerPixel.Bit8;
151+
TiffBitsPerPixel expectedBitsPerPixel = TiffBitsPerPixel.Bit8;
155152

156153
// act
157154
input.Save(memStream, tiffEncoder);
@@ -183,8 +180,7 @@ public void TiffEncoder_PreservesCompression<TPixel>(TestImageProvider<TPixel> p
183180
// assert
184181
memStream.Position = 0;
185182
using var output = Image.Load<Rgba32>(Configuration, memStream);
186-
ExifProfile exifProfile = output.Frames.RootFrame.Metadata.ExifProfile;
187-
Assert.Equal(expectedCompression, (TiffCompression)exifProfile.GetValue(ExifTag.Compression).Value);
183+
Assert.Equal(expectedCompression, output.Frames.RootFrame.Metadata.GetTiffMetadata().Compression);
188184
}
189185

190186
[Theory]
@@ -206,8 +202,8 @@ public void TiffEncoder_PreservesPredictor<TPixel>(TestImageProvider<TPixel> pro
206202
// assert
207203
memStream.Position = 0;
208204
using var output = Image.Load<Rgba32>(Configuration, memStream);
209-
ExifProfile exifProfile = output.Frames.RootFrame.Metadata.ExifProfile;
210-
Assert.Equal(expectedPredictor, (TiffPredictor)exifProfile.GetValue(ExifTag.Predictor).Value);
205+
TiffFrameMetadata frameMetadata = output.Frames.RootFrame.Metadata.GetTiffMetadata();
206+
Assert.Equal(expectedPredictor, frameMetadata.Predictor);
211207
}
212208

213209
[Theory]
@@ -229,10 +225,9 @@ public void TiffEncoder_EncodesWithCorrectBiColorModeCompression<TPixel>(TestIma
229225
// assert
230226
memStream.Position = 0;
231227
using var output = Image.Load<Rgba32>(Configuration, memStream);
232-
ExifProfile exifProfile = output.Frames.RootFrame.Metadata.ExifProfile;
233-
var frameMetaData = TiffFrameMetadata.Parse(exifProfile);
228+
TiffFrameMetadata frameMetaData = output.Frames.RootFrame.Metadata.GetTiffMetadata();
234229
Assert.Equal(TiffBitsPerPixel.Bit1, frameMetaData.BitsPerPixel);
235-
Assert.Equal(expectedCompression, (TiffCompression)exifProfile.GetValue(ExifTag.Compression).Value);
230+
Assert.Equal(expectedCompression, frameMetaData.Compression);
236231
}
237232

238233
[Theory]
@@ -402,9 +397,8 @@ private static void TestStripLength<TPixel>(TestImageProvider<TPixel> provider,
402397
var tiffEncoder = new TiffEncoder() { PhotometricInterpretation = photometricInterpretation, Compression = compression };
403398
using Image<TPixel> input = provider.GetImage();
404399
using var memStream = new MemoryStream();
405-
ExifProfile exifProfileInput = input.Frames.RootFrame.Metadata.ExifProfile;
406-
var inputCompression = (TiffCompression)exifProfileInput.GetValue(ExifTag.Compression).Value;
407-
var inputMeta = TiffFrameMetadata.Parse(exifProfileInput);
400+
TiffFrameMetadata inputMeta = input.Frames.RootFrame.Metadata.GetTiffMetadata();
401+
TiffCompression inputCompression = inputMeta.Compression ?? TiffCompression.None;
408402

409403
// act
410404
input.Save(memStream, tiffEncoder);
@@ -413,7 +407,7 @@ private static void TestStripLength<TPixel>(TestImageProvider<TPixel> provider,
413407
memStream.Position = 0;
414408
using var output = Image.Load<Rgba32>(Configuration, memStream);
415409
ExifProfile exifProfileOutput = output.Frames.RootFrame.Metadata.ExifProfile;
416-
var outputMeta = TiffFrameMetadata.Parse(exifProfileOutput);
410+
TiffFrameMetadata outputMeta = output.Frames.RootFrame.Metadata.GetTiffMetadata();
417411
ImageFrame<Rgba32> rootFrame = output.Frames.RootFrame;
418412

419413
Number rowsPerStrip = exifProfileOutput.GetValue(ExifTag.RowsPerStrip) != null ? exifProfileOutput.GetValue(ExifTag.RowsPerStrip).Value : TiffConstants.RowsPerStripInfinity;

0 commit comments

Comments
 (0)