Browse Source

Allow decoding Tiff of different frame size.

pull/2788/head
James Jackson-South 2 years ago
parent
commit
eb5c05efe1
  1. 2
      src/ImageSharp/Formats/Gif/GifEncoderCore.cs
  2. 2
      src/ImageSharp/Formats/Png/PngEncoderCore.cs
  3. 103
      src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs
  4. 4
      src/ImageSharp/Formats/Webp/WebpEncoderCore.cs
  5. 2
      src/ImageSharp/Image.cs
  6. 20
      src/ImageSharp/ImageFrame.cs
  7. 2
      src/ImageSharp/ImageFrameCollection{TPixel}.cs
  8. 2
      src/ImageSharp/ImageFrame{TPixel}.cs
  9. 4
      src/ImageSharp/Image{TPixel}.cs
  10. 4
      src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs
  11. 2
      src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor{TPixel}.cs
  12. 2
      src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor{TPixel}.cs
  13. 2
      src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs
  14. 11
      tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs
  15. 1
      tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderMultiframeTests.cs
  16. 3
      tests/ImageSharp.Tests/TestUtilities/ImageComparison/ExactImageComparer.cs
  17. 3
      tests/ImageSharp.Tests/TestUtilities/ImageComparison/TolerantImageComparer.cs

2
src/ImageSharp/Formats/Gif/GifEncoderCore.cs

