Browse Source

Allow pad to work for non-alpha pixel formats

pull/2034/head
James Jackson-South 4 years ago
parent
commit
7ca5680cd2
  1. 5
      src/ImageSharp/Processing/Extensions/Transforms/PadExtensions.cs
  2. 18
      src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs
  3. 28
      src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs
  4. 5
      src/ImageSharp/Processing/ResizeOptions.cs
  5. 40
      tests/ImageSharp.Tests/Processing/Processors/Transforms/PadTest.cs
  6. 2
      tests/ImageSharp.Tests/Processing/Transforms/PadTest.cs
  7. 10
      tests/ImageSharp.Tests/Processing/Transforms/ResizeTests.cs

5
src/ImageSharp/Processing/Extensions/Transforms/PadExtensions.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Processing namespace SixLabors.ImageSharp.Processing
@ -34,9 +34,10 @@ namespace SixLabors.ImageSharp.Processing
Size = new Size(width, height), Size = new Size(width, height),
Mode = ResizeMode.BoxPad, Mode = ResizeMode.BoxPad,
Sampler = KnownResamplers.NearestNeighbor, Sampler = KnownResamplers.NearestNeighbor,
PadColor = color
}; };
return color.Equals(default) ? source.Resize(options) : source.Resize(options).BackgroundColor(color); return source.Resize(options);
} }
} }
} }

18
src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs

@ -21,19 +21,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
(Size size, Rectangle rectangle) = ResizeHelper.CalculateTargetLocationAndBounds(sourceSize, options); (Size size, Rectangle rectangle) = ResizeHelper.CalculateTargetLocationAndBounds(sourceSize, options);
this.Sampler = options.Sampler; this.Options = options;
this.DestinationWidth = size.Width; this.DestinationWidth = size.Width;
this.DestinationHeight = size.Height; this.DestinationHeight = size.Height;
this.DestinationRectangle = rectangle; this.DestinationRectangle = rectangle;
this.Compand = options.Compand;
this.PremultiplyAlpha = options.PremultiplyAlpha;
} }
/// <summary>
/// Gets the sampler to perform the resize operation.
/// </summary>
public IResampler Sampler { get; }
/// <summary> /// <summary>
/// Gets the destination width. /// Gets the destination width.
/// </summary> /// </summary>
@ -50,14 +43,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
public Rectangle DestinationRectangle { get; } public Rectangle DestinationRectangle { get; }
/// <summary> /// <summary>
/// Gets a value indicating whether to compress or expand individual pixel color values on processing. /// Gets the resize options.
/// </summary>
public bool Compand { get; }
/// <summary>
/// Gets a value indicating whether to premultiply the alpha (if it exists) during the resize operation.
/// </summary> /// </summary>
public bool PremultiplyAlpha { get; } public ResizeOptions Options { get; }
/// <inheritdoc /> /// <inheritdoc />
public override ICloningImageProcessor<TPixel> CreatePixelSpecificCloningProcessor<TPixel>(Configuration configuration, Image<TPixel> source, Rectangle sourceRectangle) public override ICloningImageProcessor<TPixel> CreatePixelSpecificCloningProcessor<TPixel>(Configuration configuration, Image<TPixel> source, Rectangle sourceRectangle)

28
src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs

