mirror of https://github.com/SixLabors/ImageSharp
Browse Source
Former-commit-id: ff1aec34e42b49f728dfd7f370f709c48be34762 Former-commit-id: ee75403396e61e34b0d97760ee36634973d5a5ec Former-commit-id: 326da67dbfdb92272ab63c08e30a9e85904f7e60pull/1/head
3 changed files with 199 additions and 0 deletions
@ -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; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -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(); |
|||
} |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -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…
Reference in new issue