@ -209,7 +209,7 @@ internal sealed class GifEncoderCore
ImageFrame<TPixel> previousFrame = image.Frames.RootFrame; ImageFrame<TPixel> previousFrame = image.Frames.RootFrame;
// This frame is reused to store de-duplicated pixel buffers. // This frame is reused to store de-duplicated pixel buffers.
using ImageFrame<TPixel> encodingFrame = new(previousFrame.Configuration, previousFrame.Size()); using ImageFrame<TPixel> encodingFrame = new(previousFrame.Configuration, previousFrame.Size);
for (int i = 1; i < image.Frames.Count; i++) for (int i = 1; i < image.Frames.Count; i++)
{ {

2
src/ImageSharp/Formats/Png/PngEncoderCore.cs

@ -231,7 +231,7 @@ internal sealed class PngEncoderCore : IDisposable
ImageFrame<TPixel> previousFrame = image.Frames.RootFrame; ImageFrame<TPixel> previousFrame = image.Frames.RootFrame;
// This frame is reused to store de-duplicated pixel buffers. // This frame is reused to store de-duplicated pixel buffers.
using ImageFrame<TPixel> encodingFrame = new(image.Configuration, previousFrame.Size()); using ImageFrame<TPixel> encodingFrame = new(image.Configuration, previousFrame.Size);
for (; currentFrameIndex < image.Frames.Count; currentFrameIndex++) for (; currentFrameIndex < image.Frames.Count; currentFrameIndex++)
{ {

103
src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs

@ -167,11 +167,18 @@ internal class TiffDecoderCore : ImageDecoderCore
this.byteOrder = reader.ByteOrder; this.byteOrder = reader.ByteOrder;
this.isBigTiff = reader.IsBigTiff; this.isBigTiff = reader.IsBigTiff;
Size? size = null;
uint frameCount = 0; uint frameCount = 0;
foreach (ExifProfile ifd in directories) foreach (ExifProfile ifd in directories)
{ {
cancellationToken.ThrowIfCancellationRequested(); cancellationToken.ThrowIfCancellationRequested();
ImageFrame<TPixel> frame = this.DecodeFrame<TPixel>(ifd, cancellationToken); ImageFrame<TPixel> frame = this.DecodeFrame<TPixel>(ifd, size, cancellationToken);
if (!size.HasValue)
{
size = frame.Size;
}
frames.Add(frame); frames.Add(frame);
framesMetadata.Add(frame.Metadata); framesMetadata.Add(frame.Metadata);
@ -181,19 +188,8 @@ internal class TiffDecoderCore : ImageDecoderCore
} }
} }
this.Dimensions = frames[0].Size;
ImageMetadata metadata = TiffDecoderMetadataCreator.Create(framesMetadata, this.skipMetadata, reader.ByteOrder, reader.IsBigTiff); ImageMetadata metadata = TiffDecoderMetadataCreator.Create(framesMetadata, this.skipMetadata, reader.ByteOrder, reader.IsBigTiff);
// TODO: Tiff frames can have different sizes.
ImageFrame<TPixel> root = frames[0];
this.Dimensions = root.Size();
foreach (ImageFrame<TPixel> frame in frames)
{
if (frame.Size() != root.Size())
{
TiffThrowHelper.ThrowNotSupported("Images with different sizes are not supported");
}
}
return new Image<TPixel>(this.configuration, metadata, frames); return new Image<TPixel>(this.configuration, metadata, frames);
} }
catch catch
@ -235,9 +231,10 @@ internal class TiffDecoderCore : ImageDecoderCore
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="tags">The IFD tags.</param> /// <param name="tags">The IFD tags.</param>
/// <param name="size">The previously determined root frame size if decoded.</param>
/// <param name="cancellationToken">The token to monitor cancellation.</param> /// <param name="cancellationToken">The token to monitor cancellation.</param>
/// <returns>The tiff frame.</returns> /// <returns>The tiff frame.</returns>
private ImageFrame<TPixel> DecodeFrame<TPixel>(ExifProfile tags, CancellationToken cancellationToken) private ImageFrame<TPixel> DecodeFrame<TPixel>(ExifProfile tags, Size? size, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
ImageFrameMetadata imageFrameMetaData = this.CreateFrameMetadata(tags); ImageFrameMetadata imageFrameMetaData = this.CreateFrameMetadata(tags);
@ -245,15 +242,29 @@ internal class TiffDecoderCore : ImageDecoderCore
int width = GetImageWidth(tags); int width = GetImageWidth(tags);
int height = GetImageHeight(tags); int height = GetImageHeight(tags);
ImageFrame<TPixel> frame = new(this.configuration, width, height, imageFrameMetaData);
// If size has a value and the width/height off the tiff is smaller we much capture the delta.
if (size.HasValue)
{
if (size.Value.Width < width || size.Value.Height < height)
{
TiffThrowHelper.ThrowNotSupported("Images with frames of size greater than the root frame are not supported.");
}
}
else
{
size = new Size(width, height);
}
ImageFrame<TPixel> frame = new(this.configuration, size.Value.Width, size.Value.Height, imageFrameMetaData);
if (isTiled) if (isTiled)
{ {
this.DecodeImageWithTiles(tags, frame, cancellationToken); this.DecodeImageWithTiles(tags, frame, width, height, cancellationToken);
} }
else else
{ {
this.DecodeImageWithStrips(tags, frame, cancellationToken); this.DecodeImageWithStrips(tags, frame, width, height, cancellationToken);
} }
return frame; return frame;
@ -278,8 +289,10 @@ internal class TiffDecoderCore : ImageDecoderCore
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="tags">The IFD tags.</param> /// <param name="tags">The IFD tags.</param>
/// <param name="frame">The image frame to decode into.</param> /// <param name="frame">The image frame to decode into.</param>
/// <param name="width">The width in px units of the frame data.</param>
/// <param name="height">The height in px units of the frame data.</param>
/// <param name="cancellationToken">The token to monitor cancellation.</param> /// <param name="cancellationToken">The token to monitor cancellation.</param>
private void DecodeImageWithStrips<TPixel>(ExifProfile tags, ImageFrame<TPixel> frame, CancellationToken cancellationToken) private void DecodeImageWithStrips<TPixel>(ExifProfile tags, ImageFrame<TPixel> frame, int width, int height, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
int rowsPerStrip; int rowsPerStrip;
@ -302,6 +315,8 @@ internal class TiffDecoderCore : ImageDecoderCore
{ {
this.DecodeStripsPlanar( this.DecodeStripsPlanar(
frame, frame,
width,
height,
rowsPerStrip, rowsPerStrip,
stripOffsets, stripOffsets,
stripByteCounts, stripByteCounts,
@ -311,6 +326,8 @@ internal class TiffDecoderCore : ImageDecoderCore
{ {
this.DecodeStripsChunky( this.DecodeStripsChunky(
frame, frame,
width,
height,
rowsPerStrip, rowsPerStrip,
stripOffsets, stripOffsets,
stripByteCounts, stripByteCounts,
@ -324,13 +341,13 @@ internal class TiffDecoderCore : ImageDecoderCore
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="tags">The IFD tags.</param> /// <param name="tags">The IFD tags.</param>
/// <param name="frame">The image frame to decode into.</param> /// <param name="frame">The image frame to decode into.</param>
/// <param name="width">The width in px units of the frame data.</param>
/// <param name="height">The height in px units of the frame data.</param>
/// <param name="cancellationToken">The token to monitor cancellation.</param> /// <param name="cancellationToken">The token to monitor cancellation.</param>
private void DecodeImageWithTiles<TPixel>(ExifProfile tags, ImageFrame<TPixel> frame, CancellationToken cancellationToken) private void DecodeImageWithTiles<TPixel>(ExifProfile tags, ImageFrame<TPixel> frame, int width, int height, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
Buffer2D<TPixel> pixels = frame.PixelBuffer; Buffer2D<TPixel> pixels = frame.PixelBuffer;
int width = pixels.Width;
int height = pixels.Height;
if (!tags.TryGetValue(ExifTag.TileWidth, out IExifValue<Number> valueWidth)) if (!tags.TryGetValue(ExifTag.TileWidth, out IExifValue<Number> valueWidth))
{ {
@ -384,11 +401,20 @@ internal class TiffDecoderCore : ImageDecoderCore
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="frame">The image frame to decode data into.</param> /// <param name="frame">The image frame to decode data into.</param>
/// <param name="width">The width in px units of the frame data.</param>
/// <param name="height">The height in px units of the frame data.</param>
/// <param name="rowsPerStrip">The number of rows per strip of data.</param> /// <param name="rowsPerStrip">The number of rows per strip of data.</param>
/// <param name="stripOffsets">An array of byte offsets to each strip in the image.</param> /// <param name="stripOffsets">An array of byte offsets to each strip in the image.</param>
/// <param name="stripByteCounts">An array of the size of each strip (in bytes).</param> /// <param name="stripByteCounts">An array of the size of each strip (in bytes).</param>
/// <param name="cancellationToken">The token to monitor cancellation.</param> /// <param name="cancellationToken">The token to monitor cancellation.</param>
private void DecodeStripsPlanar<TPixel>(ImageFrame<TPixel> frame, int rowsPerStrip, Span<ulong> stripOffsets, Span<ulong> stripByteCounts, CancellationToken cancellationToken) private void DecodeStripsPlanar<TPixel>(
ImageFrame<TPixel> frame,
int width,
int height,
int rowsPerStrip,
Span<ulong> stripOffsets,
Span<ulong> stripByteCounts,
CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
int stripsPerPixel = this.BitsPerSample.Channels; int stripsPerPixel = this.BitsPerSample.Channels;
@ -403,18 +429,18 @@ internal class TiffDecoderCore : ImageDecoderCore
{ {
for (int stripIndex = 0; stripIndex < stripBuffers.Length; stripIndex++) for (int stripIndex = 0; stripIndex < stripBuffers.Length; stripIndex++)
{ {
int uncompressedStripSize = this.CalculateStripBufferSize(frame.Width, rowsPerStrip, stripIndex); int uncompressedStripSize = this.CalculateStripBufferSize(width, rowsPerStrip, stripIndex);
stripBuffers[stripIndex] = this.memoryAllocator.Allocate<byte>(uncompressedStripSize); stripBuffers[stripIndex] = this.memoryAllocator.Allocate<byte>(uncompressedStripSize);
} }
using TiffBaseDecompressor decompressor = this.CreateDecompressor<TPixel>(frame.Width, bitsPerPixel); using TiffBaseDecompressor decompressor = this.CreateDecompressor<TPixel>(width, bitsPerPixel);
TiffBasePlanarColorDecoder<TPixel> colorDecoder = this.CreatePlanarColorDecoder<TPixel>(); TiffBasePlanarColorDecoder<TPixel> colorDecoder = this.CreatePlanarColorDecoder<TPixel>();
for (int i = 0; i < stripsPerPlane; i++) for (int i = 0; i < stripsPerPlane; i++)
{ {
cancellationToken.ThrowIfCancellationRequested(); cancellationToken.ThrowIfCancellationRequested();
int stripHeight = i < stripsPerPlane - 1 || frame.Height % rowsPerStrip == 0 ? rowsPerStrip : frame.Height % rowsPerStrip; int stripHeight = i < stripsPerPlane - 1 || height % rowsPerStrip == 0 ? rowsPerStrip : height % rowsPerStrip;
int stripIndex = i; int stripIndex = i;
for (int planeIndex = 0; planeIndex < stripsPerPixel; planeIndex++) for (int planeIndex = 0; planeIndex < stripsPerPixel; planeIndex++)
@ -430,7 +456,7 @@ internal class TiffDecoderCore : ImageDecoderCore
stripIndex += stripsPerPlane; stripIndex += stripsPerPlane;
} }
colorDecoder.Decode(stripBuffers, pixels, 0, rowsPerStrip * i, frame.Width, stripHeight); colorDecoder.Decode(stripBuffers, pixels, 0, rowsPerStrip * i, width, stripHeight);
} }
} }
finally finally
@ -447,39 +473,48 @@ internal class TiffDecoderCore : ImageDecoderCore
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="frame">The image frame to decode data into.</param> /// <param name="frame">The image frame to decode data into.</param>
/// <param name="width">The width in px units of the frame data.</param>
/// <param name="height">The height in px units of the frame data.</param>
/// <param name="rowsPerStrip">The rows per strip.</param> /// <param name="rowsPerStrip">The rows per strip.</param>
/// <param name="stripOffsets">The strip offsets.</param> /// <param name="stripOffsets">The strip offsets.</param>
/// <param name="stripByteCounts">The strip byte counts.</param> /// <param name="stripByteCounts">The strip byte counts.</param>
/// <param name="cancellationToken">The token to monitor cancellation.</param> /// <param name="cancellationToken">The token to monitor cancellation.</param>
private void DecodeStripsChunky<TPixel>(ImageFrame<TPixel> frame, int rowsPerStrip, Span<ulong> stripOffsets, Span<ulong> stripByteCounts, CancellationToken cancellationToken) private void DecodeStripsChunky<TPixel>(
ImageFrame<TPixel> frame,
int width,
int height,
int rowsPerStrip,
Span<ulong> stripOffsets,
Span<ulong> stripByteCounts,
CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
// If the rowsPerStrip has the default value, which is effectively infinity. That is, the entire image is one strip. // If the rowsPerStrip has the default value, which is effectively infinity. That is, the entire image is one strip.
if (rowsPerStrip == TiffConstants.RowsPerStripInfinity) if (rowsPerStrip == TiffConstants.RowsPerStripInfinity)
{ {
rowsPerStrip = frame.Height; rowsPerStrip = height;
} }
int uncompressedStripSize = this.CalculateStripBufferSize(frame.Width, rowsPerStrip); int uncompressedStripSize = this.CalculateStripBufferSize(width, rowsPerStrip);
int bitsPerPixel = this.BitsPerPixel; int bitsPerPixel = this.BitsPerPixel;
using IMemoryOwner<byte> stripBuffer = this.memoryAllocator.Allocate<byte>(uncompressedStripSize, AllocationOptions.Clean); using IMemoryOwner<byte> stripBuffer = this.memoryAllocator.Allocate<byte>(uncompressedStripSize, AllocationOptions.Clean);
Span<byte> stripBufferSpan = stripBuffer.GetSpan(); Span<byte> stripBufferSpan = stripBuffer.GetSpan();
Buffer2D<TPixel> pixels = frame.PixelBuffer; Buffer2D<TPixel> pixels = frame.PixelBuffer;
using TiffBaseDecompressor decompressor = this.CreateDecompressor<TPixel>(frame.Width, bitsPerPixel); using TiffBaseDecompressor decompressor = this.CreateDecompressor<TPixel>(width, bitsPerPixel);
TiffBaseColorDecoder<TPixel> colorDecoder = this.CreateChunkyColorDecoder<TPixel>(); TiffBaseColorDecoder<TPixel> colorDecoder = this.CreateChunkyColorDecoder<TPixel>();
for (int stripIndex = 0; stripIndex < stripOffsets.Length; stripIndex++) for (int stripIndex = 0; stripIndex < stripOffsets.Length; stripIndex++)
{ {
cancellationToken.ThrowIfCancellationRequested(); cancellationToken.ThrowIfCancellationRequested();
int stripHeight = stripIndex < stripOffsets.Length - 1 || frame.Height % rowsPerStrip == 0 int stripHeight = stripIndex < stripOffsets.Length - 1 || height % rowsPerStrip == 0
? rowsPerStrip ? rowsPerStrip
: frame.Height % rowsPerStrip; : height % rowsPerStrip;
int top = rowsPerStrip * stripIndex; int top = rowsPerStrip * stripIndex;
if (top + stripHeight > frame.Height) if (top + stripHeight > height)
{ {
// Make sure we ignore any strips that are not needed for the image (if too many are present). // Make sure we ignore any strips that are not needed for the image (if too many are present).
break; break;
@ -493,7 +528,7 @@ internal class TiffDecoderCore : ImageDecoderCore
stripBufferSpan, stripBufferSpan,
cancellationToken); cancellationToken);
colorDecoder.Decode(stripBufferSpan, pixels, 0, top, frame.Width, stripHeight); colorDecoder.Decode(stripBufferSpan, pixels, 0, top, width, stripHeight);
} }
} }

4
src/ImageSharp/Formats/Webp/WebpEncoderCore.cs

@ -160,7 +160,7 @@ internal sealed class WebpEncoderCore
// Encode additional frames // Encode additional frames
// This frame is reused to store de-duplicated pixel buffers. // This frame is reused to store de-duplicated pixel buffers.
using ImageFrame<TPixel> encodingFrame = new(image.Configuration, previousFrame.Size()); using ImageFrame<TPixel> encodingFrame = new(image.Configuration, previousFrame.Size);
for (int i = 1; i < image.Frames.Count; i++) for (int i = 1; i < image.Frames.Count; i++)
{ {
@ -235,7 +235,7 @@ internal sealed class WebpEncoderCore
// Encode additional frames // Encode additional frames
// This frame is reused to store de-duplicated pixel buffers. // This frame is reused to store de-duplicated pixel buffers.
using ImageFrame<TPixel> encodingFrame = new(image.Configuration, previousFrame.Size()); using ImageFrame<TPixel> encodingFrame = new(image.Configuration, previousFrame.Size);
for (int i = 1; i < image.Frames.Count; i++) for (int i = 1; i < image.Frames.Count; i++)
{ {

2
src/ImageSharp/Image.cs

@ -77,7 +77,7 @@ public abstract partial class Image : IDisposable, IConfigurationProvider
/// <summary> /// <summary>
/// Gets the size of the image in px units. /// Gets the size of the image in px units.
/// </summary> /// </summary>
public Size Size { get; internal set; } public Size Size { get; private set; }
/// <summary> /// <summary>
/// Gets the bounds of the image. /// Gets the bounds of the image.

20
src/ImageSharp/ImageFrame.cs

@ -25,20 +25,19 @@ public abstract partial class ImageFrame : IConfigurationProvider, IDisposable
protected ImageFrame(Configuration configuration, int width, int height, ImageFrameMetadata metadata) protected ImageFrame(Configuration configuration, int width, int height, ImageFrameMetadata metadata)
{ {
this.Configuration = configuration; this.Configuration = configuration;
this.Width = width; this.Size = new(width, height);
this.Height = height;
this.Metadata = metadata; this.Metadata = metadata;
} }
/// <summary> /// <summary>
/// Gets the width. /// Gets the frame width in px units.
/// </summary> /// </summary>
public int Width { get; private set; } public int Width => this.Size.Width;
/// <summary> /// <summary>
/// Gets the height. /// Gets the frame height in px units.
/// </summary> /// </summary>
public int Height { get; private set; } public int Height => this.Size.Height;
/// <summary> /// <summary>
/// Gets the metadata of the frame. /// Gets the metadata of the frame.
@ -51,8 +50,7 @@ public abstract partial class ImageFrame : IConfigurationProvider, IDisposable
/// <summary> /// <summary>
/// Gets the size of the frame. /// Gets the size of the frame.
/// </summary> /// </summary>
/// <returns>The <see cref="Size"/></returns> public Size Size { get; private set; }
public Size Size() => new(this.Width, this.Height);
/// <summary> /// <summary>
/// Gets the bounds of the frame. /// Gets the bounds of the frame.
@ -80,9 +78,5 @@ public abstract partial class ImageFrame : IConfigurationProvider, IDisposable
/// Updates the size of the image frame. /// Updates the size of the image frame.
/// </summary> /// </summary>
/// <param name="size">The size.</param> /// <param name="size">The size.</param>
internal void UpdateSize(Size size) protected void UpdateSize(Size size) => this.Size = size;
{
this.Width = size.Width;
this.Height = size.Height;
}
} }

2
src/ImageSharp/ImageFrameCollection{TPixel}.cs

@ -414,7 +414,7 @@ public sealed class ImageFrameCollection<TPixel> : ImageFrameCollection, IEnumer
{ {
ImageFrame<TPixel> result = new( ImageFrame<TPixel> result = new(
this.parent.Configuration, this.parent.Configuration,
source.Size(), source.Size,
source.Metadata.DeepClone()); source.Metadata.DeepClone());
source.CopyPixelsTo(result.PixelBuffer.FastMemoryGroup); source.CopyPixelsTo(result.PixelBuffer.FastMemoryGroup);
return result; return result;

2
src/ImageSharp/ImageFrame{TPixel}.cs

@ -322,7 +322,7 @@ public sealed class ImageFrame<TPixel> : ImageFrame, IPixelSource<TPixel>
/// <exception cref="ArgumentException">ImageFrame{TPixel}.CopyTo(): target must be of the same size!</exception> /// <exception cref="ArgumentException">ImageFrame{TPixel}.CopyTo(): target must be of the same size!</exception>
internal void CopyTo(Buffer2D<TPixel> target) internal void CopyTo(Buffer2D<TPixel> target)
{ {
if (this.Size() != target.Size()) if (this.Size != target.Size())
{ {
throw new ArgumentException("ImageFrame<TPixel>.CopyTo(): target must be of the same size!", nameof(target)); throw new ArgumentException("ImageFrame<TPixel>.CopyTo(): target must be of the same size!", nameof(target));
} }

4
src/ImageSharp/Image{TPixel}.cs

@ -419,9 +419,9 @@ public sealed class Image<TPixel> : Image
ImageFrame<TPixel>? rootFrame = frames.FirstOrDefault() ?? throw new ArgumentException("Must not be empty.", nameof(frames)); ImageFrame<TPixel>? rootFrame = frames.FirstOrDefault() ?? throw new ArgumentException("Must not be empty.", nameof(frames));
Size rootSize = rootFrame.Size(); Size rootSize = rootFrame.Size;
if (frames.Any(f => f.Size() != rootSize)) if (frames.Any(f => f.Size != rootSize))
{ {
throw new ArgumentException("The provided frames must be of the same size.", nameof(frames)); throw new ArgumentException("The provided frames must be of the same size.", nameof(frames));
} }

4
src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs

@ -96,7 +96,7 @@ internal class BokehBlurProcessor<TPixel> : ImageProcessor<TPixel>
} }
// Create a 0-filled buffer to use to store the result of the component convolutions // Create a 0-filled buffer to use to store the result of the component convolutions
using Buffer2D<Vector4> processingBuffer = this.Configuration.MemoryAllocator.Allocate2D<Vector4>(source.Size(), AllocationOptions.Clean); using Buffer2D<Vector4> processingBuffer = this.Configuration.MemoryAllocator.Allocate2D<Vector4>(source.Size, AllocationOptions.Clean);
// Perform the 1D convolutions on all the kernel components and accumulate the results // Perform the 1D convolutions on all the kernel components and accumulate the results
this.OnFrameApplyCore(source, sourceRectangle, this.Configuration, processingBuffer); this.OnFrameApplyCore(source, sourceRectangle, this.Configuration, processingBuffer);
@ -134,7 +134,7 @@ internal class BokehBlurProcessor<TPixel> : ImageProcessor<TPixel>
Buffer2D<Vector4> processingBuffer) Buffer2D<Vector4> processingBuffer)
{ {
// Allocate the buffer with the intermediate convolution results // Allocate the buffer with the intermediate convolution results
using Buffer2D<ComplexVector4> firstPassBuffer = configuration.MemoryAllocator.Allocate2D<ComplexVector4>(source.Size()); using Buffer2D<ComplexVector4> firstPassBuffer = configuration.MemoryAllocator.Allocate2D<ComplexVector4>(source.Size);
// Unlike in the standard 2 pass convolution processor, we use a rectangle of 1x the interest width // Unlike in the standard 2 pass convolution processor, we use a rectangle of 1x the interest width
// to speedup the actual convolution, by applying bulk pixel conversion and clamping calculation. // to speedup the actual convolution, by applying bulk pixel conversion and clamping calculation.

2
src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor{TPixel}.cs

@ -66,7 +66,7 @@ internal class Convolution2PassProcessor<TPixel> : ImageProcessor<TPixel>
/// <inheritdoc/> /// <inheritdoc/>
protected override void OnFrameApply(ImageFrame<TPixel> source) protected override void OnFrameApply(ImageFrame<TPixel> source)
{ {
using Buffer2D<TPixel> firstPassPixels = this.Configuration.MemoryAllocator.Allocate2D<TPixel>(source.Size()); using Buffer2D<TPixel> firstPassPixels = this.Configuration.MemoryAllocator.Allocate2D<TPixel>(source.Size);
var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds()); var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds());

2
src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor{TPixel}.cs

@ -51,7 +51,7 @@ internal class ConvolutionProcessor<TPixel> : ImageProcessor<TPixel>
protected override void OnFrameApply(ImageFrame<TPixel> source) protected override void OnFrameApply(ImageFrame<TPixel> source)
{ {
MemoryAllocator allocator = this.Configuration.MemoryAllocator; MemoryAllocator allocator = this.Configuration.MemoryAllocator;
using Buffer2D<TPixel> targetPixels = allocator.Allocate2D<TPixel>(source.Size()); using Buffer2D<TPixel> targetPixels = allocator.Allocate2D<TPixel>(source.Size);
source.CopyTo(targetPixels); source.CopyTo(targetPixels);

2
src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs

@ -38,7 +38,7 @@ internal class OilPaintingProcessor<TPixel> : ImageProcessor<TPixel>
int levels = Math.Clamp(this.definition.Levels, 1, 255); int levels = Math.Clamp(this.definition.Levels, 1, 255);
int brushSize = Math.Clamp(this.definition.BrushSize, 1, Math.Min(source.Width, source.Height)); int brushSize = Math.Clamp(this.definition.BrushSize, 1, Math.Min(source.Width, source.Height));
using Buffer2D<TPixel> targetPixels = this.Configuration.MemoryAllocator.Allocate2D<TPixel>(source.Size()); using Buffer2D<TPixel> targetPixels = this.Configuration.MemoryAllocator.Allocate2D<TPixel>(source.Size);
source.CopyTo(targetPixels); source.CopyTo(targetPixels);

11
tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs

@ -23,7 +23,6 @@ public class TiffDecoderTests : TiffDecoderBaseTester
public static readonly string[] MultiframeTestImages = Multiframes; public static readonly string[] MultiframeTestImages = Multiframes;
[Theory] [Theory]
[WithFile(MultiframeDifferentSize, PixelTypes.Rgba32)]
[WithFile(MultiframeDifferentVariants, PixelTypes.Rgba32)] [WithFile(MultiframeDifferentVariants, PixelTypes.Rgba32)]
[WithFile(Cmyk64BitDeflate, PixelTypes.Rgba32)] [WithFile(Cmyk64BitDeflate, PixelTypes.Rgba32)]
public void ThrowsNotSupported<TPixel>(TestImageProvider<TPixel> provider) public void ThrowsNotSupported<TPixel>(TestImageProvider<TPixel> provider)
@ -596,6 +595,16 @@ public class TiffDecoderTests : TiffDecoderBaseTester
Assert.Equal(1, image.Frames.Count); Assert.Equal(1, image.Frames.Count);
} }
[Theory]
[WithFile(MultiFrameMipMap, PixelTypes.Rgba32)]
public void CanDecode_MultiFrameMipMap<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using Image<TPixel> image = provider.GetImage(TiffDecoder.Instance);
Assert.Equal(7, image.Frames.Count);
image.DebugSaveMultiFrame(provider);
}
[Theory] [Theory]
[WithFile(RgbJpegCompressed, PixelTypes.Rgba32)] [WithFile(RgbJpegCompressed, PixelTypes.Rgba32)]
[WithFile(RgbJpegCompressed2, PixelTypes.Rgba32)] [WithFile(RgbJpegCompressed2, PixelTypes.Rgba32)]

1
tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderMultiframeTests.cs

@ -18,7 +18,6 @@ public class TiffEncoderMultiframeTests : TiffEncoderBaseTester
where TPixel : unmanaged, IPixel<TPixel> => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit24, TiffPhotometricInterpretation.Rgb); where TPixel : unmanaged, IPixel<TPixel> => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit24, TiffPhotometricInterpretation.Rgb);
[Theory] [Theory]
[WithFile(MultiframeDifferentSize, PixelTypes.Rgba32)]
[WithFile(MultiframeDifferentVariants, PixelTypes.Rgba32)] [WithFile(MultiframeDifferentVariants, PixelTypes.Rgba32)]
public void TiffEncoder_EncodeMultiframe_NotSupport<TPixel>(TestImageProvider<TPixel> provider) public void TiffEncoder_EncodeMultiframe_NotSupport<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> => Assert.Throws<NotSupportedException>(() => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit24, TiffPhotometricInterpretation.Rgb)); where TPixel : unmanaged, IPixel<TPixel> => Assert.Throws<NotSupportedException>(() => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit24, TiffPhotometricInterpretation.Rgb));

3
tests/ImageSharp.Tests/TestUtilities/ImageComparison/ExactImageComparer.cs

@ -1,7 +1,6 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
@ -16,7 +15,7 @@ public class ExactImageComparer : ImageComparer
ImageFrame<TPixelA> expected, ImageFrame<TPixelA> expected,
ImageFrame<TPixelB> actual) ImageFrame<TPixelB> actual)
{ {
if (expected.Size() != actual.Size()) if (expected.Size != actual.Size)
{ {
throw new InvalidOperationException("Calling ImageComparer is invalid when dimensions mismatch!"); throw new InvalidOperationException("Calling ImageComparer is invalid when dimensions mismatch!");
} }

3
tests/ImageSharp.Tests/TestUtilities/ImageComparison/TolerantImageComparer.cs

@ -2,7 +2,6 @@
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
@ -58,7 +57,7 @@ public class TolerantImageComparer : ImageComparer
public override ImageSimilarityReport<TPixelA, TPixelB> CompareImagesOrFrames<TPixelA, TPixelB>(int index, ImageFrame<TPixelA> expected, ImageFrame<TPixelB> actual) public override ImageSimilarityReport<TPixelA, TPixelB> CompareImagesOrFrames<TPixelA, TPixelB>(int index, ImageFrame<TPixelA> expected, ImageFrame<TPixelB> actual)
{ {
if (expected.Size() != actual.Size()) if (expected.Size != actual.Size)
{ {
throw new InvalidOperationException("Calling ImageComparer is invalid when dimensions mismatch!"); throw new InvalidOperationException("Calling ImageComparer is invalid when dimensions mismatch!");
} }

Loading…
Cancel
Save