@ -4,7 +4,6 @@
using System; using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
@ -17,12 +16,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
internal class ResizeProcessor<TPixel> : TransformProcessor<TPixel>, IResamplingTransformImageProcessor<TPixel> internal class ResizeProcessor<TPixel> : TransformProcessor<TPixel>, IResamplingTransformImageProcessor<TPixel>
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
private readonly ResizeOptions options;
private readonly int destinationWidth; private readonly int destinationWidth;
private readonly int destinationHeight; private readonly int destinationHeight;
private readonly IResampler resampler; private readonly IResampler resampler;
private readonly Rectangle destinationRectangle; private readonly Rectangle destinationRectangle;
private readonly bool compand;
private readonly bool premultiplyAlpha;
private Image<TPixel> destination; private Image<TPixel> destination;
public ResizeProcessor(Configuration configuration, ResizeProcessor definition, Image<TPixel> source, Rectangle sourceRectangle) public ResizeProcessor(Configuration configuration, ResizeProcessor definition, Image<TPixel> source, Rectangle sourceRectangle)
@ -31,13 +29,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
this.destinationWidth = definition.DestinationWidth; this.destinationWidth = definition.DestinationWidth;
this.destinationHeight = definition.DestinationHeight; this.destinationHeight = definition.DestinationHeight;
this.destinationRectangle = definition.DestinationRectangle; this.destinationRectangle = definition.DestinationRectangle;
this.resampler = definition.Sampler; this.options = definition.Options;
this.premultiplyAlpha = definition.PremultiplyAlpha; this.resampler = definition.Options.Sampler;
this.compand = definition.Compand;
} }
/// <inheritdoc/> /// <inheritdoc/>
protected override Size GetDestinationSize() => new Size(this.destinationWidth, this.destinationHeight); protected override Size GetDestinationSize() => new(this.destinationWidth, this.destinationHeight);
/// <inheritdoc/> /// <inheritdoc/>
protected override void BeforeImageApply(Image<TPixel> destination) protected override void BeforeImageApply(Image<TPixel> destination)
@ -62,8 +59,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
Image<TPixel> destination = this.destination; Image<TPixel> destination = this.destination;
Rectangle sourceRectangle = this.SourceRectangle; Rectangle sourceRectangle = this.SourceRectangle;
Rectangle destinationRectangle = this.destinationRectangle; Rectangle destinationRectangle = this.destinationRectangle;
bool compand = this.compand; bool compand = this.options.Compand;
bool premultiplyAlpha = this.premultiplyAlpha; bool premultiplyAlpha = this.options.PremultiplyAlpha;
bool shouldFill = (this.options.Mode == ResizeMode.BoxPad || this.options.Mode == ResizeMode.Pad)
&& this.options.PadColor != default;
TPixel fillColor = this.options.PadColor.ToPixel<TPixel>();
// Handle resize dimensions identical to the original // Handle resize dimensions identical to the original
if (source.Width == destination.Width if (source.Width == destination.Width
@ -91,6 +91,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
ImageFrame<TPixel> sourceFrame = source.Frames[i]; ImageFrame<TPixel> sourceFrame = source.Frames[i];
ImageFrame<TPixel> destinationFrame = destination.Frames[i]; ImageFrame<TPixel> destinationFrame = destination.Frames[i];
if (shouldFill)
{
destinationFrame.Clear(fillColor);
}
ApplyNNResizeFrameTransform( ApplyNNResizeFrameTransform(
configuration, configuration,
sourceFrame, sourceFrame,
@ -123,6 +128,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
ImageFrame<TPixel> sourceFrame = source.Frames[i]; ImageFrame<TPixel> sourceFrame = source.Frames[i];
ImageFrame<TPixel> destinationFrame = destination.Frames[i]; ImageFrame<TPixel> destinationFrame = destination.Frames[i];
if (shouldFill)
{
destinationFrame.Clear(fillColor);
}
ApplyResizeFrameTransform( ApplyResizeFrameTransform(
configuration, configuration,
sourceFrame, sourceFrame,

5
src/ImageSharp/Processing/ResizeOptions.cs

@ -51,5 +51,10 @@ namespace SixLabors.ImageSharp.Processing
/// the alpha (if it exists) during the resize operation. /// the alpha (if it exists) during the resize operation.
/// </summary> /// </summary>
public bool PremultiplyAlpha { get; set; } = true; public bool PremultiplyAlpha { get; set; } = true;
/// <summary>
/// Gets or sets the color to use as a background when padding an image.
/// </summary>
public Color PadColor { get; set; }
} }
} }

40
tests/ImageSharp.Tests/Processing/Processors/Transforms/PadTest.cs

@ -20,41 +20,37 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
public void ImageShouldPad<TPixel>(TestImageProvider<TPixel> provider) public void ImageShouldPad<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
using (Image<TPixel> image = provider.GetImage()) using Image<TPixel> image = provider.GetImage();
{ image.Mutate(x => x.Pad(image.Width + 50, image.Height + 50));
image.Mutate(x => x.Pad(image.Width + 50, image.Height + 50)); image.DebugSave(provider);
image.DebugSave(provider);
// Check pixels are empty // Check pixels are empty
for (int y = 0; y < 25; y++) for (int y = 0; y < 25; y++)
{
for (int x = 0; x < 25; x++)
{ {
for (int x = 0; x < 25; x++) Assert.Equal(default, image[x, y]);
{
Assert.Equal(default, image[x, y]);
}
} }
} }
} }
[Theory] [Theory]
[WithFileCollection(nameof(CommonTestImages), PixelTypes.Rgba32)] [WithFileCollection(nameof(CommonTestImages), PixelTypes.Rgba32 | PixelTypes.Rgb24)]
public void ImageShouldPadWithBackgroundColor<TPixel>(TestImageProvider<TPixel> provider) public void ImageShouldPadWithBackgroundColor<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
var color = Color.Red; Color color = Color.Red;
TPixel expected = color.ToPixel<TPixel>(); TPixel expected = color.ToPixel<TPixel>();
using (Image<TPixel> image = provider.GetImage()) using Image<TPixel> image = provider.GetImage();
{ image.Mutate(x => x.Pad(image.Width + 50, image.Height + 50, color));
image.Mutate(x => x.Pad(image.Width + 50, image.Height + 50, color)); image.DebugSave(provider);
image.DebugSave(provider);
// Check pixels are filled // Check pixels are filled
for (int y = 0; y < 25; y++) for (int y = 0; y < 25; y++)
{
for (int x = 0; x < 25; x++)
{ {
for (int x = 0; x < 25; x++) Assert.Equal(expected, image[x, y]);
{
Assert.Equal(expected, image[x, y]);
}
} }
} }
} }

