Skip to content
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -90,16 +90,23 @@ internal override void Apply(Span<float> scanline, int x, int y)

MemoryManager memoryManager = this.Target.MemoryManager;

using (IBuffer<float> amountBuffer = memoryManager.Allocate<float>(scanline.Length))
if (this.Options.BlendPercentage == 1f)
{
Span<float> amountSpan = amountBuffer.Span;

for (int i = 0; i < scanline.Length; i++)
this.Blender.Blend(memoryManager, destinationRow, destinationRow, this.Colors.Span, scanline);
}
else
{
using (IBuffer<float> amountBuffer = memoryManager.Allocate<float>(scanline.Length))
{
amountSpan[i] = scanline[i] * this.Options.BlendPercentage;
}
Span<float> amountSpan = amountBuffer.Span;

for (int i = 0; i < scanline.Length; i++)
{
amountSpan[i] = scanline[i] * this.Options.BlendPercentage;
}

this.Blender.Blend(memoryManager, destinationRow, destinationRow, this.Colors.Span, amountSpan);
this.Blender.Blend(memoryManager, destinationRow, destinationRow, this.Colors.Span, amountSpan);
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System;
using System.Threading.Tasks;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Drawing.Brushes;
Expand Down Expand Up @@ -49,38 +50,61 @@ protected override void OnFrameApply(ImageFrame<TPixel> source, Rectangle source
int minY = Math.Max(0, startY);
int maxY = Math.Min(source.Height, endY);

// Reset offset if necessary.
if (minX > 0)
{
startX = 0;
}

if (minY > 0)
{
startY = 0;
}

int width = maxX - minX;

using (IBuffer<float> amount = source.MemoryManager.Allocate<float>(width))
using (BrushApplicator<TPixel> applicator = this.brush.CreateApplicator(
source,
sourceRectangle,
this.options))
{
amount.Span.Fill(this.options.BlendPercentage);
var solidBrush = this.brush as SolidBrush<TPixel>;

// If there's no reason for blending, then avoid it.
if (solidBrush != null &&
(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I refactor this logic into an IsSolidBrushWithoutBlending property or similar for better readability.

(this.options.BlenderMode == PixelBlenderMode.Normal && this.options.BlendPercentage == 1f && solidBrush.Color.ToVector4().W == 1f) ||
(this.options.BlenderMode == PixelBlenderMode.Over && this.options.BlendPercentage == 1f && solidBrush.Color.ToVector4().W == 1f) ||
(this.options.BlenderMode == PixelBlenderMode.Src)))
{
Parallel.For(
minY,
maxY,
configuration.ParallelOptions,
y =>
{
int offsetY = y - startY;
int offsetX = minX - startX;
{
int offsetY = y - startY;
int offsetX = minX - startX;
source.GetPixelRowSpan(y).Slice(minX, width).Fill(solidBrush.Color);
});
}
else
{
// Reset offset if necessary.
if (minX > 0)
{
startX = 0;
}

if (minY > 0)
{
startY = 0;
}

using (IBuffer<float> amount = source.MemoryManager.Allocate<float>(width))
using (BrushApplicator<TPixel> applicator = this.brush.CreateApplicator(
source,
sourceRectangle,
this.options))
{
amount.Span.Fill(this.options.BlendPercentage);

Parallel.For(
minY,
maxY,
configuration.ParallelOptions,
y =>
{
int offsetY = y - startY;
int offsetX = minX - startX;

applicator.Apply(amount.Span, offsetX, offsetY);
});
applicator.Apply(amount.Span, offsetX, offsetY);
});
}
}
}
}
Expand Down
6 changes: 3 additions & 3 deletions src/ImageSharp/ImageFrameCollection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@ internal sealed class ImageFrameCollection<TPixel> : IImageFrameCollection<TPixe
private readonly IList<ImageFrame<TPixel>> frames = new List<ImageFrame<TPixel>>();
private readonly Image<TPixel> parent;

internal ImageFrameCollection(Image<TPixel> parent, int width, int height)
internal ImageFrameCollection(Image<TPixel> parent, int width, int height, TPixel backgroundColor)
{
Guard.NotNull(parent, nameof(parent));

this.parent = parent;

// Frames are already cloned within the caller
this.frames.Add(new ImageFrame<TPixel>(parent.GetConfiguration().MemoryManager, width, height));
this.frames.Add(new ImageFrame<TPixel>(parent.GetConfiguration(), width, height, backgroundColor));
}

internal ImageFrameCollection(Image<TPixel> parent, IEnumerable<ImageFrame<TPixel>> frames)
Expand Down Expand Up @@ -143,7 +143,7 @@ public Image<TPixel> CloneFrame(int index)
/// <inheritdoc/>
public ImageFrame<TPixel> CreateFrame()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's create an overload for this that accepts a color, then we cover all bases.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see the method implements a member from interface IImageFrameCollection, wouldn't it be nice to change it to:

