diff --git a/src/Directory.Build.props b/src/Directory.Build.props
index d211992a94..faa29865f2 100644
--- a/src/Directory.Build.props
+++ b/src/Directory.Build.props
@@ -27,6 +27,7 @@
+
diff --git a/src/ImageSharp/Diagnostics/MemoryDiagnostics.cs b/src/ImageSharp/Diagnostics/MemoryDiagnostics.cs
index d83e737f12..89f18cff61 100644
--- a/src/ImageSharp/Diagnostics/MemoryDiagnostics.cs
+++ b/src/ImageSharp/Diagnostics/MemoryDiagnostics.cs
@@ -1,6 +1,7 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
+using System;
using System.Threading;
namespace SixLabors.ImageSharp.Diagnostics
@@ -47,6 +48,16 @@ namespace SixLabors.ImageSharp.Diagnostics
}
}
+ ///
+ /// Fires when ImageSharp allocates memory from a MemoryAllocator
+ ///
+ internal static event Action MemoryAllocated;
+
+ ///
+ /// Fires when ImageSharp releases memory allocated from a MemoryAllocator
+ ///
+ internal static event Action MemoryReleased;
+
///
/// Gets a value indicating the total number of memory resource objects leaked to the finalizer.
///
@@ -54,11 +65,17 @@ namespace SixLabors.ImageSharp.Diagnostics
internal static bool UndisposedAllocationSubscribed => Volatile.Read(ref undisposedAllocationSubscriptionCounter) > 0;
- internal static void IncrementTotalUndisposedAllocationCount() =>
+ internal static void IncrementTotalUndisposedAllocationCount()
+ {
Interlocked.Increment(ref totalUndisposedAllocationCount);
+ MemoryAllocated?.Invoke();
+ }
- internal static void DecrementTotalUndisposedAllocationCount() =>
+ internal static void DecrementTotalUndisposedAllocationCount()
+ {
Interlocked.Decrement(ref totalUndisposedAllocationCount);
+ MemoryReleased?.Invoke();
+ }
internal static void RaiseUndisposedMemoryResource(string allocationStackTrace)
{
diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs
index 41adc1cfff..ee0a312803 100644
--- a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs
+++ b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs
@@ -122,11 +122,12 @@ namespace SixLabors.ImageSharp.Formats.Bmp
public Image Decode(BufferedReadStream stream, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel
{
+ Image image = null;
try
{
int bytesPerColorMapEntry = this.ReadImageHeaders(stream, out bool inverted, out byte[] palette);
- var image = new Image(this.Configuration, this.infoHeader.Width, this.infoHeader.Height, this.metadata);
+ image = new Image(this.Configuration, this.infoHeader.Width, this.infoHeader.Height, this.metadata);
Buffer2D pixels = image.GetRootFramePixelBuffer();
@@ -193,8 +194,14 @@ namespace SixLabors.ImageSharp.Formats.Bmp
}
catch (IndexOutOfRangeException e)
{
+ image?.Dispose();
throw new ImageFormatException("Bitmap does not have a valid format.", e);
}
+ catch
+ {
+ image?.Dispose();
+ throw;
+ }
}
///
@@ -323,12 +330,12 @@ namespace SixLabors.ImageSharp.Formats.Bmp
color.FromBgr24(Unsafe.As(ref colors[colorIdx * 4]));
break;
case RleSkippedPixelHandling.Transparent:
- color.FromVector4(Vector4.Zero);
+ color.FromScaledVector4(Vector4.Zero);
break;
// Default handling for skipped pixels is black (which is what System.Drawing is also doing).
default:
- color.FromVector4(new Vector4(0.0f, 0.0f, 0.0f, 1.0f));
+ color.FromScaledVector4(new Vector4(0.0f, 0.0f, 0.0f, 1.0f));
break;
}
}
@@ -395,12 +402,12 @@ namespace SixLabors.ImageSharp.Formats.Bmp
color.FromBgr24(Unsafe.As(ref bufferSpan[idx]));
break;
case RleSkippedPixelHandling.Transparent:
- color.FromVector4(Vector4.Zero);
+ color.FromScaledVector4(Vector4.Zero);
break;
// Default handling for skipped pixels is black (which is what System.Drawing is also doing).
default:
- color.FromVector4(new Vector4(0.0f, 0.0f, 0.0f, 1.0f));
+ color.FromScaledVector4(new Vector4(0.0f, 0.0f, 0.0f, 1.0f));
break;
}
}
@@ -1127,7 +1134,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
g * invMaxValueGreen,
b * invMaxValueBlue,
alpha);
- color.FromVector4(vector4);
+ color.FromScaledVector4(vector4);
}
else
{
diff --git a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs
index 16dca3324f..d17e89cd45 100644
--- a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs
+++ b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs
@@ -221,7 +221,11 @@ namespace SixLabors.ImageSharp.Formats.Gif
///
private void ReadGraphicalControlExtension()
{
- this.stream.Read(this.buffer, 0, 6);
+ int bytesRead = this.stream.Read(this.buffer, 0, 6);
+ if (bytesRead != 6)
+ {
+ GifThrowHelper.ThrowInvalidImageContentException("Not enough data to read the graphic control extension");
+ }
this.graphicsControlExtension = GifGraphicControlExtension.Parse(this.buffer);
}
@@ -231,7 +235,11 @@ namespace SixLabors.ImageSharp.Formats.Gif
///
private void ReadImageDescriptor()
{
- this.stream.Read(this.buffer, 0, 9);
+ int bytesRead = this.stream.Read(this.buffer, 0, 9);
+ if (bytesRead != 9)
+ {
+ GifThrowHelper.ThrowInvalidImageContentException("Not enough data to read the image descriptor");
+ }
this.imageDescriptor = GifImageDescriptor.Parse(this.buffer);
if (this.imageDescriptor.Height == 0 || this.imageDescriptor.Width == 0)
@@ -245,7 +253,11 @@ namespace SixLabors.ImageSharp.Formats.Gif
///
private void ReadLogicalScreenDescriptor()
{
- this.stream.Read(this.buffer, 0, 7);
+ int bytesRead = this.stream.Read(this.buffer, 0, 7);
+ if (bytesRead != 7)
+ {
+ GifThrowHelper.ThrowInvalidImageContentException("Not enough data to read the logical screen descriptor");
+ }
this.logicalScreenDescriptor = GifLogicalScreenDescriptor.Parse(this.buffer);
}
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/SpectralConverter{TPixel}.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/SpectralConverter{TPixel}.cs
index 430adeb21d..532892e060 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/SpectralConverter{TPixel}.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/SpectralConverter{TPixel}.cs
@@ -95,7 +95,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
}
}
- return this.pixelBuffer;
+ var buffer = this.pixelBuffer;
+ this.pixelBuffer = null;
+ return buffer;
}
///
@@ -210,6 +212,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
this.rgbBuffer?.Dispose();
this.paddedProxyPixelRow?.Dispose();
+ this.pixelBuffer?.Dispose();
}
}
}
diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs
index ef4e3ffac2..533ffa719c 100644
--- a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs
+++ b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs
@@ -228,11 +228,17 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
this.Metadata = new ImageMetadata();
this.QuantizationTables = new Block8x8F[4];
this.scanDecoder = huffmanScanDecoder;
+
+ if (tableBytes.Length < 4)
+ {
+ JpegThrowHelper.ThrowInvalidImageContentException("Not enough data to read marker");
+ }
+
using var ms = new MemoryStream(tableBytes);
using var stream = new BufferedReadStream(this.Configuration, ms);
// Check for the Start Of Image marker.
- stream.Read(this.markerBuffer, 0, 2);
+ int bytesRead = stream.Read(this.markerBuffer, 0, 2);
var fileMarker = new JpegFileMarker(this.markerBuffer[1], 0);
if (fileMarker.Marker != JpegConstants.Markers.SOI)
{
@@ -240,16 +246,23 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
}
// Read next marker.
- stream.Read(this.markerBuffer, 0, 2);
- byte marker = this.markerBuffer[1];
- fileMarker = new JpegFileMarker(marker, (int)stream.Position - 2);
+ bytesRead = stream.Read(this.markerBuffer, 0, 2);
+ fileMarker = new JpegFileMarker(this.markerBuffer[1], (int)stream.Position - 2);
while (fileMarker.Marker != JpegConstants.Markers.EOI || (fileMarker.Marker == JpegConstants.Markers.EOI && fileMarker.Invalid))
{
if (!fileMarker.Invalid)
{
// Get the marker length.
- int remaining = this.ReadUint16(stream) - 2;
+ int markerContentByteSize = this.ReadUint16(stream) - 2;
+
+ // Check whether stream actually has enought bytes to read
+ // markerContentByteSize is always positive so we cast
+ // to uint to avoid sign extension
+ if (stream.RemainingBytes < (uint)markerContentByteSize)
+ {
+ JpegThrowHelper.ThrowNotEnoughBytesForMarker(fileMarker.Marker);
+ }
switch (fileMarker.Marker)
{
@@ -259,13 +272,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
case JpegConstants.Markers.RST7:
break;
case JpegConstants.Markers.DHT:
- this.ProcessDefineHuffmanTablesMarker(stream, remaining);
+ this.ProcessDefineHuffmanTablesMarker(stream, markerContentByteSize);
break;
case JpegConstants.Markers.DQT:
- this.ProcessDefineQuantizationTablesMarker(stream, remaining);
+ this.ProcessDefineQuantizationTablesMarker(stream, markerContentByteSize);
break;
case JpegConstants.Markers.DRI:
- this.ProcessDefineRestartIntervalMarker(stream, remaining);
+ this.ProcessDefineRestartIntervalMarker(stream, markerContentByteSize);
break;
case JpegConstants.Markers.EOI:
return;
@@ -273,7 +286,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
}
// Read next marker.
- stream.Read(this.markerBuffer, 0, 2);
+ bytesRead = stream.Read(this.markerBuffer, 0, 2);
+ if (bytesRead != 2)
+ {
+ JpegThrowHelper.ThrowInvalidImageContentException("Not enough data to read marker");
+ }
+
fileMarker = new JpegFileMarker(this.markerBuffer[1], 0);
}
}
@@ -315,14 +333,22 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
if (!fileMarker.Invalid)
{
// Get the marker length.
- int remaining = this.ReadUint16(stream) - 2;
+ int markerContentByteSize = this.ReadUint16(stream) - 2;
+
+ // Check whether stream actually has enought bytes to read
+ // markerContentByteSize is always positive so we cast
+ // to uint to avoid sign extension
+ if (stream.RemainingBytes < (uint)markerContentByteSize)
+ {
+ JpegThrowHelper.ThrowNotEnoughBytesForMarker(fileMarker.Marker);
+ }
switch (fileMarker.Marker)
{
case JpegConstants.Markers.SOF0:
case JpegConstants.Markers.SOF1:
case JpegConstants.Markers.SOF2:
- this.ProcessStartOfFrameMarker(stream, remaining, fileMarker, metadataOnly);
+ this.ProcessStartOfFrameMarker(stream, markerContentByteSize, fileMarker, metadataOnly);
break;
case JpegConstants.Markers.SOF5:
@@ -350,7 +376,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
case JpegConstants.Markers.SOS:
if (!metadataOnly)
{
- this.ProcessStartOfScanMarker(stream, remaining);
+ this.ProcessStartOfScanMarker(stream, markerContentByteSize);
break;
}
else
@@ -364,41 +390,41 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
if (metadataOnly)
{
- stream.Skip(remaining);
+ stream.Skip(markerContentByteSize);
}
else
{
- this.ProcessDefineHuffmanTablesMarker(stream, remaining);
+ this.ProcessDefineHuffmanTablesMarker(stream, markerContentByteSize);
}
break;
case JpegConstants.Markers.DQT:
- this.ProcessDefineQuantizationTablesMarker(stream, remaining);
+ this.ProcessDefineQuantizationTablesMarker(stream, markerContentByteSize);
break;
case JpegConstants.Markers.DRI:
if (metadataOnly)
{
- stream.Skip(remaining);
+ stream.Skip(markerContentByteSize);
}
else
{
- this.ProcessDefineRestartIntervalMarker(stream, remaining);
+ this.ProcessDefineRestartIntervalMarker(stream, markerContentByteSize);
}
break;
case JpegConstants.Markers.APP0:
- this.ProcessApplicationHeaderMarker(stream, remaining);
+ this.ProcessApplicationHeaderMarker(stream, markerContentByteSize);
break;
case JpegConstants.Markers.APP1:
- this.ProcessApp1Marker(stream, remaining);
+ this.ProcessApp1Marker(stream, markerContentByteSize);
break;
case JpegConstants.Markers.APP2:
- this.ProcessApp2Marker(stream, remaining);
+ this.ProcessApp2Marker(stream, markerContentByteSize);
break;
case JpegConstants.Markers.APP3:
@@ -411,20 +437,20 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
case JpegConstants.Markers.APP10:
case JpegConstants.Markers.APP11:
case JpegConstants.Markers.APP12:
- stream.Skip(remaining);
+ stream.Skip(markerContentByteSize);
break;
case JpegConstants.Markers.APP13:
- this.ProcessApp13Marker(stream, remaining);
+ this.ProcessApp13Marker(stream, markerContentByteSize);
break;
case JpegConstants.Markers.APP14:
- this.ProcessApp14Marker(stream, remaining);
+ this.ProcessApp14Marker(stream, markerContentByteSize);
break;
case JpegConstants.Markers.APP15:
case JpegConstants.Markers.COM:
- stream.Skip(remaining);
+ stream.Skip(markerContentByteSize);
break;
case JpegConstants.Markers.DAC:
@@ -722,7 +748,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
if (ProfileResolver.IsProfile(this.temp, ProfileResolver.XmpMarker.Slice(0, ExifMarkerLength)))
{
- int remainingXmpMarkerBytes = XmpMarkerLength - ExifMarkerLength;
+ const int remainingXmpMarkerBytes = XmpMarkerLength - ExifMarkerLength;
+ if (remaining < remainingXmpMarkerBytes || this.IgnoreMetadata)
+ {
+ // Skip the application header length.
+ stream.Skip(remaining);
+ return;
+ }
+
stream.Read(this.temp, ExifMarkerLength, remainingXmpMarkerBytes);
remaining -= remainingXmpMarkerBytes;
if (ProfileResolver.IsProfile(this.temp, ProfileResolver.XmpMarker))
@@ -1260,7 +1293,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
int selectorsBytes = selectorsCount * 2;
if (remaining != 4 + selectorsBytes)
{
- JpegThrowHelper.ThrowBadMarker("SOS", remaining);
+ JpegThrowHelper.ThrowBadMarker(nameof(JpegConstants.Markers.SOS), remaining);
}
// selectorsCount*2 bytes: component index + huffman tables indices
@@ -1312,8 +1345,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
component.ACHuffmanTableId = acTableIndex;
}
- // 3 bytes: Progressive scan decoding data
- stream.Read(this.temp, 0, 3);
+ // 3 bytes: Progressive scan decoding data.
+ int bytesRead = stream.Read(this.temp, 0, 3);
+ if (bytesRead != 3)
+ {
+ JpegThrowHelper.ThrowInvalidImageContentException("Not enough data to read progressive scan decoding data");
+ }
int spectralStart = this.temp[0];
this.scanDecoder.SpectralStart = spectralStart;
@@ -1336,7 +1373,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
[MethodImpl(InliningOptions.ShortMethod)]
private ushort ReadUint16(BufferedReadStream stream)
{
- stream.Read(this.markerBuffer, 0, 2);
+ int bytesRead = stream.Read(this.markerBuffer, 0, 2);
+ if (bytesRead != 2)
+ {
+ JpegThrowHelper.ThrowInvalidImageContentException("jpeg stream does not contain enough data, could not read ushort.");
+ }
+
return BinaryPrimitives.ReadUInt16BigEndian(this.markerBuffer);
}
}
diff --git a/src/ImageSharp/Formats/Jpeg/JpegThrowHelper.cs b/src/ImageSharp/Formats/Jpeg/JpegThrowHelper.cs
index b238e45ef3..1073ffff78 100644
--- a/src/ImageSharp/Formats/Jpeg/JpegThrowHelper.cs
+++ b/src/ImageSharp/Formats/Jpeg/JpegThrowHelper.cs
@@ -25,6 +25,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
[MethodImpl(InliningOptions.ColdPath)]
public static void ThrowBadMarker(string marker, int length) => throw new InvalidImageContentException($"Marker {marker} has bad length {length}.");
+ [MethodImpl(InliningOptions.ColdPath)]
+ public static void ThrowNotEnoughBytesForMarker(byte marker) => throw new InvalidImageContentException($"Input stream does not have enough bytes to parse declared contents of the {marker:X2} marker.");
+
[MethodImpl(InliningOptions.ColdPath)]
public static void ThrowBadQuantizationTableIndex(int index) => throw new InvalidImageContentException($"Bad Quantization Table index {index}.");
diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs
index 497dc39674..12770bc521 100644
--- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs
+++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs
@@ -227,10 +227,16 @@ namespace SixLabors.ImageSharp.Formats.Png
return image;
}
+ catch
+ {
+ image?.Dispose();
+ throw;
+ }
finally
{
this.scanline?.Dispose();
this.previousScanline?.Dispose();
+ this.nextChunk?.Data?.Dispose();
}
}
@@ -472,6 +478,8 @@ namespace SixLabors.ImageSharp.Formats.Png
this.bytesPerSample = this.header.BitDepth / 8;
}
+ this.previousScanline?.Dispose();
+ this.scanline?.Dispose();
this.previousScanline = this.memoryAllocator.Allocate(this.bytesPerScanline, AllocationOptions.Clean);
this.scanline = this.Configuration.MemoryAllocator.Allocate(this.bytesPerScanline, AllocationOptions.Clean);
}
@@ -1359,6 +1367,7 @@ namespace SixLabors.ImageSharp.Formats.Png
{
if (chunk.Type == PngChunkType.Data)
{
+ chunk.Data?.Dispose();
return chunk.Length;
}
@@ -1453,6 +1462,9 @@ namespace SixLabors.ImageSharp.Formats.Png
if (validCrc != inputCrc)
{
string chunkTypeName = Encoding.ASCII.GetString(chunkType);
+
+ // ensure when throwing we dispose the data back to the memory allocator
+ chunk.Data?.Dispose();
PngThrowHelper.ThrowInvalidChunkCrc(chunkTypeName);
}
}
diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/JpegTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/JpegTiffCompression.cs
index cfbc32f4f6..4e788c76af 100644
--- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/JpegTiffCompression.cs
+++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/JpegTiffCompression.cs
@@ -65,7 +65,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors
jpegDecoder.ParseStream(stream, scanDecoderGray, CancellationToken.None);
// TODO: Should we pass through the CancellationToken from the tiff decoder?
- CopyImageBytesToBuffer(buffer, spectralConverterGray.GetPixelBuffer(CancellationToken.None));
+ using var decompressedBuffer = spectralConverterGray.GetPixelBuffer(CancellationToken.None);
+ CopyImageBytesToBuffer(buffer, decompressedBuffer);
break;
}
@@ -81,7 +82,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors
jpegDecoder.ParseStream(stream, scanDecoder, CancellationToken.None);
// TODO: Should we pass through the CancellationToken from the tiff decoder?
- CopyImageBytesToBuffer(buffer, spectralConverter.GetPixelBuffer(CancellationToken.None));
+ using var decompressedBuffer = spectralConverter.GetPixelBuffer(CancellationToken.None);
+ CopyImageBytesToBuffer(buffer, decompressedBuffer);
break;
}
diff --git a/src/ImageSharp/Formats/Tiff/Constants/TiffCompression.cs b/src/ImageSharp/Formats/Tiff/Constants/TiffCompression.cs
index 0baf4c89c5..765f2c237d 100644
--- a/src/ImageSharp/Formats/Tiff/Constants/TiffCompression.cs
+++ b/src/ImageSharp/Formats/Tiff/Constants/TiffCompression.cs
@@ -23,11 +23,6 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Constants
///
Ccitt1D = 2,
- ///
- /// PackBits compression
- ///
- PackBits = 32773,
-
///
/// T4-encoding: CCITT T.4 bi-level encoding (see Section 11 of the TIFF 6.0 specification).
///
@@ -65,27 +60,48 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Constants
Deflate = 8,
///
- /// Deflate compression - old.
+ /// ITU-T Rec. T.82 coding, applying ITU-T Rec. T.85 (JBIG) (see RFC2301).
///
- /// Note: The TIFF encoder does not support this compression and will default to use no compression instead,
+ /// Note: The TIFF encoder does not yet support this compression and will default to use no compression instead,
/// if this is chosen.
///
- OldDeflate = 32946,
+ ItuTRecT82 = 9,
///
- /// ITU-T Rec. T.82 coding, applying ITU-T Rec. T.85 (JBIG) (see RFC2301).
+ /// ITU-T Rec. T.43 representation, using ITU-T Rec. T.82 (JBIG) (see RFC2301).
///
/// Note: The TIFF encoder does not yet support this compression and will default to use no compression instead,
/// if this is chosen.
///
- ItuTRecT82 = 9,
+ ItuTRecT43 = 10,
///
- /// ITU-T Rec. T.43 representation, using ITU-T Rec. T.82 (JBIG) (see RFC2301).
+ /// NeXT 2-bit Grey Scale compression algorithm.
///
- /// Note: The TIFF encoder does not yet support this compression and will default to use no compression instead,
+ /// Note: The TIFF encoder does not support this compression and will default to use no compression instead,
+ /// if this is chosen.
+ ///
+ NeXT = 32766,
+
+ ///
+ /// PackBits compression.
+ ///
+ PackBits = 32773,
+
+ ///
+ /// ThunderScan 4-bit compression.
+ ///
+ /// Note: The TIFF encoder does not support this compression and will default to use no compression instead,
/// if this is chosen.
///
- ItuTRecT43 = 10
+ ThunderScan = 32809,
+
+ ///
+ /// Deflate compression - old.
+ ///
+ /// Note: The TIFF encoder does not support this compression and will default to use no compression instead,
+ /// if this is chosen.
+ ///
+ OldDeflate = 32946,
}
}
diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero16TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero16TiffColor{TPixel}.cs
index 5d910d16e7..e605629122 100644
--- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero16TiffColor{TPixel}.cs
+++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero16TiffColor{TPixel}.cs
@@ -36,7 +36,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
// we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623
L16 l16 = TiffUtils.L16Default;
var color = default(TPixel);
- color.FromVector4(TiffUtils.Vector4Default);
+ color.FromScaledVector4(TiffUtils.Vector4Default);
int offset = 0;
for (int y = top; y < top + height; y++)
diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero24TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero24TiffColor{TPixel}.cs
index 4cda954804..7d230dfd5e 100644
--- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero24TiffColor{TPixel}.cs
+++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero24TiffColor{TPixel}.cs
@@ -28,7 +28,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
// Note: due to an issue with netcore 2.1 and default values and unpredictable behavior with those,
// we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623
var color = default(TPixel);
- color.FromVector4(TiffUtils.Vector4Default);
+ color.FromScaledVector4(TiffUtils.Vector4Default);
byte[] buffer = new byte[4];
int bufferStartIdx = this.isBigEndian ? 1 : 0;
diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero32FloatTiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero32FloatTiffColor{TPixel}.cs
index ee9bf8a9ce..c43b121caf 100644
--- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero32FloatTiffColor{TPixel}.cs
+++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero32FloatTiffColor{TPixel}.cs
@@ -29,7 +29,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
// Note: due to an issue with netcore 2.1 and default values and unpredictable behavior with those,
// we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623
var color = default(TPixel);
- color.FromVector4(TiffUtils.Vector4Default);
+ color.FromScaledVector4(TiffUtils.Vector4Default);
byte[] buffer = new byte[4];
int offset = 0;
@@ -46,7 +46,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
offset += 4;
var colorVector = new Vector4(intensity, intensity, intensity, 1.0f);
- color.FromVector4(colorVector);
+ color.FromScaledVector4(colorVector);
pixelRow[x] = color;
}
}
@@ -59,7 +59,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
offset += 4;
var colorVector = new Vector4(intensity, intensity, intensity, 1.0f);
- color.FromVector4(colorVector);
+ color.FromScaledVector4(colorVector);
pixelRow[x] = color;
}
}
diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero32TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero32TiffColor{TPixel}.cs
index 7367a78e34..00e4caef79 100644
--- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero32TiffColor{TPixel}.cs
+++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero32TiffColor{TPixel}.cs
@@ -28,7 +28,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
// Note: due to an issue with netcore 2.1 and default values and unpredictable behavior with those,
// we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623
var color = default(TPixel);
- color.FromVector4(TiffUtils.Vector4Default);
+ color.FromScaledVector4(TiffUtils.Vector4Default);
int offset = 0;
for (int y = top; y < top + height; y++)
diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColor{TPixel}.cs
index a40fa76675..59be3f8918 100644
--- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColor{TPixel}.cs
+++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColor{TPixel}.cs
@@ -40,7 +40,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
int value = bitReader.ReadBits(this.bitsPerSample0);
float intensity = value / this.factor;
- color.FromVector4(new Vector4(intensity, intensity, intensity, 1.0f));
+ color.FromScaledVector4(new Vector4(intensity, intensity, intensity, 1.0f));
pixelRow[x] = color;
}
diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/PaletteTiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/PaletteTiffColor{TPixel}.cs
index 29e03c6c6a..ad5793084b 100644
--- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/PaletteTiffColor{TPixel}.cs
+++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/PaletteTiffColor{TPixel}.cs
@@ -59,7 +59,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
float r = colorMap[rOffset + i] / 65535F;
float g = colorMap[gOffset + i] / 65535F;
float b = colorMap[bOffset + i] / 65535F;
- palette[i].FromVector4(new Vector4(r, g, b, 1.0f));
+ palette[i].FromScaledVector4(new Vector4(r, g, b, 1.0f));
}
return palette;
diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb161616TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb161616TiffColor{TPixel}.cs
index a4d725bcf4..6093690117 100644
--- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb161616TiffColor{TPixel}.cs
+++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb161616TiffColor{TPixel}.cs
@@ -36,7 +36,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
// we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623
Rgba64 rgba = TiffUtils.Rgba64Default;
var color = default(TPixel);
- color.FromVector4(TiffUtils.Vector4Default);
+ color.FromScaledVector4(TiffUtils.Vector4Default);
int offset = 0;
@@ -55,7 +55,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
ulong b = TiffUtils.ConvertToUShortBigEndian(data.Slice(offset, 2));
offset += 2;
- pixelRow[x] = TiffUtils.ColorFromRgba64(rgba, r, g, b, color);
+ pixelRow[x] = TiffUtils.ColorFromRgb64(rgba, r, g, b, color);
}
}
else
diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb16PlanarTiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb16PlanarTiffColor{TPixel}.cs
index 1c61b0991c..76fed3c93e 100644
--- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb16PlanarTiffColor{TPixel}.cs
+++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb16PlanarTiffColor{TPixel}.cs
@@ -30,7 +30,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
// we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623
Rgba64 rgba = TiffUtils.Rgba64Default;
var color = default(TPixel);
- color.FromVector4(TiffUtils.Vector4Default);
+ color.FromScaledVector4(TiffUtils.Vector4Default);
Span redData = data[0].GetSpan();
Span greenData = data[1].GetSpan();
@@ -50,7 +50,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
offset += 2;
- pixelRow[x] = TiffUtils.ColorFromRgba64(rgba, r, g, b, color);
+ pixelRow[x] = TiffUtils.ColorFromRgb64(rgba, r, g, b, color);
}
}
else
@@ -63,7 +63,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
offset += 2;
- pixelRow[x] = TiffUtils.ColorFromRgba64(rgba, r, g, b, color);
+ pixelRow[x] = TiffUtils.ColorFromRgb64(rgba, r, g, b, color);
}
}
}
diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb242424TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb242424TiffColor{TPixel}.cs
index 4130ee1a29..addf576e95 100644
--- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb242424TiffColor{TPixel}.cs
+++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb242424TiffColor{TPixel}.cs
@@ -28,7 +28,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
// Note: due to an issue with netcore 2.1 and default values and unpredictable behavior with those,
// we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623
var color = default(TPixel);
- color.FromVector4(TiffUtils.Vector4Default);
+ color.FromScaledVector4(TiffUtils.Vector4Default);
int offset = 0;
Span buffer = stackalloc byte[4];
int bufferStartIdx = this.isBigEndian ? 1 : 0;
diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb24PlanarTiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb24PlanarTiffColor{TPixel}.cs
index e37fff1e74..2eda3b5af7 100644
--- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb24PlanarTiffColor{TPixel}.cs
+++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb24PlanarTiffColor{TPixel}.cs
@@ -29,7 +29,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
// Note: due to an issue with netcore 2.1 and default values and unpredictable behavior with those,
// we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623
var color = default(TPixel);
- color.FromVector4(TiffUtils.Vector4Default);
+ color.FromScaledVector4(TiffUtils.Vector4Default);
Span buffer = stackalloc byte[4];
int bufferStartIdx = this.isBigEndian ? 1 : 0;
diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb323232TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb323232TiffColor{TPixel}.cs
index bf1e65e1c7..02319bfa66 100644
--- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb323232TiffColor{TPixel}.cs
+++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb323232TiffColor{TPixel}.cs
@@ -28,7 +28,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
// Note: due to an issue with netcore 2.1 and default values and unpredictable behavior with those,
// we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623
var color = default(TPixel);
- color.FromVector4(TiffUtils.Vector4Default);
+ color.FromScaledVector4(TiffUtils.Vector4Default);
int offset = 0;
for (int y = top; y < top + height; y++)
diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb32PlanarTiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb32PlanarTiffColor{TPixel}.cs
index cdc6942bd7..26f75bfcf8 100644
--- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb32PlanarTiffColor{TPixel}.cs
+++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb32PlanarTiffColor{TPixel}.cs
@@ -29,7 +29,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
// Note: due to an issue with netcore 2.1 and default values and unpredictable behavior with those,
// we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623
var color = default(TPixel);
- color.FromVector4(TiffUtils.Vector4Default);
+ color.FromScaledVector4(TiffUtils.Vector4Default);
Span redData = data[0].GetSpan();
Span greenData = data[1].GetSpan();
diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbFloat323232TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbFloat323232TiffColor{TPixel}.cs
index 4dc3295a44..7fd98dd504 100644
--- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbFloat323232TiffColor{TPixel}.cs
+++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbFloat323232TiffColor{TPixel}.cs
@@ -29,7 +29,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
// Note: due to an issue with netcore 2.1 and default values and unpredictable behavior with those,
// we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623
var color = default(TPixel);
- color.FromVector4(TiffUtils.Vector4Default);
+ color.FromScaledVector4(TiffUtils.Vector4Default);
int offset = 0;
byte[] buffer = new byte[4];
@@ -57,7 +57,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
offset += 4;
var colorVector = new Vector4(r, g, b, 1.0f);
- color.FromVector4(colorVector);
+ color.FromScaledVector4(colorVector);
pixelRow[x] = color;
}
}
@@ -78,7 +78,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
offset += 4;
var colorVector = new Vector4(r, g, b, 1.0f);
- color.FromVector4(colorVector);
+ color.FromScaledVector4(colorVector);
pixelRow[x] = color;
}
}
diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColor{TPixel}.cs
index 54466e05bc..f1ff91382e 100644
--- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColor{TPixel}.cs
+++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbPlanarTiffColor{TPixel}.cs
@@ -65,7 +65,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
float g = gBitReader.ReadBits(this.bitsPerSampleG) / this.gFactor;
float b = bBitReader.ReadBits(this.bitsPerSampleB) / this.bFactor;
- color.FromVector4(new Vector4(r, g, b, 1.0f));
+ color.FromScaledVector4(new Vector4(r, g, b, 1.0f));
pixelRow[x] = color;
}
diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbTiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbTiffColor{TPixel}.cs
index 4a887c426f..9d037cec74 100644
--- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbTiffColor{TPixel}.cs
+++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbTiffColor{TPixel}.cs
@@ -54,7 +54,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
float g = bitReader.ReadBits(this.bitsPerSampleG) / this.gFactor;
float b = bitReader.ReadBits(this.bitsPerSampleB) / this.bFactor;
- color.FromVector4(new Vector4(r, g, b, 1.0f));
+ color.FromScaledVector4(new Vector4(r, g, b, 1.0f));
pixelRow[x] = color;
}
diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba16161616TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba16161616TiffColor{TPixel}.cs
index 7fd8d9879b..0340438cbf 100644
--- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba16161616TiffColor{TPixel}.cs
+++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba16161616TiffColor{TPixel}.cs
@@ -2,6 +2,8 @@
// Licensed under the Apache License, Version 2.0.
using System;
+using System.Buffers;
+using System.Numerics;
using SixLabors.ImageSharp.Formats.Tiff.Utils;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
@@ -18,15 +20,23 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
private readonly Configuration configuration;
+ private readonly MemoryAllocator memoryAllocator;
+
+ private readonly TiffExtraSampleType? extraSamplesType;
+
///
/// Initializes a new instance of the class.
///
/// The configuration.
+ /// The memory allocator.
/// if set to true decodes the pixel data as big endian, otherwise as little endian.
- public Rgba16161616TiffColor(Configuration configuration, bool isBigEndian)
+ /// The type of the extra samples.
+ public Rgba16161616TiffColor(Configuration configuration, MemoryAllocator memoryAllocator, TiffExtraSampleType? extraSamplesType, bool isBigEndian)
{
this.configuration = configuration;
this.isBigEndian = isBigEndian;
+ this.memoryAllocator = memoryAllocator;
+ this.extraSamplesType = extraSamplesType;
}
///
@@ -36,10 +46,13 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
// we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623
Rgba64 rgba = TiffUtils.Rgba64Default;
var color = default(TPixel);
- color.FromVector4(TiffUtils.Vector4Default);
+ color.FromScaledVector4(TiffUtils.Vector4Default);
+ bool hasAssociatedAlpha = this.extraSamplesType.HasValue && this.extraSamplesType == TiffExtraSampleType.AssociatedAlphaData;
int offset = 0;
+ using IMemoryOwner vectors = hasAssociatedAlpha ? this.memoryAllocator.Allocate(width) : null;
+ Span vectorsSpan = hasAssociatedAlpha ? vectors.GetSpan() : Span.Empty;
for (int y = top; y < top + height; y++)
{
Span pixelRow = pixels.DangerousGetRowSpan(y).Slice(left, width);
@@ -57,7 +70,9 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
ulong a = TiffUtils.ConvertToUShortBigEndian(data.Slice(offset, 2));
offset += 2;
- pixelRow[x] = TiffUtils.ColorFromRgba64(rgba, r, g, b, a, color);
+ pixelRow[x] = hasAssociatedAlpha ?
+ TiffUtils.ColorFromRgba64Premultiplied(rgba, r, g, b, a, color) :
+ TiffUtils.ColorFromRgba64(rgba, r, g, b, a, color);
}
}
else
@@ -69,6 +84,12 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
pixelRow,
pixelRow.Length);
+ if (hasAssociatedAlpha)
+ {
+ PixelOperations.Instance.ToVector4(this.configuration, pixelRow, vectorsSpan);
+ PixelOperations.Instance.FromVector4Destructive(this.configuration, vectorsSpan, pixelRow, PixelConversionModifiers.Premultiply | PixelConversionModifiers.Scale);
+ }
+
offset += byteCount;
}
}
diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba16PlanarTiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba16PlanarTiffColor{TPixel}.cs
index 705010ce90..856d810d31 100644
--- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba16PlanarTiffColor{TPixel}.cs
+++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba16PlanarTiffColor{TPixel}.cs
@@ -17,11 +17,18 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
{
private readonly bool isBigEndian;
+ private readonly TiffExtraSampleType? extraSamplesType;
+
///
/// Initializes a new instance of the class.
///
- /// if set to true decodes the pixel data as big endian, otherwise as little endian.
- public Rgba16PlanarTiffColor(bool isBigEndian) => this.isBigEndian = isBigEndian;
+ /// The extra samples type.
+ /// If set to true decodes the pixel data as big endian, otherwise as little endian.
+ public Rgba16PlanarTiffColor(TiffExtraSampleType? extraSamplesType, bool isBigEndian)
+ {
+ this.extraSamplesType = extraSamplesType;
+ this.isBigEndian = isBigEndian;
+ }
///
public override void Decode(IMemoryOwner[] data, Buffer2D pixels, int left, int top, int width, int height)
@@ -30,13 +37,14 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
// we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623
Rgba64 rgba = TiffUtils.Rgba64Default;
var color = default(TPixel);
- color.FromVector4(TiffUtils.Vector4Default);
+ color.FromScaledVector4(TiffUtils.Vector4Default);
Span redData = data[0].GetSpan();
Span greenData = data[1].GetSpan();
Span blueData = data[2].GetSpan();
Span alphaData = data[3].GetSpan();
+ bool hasAssociatedAlpha = this.extraSamplesType.HasValue && this.extraSamplesType == TiffExtraSampleType.AssociatedAlphaData;
int offset = 0;
for (int y = top; y < top + height; y++)
{
@@ -52,7 +60,9 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
offset += 2;
- pixelRow[x] = TiffUtils.ColorFromRgba64(rgba, r, g, b, a, color);
+ pixelRow[x] = hasAssociatedAlpha ?
+ TiffUtils.ColorFromRgba64Premultiplied(rgba, r, g, b, a, color) :
+ TiffUtils.ColorFromRgba64(rgba, r, g, b, a, color);
}
}
else
@@ -62,11 +72,13 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
ulong r = TiffUtils.ConvertToUShortLittleEndian(redData.Slice(offset, 2));
ulong g = TiffUtils.ConvertToUShortLittleEndian(greenData.Slice(offset, 2));
ulong b = TiffUtils.ConvertToUShortLittleEndian(blueData.Slice(offset, 2));
- ulong a = TiffUtils.ConvertToUShortBigEndian(alphaData.Slice(offset, 2));
+ ulong a = TiffUtils.ConvertToUShortLittleEndian(alphaData.Slice(offset, 2));
offset += 2;
- pixelRow[x] = TiffUtils.ColorFromRgba64(rgba, r, g, b, a, color);
+ pixelRow[x] = hasAssociatedAlpha ?
+ TiffUtils.ColorFromRgba64Premultiplied(rgba, r, g, b, a, color) :
+ TiffUtils.ColorFromRgba64(rgba, r, g, b, a, color);
}
}
}
diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba24242424TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba24242424TiffColor{TPixel}.cs
index 71e1f7abd6..2ce30252f5 100644
--- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba24242424TiffColor{TPixel}.cs
+++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba24242424TiffColor{TPixel}.cs
@@ -16,11 +16,18 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
{
private readonly bool isBigEndian;
+ private readonly TiffExtraSampleType? extraSamplesType;
+
///
/// Initializes a new instance of the class.
///
+ /// The type of the extra samples.
/// if set to true decodes the pixel data as big endian, otherwise as little endian.
- public Rgba24242424TiffColor(bool isBigEndian) => this.isBigEndian = isBigEndian;
+ public Rgba24242424TiffColor(TiffExtraSampleType? extraSamplesType, bool isBigEndian)
+ {
+ this.extraSamplesType = extraSamplesType;
+ this.isBigEndian = isBigEndian;
+ }
///
public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height)
@@ -28,8 +35,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
// Note: due to an issue with netcore 2.1 and default values and unpredictable behavior with those,
// we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623
var color = default(TPixel);
- color.FromVector4(TiffUtils.Vector4Default);
+ color.FromScaledVector4(TiffUtils.Vector4Default);
+
+ bool hasAssociatedAlpha = this.extraSamplesType.HasValue && this.extraSamplesType == TiffExtraSampleType.AssociatedAlphaData;
int offset = 0;
+
Span buffer = stackalloc byte[4];
int bufferStartIdx = this.isBigEndian ? 1 : 0;
@@ -58,7 +68,9 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
ulong a = TiffUtils.ConvertToUIntBigEndian(buffer);
offset += 3;
- pixelRow[x] = TiffUtils.ColorScaleTo24Bit(r, g, b, a, color);
+ pixelRow[x] = hasAssociatedAlpha ?
+ TiffUtils.ColorScaleTo24BitPremultiplied(r, g, b, a, color) :
+ TiffUtils.ColorScaleTo24Bit(r, g, b, a, color);
}
}
else
@@ -81,7 +93,9 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
ulong a = TiffUtils.ConvertToUIntLittleEndian(buffer);
offset += 3;
- pixelRow[x] = TiffUtils.ColorScaleTo24Bit(r, g, b, a, color);
+ pixelRow[x] = hasAssociatedAlpha ?
+ TiffUtils.ColorScaleTo24BitPremultiplied(r, g, b, a, color) :
+ TiffUtils.ColorScaleTo24Bit(r, g, b, a, color);
}
}
}
diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba24PlanarTiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba24PlanarTiffColor{TPixel}.cs
index 03b78c3f87..89172cfe72 100644
--- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba24PlanarTiffColor{TPixel}.cs
+++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba24PlanarTiffColor{TPixel}.cs
@@ -17,11 +17,18 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
{
private readonly bool isBigEndian;
+ private readonly TiffExtraSampleType? extraSamplesType;
+
///
/// Initializes a new instance of the class.
///
+ /// The extra samples type.
/// if set to true decodes the pixel data as big endian, otherwise as little endian.
- public Rgba24PlanarTiffColor(bool isBigEndian) => this.isBigEndian = isBigEndian;
+ public Rgba24PlanarTiffColor(TiffExtraSampleType? extraSamplesType, bool isBigEndian)
+ {
+ this.extraSamplesType = extraSamplesType;
+ this.isBigEndian = isBigEndian;
+ }
///
public override void Decode(IMemoryOwner[] data, Buffer2D pixels, int left, int top, int width, int height)
@@ -29,7 +36,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
// Note: due to an issue with netcore 2.1 and default values and unpredictable behavior with those,
// we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623
var color = default(TPixel);
- color.FromVector4(TiffUtils.Vector4Default);
+ color.FromScaledVector4(TiffUtils.Vector4Default);
Span buffer = stackalloc byte[4];
int bufferStartIdx = this.isBigEndian ? 1 : 0;
@@ -39,6 +46,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
Span alphaData = data[3].GetSpan();
Span bufferSpan = buffer.Slice(bufferStartIdx);
+ bool hasAssociatedAlpha = this.extraSamplesType.HasValue && this.extraSamplesType == TiffExtraSampleType.AssociatedAlphaData;
int offset = 0;
for (int y = top; y < top + height; y++)
{
@@ -58,7 +66,9 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
offset += 3;
- pixelRow[x] = TiffUtils.ColorScaleTo24Bit(r, g, b, a, color);
+ pixelRow[x] = hasAssociatedAlpha ?
+ TiffUtils.ColorScaleTo24BitPremultiplied(r, g, b, a, color) :
+ TiffUtils.ColorScaleTo24Bit(r, g, b, a, color);
}
}
else
@@ -76,7 +86,9 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
offset += 3;
- pixelRow[x] = TiffUtils.ColorScaleTo24Bit(r, g, b, a, color);
+ pixelRow[x] = hasAssociatedAlpha ?
+ TiffUtils.ColorScaleTo24BitPremultiplied(r, g, b, a, color) :
+ TiffUtils.ColorScaleTo24Bit(r, g, b, a, color);
}
}
}
diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba32323232TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba32323232TiffColor{TPixel}.cs
index fbd18ca3e0..8ee9eb0bf9 100644
--- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba32323232TiffColor{TPixel}.cs
+++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba32323232TiffColor{TPixel}.cs
@@ -16,11 +16,18 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
{
private readonly bool isBigEndian;
+ private readonly TiffExtraSampleType? extraSamplesType;
+
///
/// Initializes a new instance of the class.
///
+ /// The type of the extra samples.
/// if set to true decodes the pixel data as big endian, otherwise as little endian.
- public Rgba32323232TiffColor(bool isBigEndian) => this.isBigEndian = isBigEndian;
+ public Rgba32323232TiffColor(TiffExtraSampleType? extraSamplesType, bool isBigEndian)
+ {
+ this.extraSamplesType = extraSamplesType;
+ this.isBigEndian = isBigEndian;
+ }
///
public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height)
@@ -28,7 +35,9 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
// Note: due to an issue with netcore 2.1 and default values and unpredictable behavior with those,
// we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623
var color = default(TPixel);
- color.FromVector4(TiffUtils.Vector4Default);
+ color.FromScaledVector4(TiffUtils.Vector4Default);
+
+ bool hasAssociatedAlpha = this.extraSamplesType.HasValue && this.extraSamplesType == TiffExtraSampleType.AssociatedAlphaData;
int offset = 0;
for (int y = top; y < top + height; y++)
@@ -51,7 +60,9 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
ulong a = TiffUtils.ConvertToUIntBigEndian(data.Slice(offset, 4));
offset += 4;
- pixelRow[x] = TiffUtils.ColorScaleTo32Bit(r, g, b, a, color);
+ pixelRow[x] = hasAssociatedAlpha ?
+ TiffUtils.ColorScaleTo32BitPremultiplied(r, g, b, a, color) :
+ TiffUtils.ColorScaleTo32Bit(r, g, b, a, color);
}
}
else
@@ -70,7 +81,9 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
ulong a = TiffUtils.ConvertToUIntLittleEndian(data.Slice(offset, 4));
offset += 4;
- pixelRow[x] = TiffUtils.ColorScaleTo32Bit(r, g, b, a, color);
+ pixelRow[x] = hasAssociatedAlpha ?
+ TiffUtils.ColorScaleTo32BitPremultiplied(r, g, b, a, color) :
+ TiffUtils.ColorScaleTo32Bit(r, g, b, a, color);
}
}
}
diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba32PlanarTiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba32PlanarTiffColor{TPixel}.cs
index e231111590..c98ac1cf00 100644
--- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba32PlanarTiffColor{TPixel}.cs
+++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba32PlanarTiffColor{TPixel}.cs
@@ -17,11 +17,18 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
{
private readonly bool isBigEndian;
+ private readonly TiffExtraSampleType? extraSamplesType;
+
///
/// Initializes a new instance of the class.
///
+ /// The extra samples type.
/// if set to true decodes the pixel data as big endian, otherwise as little endian.
- public Rgba32PlanarTiffColor(bool isBigEndian) => this.isBigEndian = isBigEndian;
+ public Rgba32PlanarTiffColor(TiffExtraSampleType? extraSamplesType, bool isBigEndian)
+ {
+ this.extraSamplesType = extraSamplesType;
+ this.isBigEndian = isBigEndian;
+ }
///
public override void Decode(IMemoryOwner[] data, Buffer2D pixels, int left, int top, int width, int height)
@@ -29,13 +36,14 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
// Note: due to an issue with netcore 2.1 and default values and unpredictable behavior with those,
// we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623
var color = default(TPixel);
- color.FromVector4(TiffUtils.Vector4Default);
+ color.FromScaledVector4(TiffUtils.Vector4Default);
Span redData = data[0].GetSpan();
Span greenData = data[1].GetSpan();
Span blueData = data[2].GetSpan();
Span alphaData = data[3].GetSpan();
+ bool hasAssociatedAlpha = this.extraSamplesType.HasValue && this.extraSamplesType == TiffExtraSampleType.AssociatedAlphaData;
int offset = 0;
for (int y = top; y < top + height; y++)
{
@@ -51,7 +59,9 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
offset += 4;
- pixelRow[x] = TiffUtils.ColorScaleTo32Bit(r, g, b, a, color);
+ pixelRow[x] = hasAssociatedAlpha ?
+ TiffUtils.ColorScaleTo32BitPremultiplied(r, g, b, a, color) :
+ TiffUtils.ColorScaleTo32Bit(r, g, b, a, color);
}
}
else
@@ -65,7 +75,9 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
offset += 4;
- pixelRow[x] = TiffUtils.ColorScaleTo32Bit(r, g, b, a, color);
+ pixelRow[x] = hasAssociatedAlpha ?
+ TiffUtils.ColorScaleTo32BitPremultiplied(r, g, b, a, color) :
+ TiffUtils.ColorScaleTo32Bit(r, g, b, a, color);
}
}
}
diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba8888TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba8888TiffColor{TPixel}.cs
index 491a42fb75..967a68ad0c 100644
--- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba8888TiffColor{TPixel}.cs
+++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba8888TiffColor{TPixel}.cs
@@ -2,6 +2,9 @@
// Licensed under the Apache License, Version 2.0.
using System;
+using System.Buffers;
+using System.Numerics;
+using SixLabors.ImageSharp.Formats.Tiff.Utils;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
@@ -15,13 +18,27 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
{
private readonly Configuration configuration;
- public Rgba8888TiffColor(Configuration configuration) => this.configuration = configuration;
+ private readonly MemoryAllocator memoryAllocator;
+
+ private readonly TiffExtraSampleType? extraSamplesType;
+
+ public Rgba8888TiffColor(Configuration configuration, MemoryAllocator memoryAllocator, TiffExtraSampleType? extraSamplesType)
+ {
+ this.configuration = configuration;
+ this.memoryAllocator = memoryAllocator;
+ this.extraSamplesType = extraSamplesType;
+ }
///
public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height)
{
int offset = 0;
+ bool hasAssociatedAlpha = this.extraSamplesType.HasValue && this.extraSamplesType == TiffExtraSampleType.AssociatedAlphaData;
+ var color = default(TPixel);
+ color.FromScaledVector4(TiffUtils.Vector4Default);
+ using IMemoryOwner vectors = hasAssociatedAlpha ? this.memoryAllocator.Allocate(width) : null;
+ Span vectorsSpan = hasAssociatedAlpha ? vectors.GetSpan() : Span.Empty;
for (int y = top; y < top + height; y++)
{
Span pixelRow = pixels.DangerousGetRowSpan(y).Slice(left, width);
@@ -32,6 +49,12 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
pixelRow,
pixelRow.Length);
+ if (hasAssociatedAlpha)
+ {
+ PixelOperations.Instance.ToVector4(this.configuration, pixelRow, vectorsSpan);
+ PixelOperations.Instance.FromVector4Destructive(this.configuration, vectorsSpan, pixelRow, PixelConversionModifiers.Premultiply | PixelConversionModifiers.Scale);
+ }
+
offset += byteCount;
}
}
diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbaFloat32323232TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbaFloat32323232TiffColor{TPixel}.cs
index 4fb0797dc5..f95045ec5a 100644
--- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbaFloat32323232TiffColor{TPixel}.cs
+++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbaFloat32323232TiffColor{TPixel}.cs
@@ -29,7 +29,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
// Note: due to an issue with netcore 2.1 and default values and unpredictable behavior with those,
// we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623
var color = default(TPixel);
- color.FromVector4(TiffUtils.Vector4Default);
+ color.FromScaledVector4(TiffUtils.Vector4Default);
int offset = 0;
byte[] buffer = new byte[4];
@@ -62,7 +62,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
offset += 4;
var colorVector = new Vector4(r, g, b, a);
- color.FromVector4(colorVector);
+ color.FromScaledVector4(colorVector);
pixelRow[x] = color;
}
}
@@ -87,7 +87,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
offset += 4;
var colorVector = new Vector4(r, g, b, a);
- color.FromVector4(colorVector);
+ color.FromScaledVector4(colorVector);
pixelRow[x] = color;
}
}
diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbaPlanarTiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbaPlanarTiffColor{TPixel}.cs
index f2ccf2ec81..e2dbdfb00a 100644
--- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbaPlanarTiffColor{TPixel}.cs
+++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbaPlanarTiffColor{TPixel}.cs
@@ -32,7 +32,9 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
private readonly ushort bitsPerSampleA;
- public RgbaPlanarTiffColor(TiffBitsPerSample bitsPerSample)
+ private readonly TiffExtraSampleType? extraSampleType;
+
+ public RgbaPlanarTiffColor(TiffExtraSampleType? extraSampleType, TiffBitsPerSample bitsPerSample)
{
this.bitsPerSampleR = bitsPerSample.Channel0;
this.bitsPerSampleG = bitsPerSample.Channel1;
@@ -43,6 +45,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
this.gFactor = (1 << this.bitsPerSampleG) - 1.0f;
this.bFactor = (1 << this.bitsPerSampleB) - 1.0f;
this.aFactor = (1 << this.bitsPerSampleA) - 1.0f;
+
+ this.extraSampleType = extraSampleType;
}
///
@@ -57,6 +61,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
public override void Decode(IMemoryOwner[] data, Buffer2D pixels, int left, int top, int width, int height)
{
var color = default(TPixel);
+ bool hasAssociatedAlpha = this.extraSampleType.HasValue && this.extraSampleType == TiffExtraSampleType.AssociatedAlphaData;
var rBitReader = new BitReader(data[0].GetSpan());
var gBitReader = new BitReader(data[1].GetSpan());
@@ -73,7 +78,16 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
float b = bBitReader.ReadBits(this.bitsPerSampleB) / this.bFactor;
float a = aBitReader.ReadBits(this.bitsPerSampleA) / this.aFactor;
- color.FromVector4(new Vector4(r, g, b, a));
+ var vec = new Vector4(r, g, b, a);
+ if (hasAssociatedAlpha)
+ {
+ color = TiffUtils.UnPremultiply(ref vec, color);
+ }
+ else
+ {
+ color.FromScaledVector4(vec);
+ }
+
pixelRow[x] = color;
}
diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbaTiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbaTiffColor{TPixel}.cs
index 8bec7da89d..74b816dbc9 100644
--- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbaTiffColor{TPixel}.cs
+++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbaTiffColor{TPixel}.cs
@@ -31,7 +31,9 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
private readonly ushort bitsPerSampleA;
- public RgbaTiffColor(TiffBitsPerSample bitsPerSample)
+ private readonly TiffExtraSampleType? extraSamplesType;
+
+ public RgbaTiffColor(TiffExtraSampleType? extraSampleType, TiffBitsPerSample bitsPerSample)
{
this.bitsPerSampleR = bitsPerSample.Channel0;
this.bitsPerSampleG = bitsPerSample.Channel1;
@@ -42,6 +44,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
this.gFactor = (1 << this.bitsPerSampleG) - 1.0f;
this.bFactor = (1 << this.bitsPerSampleB) - 1.0f;
this.aFactor = (1 << this.bitsPerSampleA) - 1.0f;
+
+ this.extraSamplesType = extraSampleType;
}
///
@@ -51,6 +55,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
var bitReader = new BitReader(data);
+ bool hasAssociatedAlpha = this.extraSamplesType.HasValue && this.extraSamplesType == TiffExtraSampleType.AssociatedAlphaData;
+
for (int y = top; y < top + height; y++)
{
Span pixelRow = pixels.DangerousGetRowSpan(y).Slice(left, width);
@@ -61,8 +67,16 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
float b = bitReader.ReadBits(this.bitsPerSampleB) / this.bFactor;
float a = bitReader.ReadBits(this.bitsPerSampleB) / this.aFactor;
- color.FromVector4(new Vector4(r, g, b, a));
- pixelRow[x] = color;
+ var vec = new Vector4(r, g, b, a);
+ if (hasAssociatedAlpha)
+ {
+ pixelRow[x] = TiffUtils.UnPremultiply(ref vec, color);
+ }
+ else
+ {
+ color.FromScaledVector4(vec);
+ pixelRow[x] = color;
+ }
}
bitReader.NextRow();
diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorDecoderFactory{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorDecoderFactory{TPixel}.cs
index 1c608e3033..c95d039461 100644
--- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorDecoderFactory{TPixel}.cs
+++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorDecoderFactory{TPixel}.cs
@@ -14,6 +14,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
MemoryAllocator memoryAllocator,
TiffColorType colorType,
TiffBitsPerSample bitsPerSample,
+ TiffExtraSampleType? extraSampleType,
ushort[] colorMap,
Rational[] referenceBlackAndWhite,
Rational[] ycbcrCoefficients,
@@ -125,7 +126,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
&& bitsPerSample.Channel0 == 2,
"bitsPerSample");
DebugGuard.IsTrue(colorMap == null, "colorMap");
- return new RgbaTiffColor(bitsPerSample);
+ return new RgbaTiffColor(extraSampleType, bitsPerSample);
case TiffColorType.Rgb333:
DebugGuard.IsTrue(
@@ -146,7 +147,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
&& bitsPerSample.Channel0 == 3,
"bitsPerSample");
DebugGuard.IsTrue(colorMap == null, "colorMap");
- return new RgbaTiffColor(bitsPerSample);
+ return new RgbaTiffColor(extraSampleType, bitsPerSample);
case TiffColorType.Rgb444:
DebugGuard.IsTrue(
@@ -167,7 +168,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
&& bitsPerSample.Channel0 == 4,
"bitsPerSample");
DebugGuard.IsTrue(colorMap == null, "colorMap");
- return new RgbaTiffColor(bitsPerSample);
+ return new RgbaTiffColor(extraSampleType, bitsPerSample);
case TiffColorType.Rgb555:
DebugGuard.IsTrue(
@@ -188,7 +189,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
&& bitsPerSample.Channel0 == 5,
"bitsPerSample");
DebugGuard.IsTrue(colorMap == null, "colorMap");
- return new RgbaTiffColor(bitsPerSample);
+ return new RgbaTiffColor(extraSampleType, bitsPerSample);
case TiffColorType.Rgb666:
DebugGuard.IsTrue(
@@ -209,7 +210,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
&& bitsPerSample.Channel0 == 6,
"bitsPerSample");
DebugGuard.IsTrue(colorMap == null, "colorMap");
- return new RgbaTiffColor(bitsPerSample);
+ return new RgbaTiffColor(extraSampleType, bitsPerSample);
case TiffColorType.Rgb888:
DebugGuard.IsTrue(
@@ -230,7 +231,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
&& bitsPerSample.Channel0 == 8,
"bitsPerSample");
DebugGuard.IsTrue(colorMap == null, "colorMap");
- return new Rgba8888TiffColor(configuration);
+ return new Rgba8888TiffColor(configuration, memoryAllocator, extraSampleType);
case TiffColorType.Rgb101010:
DebugGuard.IsTrue(
@@ -251,7 +252,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
&& bitsPerSample.Channel0 == 10,
"bitsPerSample");
DebugGuard.IsTrue(colorMap == null, "colorMap");
- return new RgbaTiffColor(bitsPerSample);
+ return new RgbaTiffColor(extraSampleType, bitsPerSample);
case TiffColorType.Rgb121212:
DebugGuard.IsTrue(
@@ -272,7 +273,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
&& bitsPerSample.Channel0 == 12,
"bitsPerSample");
DebugGuard.IsTrue(colorMap == null, "colorMap");
- return new RgbaTiffColor(bitsPerSample);
+ return new RgbaTiffColor(extraSampleType, bitsPerSample);
case TiffColorType.Rgb141414:
DebugGuard.IsTrue(
@@ -293,7 +294,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
&& bitsPerSample.Channel0 == 14,
"bitsPerSample");
DebugGuard.IsTrue(colorMap == null, "colorMap");
- return new RgbaTiffColor(bitsPerSample);
+ return new RgbaTiffColor(extraSampleType, bitsPerSample);
case TiffColorType.Rgb161616:
DebugGuard.IsTrue(
@@ -314,7 +315,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
&& bitsPerSample.Channel0 == 16,
"bitsPerSample");
DebugGuard.IsTrue(colorMap == null, "colorMap");
- return new Rgba16161616TiffColor(configuration, isBigEndian: byteOrder == ByteOrder.BigEndian);
+ return new Rgba16161616TiffColor(configuration, memoryAllocator, extraSampleType, isBigEndian: byteOrder == ByteOrder.BigEndian);
case TiffColorType.Rgb242424:
DebugGuard.IsTrue(
@@ -335,7 +336,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
&& bitsPerSample.Channel0 == 24,
"bitsPerSample");
DebugGuard.IsTrue(colorMap == null, "colorMap");
- return new Rgba24242424TiffColor(isBigEndian: byteOrder == ByteOrder.BigEndian);
+ return new Rgba24242424TiffColor(extraSampleType, isBigEndian: byteOrder == ByteOrder.BigEndian);
case TiffColorType.Rgb323232:
DebugGuard.IsTrue(
@@ -356,7 +357,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
&& bitsPerSample.Channel0 == 32,
"bitsPerSample");
DebugGuard.IsTrue(colorMap == null, "colorMap");
- return new Rgba32323232TiffColor(isBigEndian: byteOrder == ByteOrder.BigEndian);
+ return new Rgba32323232TiffColor(extraSampleType, isBigEndian: byteOrder == ByteOrder.BigEndian);
case TiffColorType.RgbFloat323232:
DebugGuard.IsTrue(
@@ -394,6 +395,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
public static TiffBasePlanarColorDecoder CreatePlanar(
TiffColorType colorType,
TiffBitsPerSample bitsPerSample,
+ TiffExtraSampleType? extraSampleType,
ushort[] colorMap,
Rational[] referenceBlackAndWhite,
Rational[] ycbcrCoefficients,
@@ -408,7 +410,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
case TiffColorType.Rgba8888Planar:
DebugGuard.IsTrue(colorMap == null, "colorMap");
- return new RgbaPlanarTiffColor(bitsPerSample);
+ return new RgbaPlanarTiffColor(extraSampleType, bitsPerSample);
case TiffColorType.YCbCrPlanar:
return new YCbCrPlanarTiffColor(referenceBlackAndWhite, ycbcrCoefficients, ycbcrSubSampling);
@@ -419,7 +421,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
case TiffColorType.Rgba16161616Planar:
DebugGuard.IsTrue(colorMap == null, "colorMap");
- return new Rgba16PlanarTiffColor(byteOrder == ByteOrder.BigEndian);
+ return new Rgba16PlanarTiffColor(extraSampleType, byteOrder == ByteOrder.BigEndian);
case TiffColorType.Rgb242424Planar:
DebugGuard.IsTrue(colorMap == null, "colorMap");
@@ -427,7 +429,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
case TiffColorType.Rgba24242424Planar:
DebugGuard.IsTrue(colorMap == null, "colorMap");
- return new Rgba24PlanarTiffColor(byteOrder == ByteOrder.BigEndian);
+ return new Rgba24PlanarTiffColor(extraSampleType, byteOrder == ByteOrder.BigEndian);
case TiffColorType.Rgb323232Planar:
DebugGuard.IsTrue(colorMap == null, "colorMap");
@@ -435,7 +437,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
case TiffColorType.Rgba32323232Planar:
DebugGuard.IsTrue(colorMap == null, "colorMap");
- return new Rgba32PlanarTiffColor(byteOrder == ByteOrder.BigEndian);
+ return new Rgba32PlanarTiffColor(extraSampleType, byteOrder == ByteOrder.BigEndian);
default:
throw TiffThrowHelper.InvalidColorType(colorType.ToString());
diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero16TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero16TiffColor{TPixel}.cs
index 038281c998..d509776d7d 100644
--- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero16TiffColor{TPixel}.cs
+++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero16TiffColor{TPixel}.cs
@@ -29,7 +29,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
// we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623
L16 l16 = TiffUtils.L16Default;
var color = default(TPixel);
- color.FromVector4(TiffUtils.Vector4Default);
+ color.FromScaledVector4(TiffUtils.Vector4Default);
int offset = 0;
for (int y = top; y < top + height; y++)
diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero24TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero24TiffColor{TPixel}.cs
index 807023b6bf..fbf8130789 100644
--- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero24TiffColor{TPixel}.cs
+++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero24TiffColor{TPixel}.cs
@@ -28,7 +28,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
// Note: due to an issue with netcore 2.1 and default values and unpredictable behavior with those,
// we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623
var color = default(TPixel);
- color.FromVector4(TiffUtils.Vector4Default);
+ color.FromScaledVector4(TiffUtils.Vector4Default);
byte[] buffer = new byte[4];
int bufferStartIdx = this.isBigEndian ? 1 : 0;
const uint maxValue = 0xFFFFFF;
diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero32FloatTiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero32FloatTiffColor{TPixel}.cs
index 71323c7bae..40d1541c51 100644
--- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero32FloatTiffColor{TPixel}.cs
+++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero32FloatTiffColor{TPixel}.cs
@@ -29,7 +29,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
// Note: due to an issue with netcore 2.1 and default values and unpredictable behavior with those,
// we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623
var color = default(TPixel);
- color.FromVector4(TiffUtils.Vector4Default);
+ color.FromScaledVector4(TiffUtils.Vector4Default);
byte[] buffer = new byte[4];
int offset = 0;
@@ -46,7 +46,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
offset += 4;
var colorVector = new Vector4(intensity, intensity, intensity, 1.0f);
- color.FromVector4(colorVector);
+ color.FromScaledVector4(colorVector);
pixelRow[x] = color;
}
}
@@ -59,7 +59,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
offset += 4;
var colorVector = new Vector4(intensity, intensity, intensity, 1.0f);
- color.FromVector4(colorVector);
+ color.FromScaledVector4(colorVector);
pixelRow[x] = color;
}
}
diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero32TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero32TiffColor{TPixel}.cs
index e433956f09..fd908c1e9f 100644
--- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero32TiffColor{TPixel}.cs
+++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero32TiffColor{TPixel}.cs
@@ -28,7 +28,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
// Note: due to an issue with netcore 2.1 and default values and unpredictable behavior with those,
// we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623
var color = default(TPixel);
- color.FromVector4(TiffUtils.Vector4Default);
+ color.FromScaledVector4(TiffUtils.Vector4Default);
const uint maxValue = 0xFFFFFFFF;
int offset = 0;
diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColor{TPixel}.cs
index d692fc7897..e1e2ba9838 100644
--- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColor{TPixel}.cs
+++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZeroTiffColor{TPixel}.cs
@@ -40,7 +40,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
int value = bitReader.ReadBits(this.bitsPerSample0);
float intensity = 1.0f - (value / this.factor);
- color.FromVector4(new Vector4(intensity, intensity, intensity, 1.0f));
+ color.FromScaledVector4(new Vector4(intensity, intensity, intensity, 1.0f));
pixelRow[x] = color;
}
diff --git a/src/ImageSharp/Formats/Tiff/README.md b/src/ImageSharp/Formats/Tiff/README.md
index aa960b373c..8cb327a7b0 100644
--- a/src/ImageSharp/Formats/Tiff/README.md
+++ b/src/ImageSharp/Formats/Tiff/README.md
@@ -28,15 +28,6 @@
- The Decoder currently only supports decoding multiframe images, which have the same dimensions.
- Some compression formats are not yet supported. See the list below.
-### Deviations from the TIFF spec (to be fixed)
-
-- Decoder
- - A Baseline TIFF reader must skip over extra components (e.g. RGB with 4 samples per pixels)
- - NB: Need to handle this for both planar and chunky data
- - If the SampleFormat field is present and not 1 - fail gracefully if you cannot handle this
- - Compression=None should treat 16/32-BitsPerSample for all samples as SHORT/LONG (for byte order and padding rows)
- - Check Planar format data - is this encoded as strips in order RGBRGBRGB or RRRGGGBBB?
-
### Compression Formats
| |Encoder|Decoder|Comments |
@@ -87,7 +78,7 @@
|Model | Y | Y | |
|StripOffsets | Y | Y | |
|Orientation | | - | Ignore. Many readers ignore this tag. |
-|SamplesPerPixel | Y | - | Currently ignored, as can be inferred from count of BitsPerSample |
+|SamplesPerPixel | Y | - | Currently ignored, as can be inferred from count of BitsPerSample. |
|RowsPerStrip | Y | Y | |
|StripByteCounts | Y | Y | |
|MinSampleValue | | | |
@@ -105,7 +96,7 @@
|Artist | Y | Y | |
|HostComputer | Y | Y | |
|ColorMap | Y | Y | |
-|ExtraSamples | | (Y) | Only UnassociatedAlphaData is supported so far |
+|ExtraSamples | | Y | Unspecified alpha data is not supported. |
|Copyright | Y | Y | |
### Extension TIFF Tags
diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs
index e5b810738a..1cd3d2c0c1 100644
--- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs
+++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs
@@ -118,9 +118,9 @@ namespace SixLabors.ImageSharp.Formats.Tiff
public TiffFillOrder FillOrder { get; set; }
///
- /// Gets or sets the extra samples, which can contain the alpha channel data.
+ /// Gets or sets the extra samples type.
///
- public TiffExtraSampleType? ExtraSamples { get; set; }
+ public TiffExtraSampleType? ExtraSamplesType { get; set; }
///
/// Gets or sets the JPEG tables when jpeg compression is used.
@@ -157,40 +157,52 @@ namespace SixLabors.ImageSharp.Formats.Tiff
public Image Decode(BufferedReadStream stream, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel
{
- this.inputStream = stream;
- var reader = new DirectoryReader(stream, this.Configuration.MemoryAllocator);
-
- IEnumerable directories = reader.Read();
- this.byteOrder = reader.ByteOrder;
- this.isBigTiff = reader.IsBigTiff;
-
var frames = new List>();
- foreach (ExifProfile ifd in directories)
+ try
{
- cancellationToken.ThrowIfCancellationRequested();
- ImageFrame frame = this.DecodeFrame(ifd, cancellationToken);
- frames.Add(frame);
+ this.inputStream = stream;
+ var reader = new DirectoryReader(stream, this.Configuration.MemoryAllocator);
+
+ IEnumerable directories = reader.Read();
+ this.byteOrder = reader.ByteOrder;
+ this.isBigTiff = reader.IsBigTiff;
- if (this.decodingMode is FrameDecodingMode.First)
+ foreach (ExifProfile ifd in directories)
{
- break;
+ cancellationToken.ThrowIfCancellationRequested();
+ ImageFrame frame = this.DecodeFrame(ifd, cancellationToken);
+ frames.Add(frame);
+
+ if (this.decodingMode is FrameDecodingMode.First)
+ {
+ break;
+ }
}
- }
- ImageMetadata metadata = TiffDecoderMetadataCreator.Create(frames, this.ignoreMetadata, reader.ByteOrder, reader.IsBigTiff);
+ ImageMetadata metadata = TiffDecoderMetadataCreator.Create(frames, this.ignoreMetadata, reader.ByteOrder, reader.IsBigTiff);
- // TODO: Tiff frames can have different sizes.
- ImageFrame root = frames[0];
- this.Dimensions = root.Size();
- foreach (ImageFrame frame in frames)
- {
- if (frame.Size() != root.Size())
+ // TODO: Tiff frames can have different sizes.
+ ImageFrame root = frames[0];
+ this.Dimensions = root.Size();
+ foreach (ImageFrame frame in frames)
{
- TiffThrowHelper.ThrowNotSupported("Images with different sizes are not supported");
+ if (frame.Size() != root.Size())
+ {
+ TiffThrowHelper.ThrowNotSupported("Images with different sizes are not supported");
+ }
}
+
+ return new Image(this.Configuration, metadata, frames);
}
+ catch
+ {
+ foreach (ImageFrame f in frames)
+ {
+ f.Dispose();
+ }
- return new Image(this.Configuration, metadata, frames);
+ throw;
+ }
}
///
@@ -240,8 +252,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff
var stripOffsetsArray = (Array)tags.GetValueInternal(ExifTag.StripOffsets).GetValue();
var stripByteCountsArray = (Array)tags.GetValueInternal(ExifTag.StripByteCounts).GetValue();
- IMemoryOwner stripOffsetsMemory = this.ConvertNumbers(stripOffsetsArray, out Span stripOffsets);
- IMemoryOwner stripByteCountsMemory = this.ConvertNumbers(stripByteCountsArray, out Span stripByteCounts);
+ using IMemoryOwner stripOffsetsMemory = this.ConvertNumbers(stripOffsetsArray, out Span stripOffsets);
+ using IMemoryOwner stripByteCountsMemory = this.ConvertNumbers(stripByteCountsArray, out Span stripByteCounts);
if (this.PlanarConfiguration == TiffPlanarConfiguration.Planar)
{
@@ -262,8 +274,6 @@ namespace SixLabors.ImageSharp.Formats.Tiff
cancellationToken);
}
- stripOffsetsMemory?.Dispose();
- stripByteCountsMemory?.Dispose();
return frame;
}
@@ -375,6 +385,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff
TiffBasePlanarColorDecoder colorDecoder = TiffColorDecoderFactory.CreatePlanar(
this.ColorType,
this.BitsPerSample,
+ this.ExtraSamplesType,
this.ColorMap,
this.ReferenceBlackAndWhite,
this.YcbcrCoefficients,
@@ -456,6 +467,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff
this.memoryAllocator,
this.ColorType,
this.BitsPerSample,
+ this.ExtraSamplesType,
this.ColorMap,
this.ReferenceBlackAndWhite,
this.YcbcrCoefficients,
diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs
index 23bc5f15f2..6baf71466a 100644
--- a/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs
+++ b/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs
@@ -39,10 +39,10 @@ namespace SixLabors.ImageSharp.Formats.Tiff
}
var extraSamplesType = (TiffExtraSampleType)extraSamples[0];
- options.ExtraSamples = extraSamplesType;
- if (extraSamplesType is not TiffExtraSampleType.UnassociatedAlphaData)
+ options.ExtraSamplesType = extraSamplesType;
+ if (extraSamplesType is not (TiffExtraSampleType.UnassociatedAlphaData or TiffExtraSampleType.AssociatedAlphaData))
{
- TiffThrowHelper.ThrowNotSupported("Decoding Tiff images with ExtraSamples is only supported with UnassociatedAlphaData.");
+ TiffThrowHelper.ThrowNotSupported("Decoding Tiff images with ExtraSamples is not supported with UnspecifiedData.");
}
}
diff --git a/src/ImageSharp/Formats/Tiff/Utils/TiffUtils.cs b/src/ImageSharp/Formats/Tiff/Utils/TiffUtils.cs
index b7d4b6e7cc..532423c4f1 100644
--- a/src/ImageSharp/Formats/Tiff/Utils/TiffUtils.cs
+++ b/src/ImageSharp/Formats/Tiff/Utils/TiffUtils.cs
@@ -46,7 +46,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Utils
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static TPixel ColorFromRgba64(Rgba64 rgba, ulong r, ulong g, ulong b, TPixel color)
+ public static TPixel ColorFromRgb64(Rgba64 rgba, ulong r, ulong g, ulong b, TPixel color)
where TPixel : unmanaged, IPixel
{
rgba.PackedValue = r | (g << 16) | (b << 32) | (0xfffful << 48);
@@ -63,12 +63,21 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Utils
return color;
}
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static TPixel ColorFromRgba64Premultiplied(Rgba64 rgba, ulong r, ulong g, ulong b, ulong a, TPixel color)
+ where TPixel : unmanaged, IPixel
+ {
+ rgba.PackedValue = r | (g << 16) | (b << 32) | (a << 48);
+ var vec = rgba.ToVector4();
+ return UnPremultiply(ref vec, color);
+ }
+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TPixel ColorScaleTo24Bit(ulong r, ulong g, ulong b, TPixel color)
where TPixel : unmanaged, IPixel
{
var colorVector = new Vector4(r * Scale24Bit, g * Scale24Bit, b * Scale24Bit, 1.0f);
- color.FromVector4(colorVector);
+ color.FromScaledVector4(colorVector);
return color;
}
@@ -76,17 +85,25 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Utils
public static TPixel ColorScaleTo24Bit(ulong r, ulong g, ulong b, ulong a, TPixel color)
where TPixel : unmanaged, IPixel
{
- var colorVector = new Vector4(r * Scale24Bit, g * Scale24Bit, b * Scale24Bit, a * Scale24Bit);
- color.FromVector4(colorVector);
+ Vector4 colorVector = new Vector4(r, g, b, a) * Scale24Bit;
+ color.FromScaledVector4(colorVector);
return color;
}
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static TPixel ColorScaleTo24BitPremultiplied(ulong r, ulong g, ulong b, ulong a, TPixel color)
+ where TPixel : unmanaged, IPixel
+ {
+ Vector4 colorVector = new Vector4(r, g, b, a) * Scale24Bit;
+ return UnPremultiply(ref colorVector, color);
+ }
+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TPixel ColorScaleTo32Bit(ulong r, ulong g, ulong b, TPixel color)
where TPixel : unmanaged, IPixel
{
var colorVector = new Vector4(r * Scale32Bit, g * Scale32Bit, b * Scale32Bit, 1.0f);
- color.FromVector4(colorVector);
+ color.FromScaledVector4(colorVector);
return color;
}
@@ -94,11 +111,19 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Utils
public static TPixel ColorScaleTo32Bit(ulong r, ulong g, ulong b, ulong a, TPixel color)
where TPixel : unmanaged, IPixel
{
- var colorVector = new Vector4(r * Scale32Bit, g * Scale32Bit, b * Scale32Bit, a * Scale32Bit);
- color.FromVector4(colorVector);
+ Vector4 colorVector = new Vector4(r, g, b, a) * Scale32Bit;
+ color.FromScaledVector4(colorVector);
return color;
}
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static TPixel ColorScaleTo32BitPremultiplied(ulong r, ulong g, ulong b, ulong a, TPixel color)
+ where TPixel : unmanaged, IPixel
+ {
+ Vector4 colorVector = new Vector4(r, g, b, a) * Scale32Bit;
+ return UnPremultiply(ref colorVector, color);
+ }
+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TPixel ColorFromL16(L16 l16, ushort intensity, TPixel color)
where TPixel : unmanaged, IPixel
@@ -113,7 +138,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Utils
where TPixel : unmanaged, IPixel
{
var colorVector = new Vector4(intensity * Scale24Bit, intensity * Scale24Bit, intensity * Scale24Bit, 1.0f);
- color.FromVector4(colorVector);
+ color.FromScaledVector4(colorVector);
return color;
}
@@ -122,7 +147,17 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Utils
where TPixel : unmanaged, IPixel
{
var colorVector = new Vector4(intensity * Scale32Bit, intensity * Scale32Bit, intensity * Scale32Bit, 1.0f);
- color.FromVector4(colorVector);
+ color.FromScaledVector4(colorVector);
+ return color;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static TPixel UnPremultiply(ref Vector4 vector, TPixel color)
+ where TPixel : unmanaged, IPixel
+ {
+ Numerics.UnPremultiply(ref vector);
+ color.FromScaledVector4(vector);
+
return color;
}
@@ -139,8 +174,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Utils
return 0;
}
- int padding = subSampling - (valueToRoundUp % subSampling);
- return padding;
+ return subSampling - (valueToRoundUp % subSampling);
}
}
}
diff --git a/src/ImageSharp/Formats/Webp/WebpDecoderCore.cs b/src/ImageSharp/Formats/Webp/WebpDecoderCore.cs
index 9d18e5d821..0e00f037ca 100644
--- a/src/ImageSharp/Formats/Webp/WebpDecoderCore.cs
+++ b/src/ImageSharp/Formats/Webp/WebpDecoderCore.cs
@@ -82,38 +82,47 @@ namespace SixLabors.ImageSharp.Formats.Webp
public Image Decode(BufferedReadStream stream, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel
{
- this.Metadata = new ImageMetadata();
- this.currentStream = stream;
+ Image image = null;
+ try
+ {
+ this.Metadata = new ImageMetadata();
+ this.currentStream = stream;
- uint fileSize = this.ReadImageHeader();
+ uint fileSize = this.ReadImageHeader();
- using (this.webImageInfo = this.ReadVp8Info())
- {
- if (this.webImageInfo.Features is { Animation: true })
+ using (this.webImageInfo = this.ReadVp8Info())
{
- WebpThrowHelper.ThrowNotSupportedException("Animations are not supported");
- }
+ if (this.webImageInfo.Features is { Animation: true })
+ {
+ WebpThrowHelper.ThrowNotSupportedException("Animations are not supported");
+ }
- var image = new Image(this.Configuration, (int)this.webImageInfo.Width, (int)this.webImageInfo.Height, this.Metadata);
- Buffer2D pixels = image.GetRootFramePixelBuffer();
- if (this.webImageInfo.IsLossless)
- {
- var losslessDecoder = new WebpLosslessDecoder(this.webImageInfo.Vp8LBitReader, this.memoryAllocator, this.Configuration);
- losslessDecoder.Decode(pixels, image.Width, image.Height);
- }
- else
- {
- var lossyDecoder = new WebpLossyDecoder(this.webImageInfo.Vp8BitReader, this.memoryAllocator, this.Configuration);
- lossyDecoder.Decode(pixels, image.Width, image.Height, this.webImageInfo);
- }
+ image = new Image(this.Configuration, (int)this.webImageInfo.Width, (int)this.webImageInfo.Height, this.Metadata);
+ Buffer2D pixels = image.GetRootFramePixelBuffer();
+ if (this.webImageInfo.IsLossless)
+ {
+ var losslessDecoder = new WebpLosslessDecoder(this.webImageInfo.Vp8LBitReader, this.memoryAllocator, this.Configuration);
+ losslessDecoder.Decode(pixels, image.Width, image.Height);
+ }
+ else
+ {
+ var lossyDecoder = new WebpLossyDecoder(this.webImageInfo.Vp8BitReader, this.memoryAllocator, this.Configuration);
+ lossyDecoder.Decode(pixels, image.Width, image.Height, this.webImageInfo);
+ }
- // There can be optional chunks after the image data, like EXIF and XMP.
- if (this.webImageInfo.Features != null)
- {
- this.ParseOptionalChunks(this.webImageInfo.Features);
- }
+ // There can be optional chunks after the image data, like EXIF and XMP.
+ if (this.webImageInfo.Features != null)
+ {
+ this.ParseOptionalChunks(this.webImageInfo.Features);
+ }
- return image;
+ return image;
+ }
+ }
+ catch
+ {
+ image?.Dispose();
+ throw;
}
}
@@ -190,7 +199,11 @@ namespace SixLabors.ImageSharp.Formats.Webp
uint fileSize = this.ReadChunkSize();
// The first byte contains information about the image features used.
- byte imageFeatures = (byte)this.currentStream.ReadByte();
+ int imageFeatures = this.currentStream.ReadByte();
+ if (imageFeatures == -1)
+ {
+ WebpThrowHelper.ThrowInvalidImageContentException("VP8X header doe not contain enough data");
+ }
// The first two bit of it are reserved and should be 0.
if (imageFeatures >> 6 != 0)
@@ -214,19 +227,34 @@ namespace SixLabors.ImageSharp.Formats.Webp
features.Animation = (imageFeatures & (1 << 1)) != 0;
// 3 reserved bytes should follow which are supposed to be zero.
- this.currentStream.Read(this.buffer, 0, 3);
+ int bytesRead = this.currentStream.Read(this.buffer, 0, 3);
+ if (bytesRead != 3)
+ {
+ WebpThrowHelper.ThrowInvalidImageContentException("VP8X header does not contain enough data");
+ }
+
if (this.buffer[0] != 0 || this.buffer[1] != 0 || this.buffer[2] != 0)
{
WebpThrowHelper.ThrowImageFormatException("reserved bytes should be zero");
}
// 3 bytes for the width.
- this.currentStream.Read(this.buffer, 0, 3);
+ bytesRead = this.currentStream.Read(this.buffer, 0, 3);
+ if (bytesRead != 3)
+ {
+ WebpThrowHelper.ThrowInvalidImageContentException("VP8 header does not contain enough data to read the width");
+ }
+
this.buffer[3] = 0;
uint width = (uint)BinaryPrimitives.ReadInt32LittleEndian(this.buffer) + 1;
// 3 bytes for the height.
- this.currentStream.Read(this.buffer, 0, 3);
+ bytesRead = this.currentStream.Read(this.buffer, 0, 3);
+ if (bytesRead != 3)
+ {
+ WebpThrowHelper.ThrowInvalidImageContentException("VP8 header does not contain enough data to read the height");
+ }
+
this.buffer[3] = 0;
uint height = (uint)BinaryPrimitives.ReadInt32LittleEndian(this.buffer) + 1;
@@ -272,7 +300,12 @@ namespace SixLabors.ImageSharp.Formats.Webp
this.webpMetadata.FileFormat = WebpFileFormatType.Lossy;
// VP8 data size (not including this 4 bytes).
- this.currentStream.Read(this.buffer, 0, 4);
+ int bytesRead = this.currentStream.Read(this.buffer, 0, 4);
+ if (bytesRead != 4)
+ {
+ WebpThrowHelper.ThrowInvalidImageContentException("Not enough data to read the VP8 data size");
+ }
+
uint dataSize = BinaryPrimitives.ReadUInt32LittleEndian(this.buffer);
// remaining counts the available image data payload.
@@ -284,7 +317,12 @@ namespace SixLabors.ImageSharp.Formats.Webp
// - A 3-bit version number.
// - A 1-bit show_frame flag.
// - A 19-bit field containing the size of the first data partition in bytes.
- this.currentStream.Read(this.buffer, 0, 3);
+ bytesRead = this.currentStream.Read(this.buffer, 0, 3);
+ if (bytesRead != 3)
+ {
+ WebpThrowHelper.ThrowInvalidImageContentException("Not enough data to read the VP8 frame tag");
+ }
+
uint frameTag = (uint)(this.buffer[0] | (this.buffer[1] << 8) | (this.buffer[2] << 16));
remaining -= 3;
bool isNoKeyFrame = (frameTag & 0x1) == 1;
@@ -312,13 +350,23 @@ namespace SixLabors.ImageSharp.Formats.Webp
}
// Check for VP8 magic bytes.
- this.currentStream.Read(this.buffer, 0, 3);
+ bytesRead = this.currentStream.Read(this.buffer, 0, 3);
+ if (bytesRead != 3)
+ {
+ WebpThrowHelper.ThrowInvalidImageContentException("Not enough data to read the VP8 magic bytes");
+ }
+
if (!this.buffer.AsSpan(0, 3).SequenceEqual(WebpConstants.Vp8HeaderMagicBytes))
{
WebpThrowHelper.ThrowImageFormatException("VP8 magic bytes not found");
}
- this.currentStream.Read(this.buffer, 0, 4);
+ bytesRead = this.currentStream.Read(this.buffer, 0, 4);
+ if (bytesRead != 4)
+ {
+ WebpThrowHelper.ThrowInvalidImageContentException("VP8 header does not contain enough data to read the image width and height");
+ }
+
uint tmp = (uint)BinaryPrimitives.ReadInt16LittleEndian(this.buffer);
uint width = tmp & 0x3fff;
sbyte xScale = (sbyte)(tmp >> 6);
@@ -429,54 +477,15 @@ namespace SixLabors.ImageSharp.Formats.Webp
switch (chunkType)
{
case WebpChunkType.Iccp:
- uint iccpChunkSize = this.ReadChunkSize();
- if (this.IgnoreMetadata)
- {
- this.currentStream.Skip((int)iccpChunkSize);
- }
- else
- {
- byte[] iccpData = new byte[iccpChunkSize];
- this.currentStream.Read(iccpData, 0, (int)iccpChunkSize);
- var profile = new IccProfile(iccpData);
- if (profile.CheckIsValid())
- {
- this.Metadata.IccProfile = profile;
- }
- }
-
+ this.ReadIccProfile();
break;
case WebpChunkType.Exif:
- uint exifChunkSize = this.ReadChunkSize();
- if (this.IgnoreMetadata)
- {
- this.currentStream.Skip((int)exifChunkSize);
- }
- else
- {
- byte[] exifData = new byte[exifChunkSize];
- this.currentStream.Read(exifData, 0, (int)exifChunkSize);
- var profile = new ExifProfile(exifData);
- this.Metadata.ExifProfile = profile;
- }
-
+ this.ReadExifProfile();
break;
case WebpChunkType.Xmp:
- uint xmpChunkSize = this.ReadChunkSize();
- if (this.IgnoreMetadata)
- {
- this.currentStream.Skip((int)xmpChunkSize);
- }
- else
- {
- byte[] xmpData = new byte[xmpChunkSize];
- this.currentStream.Read(xmpData, 0, (int)xmpChunkSize);
- var profile = new XmpProfile(xmpData);
- this.Metadata.XmpProfile = profile;
- }
-
+ this.ReadXmpProfile();
break;
case WebpChunkType.Animation:
@@ -488,7 +497,12 @@ namespace SixLabors.ImageSharp.Formats.Webp
features.AlphaChunkHeader = (byte)this.currentStream.ReadByte();
int alphaDataSize = (int)(alphaChunkSize - 1);
features.AlphaData = this.memoryAllocator.Allocate(alphaDataSize);
- this.currentStream.Read(features.AlphaData.Memory.Span, 0, alphaDataSize);
+ int bytesRead = this.currentStream.Read(features.AlphaData.Memory.Span, 0, alphaDataSize);
+ if (bytesRead != alphaDataSize)
+ {
+ WebpThrowHelper.ThrowInvalidImageContentException("Not enough data to read the alpha chunk");
+ }
+
break;
default:
WebpThrowHelper.ThrowImageFormatException("Unexpected chunk followed VP8X header");
@@ -514,22 +528,100 @@ namespace SixLabors.ImageSharp.Formats.Webp
{
// Read chunk header.
WebpChunkType chunkType = this.ReadChunkType();
- uint chunkLength = this.ReadChunkSize();
-
if (chunkType == WebpChunkType.Exif && this.Metadata.ExifProfile == null)
{
- byte[] exifData = new byte[chunkLength];
- this.currentStream.Read(exifData, 0, (int)chunkLength);
- this.Metadata.ExifProfile = new ExifProfile(exifData);
+ this.ReadExifProfile();
+ }
+ else if (chunkType == WebpChunkType.Xmp && this.Metadata.XmpProfile == null)
+ {
+ this.ReadXmpProfile();
}
else
{
- // Skip XMP chunk data or any duplicate EXIF chunk.
+ // Skip duplicate XMP or EXIF chunk.
+ uint chunkLength = this.ReadChunkSize();
this.currentStream.Skip((int)chunkLength);
}
}
}
+ ///
+ /// Reads the EXIF profile from the stream.
+ ///
+ private void ReadExifProfile()
+ {
+ uint exifChunkSize = this.ReadChunkSize();
+ if (this.IgnoreMetadata)
+ {
+ this.currentStream.Skip((int)exifChunkSize);
+ }
+ else
+ {
+ byte[] exifData = new byte[exifChunkSize];
+ int bytesRead = this.currentStream.Read(exifData, 0, (int)exifChunkSize);
+ if (bytesRead != exifChunkSize)
+ {
+ // Ignore invalid chunk.
+ return;
+ }
+
+ var profile = new ExifProfile(exifData);
+ this.Metadata.ExifProfile = profile;
+ }
+ }
+
+ ///
+ /// Reads the XMP profile the stream.
+ ///
+ private void ReadXmpProfile()
+ {
+ uint xmpChunkSize = this.ReadChunkSize();
+ if (this.IgnoreMetadata)
+ {
+ this.currentStream.Skip((int)xmpChunkSize);
+ }
+ else
+ {
+ byte[] xmpData = new byte[xmpChunkSize];
+ int bytesRead = this.currentStream.Read(xmpData, 0, (int)xmpChunkSize);
+ if (bytesRead != xmpChunkSize)
+ {
+ // Ignore invalid chunk.
+ return;
+ }
+
+ var profile = new XmpProfile(xmpData);
+ this.Metadata.XmpProfile = profile;
+ }
+ }
+
+ ///
+ /// Reads the ICCP chunk from the stream.
+ ///
+ private void ReadIccProfile()
+ {
+ uint iccpChunkSize = this.ReadChunkSize();
+ if (this.IgnoreMetadata)
+ {
+ this.currentStream.Skip((int)iccpChunkSize);
+ }
+ else
+ {
+ byte[] iccpData = new byte[iccpChunkSize];
+ int bytesRead = this.currentStream.Read(iccpData, 0, (int)iccpChunkSize);
+ if (bytesRead != iccpChunkSize)
+ {
+ WebpThrowHelper.ThrowInvalidImageContentException("Not enough data to read the iccp chunk");
+ }
+
+ var profile = new IccProfile(iccpData);
+ if (profile.CheckIsValid())
+ {
+ this.Metadata.IccProfile = profile;
+ }
+ }
+ }
+
///
/// Identifies the chunk type from the chunk.
///
diff --git a/src/ImageSharp/Formats/Webp/WebpThrowHelper.cs b/src/ImageSharp/Formats/Webp/WebpThrowHelper.cs
index fffdd34101..5f063fd11a 100644
--- a/src/ImageSharp/Formats/Webp/WebpThrowHelper.cs
+++ b/src/ImageSharp/Formats/Webp/WebpThrowHelper.cs
@@ -8,6 +8,13 @@ namespace SixLabors.ImageSharp.Formats.Webp
{
internal static class WebpThrowHelper
{
+ ///
+ /// Cold path optimization for throwing 's.
+ ///
+ /// The error message for the exception.
+ [MethodImpl(InliningOptions.ColdPath)]
+ public static void ThrowInvalidImageContentException(string errorMessage) => throw new InvalidImageContentException(errorMessage);
+
///
/// Cold path optimization for throwing -s
///
diff --git a/src/ImageSharp/IO/BufferedReadStream.cs b/src/ImageSharp/IO/BufferedReadStream.cs
index 4ab7f312b2..2823b8ed6f 100644
--- a/src/ImageSharp/IO/BufferedReadStream.cs
+++ b/src/ImageSharp/IO/BufferedReadStream.cs
@@ -114,6 +114,15 @@ namespace SixLabors.ImageSharp.IO
///
public override bool CanWrite { get; } = false;
+ ///
+ /// Gets remaining byte count available to read.
+ ///
+ public long RemainingBytes
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => this.Length - this.Position;
+ }
+
///
/// Gets the underlying stream.
///
diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifEncodedStringHelpers.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifEncodedStringHelpers.cs
index 5fd613b1f0..4ec9b3267e 100644
--- a/src/ImageSharp/Metadata/Profiles/Exif/ExifEncodedStringHelpers.cs
+++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifEncodedStringHelpers.cs
@@ -80,7 +80,15 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
}
public static unsafe int Write(Encoding encoding, string value, Span destination)
+#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP2_1_OR_GREATER || NET
+ => encoding.GetBytes(value.AsSpan(), destination);
+#else
{
+ if (value.Length == 0)
+ {
+ return 0;
+ }
+
fixed (char* c = value)
{
fixed (byte* b = destination)
@@ -89,6 +97,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
}
}
}
+#endif
private static bool TryDetect(ReadOnlySpan buffer, out CharacterCode code)
{
diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifProfile.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifProfile.cs
index e0ab8ecf95..665e4753a7 100644
--- a/src/ImageSharp/Metadata/Profiles/Exif/ExifProfile.cs
+++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifProfile.cs
@@ -121,6 +121,14 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
}
}
+ ///
+ /// Returns the thumbnail in the EXIF profile when available.
+ ///
+ ///
+ /// The .
+ ///
+ public Image CreateThumbnail() => this.CreateThumbnail();
+
///
/// Returns the thumbnail in the EXIF profile when available.
///
diff --git a/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs b/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs
index 9b5e098c83..abfe835b16 100644
--- a/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs
+++ b/src/ImageSharp/Metadata/Profiles/Exif/ExifReader.cs
@@ -202,7 +202,7 @@ namespace SixLabors.ImageSharp.Metadata.Profiles.Exif
protected void ReadValues64(List values, ulong offset)
{
- DebugGuard.MustBeLessThanOrEqualTo(offset, (ulong)this.data.Length, "By spec UInt64.MaxValue is supported, but .Net Stream.Length can Int64.MaxValue.");
+ DebugGuard.MustBeLessThanOrEqualTo(offset, (ulong)this.data.Length, "By spec UInt64.MaxValue is supported, but .NET Stream.Length can Int64.MaxValue.");
this.Seek(offset);
ulong count = this.ReadUInt64();
diff --git a/src/ImageSharp/Processing/Processors/Transforms/Linear/AffineTransformProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/Linear/AffineTransformProcessor{TPixel}.cs
index f6fadca33e..f74cf59493 100644
--- a/src/ImageSharp/Processing/Processors/Transforms/Linear/AffineTransformProcessor{TPixel}.cs
+++ b/src/ImageSharp/Processing/Processors/Transforms/Linear/AffineTransformProcessor{TPixel}.cs
@@ -187,12 +187,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
&& RuntimeEnvironment.IsNetCore)
{
// There's something wrong with the JIT in .NET Core 3.1 on certain
- // MacOSX machines so we have to use different pipelines.
+ // macOS machines so we have to use different pipelines.
// It's:
// - Not reproducable locally
// - Doesn't seem to be triggered by the bulk Numerics.UnPremultiply method but by caller.
// https://github.com/SixLabors/ImageSharp/pull/1591
- this.InvokeMacOSX(in rows, span);
+ this.InvokeMacOS(in rows, span);
return;
}
@@ -259,7 +259,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
[ExcludeFromCodeCoverage]
[MethodImpl(InliningOptions.ShortMethod)]
- private void InvokeMacOSX(in RowInterval rows, Span span)
+ private void InvokeMacOS(in RowInterval rows, Span span)
{
Matrix3x2 matrix = this.matrix;
TResampler sampler = this.sampler;
diff --git a/src/ImageSharp/Processing/Processors/Transforms/Linear/ProjectiveTransformProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/Linear/ProjectiveTransformProcessor{TPixel}.cs
index 26b970d8b5..9bb9c113d4 100644
--- a/src/ImageSharp/Processing/Processors/Transforms/Linear/ProjectiveTransformProcessor{TPixel}.cs
+++ b/src/ImageSharp/Processing/Processors/Transforms/Linear/ProjectiveTransformProcessor{TPixel}.cs
@@ -186,12 +186,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
&& RuntimeEnvironment.IsNetCore)
{
// There's something wrong with the JIT in .NET Core 3.1 on certain
- // MacOSX machines so we have to use different pipelines.
+ // macOS machines so we have to use different pipelines.
// It's:
// - Not reproducable locally
// - Doesn't seem to be triggered by the bulk Numerics.UnPremultiply method but by caller.
// https://github.com/SixLabors/ImageSharp/pull/1591
- this.InvokeMacOSX(in rows, span);
+ this.InvokeMacOS(in rows, span);
return;
}
@@ -258,7 +258,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
[ExcludeFromCodeCoverage]
[MethodImpl(InliningOptions.ShortMethod)]
- public void InvokeMacOSX(in RowInterval rows, Span span)
+ public void InvokeMacOS(in RowInterval rows, Span span)
{
Matrix4x4 matrix = this.matrix;
TResampler sampler = this.sampler;
diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs
index 71d0ab34a4..43ec45a34f 100644
--- a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs
@@ -20,6 +20,7 @@ using static SixLabors.ImageSharp.Tests.TestImages.Bmp;
namespace SixLabors.ImageSharp.Tests.Formats.Bmp
{
[Trait("Format", "Bmp")]
+ [ValidateDisposedMemoryAllocations]
public class BmpDecoderTests
{
public const PixelTypes CommonNonDefaultPixelTypes = PixelTypes.Rgba32 | PixelTypes.Bgra32 | PixelTypes.RgbaVector;
diff --git a/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs
index 1922b161f2..7a5241c5a8 100644
--- a/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs
@@ -18,6 +18,7 @@ using Xunit;
namespace SixLabors.ImageSharp.Tests.Formats.Gif
{
[Trait("Format", "Gif")]
+ [ValidateDisposedMemoryAllocations]
public class GifDecoderTests
{
private const PixelTypes TestPixelTypes = PixelTypes.Rgba32 | PixelTypes.RgbaVector | PixelTypes.Argb32;
diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Images.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Images.cs
index 70cbc3af72..d5e0f081bf 100644
--- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Images.cs
+++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Images.cs
@@ -105,6 +105,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
TestImages.Jpeg.Issues.Fuzz.IndexOutOfRangeException1693A,
TestImages.Jpeg.Issues.Fuzz.IndexOutOfRangeException1693B,
TestImages.Jpeg.Issues.Fuzz.IndexOutOfRangeException824C,
+ TestImages.Jpeg.Issues.Fuzz.NullReferenceException2085,
};
private static readonly Dictionary CustomToleranceValues = new()
diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Metadata.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Metadata.cs
index 9864d62b8f..d9915f17d6 100644
--- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Metadata.cs
+++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Metadata.cs
@@ -179,11 +179,16 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
var testFile = TestFile.Create(imagePath);
using (var stream = new MemoryStream(testFile.Bytes, false))
{
- IImageInfo imageInfo = useIdentify
- ? ((IImageInfoDetector)decoder).Identify(Configuration.Default, stream, default)
- : decoder.Decode(Configuration.Default, stream, default);
-
- test(imageInfo);
+ if (useIdentify)
+ {
+ IImageInfo imageInfo = ((IImageInfoDetector)decoder).Identify(Configuration.Default, stream, default);
+ test(imageInfo);
+ }
+ else
+ {
+ using var img = decoder.Decode(Configuration.Default, stream, default);
+ test(img);
+ }
}
}
diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs
index faf40ccf7d..1faa6f0f4c 100644
--- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs
@@ -22,6 +22,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
{
// TODO: Scatter test cases into multiple test classes
[Trait("Format", "Jpg")]
+ [ValidateDisposedMemoryAllocations]
public partial class JpegDecoderTests
{
public const PixelTypes CommonNonDefaultPixelTypes = PixelTypes.Rgba32 | PixelTypes.Argb32 | PixelTypes.Bgr24 | PixelTypes.RgbaVector;
diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.Metadata.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.Metadata.cs
new file mode 100644
index 0000000000..2bbce6cb1b
--- /dev/null
+++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.Metadata.cs
@@ -0,0 +1,33 @@
+// Copyright (c) Six Labors.
+// Licensed under the Apache License, Version 2.0.
+
+using System;
+using System.IO;
+using SixLabors.ImageSharp.Formats.Jpeg;
+using SixLabors.ImageSharp.PixelFormats;
+
+using Xunit;
+
+namespace SixLabors.ImageSharp.Tests.Formats.Jpg
+{
+ [Trait("Format", "Jpg")]
+ public partial class JpegEncoderTests
+ {
+ [Theory]
+ [WithFile(TestImages.Jpeg.Issues.ValidExifArgumentNullExceptionOnEncode, PixelTypes.Rgba32)]
+ public void Encode_WithValidExifProfile_DoesNotThrowException(TestImageProvider provider)
+ where TPixel : unmanaged, IPixel
+ {
+ Exception ex = Record.Exception(() =>
+ {
+ var encoder = new JpegEncoder();
+ var stream = new MemoryStream();
+
+ using Image image = provider.GetImage(JpegDecoder);
+ image.Save(stream, encoder);
+ });
+
+ Assert.Null(ex);
+ }
+ }
+}
diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs
index 18eae9fbd3..d860836e08 100644
--- a/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs
@@ -20,7 +20,7 @@ using Xunit;
namespace SixLabors.ImageSharp.Tests.Formats.Jpg
{
[Trait("Format", "Jpg")]
- public class JpegEncoderTests
+ public partial class JpegEncoderTests
{
private static JpegEncoder JpegEncoder => new();
diff --git a/tests/ImageSharp.Tests/Formats/Pbm/PbmDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Pbm/PbmDecoderTests.cs
index 97237bca59..eb3bc8c9a5 100644
--- a/tests/ImageSharp.Tests/Formats/Pbm/PbmDecoderTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Pbm/PbmDecoderTests.cs
@@ -11,6 +11,7 @@ using static SixLabors.ImageSharp.Tests.TestImages.Pbm;
namespace SixLabors.ImageSharp.Tests.Formats.Pbm
{
[Trait("Format", "Pbm")]
+ [ValidateDisposedMemoryAllocations]
public class PbmDecoderTests
{
[Theory]
diff --git a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs
index 752036126f..a4fcf63baf 100644
--- a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs
@@ -19,6 +19,7 @@ using Xunit;
namespace SixLabors.ImageSharp.Tests.Formats.Png
{
[Trait("Format", "Png")]
+ [ValidateDisposedMemoryAllocations]
public partial class PngDecoderTests
{
private const PixelTypes TestPixelTypes = PixelTypes.Rgba32 | PixelTypes.RgbaVector | PixelTypes.Argb32;
diff --git a/tests/ImageSharp.Tests/Formats/Tga/TgaDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Tga/TgaDecoderTests.cs
index 55dc2ecdd8..e83c5a98cc 100644
--- a/tests/ImageSharp.Tests/Formats/Tga/TgaDecoderTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Tga/TgaDecoderTests.cs
@@ -16,6 +16,7 @@ using static SixLabors.ImageSharp.Tests.TestImages.Tga;
namespace SixLabors.ImageSharp.Tests.Formats.Tga
{
[Trait("Format", "Tga")]
+ [ValidateDisposedMemoryAllocations]
public class TgaDecoderTests
{
private static TgaDecoder TgaDecoder => new();
diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderBaseTester.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderBaseTester.cs
index aeadae2557..7e45edb6d1 100644
--- a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderBaseTester.cs
+++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderBaseTester.cs
@@ -12,9 +12,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
{
public abstract class TiffDecoderBaseTester
{
- protected static TiffDecoder TiffDecoder => new TiffDecoder();
+ protected static TiffDecoder TiffDecoder => new();
- protected static MagickReferenceDecoder ReferenceDecoder => new MagickReferenceDecoder();
+ protected static MagickReferenceDecoder ReferenceDecoder => new();
protected static void TestTiffDecoder(TestImageProvider provider, IImageDecoder referenceDecoder = null, bool useExactComparer = true, float compareTolerance = 0.001f)
where TPixel : unmanaged, IPixel
diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs
index bd300f799a..9460f3a351 100644
--- a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs
@@ -14,6 +14,7 @@ using static SixLabors.ImageSharp.Tests.TestImages.Tiff;
namespace SixLabors.ImageSharp.Tests.Formats.Tiff
{
[Trait("Format", "Tiff")]
+ [ValidateDisposedMemoryAllocations]
public class TiffDecoderTests : TiffDecoderBaseTester
{
public static readonly string[] MultiframeTestImages = Multiframes;
@@ -187,6 +188,23 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
public void TiffDecoder_CanDecode_12Bit_WithUnassociatedAlpha(TestImageProvider provider)
where TPixel : unmanaged, IPixel => TestTiffDecoder(provider);
+ [Theory]
+ [WithFile(Rgba3BitAssociatedAlpha, PixelTypes.Rgba32)]
+ public void TiffDecoder_CanDecode_12Bit_WithAssociatedAlpha(TestImageProvider provider)
+ where TPixel : unmanaged, IPixel
+ {
+ if (TestEnvironment.IsMacOS)
+ {
+ // Only debug save on OSX: For some reason the reference image has a difference of 50%. The imagesharp output file looks ok though.
+ using Image image = provider.GetImage(TiffDecoder);
+ image.DebugSave(provider);
+ return;
+ }
+
+ // Note: Using tolerant comparer here, because there is a small difference to the reference decoder probably due to floating point rounding issues.
+ TestTiffDecoder(provider, useExactComparer: false);
+ }
+
[Theory]
[WithFile(Flower14BitGray, PixelTypes.Rgba32)]
public void TiffDecoder_CanDecode_14Bit_Gray(TestImageProvider provider)
@@ -226,6 +244,24 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
public void TiffDecoder_CanDecode_20Bit_WithUnassociatedAlpha(TestImageProvider provider)
where TPixel : unmanaged, IPixel => TestTiffDecoder(provider);
+ [Theory]
+ [WithFile(Rgba5BitAssociatedAlpha, PixelTypes.Rgba32)]
+ public void TiffDecoder_CanDecode_20Bit_WithAssociatedAlpha(TestImageProvider provider)
+
+ where TPixel : unmanaged, IPixel
+ {
+ if (TestEnvironment.IsMacOS)
+ {
+ // Only debug save on OSX: For some reason the reference image has a difference of 50%. The imagesharp output file looks ok though.
+ using Image image = provider.GetImage(TiffDecoder);
+ image.DebugSave(provider);
+ return;
+ }
+
+ // Note: Using tolerant comparer here, because there is a small difference to the reference decoder probably due to floating point rounding issues.
+ TestTiffDecoder(provider, useExactComparer: false);
+ }
+
[Theory]
[WithFile(FlowerRgb888Contiguous, PixelTypes.Rgba32)]
public void TiffDecoder_CanDecode_24Bit(TestImageProvider provider)
@@ -236,6 +272,23 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
public void TiffDecoder_CanDecode_24Bit_WithUnassociatedAlpha(TestImageProvider provider)
where TPixel : unmanaged, IPixel => TestTiffDecoder(provider);
+ [Theory]
+ [WithFile(Rgba6BitAssociatedAlpha, PixelTypes.Rgba32)]
+ public void TiffDecoder_CanDecode_24Bit_WithAssociatedAlpha(TestImageProvider provider)
+ where TPixel : unmanaged, IPixel
+ {
+ if (TestEnvironment.IsMacOS)
+ {
+ // Only debug save on OSX: For some reason the reference image has a difference of 50%. The imagesharp output file looks ok though.
+ using Image image = provider.GetImage(TiffDecoder);
+ image.DebugSave(provider);
+ return;
+ }
+
+ // Note: Using tolerant comparer here, because there is a small difference to the reference decoder probably due to floating point rounding issues.
+ TestTiffDecoder(provider, useExactComparer: false);
+ }
+
[Theory]
[WithFile(Flower24BitGray, PixelTypes.Rgba32)]
[WithFile(Flower24BitGrayLittleEndian, PixelTypes.Rgba32)]
@@ -261,7 +314,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
where TPixel : unmanaged, IPixel
{
// Note: The image from MagickReferenceDecoder does not look right, maybe we are doing something wrong
- // converting the pixel data from Magick.Net to our format with YCbCr?
+ // converting the pixel data from Magick.NET to our format with YCbCr?
using Image image = provider.GetImage();
image.DebugSave(provider);
}
@@ -296,6 +349,23 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
image.DebugSave(provider);
}
+ [Theory]
+ [WithFile(Rgba8BitAssociatedAlpha, PixelTypes.Rgba32)]
+ public void TiffDecoder_CanDecode_32Bit_WithAssociatedAlpha(TestImageProvider provider)
+ where TPixel : unmanaged, IPixel
+ {
+ if (TestEnvironment.IsMacOS)
+ {
+ // Only debug save on OSX: For some reason the reference image has a difference of 50%. The imagesharp output file looks ok though.
+ using Image image = provider.GetImage(TiffDecoder);
+ image.DebugSave(provider);
+ return;
+ }
+
+ // Note: Using tolerant comparer here, because there is a small difference to the reference decoder probably due to floating point rounding issues.
+ TestTiffDecoder(provider, useExactComparer: false, compareTolerance: 0.004F);
+ }
+
[Theory]
[WithFile(Flower32BitGrayPredictorBigEndian, PixelTypes.Rgba32)]
[WithFile(Flower32BitGrayPredictorLittleEndian, PixelTypes.Rgba32)]
@@ -318,6 +388,24 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
public void TiffDecoder_CanDecode_40Bit_WithUnassociatedAlpha(TestImageProvider provider)
where TPixel : unmanaged, IPixel => TestTiffDecoder(provider);
+ [Theory]
+ [WithFile(Rgba10BitAssociatedAlphaBigEndian, PixelTypes.Rgba32)]
+ [WithFile(Rgba10BitAssociatedAlphaLittleEndian, PixelTypes.Rgba32)]
+ public void TiffDecoder_CanDecode_40Bit_WithAssociatedAlpha(TestImageProvider provider)
+ where TPixel : unmanaged, IPixel
+ {
+ if (TestEnvironment.IsMacOS)
+ {
+ // Only debug save on OSX: For some reason the reference image has a difference of 50%. The imagesharp output file looks ok though.
+ using Image image = provider.GetImage(TiffDecoder);
+ image.DebugSave(provider);
+ return;
+ }
+
+ // Note: Using tolerant comparer here, because there is a small difference to the reference decoder probably due to floating point rounding issues.
+ TestTiffDecoder(provider, useExactComparer: false);
+ }
+
[Theory]
[WithFile(FlowerRgb141414Contiguous, PixelTypes.Rgba32)]
[WithFile(FlowerRgb141414Planar, PixelTypes.Rgba32)]
@@ -339,6 +427,24 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
public void TiffDecoder_CanDecode_48Bit_WithUnassociatedAlpha(TestImageProvider provider)
where TPixel : unmanaged, IPixel => TestTiffDecoder(provider);
+ [Theory]
+ [WithFile(Rgba12BitAssociatedAlphaBigEndian, PixelTypes.Rgba32)]
+ [WithFile(Rgba12BitAssociatedAlphaLittleEndian, PixelTypes.Rgba32)]
+ public void TiffDecoder_CanDecode_48Bit_WithAssociatedAlpha(TestImageProvider provider)
+ where TPixel : unmanaged, IPixel
+ {
+ if (TestEnvironment.IsMacOS)
+ {
+ // Only debug save on OSX: For some reason the reference image has a difference of 50%. The imagesharp output file looks ok though.
+ using Image image = provider.GetImage(TiffDecoder);
+ image.DebugSave(provider);
+ return;
+ }
+
+ // Note: Using tolerant comparer here, because there is a small difference to the reference decoder probably due to floating point rounding issues.
+ TestTiffDecoder(provider, useExactComparer: false, compareTolerance: 0.0002f);
+ }
+
[Theory]
[WithFile(FlowerRgb161616PredictorBigEndian, PixelTypes.Rgba32)]
[WithFile(FlowerRgb161616PredictorLittleEndian, PixelTypes.Rgba32)]
@@ -351,6 +457,24 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
public void TiffDecoder_CanDecode_56Bit_WithUnassociatedAlpha(TestImageProvider provider)
where TPixel : unmanaged, IPixel => TestTiffDecoder(provider);
+ [Theory]
+ [WithFile(Rgba14BitAssociatedAlphaBigEndian, PixelTypes.Rgba32)]
+ [WithFile(Rgba14BitAssociatedAlphaLittleEndian, PixelTypes.Rgba32)]
+ public void TiffDecoder_CanDecode_56Bit_WithAssociatedAlpha(TestImageProvider provider)
+ where TPixel : unmanaged, IPixel
+ {
+ if (TestEnvironment.IsMacOS)
+ {
+ // Only debug save on OSX: For some reason the reference image has a difference of 50%. The imagesharp output file looks ok though.
+ using Image image = provider.GetImage(TiffDecoder);
+ image.DebugSave(provider);
+ return;
+ }
+
+ // Note: Using tolerant comparer here, because there is a small difference to the reference decoder probably due to floating point rounding issues.
+ TestTiffDecoder(provider, useExactComparer: false, compareTolerance: 0.0002f);
+ }
+
[Theory]
[WithFile(FlowerRgb242424Contiguous, PixelTypes.Rgba32)]
[WithFile(FlowerRgb242424ContiguousLittleEndian, PixelTypes.Rgba32)]
diff --git a/tests/ImageSharp.Tests/Formats/WebP/WebpDecoderTests.cs b/tests/ImageSharp.Tests/Formats/WebP/WebpDecoderTests.cs
index 1c92fdf335..f29fa5d793 100644
--- a/tests/ImageSharp.Tests/Formats/WebP/WebpDecoderTests.cs
+++ b/tests/ImageSharp.Tests/Formats/WebP/WebpDecoderTests.cs
@@ -13,6 +13,7 @@ using static SixLabors.ImageSharp.Tests.TestImages.Webp;
namespace SixLabors.ImageSharp.Tests.Formats.Webp
{
[Trait("Format", "Webp")]
+ [ValidateDisposedMemoryAllocations]
public class WebpDecoderTests
{
private static WebpDecoder WebpDecoder => new();
diff --git a/tests/ImageSharp.Tests/Formats/WebP/WebpMetaDataTests.cs b/tests/ImageSharp.Tests/Formats/WebP/WebpMetaDataTests.cs
index eaa7fb5646..456b9a3f52 100644
--- a/tests/ImageSharp.Tests/Formats/WebP/WebpMetaDataTests.cs
+++ b/tests/ImageSharp.Tests/Formats/WebP/WebpMetaDataTests.cs
@@ -1,6 +1,7 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
+using System;
using System.IO;
using System.Threading.Tasks;
using SixLabors.ImageSharp.Formats.Webp;
@@ -150,5 +151,17 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
Assert.NotNull(actualExif);
Assert.Equal(expectedExif.Values.Count, actualExif.Values.Count);
}
+
+ [Theory]
+ [WithFile(TestImages.Webp.Lossy.WithExifNotEnoughData, PixelTypes.Rgba32)]
+ public void WebpDecoder_IgnoresInvalidExifChunk(TestImageProvider provider)
+ where TPixel : unmanaged, IPixel
+ {
+ Exception ex = Record.Exception(() =>
+ {
+ using Image image = provider.GetImage();
+ });
+ Assert.Null(ex);
+ }
}
}
diff --git a/tests/ImageSharp.Tests/Helpers/RuntimeEnvironmentTests.cs b/tests/ImageSharp.Tests/Helpers/RuntimeEnvironmentTests.cs
index 8074b8b150..5246f8f911 100644
--- a/tests/ImageSharp.Tests/Helpers/RuntimeEnvironmentTests.cs
+++ b/tests/ImageSharp.Tests/Helpers/RuntimeEnvironmentTests.cs
@@ -28,7 +28,7 @@ namespace SixLabors.ImageSharp.Tests.Helpers
{
Assert.True(RuntimeEnvironment.IsOSPlatform(OSPlatform.Linux));
}
- else if (TestEnvironment.IsOSX)
+ else if (TestEnvironment.IsMacOS)
{
Assert.True(RuntimeEnvironment.IsOSPlatform(OSPlatform.OSX));
}
diff --git a/tests/ImageSharp.Tests/Image/ImageTests.LoadPixelData.cs b/tests/ImageSharp.Tests/Image/ImageTests.LoadPixelData.cs
index a4044b906c..7683ee6889 100644
--- a/tests/ImageSharp.Tests/Image/ImageTests.LoadPixelData.cs
+++ b/tests/ImageSharp.Tests/Image/ImageTests.LoadPixelData.cs
@@ -14,6 +14,7 @@ namespace SixLabors.ImageSharp.Tests
[Theory]
[InlineData(false)]
[InlineData(true)]
+ [ValidateDisposedMemoryAllocations]
public void FromPixels(bool useSpan)
{
Rgba32[] data = { Color.Black, Color.White, Color.White, Color.Black, };
diff --git a/tests/ImageSharp.Tests/Image/LargeImageIntegrationTests.cs b/tests/ImageSharp.Tests/Image/LargeImageIntegrationTests.cs
index 357d02be4b..ce1f902e59 100644
--- a/tests/ImageSharp.Tests/Image/LargeImageIntegrationTests.cs
+++ b/tests/ImageSharp.Tests/Image/LargeImageIntegrationTests.cs
@@ -55,6 +55,8 @@ namespace SixLabors.ImageSharp.Tests
static void RunTest(string formatInner)
{
+ using IDisposable mem = MemoryAllocatorValidator.MonitorAllocations();
+
Configuration configuration = Configuration.Default.Clone();
configuration.PreferContiguousImageBuffers = true;
IImageEncoder encoder = configuration.ImageFormatsManager.FindEncoder(
diff --git a/tests/ImageSharp.Tests/Memory/Allocators/UniformUnmanagedMemoryPoolTests.Trim.cs b/tests/ImageSharp.Tests/Memory/Allocators/UniformUnmanagedMemoryPoolTests.Trim.cs
index f748b7feb4..d1a8e25bb5 100644
--- a/tests/ImageSharp.Tests/Memory/Allocators/UniformUnmanagedMemoryPoolTests.Trim.cs
+++ b/tests/ImageSharp.Tests/Memory/Allocators/UniformUnmanagedMemoryPoolTests.Trim.cs
@@ -58,11 +58,11 @@ namespace SixLabors.ImageSharp.Tests.Memory.Allocators
[Collection(nameof(NonParallelCollection))]
public class NonParallel
{
- public static readonly bool IsNotMacOs = !TestEnvironment.IsOSX;
+ public static readonly bool IsNotMacOS = !TestEnvironment.IsMacOS;
- // TODO: Investigate failures on MacOS. All handles are released after GC.
+ // TODO: Investigate failures on macOS. All handles are released after GC.
// (It seems to happen more consistently on .NET 6.)
- [ConditionalFact(nameof(IsNotMacOs))]
+ [ConditionalFact(nameof(IsNotMacOS))]
public void MultiplePoolInstances_TrimPeriodElapsed_AllAreTrimmed()
{
if (!TestEnvironment.RunsOnCI)
diff --git a/tests/ImageSharp.Tests/Memory/Allocators/UniformUnmanagedMemoryPoolTests.cs b/tests/ImageSharp.Tests/Memory/Allocators/UniformUnmanagedMemoryPoolTests.cs
index 7d98eff611..2976bf7fde 100644
--- a/tests/ImageSharp.Tests/Memory/Allocators/UniformUnmanagedMemoryPoolTests.cs
+++ b/tests/ImageSharp.Tests/Memory/Allocators/UniformUnmanagedMemoryPoolTests.cs
@@ -245,9 +245,9 @@ namespace SixLabors.ImageSharp.Tests.Memory.Allocators
cleanup.Register(b1);
}
- public static readonly bool IsNotMacOS = !TestEnvironment.IsOSX;
+ public static readonly bool IsNotMacOS = !TestEnvironment.IsMacOS;
- // TODO: Investigate MacOS failures
+ // TODO: Investigate macOS failures
[ConditionalTheory(nameof(IsNotMacOS))]
[InlineData(false)]
[InlineData(true)]
diff --git a/tests/ImageSharp.Tests/Memory/Allocators/UniformUnmanagedPoolMemoryAllocatorTests.cs b/tests/ImageSharp.Tests/Memory/Allocators/UniformUnmanagedPoolMemoryAllocatorTests.cs
index 414f991f70..0520c3c1fe 100644
--- a/tests/ImageSharp.Tests/Memory/Allocators/UniformUnmanagedPoolMemoryAllocatorTests.cs
+++ b/tests/ImageSharp.Tests/Memory/Allocators/UniformUnmanagedPoolMemoryAllocatorTests.cs
@@ -259,9 +259,9 @@ namespace SixLabors.ImageSharp.Tests.Memory.Allocators
[InlineData(1200)] // Group of two UniformUnmanagedMemoryPool buffers
public void AllocateMemoryGroup_Finalization_ReturnsToPool(int length)
{
- if (TestEnvironment.IsOSX)
+ if (TestEnvironment.IsMacOS)
{
- // Skip on OSX: https://github.com/SixLabors/ImageSharp/issues/1887
+ // Skip on macOS: https://github.com/SixLabors/ImageSharp/issues/1887
return;
}
@@ -321,9 +321,9 @@ namespace SixLabors.ImageSharp.Tests.Memory.Allocators
[InlineData(600)] // Group of single UniformUnmanagedMemoryPool buffer
public void AllocateSingleMemoryOwner_Finalization_ReturnsToPool(int length)
{
- if (TestEnvironment.IsOSX)
+ if (TestEnvironment.IsMacOS)
{
- // Skip on OSX: https://github.com/SixLabors/ImageSharp/issues/1887
+ // Skip on macOS: https://github.com/SixLabors/ImageSharp/issues/1887
return;
}
diff --git a/tests/ImageSharp.Tests/MemoryAllocatorValidator.cs b/tests/ImageSharp.Tests/MemoryAllocatorValidator.cs
new file mode 100644
index 0000000000..13664ee9b2
--- /dev/null
+++ b/tests/ImageSharp.Tests/MemoryAllocatorValidator.cs
@@ -0,0 +1,77 @@
+// Copyright (c) Six Labors.
+// Licensed under the Apache License, Version 2.0.
+
+using System;
+using System.Threading;
+using SixLabors.ImageSharp.Diagnostics;
+using Xunit;
+
+namespace SixLabors.ImageSharp.Tests
+{
+ public static class MemoryAllocatorValidator
+ {
+ private static readonly AsyncLocal LocalInstance = new();
+
+ public static bool MonitoringAllocations => LocalInstance.Value != null;
+
+ static MemoryAllocatorValidator()
+ {
+ MemoryDiagnostics.MemoryAllocated += MemoryDiagnostics_MemoryAllocated;
+ MemoryDiagnostics.MemoryReleased += MemoryDiagnostics_MemoryReleased;
+ }
+
+ private static void MemoryDiagnostics_MemoryReleased()
+ {
+ TestMemoryDiagnostics backing = LocalInstance.Value;
+ if (backing != null)
+ {
+ backing.TotalRemainingAllocated--;
+ }
+ }
+
+ private static void MemoryDiagnostics_MemoryAllocated()
+ {
+ TestMemoryDiagnostics backing = LocalInstance.Value;
+ if (backing != null)
+ {
+ backing.TotalAllocated++;
+ backing.TotalRemainingAllocated++;
+ }
+ }
+
+ public static TestMemoryDiagnostics MonitorAllocations()
+ {
+ var diag = new TestMemoryDiagnostics();
+ LocalInstance.Value = diag;
+ return diag;
+ }
+
+ public static void StopMonitoringAllocations() => LocalInstance.Value = null;
+
+ public static void ValidateAllocations(int expectedAllocationCount = 0)
+ => LocalInstance.Value?.Validate(expectedAllocationCount);
+
+ public class TestMemoryDiagnostics : IDisposable
+ {
+ public int TotalAllocated { get; set; }
+
+ public int TotalRemainingAllocated { get; set; }
+
+ public void Validate(int expectedAllocationCount)
+ {
+ var count = this.TotalRemainingAllocated;
+ var pass = expectedAllocationCount == count;
+ Assert.True(pass, $"Expected a {expectedAllocationCount} undisposed buffers but found {count}");
+ }
+
+ public void Dispose()
+ {
+ this.Validate(0);
+ if (LocalInstance.Value == this)
+ {
+ StopMonitoringAllocations();
+ }
+ }
+ }
+ }
+}
diff --git a/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs b/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs
index a859852279..1668b7a125 100644
--- a/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs
+++ b/tests/ImageSharp.Tests/Metadata/Profiles/Exif/ExifProfileTests.cs
@@ -354,10 +354,15 @@ namespace SixLabors.ImageSharp.Tests.Metadata.Profiles.Exif
TestProfile(profile);
- using Image thumbnail = profile.CreateThumbnail();
+ using Image thumbnail = profile.CreateThumbnail();
Assert.NotNull(thumbnail);
Assert.Equal(256, thumbnail.Width);
Assert.Equal(170, thumbnail.Height);
+
+ using Image genericThumbnail = profile.CreateThumbnail();
+ Assert.NotNull(genericThumbnail);
+ Assert.Equal(256, genericThumbnail.Width);
+ Assert.Equal(170, genericThumbnail.Height);
}
[Fact]
diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs
index b1ff3df08c..9d766e0585 100644
--- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs
+++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs
@@ -34,7 +34,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
};
private static readonly ImageComparer ValidatorComparer =
- ImageComparer.TolerantPercentage(TestEnvironment.IsOSX && TestEnvironment.RunsOnCI ? 0.26F : 0.07F);
+ ImageComparer.TolerantPercentage(TestEnvironment.IsMacOS && TestEnvironment.RunsOnCI ? 0.26F : 0.07F);
[Fact]
public void Resize_PixelAgnostic()
diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs
index 2009d981dc..310056fb35 100644
--- a/tests/ImageSharp.Tests/TestImages.cs
+++ b/tests/ImageSharp.Tests/TestImages.cs
@@ -264,6 +264,7 @@ namespace SixLabors.ImageSharp.Tests
public const string InvalidIptcTag = "Jpg/issues/Issue1942InvalidIptcTag.jpg";
public const string Issue2057App1Parsing = "Jpg/issues/Issue2057-App1Parsing.jpg";
public const string ExifNullArrayTag = "Jpg/issues/issue-2056-exif-null-array.jpg";
+ public const string ValidExifArgumentNullExceptionOnEncode = "Jpg/issues/Issue2087-exif-null-reference-on-encode.jpg";
public static class Fuzz
{
@@ -292,6 +293,7 @@ namespace SixLabors.ImageSharp.Tests
public const string AccessViolationException922 = "Jpg/issues/fuzz/Issue922-AccessViolationException.jpg";
public const string IndexOutOfRangeException1693A = "Jpg/issues/fuzz/Issue1693-IndexOutOfRangeException-A.jpg";
public const string IndexOutOfRangeException1693B = "Jpg/issues/fuzz/Issue1693-IndexOutOfRangeException-B.jpg";
+ public const string NullReferenceException2085 = "Jpg/issues/fuzz/Issue2085-NullReferenceException.jpg";
}
}
@@ -635,6 +637,7 @@ namespace SixLabors.ImageSharp.Tests
{
public const string Earth = "Webp/earth_lossy.webp";
public const string WithExif = "Webp/exif_lossy.webp";
+ public const string WithExifNotEnoughData = "Webp/exif_lossy_not_enough_data.webp";
public const string WithIccp = "Webp/lossy_with_iccp.webp";
public const string WithXmp = "Webp/xmp_lossy.webp";
public const string BikeSmall = "Webp/bike_lossless_small.webp";
@@ -863,20 +866,33 @@ namespace SixLabors.ImageSharp.Tests
// Images with alpha channel.
public const string Rgba2BitUnassociatedAlpha = "Tiff/RgbaUnassociatedAlpha2bit.tiff";
public const string Rgba3BitUnassociatedAlpha = "Tiff/RgbaUnassociatedAlpha3bit.tiff";
+ public const string Rgba3BitAssociatedAlpha = "Tiff/RgbaAssociatedAlpha3bit.tiff";
public const string Rgba4BitUnassociatedAlpha = "Tiff/RgbaUnassociatedAlpha4bit.tiff";
+ public const string Rgba4BitAassociatedAlpha = "Tiff/RgbaAssociatedAlpha4bit.tiff";
public const string Rgba5BitUnassociatedAlpha = "Tiff/RgbaUnassociatedAlpha5bit.tiff";
+ public const string Rgba5BitAssociatedAlpha = "Tiff/RgbaAssociatedAlpha5bit.tiff";
public const string Rgba6BitUnassociatedAlpha = "Tiff/RgbaUnassociatedAlpha6bit.tiff";
+ public const string Rgba6BitAssociatedAlpha = "Tiff/RgbaAssociatedAlpha6bit.tiff";
public const string Rgba8BitUnassociatedAlpha = "Tiff/RgbaUnassociatedAlpha8bit.tiff";
+ public const string Rgba8BitAssociatedAlpha = "Tiff/RgbaAlpha8bit.tiff";
public const string Rgba8BitUnassociatedAlphaWithPredictor = "Tiff/RgbaUnassociatedAlphaPredictor8bit.tiff";
public const string Rgba8BitPlanarUnassociatedAlpha = "Tiff/RgbaUnassociatedAlphaPlanar8bit.tiff";
public const string Rgba10BitUnassociatedAlphaBigEndian = "Tiff/RgbaUnassociatedAlpha10bit_msb.tiff";
public const string Rgba10BitUnassociatedAlphaLittleEndian = "Tiff/RgbaUnassociatedAlpha10bit_lsb.tiff";
+ public const string Rgba10BitAssociatedAlphaBigEndian = "Tiff/RgbaAssociatedAlpha10bit_msb.tiff";
+ public const string Rgba10BitAssociatedAlphaLittleEndian = "Tiff/RgbaAssociatedAlpha10bit_lsb.tiff";
public const string Rgba12BitUnassociatedAlphaBigEndian = "Tiff/RgbaUnassociatedAlpha12bit_msb.tiff";
public const string Rgba12BitUnassociatedAlphaLittleEndian = "Tiff/RgbaUnassociatedAlpha12bit_lsb.tiff";
+ public const string Rgba12BitAssociatedAlphaBigEndian = "Tiff/RgbaAssociatedAlpha12bit_msb.tiff";
+ public const string Rgba12BitAssociatedAlphaLittleEndian = "Tiff/RgbaAssociatedAlpha12bit_lsb.tiff";
public const string Rgba14BitUnassociatedAlphaBigEndian = "Tiff/RgbaUnassociatedAlpha14bit_msb.tiff";
public const string Rgba14BitUnassociatedAlphaLittleEndian = "Tiff/RgbaUnassociatedAlpha14bit_lsb.tiff";
+ public const string Rgba14BitAssociatedAlphaBigEndian = "Tiff/RgbaAssociatedAlpha14bit_msb.tiff";
+ public const string Rgba14BitAssociatedAlphaLittleEndian = "Tiff/RgbaAssociatedAlpha14bit_lsb.tiff";
public const string Rgba16BitUnassociatedAlphaBigEndian = "Tiff/RgbaUnassociatedAlpha16bit_msb.tiff";
public const string Rgba16BitUnassociatedAlphaLittleEndian = "Tiff/RgbaUnassociatedAlpha16bit_lsb.tiff";
+ public const string Rgba16BitAssociatedAlphaBigEndian = "Tiff/RgbaAssociatedAlpha16bit_msb.tiff";
+ public const string Rgba16BitAssociatedAlphaLittleEndian = "Tiff/RgbaAssociatedAlpha16bit_lsb.tiff";
public const string Rgba16BitUnassociatedAlphaBigEndianWithPredictor = "Tiff/RgbaUnassociatedAlphaPredictor16bit_msb.tiff";
public const string Rgba16BitUnassociatedAlphaLittleEndianWithPredictor = "Tiff/RgbaUnassociatedAlphaPredictor16bit_lsb.tiff";
public const string Rgba16BitPlanarUnassociatedAlphaBigEndian = "Tiff/RgbaUnassociatedAlphaPlanar16bit_msb.tiff";
diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageComparison/Exceptions/ImageDifferenceIsOverThresholdException.cs b/tests/ImageSharp.Tests/TestUtilities/ImageComparison/Exceptions/ImageDifferenceIsOverThresholdException.cs
index e1cbf12ac9..220b78e48d 100644
--- a/tests/ImageSharp.Tests/TestUtilities/ImageComparison/Exceptions/ImageDifferenceIsOverThresholdException.cs
+++ b/tests/ImageSharp.Tests/TestUtilities/ImageComparison/Exceptions/ImageDifferenceIsOverThresholdException.cs
@@ -24,7 +24,7 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison
sb.Append(Environment.NewLine);
- // TODO: We should add OSX.
+ // TODO: We should add macOS.
sb.AppendFormat("Test Environment OS : {0}", TestEnvironment.IsWindows ? "Windows" : "Linux");
sb.Append(Environment.NewLine);
diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageComparison/ImageComparingUtils.cs b/tests/ImageSharp.Tests/TestUtilities/ImageComparison/ImageComparingUtils.cs
index 1801d6b590..fa25846748 100644
--- a/tests/ImageSharp.Tests/TestUtilities/ImageComparison/ImageComparingUtils.cs
+++ b/tests/ImageSharp.Tests/TestUtilities/ImageComparison/ImageComparingUtils.cs
@@ -26,7 +26,7 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison
}
var testFile = TestFile.Create(path);
- Image magickImage = DecodeWithMagick(new FileInfo(testFile.FullPath));
+ using Image magickImage = DecodeWithMagick(new FileInfo(testFile.FullPath));
if (useExactComparer)
{
ImageComparer.Exact.VerifySimilarity(magickImage, image);
diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/FileProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/FileProvider.cs
index e2e7d73bc6..63c5ce31a2 100644
--- a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/FileProvider.cs
+++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/FileProvider.cs
@@ -7,6 +7,7 @@ using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Threading.Tasks;
+using SixLabors.ImageSharp.Diagnostics;
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
@@ -158,8 +159,13 @@ namespace SixLabors.ImageSharp.Tests
return this.LoadImage(decoder);
}
- var key = new Key(this.PixelType, this.FilePath, decoder);
+ // do not cache so we can track allocation correctly when validating memory
+ if (MemoryAllocatorValidator.MonitoringAllocations)
+ {
+ return this.LoadImage(decoder);
+ }
+ var key = new Key(this.PixelType, this.FilePath, decoder);
Image cachedImage = Cache.GetOrAdd(key, _ => this.LoadImage(decoder));
return cachedImage.Clone(this.Configuration);
diff --git a/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.cs b/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.cs
index 3ccaf2ba37..df584173ee 100644
--- a/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.cs
+++ b/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.cs
@@ -110,7 +110,7 @@ namespace SixLabors.ImageSharp.Tests
internal static bool IsLinux => RuntimeInformation.IsOSPlatform(OSPlatform.Linux);
- internal static bool IsOSX => RuntimeInformation.IsOSPlatform(OSPlatform.OSX);
+ internal static bool IsMacOS => RuntimeInformation.IsOSPlatform(OSPlatform.OSX);
internal static bool IsMono => Type.GetType("Mono.Runtime") != null; // https://stackoverflow.com/a/721194
diff --git a/tests/ImageSharp.Tests/ValidateDisposedMemoryAllocationsAttribute.cs b/tests/ImageSharp.Tests/ValidateDisposedMemoryAllocationsAttribute.cs
new file mode 100644
index 0000000000..65ed990dd7
--- /dev/null
+++ b/tests/ImageSharp.Tests/ValidateDisposedMemoryAllocationsAttribute.cs
@@ -0,0 +1,33 @@
+// Copyright (c) Six Labors.
+// Licensed under the Apache License, Version 2.0.
+
+using System;
+using System.Diagnostics;
+using System.Reflection;
+using Xunit.Sdk;
+
+namespace SixLabors.ImageSharp.Tests
+{
+ [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
+ public class ValidateDisposedMemoryAllocationsAttribute : BeforeAfterTestAttribute
+ {
+ private readonly int expected = 0;
+
+ public ValidateDisposedMemoryAllocationsAttribute()
+ : this(0)
+ {
+ }
+
+ public ValidateDisposedMemoryAllocationsAttribute(int expected)
+ => this.expected = expected;
+
+ public override void Before(MethodInfo methodUnderTest)
+ => MemoryAllocatorValidator.MonitorAllocations();
+
+ public override void After(MethodInfo methodUnderTest)
+ {
+ MemoryAllocatorValidator.ValidateAllocations(this.expected);
+ MemoryAllocatorValidator.StopMonitoringAllocations();
+ }
+ }
+}
diff --git a/tests/Images/Input/Jpg/issues/Issue2087-exif-null-reference-on-encode.jpg b/tests/Images/Input/Jpg/issues/Issue2087-exif-null-reference-on-encode.jpg
new file mode 100644
index 0000000000..e95ef7a73d
--- /dev/null
+++ b/tests/Images/Input/Jpg/issues/Issue2087-exif-null-reference-on-encode.jpg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:4d41a41180a3371d0c4a724b40a4c86f6f975dab6be9da96964a484818770394
+size 30715
diff --git a/tests/Images/Input/Jpg/issues/fuzz/Issue2085-NullReferenceException.jpg b/tests/Images/Input/Jpg/issues/fuzz/Issue2085-NullReferenceException.jpg
new file mode 100644
index 0000000000..8a680ff6a6
--- /dev/null
+++ b/tests/Images/Input/Jpg/issues/fuzz/Issue2085-NullReferenceException.jpg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:d478beff34179fda26238a44434607c276f55438ee96824c5af8c0188d358d8d
+size 234
diff --git a/tests/Images/Input/Tiff/RgbaAlpha8bit.tiff b/tests/Images/Input/Tiff/RgbaAlpha8bit.tiff
new file mode 100644
index 0000000000..d0d9f2d1ea
--- /dev/null
+++ b/tests/Images/Input/Tiff/RgbaAlpha8bit.tiff
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f8bc77670ea12cd163fbf21fa7dd6d6c10e3b8c1afc83f26618f92303d0cbfcf
+size 235478
diff --git a/tests/Images/Input/Tiff/RgbaAssociatedAlpha10bit_lsb.tiff b/tests/Images/Input/Tiff/RgbaAssociatedAlpha10bit_lsb.tiff
new file mode 100644
index 0000000000..01ff24afc0
--- /dev/null
+++ b/tests/Images/Input/Tiff/RgbaAssociatedAlpha10bit_lsb.tiff
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:1a9878183ebe84506810ea2edc6dc95cd76780f3847ac3a614ce2dcfb355ea9f
+size 294278
diff --git a/tests/Images/Input/Tiff/RgbaAssociatedAlpha10bit_msb.tiff b/tests/Images/Input/Tiff/RgbaAssociatedAlpha10bit_msb.tiff
new file mode 100644
index 0000000000..a0eabbea2b
--- /dev/null
+++ b/tests/Images/Input/Tiff/RgbaAssociatedAlpha10bit_msb.tiff
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:4b95334e1e4b78f5106cacd66d6dc9ad15d023c5449e9562b7489982c5336715
+size 294278
diff --git a/tests/Images/Input/Tiff/RgbaAssociatedAlpha12bit_lsb.tiff b/tests/Images/Input/Tiff/RgbaAssociatedAlpha12bit_lsb.tiff
new file mode 100644
index 0000000000..9348957077
--- /dev/null
+++ b/tests/Images/Input/Tiff/RgbaAssociatedAlpha12bit_lsb.tiff
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:4b1aa42b51bd5474cbf333d9a0b89634198250d3eb5de4cf3af2a8d846395bf0
+size 353078
diff --git a/tests/Images/Input/Tiff/RgbaAssociatedAlpha12bit_msb.tiff b/tests/Images/Input/Tiff/RgbaAssociatedAlpha12bit_msb.tiff
new file mode 100644
index 0000000000..50e9d9c83d
--- /dev/null
+++ b/tests/Images/Input/Tiff/RgbaAssociatedAlpha12bit_msb.tiff
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:9a146f0bfcac158dfda1ba307737b39d472a95926ff172bc8c798557f2dd1e1b
+size 353078
diff --git a/tests/Images/Input/Tiff/RgbaAssociatedAlpha14bit_lsb.tiff b/tests/Images/Input/Tiff/RgbaAssociatedAlpha14bit_lsb.tiff
new file mode 100644
index 0000000000..b58f097c1f
--- /dev/null
+++ b/tests/Images/Input/Tiff/RgbaAssociatedAlpha14bit_lsb.tiff
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:3190269edb721175e8f3788528bd84aee3a12b2c7fb970c02c98822452fc81b0
+size 411878
diff --git a/tests/Images/Input/Tiff/RgbaAssociatedAlpha14bit_msb.tiff b/tests/Images/Input/Tiff/RgbaAssociatedAlpha14bit_msb.tiff
new file mode 100644
index 0000000000..cfe66071da
--- /dev/null
+++ b/tests/Images/Input/Tiff/RgbaAssociatedAlpha14bit_msb.tiff
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:4d65b9b1172d125fece197c2bf332bff9074db2b443e74d1973a14dbe45865e8
+size 411878
diff --git a/tests/Images/Input/Tiff/RgbaAssociatedAlpha16bit_lsb.tiff b/tests/Images/Input/Tiff/RgbaAssociatedAlpha16bit_lsb.tiff
new file mode 100644
index 0000000000..a6199845a5
--- /dev/null
+++ b/tests/Images/Input/Tiff/RgbaAssociatedAlpha16bit_lsb.tiff
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:5ee223455c3e2f748d4dc9a1446047adb3c547878b815504c785497d8c059d34
+size 470678
diff --git a/tests/Images/Input/Tiff/RgbaAssociatedAlpha16bit_msb.tiff b/tests/Images/Input/Tiff/RgbaAssociatedAlpha16bit_msb.tiff
new file mode 100644
index 0000000000..3bc030c509
--- /dev/null
+++ b/tests/Images/Input/Tiff/RgbaAssociatedAlpha16bit_msb.tiff
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:9ea0206508fe23dfb8653ac1706894e54a4ad980b3bf75b68e5deb9f2838a1de
+size 470678
diff --git a/tests/Images/Input/Tiff/RgbaAssociatedAlpha3bit.tiff b/tests/Images/Input/Tiff/RgbaAssociatedAlpha3bit.tiff
new file mode 100644
index 0000000000..7a87e5ef8c
--- /dev/null
+++ b/tests/Images/Input/Tiff/RgbaAssociatedAlpha3bit.tiff
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:03d1050a606b6fa9e909f54991ed12b1aa96b739e5f69f4b1bae7c903b1236ef
+size 88478
diff --git a/tests/Images/Input/Tiff/RgbaAssociatedAlpha4bit.tiff b/tests/Images/Input/Tiff/RgbaAssociatedAlpha4bit.tiff
new file mode 100644
index 0000000000..2679e58298
--- /dev/null
+++ b/tests/Images/Input/Tiff/RgbaAssociatedAlpha4bit.tiff
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:41481c59d9b63ca946d9d2cf2ccc599d1e48ef6fdfa00926c1e6d5cdfd35eb47
+size 117878
diff --git a/tests/Images/Input/Tiff/RgbaAssociatedAlpha5bit.tiff b/tests/Images/Input/Tiff/RgbaAssociatedAlpha5bit.tiff
new file mode 100644
index 0000000000..1cdb3d475f
--- /dev/null
+++ b/tests/Images/Input/Tiff/RgbaAssociatedAlpha5bit.tiff
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:4bc3b2b8031593ff9a79d7caccd2ed614cf8d788dedbefbd27d0a4f88399be4e
+size 147278
diff --git a/tests/Images/Input/Tiff/RgbaAssociatedAlpha6bit.tiff b/tests/Images/Input/Tiff/RgbaAssociatedAlpha6bit.tiff
new file mode 100644
index 0000000000..3184bd90b3
--- /dev/null
+++ b/tests/Images/Input/Tiff/RgbaAssociatedAlpha6bit.tiff
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a9c1c64ce9a002337c3f6d332b4a5bb3016718b672f9c95d8792b013545553c2
+size 176678
diff --git a/tests/Images/Input/Webp/exif_lossy.webp b/tests/Images/Input/Webp/exif_lossy.webp
index 35e454b96f..5d6db3800f 100644
--- a/tests/Images/Input/Webp/exif_lossy.webp
+++ b/tests/Images/Input/Webp/exif_lossy.webp
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:fdf4e9b20af4168f4177d33f7f502906343bbaaae2af9b90e1531bd4452b317b
-size 40765
+oid sha256:5c53967bfefcfece8cd4411740c1c394e75864ca61a7a9751df3b28e727c0205
+size 68646
diff --git a/tests/Images/Input/Webp/exif_lossy_not_enough_data.webp b/tests/Images/Input/Webp/exif_lossy_not_enough_data.webp
new file mode 100644
index 0000000000..35e454b96f
--- /dev/null
+++ b/tests/Images/Input/Webp/exif_lossy_not_enough_data.webp
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:fdf4e9b20af4168f4177d33f7f502906343bbaaae2af9b90e1531bd4452b317b
+size 40765