2
tests/ImageSharp.Tests/Processing/Transforms/PadTest.cs

@ -23,7 +23,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms
Assert.Equal(width, resizeProcessor.DestinationWidth); Assert.Equal(width, resizeProcessor.DestinationWidth);
Assert.Equal(height, resizeProcessor.DestinationHeight); Assert.Equal(height, resizeProcessor.DestinationHeight);
Assert.Equal(sampler, resizeProcessor.Sampler); Assert.Equal(sampler, resizeProcessor.Options.Sampler);
} }
} }
} }

10
tests/ImageSharp.Tests/Processing/Transforms/ResizeTests.cs

@ -35,7 +35,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms
Assert.Equal(width, resizeProcessor.DestinationWidth); Assert.Equal(width, resizeProcessor.DestinationWidth);
Assert.Equal(height, resizeProcessor.DestinationHeight); Assert.Equal(height, resizeProcessor.DestinationHeight);
Assert.Equal(sampler, resizeProcessor.Sampler); Assert.Equal(sampler, resizeProcessor.Options.Sampler);
} }
[Fact] [Fact]
@ -52,8 +52,8 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms
Assert.Equal(width, resizeProcessor.DestinationWidth); Assert.Equal(width, resizeProcessor.DestinationWidth);
Assert.Equal(height, resizeProcessor.DestinationHeight); Assert.Equal(height, resizeProcessor.DestinationHeight);
Assert.Equal(sampler, resizeProcessor.Sampler); Assert.Equal(sampler, resizeProcessor.Options.Sampler);
Assert.Equal(compand, resizeProcessor.Compand); Assert.Equal(compand, resizeProcessor.Options.Compand);
} }
[Fact] [Fact]
@ -78,8 +78,8 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms
Assert.Equal(width, resizeProcessor.DestinationWidth); Assert.Equal(width, resizeProcessor.DestinationWidth);
Assert.Equal(height, resizeProcessor.DestinationHeight); Assert.Equal(height, resizeProcessor.DestinationHeight);
Assert.Equal(sampler, resizeProcessor.Sampler); Assert.Equal(sampler, resizeProcessor.Options.Sampler);
Assert.Equal(compand, resizeProcessor.Compand); Assert.Equal(compand, resizeProcessor.Options.Compand);
// Ensure options are not altered. // Ensure options are not altered.
Assert.Equal(width, resizeOptions.Size.Width); Assert.Equal(width, resizeOptions.Size.Width);

Loading…
Cancel
Save