CreateFrame(TPixel backgroundColor = default)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we don’t actually need the interface. I can’t see why someone would need to implement it themselves.

Copy link
Member

@antonfirsov antonfirsov May 1, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JimBobSquarePants I remember having a conversation about optional arguments, with the conclusion that we should prefer overloads instead because of better binary compatibility.

Is this still a thing we should follow?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, always choose overloads.

{
var frame = new ImageFrame<TPixel>(this.parent.GetConfiguration().MemoryManager, this.RootFrame.Width, this.RootFrame.Height);
var frame = new ImageFrame<TPixel>(this.parent.GetConfiguration(), this.RootFrame.Width, this.RootFrame.Height, this.RootFrame.BackgroundColor);
this.frames.Add(frame);
return frame;
}
Expand Down
65 changes: 59 additions & 6 deletions src/ImageSharp/ImageFrame{TPixel}.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
Expand All @@ -18,8 +19,7 @@ namespace SixLabors.ImageSharp
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
public sealed class ImageFrame<TPixel> : IPixelSource<TPixel>, IDisposable
where TPixel : struct, IPixel<TPixel>
{
where TPixel : struct, IPixel<TPixel> {
private bool isDisposed;

/// <summary>
Expand All @@ -29,8 +29,7 @@ public sealed class ImageFrame<TPixel> : IPixelSource<TPixel>, IDisposable
/// <param name="width">The width of the image in pixels.</param>
/// <param name="height">The height of the image in pixels.</param>
internal ImageFrame(MemoryManager memoryManager, int width, int height)
: this(memoryManager, width, height, new ImageFrameMetaData())
{
: this(memoryManager, width, height, new ImageFrameMetaData()) {
}

/// <summary>
Expand All @@ -52,15 +51,47 @@ internal ImageFrame(MemoryManager memoryManager, int width, int height, ImageFra
this.MetaData = metaData;
}

/// <summary>
/// Initializes a new instance of the <see cref="ImageFrame{TPixel}" /> class.
/// </summary>
/// <param name="configuration">The <see cref="Configuration"/> to use for buffer allocation and parallel options to clear the buffer with.</param>
/// <param name="width">The width of the image in pixels.</param>
/// <param name="height">The height of the image in pixels.</param>
/// <param name="backgroundColor">The color to clear the image with.</param>
internal ImageFrame(Configuration configuration, int width, int height, TPixel backgroundColor)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is good! I actually wanted to introduce the configuration as a parameter and drop the MemoryManager overload so we're halfway there.

: this(configuration, width, height, backgroundColor, new ImageFrameMetaData()) {
}

/// <summary>
/// Initializes a new instance of the <see cref="ImageFrame{TPixel}" /> class.
/// </summary>
/// <param name="configuration">The <see cref="Configuration"/> to use for buffer allocation and parallel options to clear the buffer with.</param>
/// <param name="width">The width of the image in pixels.</param>
/// <param name="height">The height of the image in pixels.</param>
/// <param name="backgroundColor">The color to clear the image with.</param>
/// <param name="metaData">The meta data.</param>
internal ImageFrame(Configuration configuration, int width, int height, TPixel backgroundColor, ImageFrameMetaData metaData)
{
Guard.NotNull(configuration, nameof(configuration));
Guard.MustBeGreaterThan(width, 0, nameof(width));
Guard.MustBeGreaterThan(height, 0, nameof(height));
Guard.NotNull(metaData, nameof(metaData));

this.MemoryManager = configuration.MemoryManager;
this.PixelBuffer = this.MemoryManager.Allocate2D<TPixel>(width, height, false);
this.BackgroundColor = backgroundColor;
this.Clear(configuration.ParallelOptions, backgroundColor);
this.MetaData = metaData;
}

/// <summary>
/// Initializes a new instance of the <see cref="ImageFrame{TPixel}" /> class.
/// </summary>
/// <param name="memoryManager">The <see cref="MemoryManager"/> to use for buffer allocations.</param>
/// <param name="size">The <see cref="Size"/> of the frame.</param>
/// <param name="metaData">The meta data.</param>
internal ImageFrame(MemoryManager memoryManager, Size size, ImageFrameMetaData metaData)
: this(memoryManager, size.Width, size.Height, metaData)
{
: this(memoryManager, size.Width, size.Height, metaData) {
}

/// <summary>
Expand Down Expand Up @@ -99,6 +130,11 @@ internal ImageFrame(MemoryManager memoryManager, ImageFrame<TPixel> source)
/// </summary>
public int Height => this.PixelBuffer.Height;

/// <summary>
/// Gets the background color.
/// </summary>
public TPixel BackgroundColor { get; }

/// <summary>
/// Gets the meta data of the frame.
/// </summary>
Expand Down Expand Up @@ -267,6 +303,23 @@ internal ImageFrame<TPixel2> CloneAs<TPixel2>()
return target;
}

/// <summary>
/// Clears the bitmap.
/// </summary>
/// <param name="parallelOptions">The parallel options.</param>
/// <param name="value">The value to initialize the bitmap with.</param>
internal void Clear(ParallelOptions parallelOptions, TPixel value) {
Parallel.For(
0,
this.Height,
parallelOptions,
(int y) =>
{
Span<TPixel> targetRow = this.GetPixelRowSpan(y);
targetRow.Fill(value);
});
}

/// <summary>
/// Clones the current instance.
/// </summary>
Expand Down
35 changes: 34 additions & 1 deletion src/ImageSharp/Image{TPixel}.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,21 @@ public Image(Configuration configuration, int width, int height)
{
}

/// <summary>
/// Initializes a new instance of the <see cref="Image{TPixel}"/> class
/// with the height and the width of the image.
/// </summary>
/// <param name="configuration">
/// The configuration providing initialization code which allows extending the library.
/// </param>
/// <param name="width">The width of the image in pixels.</param>
/// <param name="height">The height of the image in pixels.</param>
/// <param name="backgroundColor">The color to initialize the pixels with.</param>
public Image(Configuration configuration, int width, int height, TPixel backgroundColor)
: this(configuration, width, height, backgroundColor, new ImageMetaData())
{
}

/// <summary>
/// Initializes a new instance of the <see cref="Image{TPixel}"/> class
/// with the height and the width of the image.
Expand All @@ -63,7 +78,25 @@ internal Image(Configuration configuration, int width, int height, ImageMetaData
this.configuration = configuration ?? Configuration.Default;
this.PixelType = new PixelTypeInfo(Unsafe.SizeOf<TPixel>() * 8);
this.MetaData = metadata ?? new ImageMetaData();
this.frames = new ImageFrameCollection<TPixel>(this, width, height);
this.frames = new ImageFrameCollection<TPixel>(this, width, height, default(TPixel));
}

/// <summary>
/// Initializes a new instance of the <see cref="Image{TPixel}"/> class
/// with the height and the width of the image.
/// </summary>
/// <param name="configuration">
/// The configuration providing initialization code which allows extending the library.
/// </param>
/// <param name="width">The width of the image in pixels.</param>
/// <param name="height">The height of the image in pixels.</param>
/// <param name="backgroundColor">The color to initialize the pixels with.</param>
/// <param name="metadata">The images metadata.</param>
internal Image(Configuration configuration, int width, int height, TPixel backgroundColor, ImageMetaData metadata) {
this.configuration = configuration ?? Configuration.Default;
this.PixelType = new PixelTypeInfo(Unsafe.SizeOf<TPixel>() * 8);
this.MetaData = metadata ?? new ImageMetaData();
this.frames = new ImageFrameCollection<TPixel>(this, width, height, backgroundColor);
}

/// <summary>
Expand Down
24 changes: 12 additions & 12 deletions src/ImageSharp/PixelFormats/PixelBlenderMode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,62 +54,62 @@ public enum PixelBlenderMode
HardLight,

/// <summary>
/// returns the source colors
/// returns the source colors.
/// </summary>
Src,

/// <summary>
/// returns the source over the destination
/// returns the source over the destination.
/// </summary>
Atop,

/// <summary>
/// returns the detination over the source
/// returns the destination over the source.
/// </summary>
Over,

/// <summary>
/// the source where the desitnation and source overlap
/// The source where the destination and source overlap.
/// </summary>
In,

/// <summary>
/// the destination where the desitnation and source overlap
/// The destination where the destination and source overlap.
/// </summary>
Out,

/// <summary>
/// the destination where the source does not overlap it
/// The destination where the source does not overlap it.
/// </summary>
Dest,

/// <summary>
/// the source where they dont overlap othersie dest in overlapping parts
/// The source where they don't overlap othersie dest in overlapping parts.
/// </summary>
DestAtop,

/// <summary>
/// the destnation over the source
/// The destination over the source.
/// </summary>
DestOver,

/// <summary>
/// the destination where the desitnation and source overlap
/// The destination where the destination and source overlap.
/// </summary>
DestIn,

/// <summary>
/// the source where the desitnation and source overlap
/// The source where the destination and source overlap.
/// </summary>
DestOut,

/// <summary>
/// the clear.
/// The clear.
/// </summary>
Clear,

/// <summary>
/// clear where they overlap
/// Clear where they overlap.
/// </summary>
Xor
}
Expand Down
2 changes: 1 addition & 1 deletion tests/ImageSharp.Tests/Image/ImageFramesCollectionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public ImageFramesCollectionTests()
System.Threading.Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo("en-US");

this.image = new Image<Rgba32>(10, 10);
this.collection = new ImageFrameCollection<Rgba32>(this.image, 10, 10);
this.collection = new ImageFrameCollection<Rgba32>(this.image, 10, 10, default);
}

[Fact]
Expand Down