Browse Source

Blend

Former-commit-id: ff1aec34e42b49f728dfd7f370f709c48be34762
Former-commit-id: ee75403396e61e34b0d97760ee36634973d5a5ec
Former-commit-id: 326da67dbfdb92272ab63c08e30a9e85904f7e60
pull/1/head
James Jackson-South 10 years ago
parent
commit
cce2265f7e
  1. 62
      src/ImageProcessorCore/Filters/Blend.cs
  2. 93
      src/ImageProcessorCore/Filters/Processors/BlendProcessor.cs
  3. 44
      tests/ImageProcessorCore.Tests/Processors/Filters/BlendTest.cs

62
src/ImageProcessorCore/Filters/Blend.cs

@ -0,0 +1,62 @@
// <copyright file="Blend.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>-------------------------------------------------------------------------------------------------------------------
namespace ImageProcessorCore
{
using Processors;
/// <summary>
/// Extension methods for the <see cref="Image"/> type.
/// </summary>
public static partial class ImageExtensions
{
/// <summary>
/// Combines the given image together with the current one by blending their pixels.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="image">The image to blend with the currently processing image.</param>
/// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
/// <param name="percent">The opacity of the image image to blend. Must be between 0 and 100.</param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image{T,TP}"/>.</returns>
public static Image<T, TP> Blend<T, TP>(this Image<T, TP> source, ImageBase<T, TP> image, int percent = 50, ProgressEventHandler progressHandler = null)
where T : IPackedVector<TP>
where TP : struct
{
return Blend(source, image, percent, source.Bounds, progressHandler);
}
/// <summary>
/// Combines the given image together with the current one by blending their pixels.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="image">The image to blend with the currently processing image.</param>
/// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
/// <param name="percent">The opacity of the image image to blend. Must be between 0 and 100.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image{T,TP}"/>.</returns>
public static Image<T, TP> Blend<T, TP>(this Image<T, TP> source, ImageBase<T, TP> image, int percent, Rectangle rectangle, ProgressEventHandler progressHandler = null)
where T : IPackedVector<TP>
where TP : struct
{
BlendProcessor<T, TP> processor = new BlendProcessor<T, TP>(image, percent);
processor.OnProgress += progressHandler;
try
{
return source.Process(rectangle, processor);
}
finally
{
processor.OnProgress -= progressHandler;
}
}
}
}

93
src/ImageProcessorCore/Filters/Processors/BlendProcessor.cs

@ -0,0 +1,93 @@
// <copyright file="BlendProcessor.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessorCore.Processors
{
using System.Numerics;
using System.Threading.Tasks;
/// <summary>
/// Combines two images together by blending the pixels.
/// </summary>
/// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
public class BlendProcessor<T, TP> : ImageProcessor<T, TP>
where T : IPackedVector<TP>
where TP : struct
{
/// <summary>
/// The image to blend.
/// </summary>
private readonly ImageBase<T, TP> blend;
/// <summary>
/// Initializes a new instance of the <see cref="BlendProcessor"/> class.
/// </summary>
/// <param name="image">
/// The image to blend with the currently processing image.
/// Disposal of this image is the responsibility of the developer.
/// </param>
/// <param name="alpha">The opacity of the image to blend. Between 0 and 100.</param>
public BlendProcessor(ImageBase<T, TP> image, int alpha = 100)
{
Guard.MustBeBetweenOrEqualTo(alpha, 0, 100, nameof(alpha));
this.blend = image;
this.Value = alpha;
}
/// <summary>
/// Gets the alpha percentage value.
/// </summary>
public int Value { get; }
/// <inheritdoc/>
protected override void Apply(ImageBase<T, TP> target, ImageBase<T, TP> source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY)
{
int sourceY = sourceRectangle.Y;
int sourceBottom = sourceRectangle.Bottom;
int startX = sourceRectangle.X;
int endX = sourceRectangle.Right;
Rectangle bounds = this.blend.Bounds;
float alpha = this.Value / 100f;
using (IPixelAccessor<T, TP> toBlendPixels = this.blend.Lock())
using (IPixelAccessor<T, TP> sourcePixels = source.Lock())
using (IPixelAccessor<T, TP> targetPixels = target.Lock())
{
Parallel.For(
startY,
endY,
y =>
{
if (y >= sourceY && y < sourceBottom)
{
for (int x = startX; x < endX; x++)
{
Vector4 color = sourcePixels[x, y].ToVector4();
if (bounds.Contains(x, y))
{
Vector4 blendedColor = toBlendPixels[x, y].ToVector4();
if (blendedColor.W > 0)
{
// Lerping colors is dependent on the alpha of the blended color
float alphaFactor = alpha > 0 ? alpha : blendedColor.W;
color = Vector4.Lerp(color, blendedColor, alphaFactor);
}
}
T packed = default(T);
packed.PackVector(color);
targetPixels[x, y] = packed;
}
this.OnRowProcessed();
}
});
}
}
}
}

44
tests/ImageProcessorCore.Tests/Processors/Filters/BlendTest.cs

@ -0,0 +1,44 @@
// <copyright file="BlendTest.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessorCore.Tests
{
using System.IO;
using Xunit;
public class BlendTest : FileTestBase
{
[Fact]
public void ImageShouldApplyBlendFilter()
{
const string path = "TestOutput/Blend";
if (!Directory.Exists(path))
{
Directory.CreateDirectory(path);
}
Image blend;
using (FileStream stream = File.OpenRead("TestImages/Formats/Bmp/Car.bmp"))
{
blend = new Image(stream);
}
foreach (string file in Files)
{
using (FileStream stream = File.OpenRead(file))
{
string filename = Path.GetFileName(file);
Image image = new Image(stream);
using (FileStream output = File.OpenWrite($"{path}/{filename}"))
{
image.Blend(blend)
.Save(output);
}
}
}
}
}
}
Loading…
Cancel
Save