mirror of https://github.com/SixLabors/ImageSharp
14 changed files with 312 additions and 49 deletions
@ -0,0 +1,24 @@ |
|||
// <copyright file="RecolorBrush.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageSharp.Drawing.Brushes |
|||
{ |
|||
/// <summary>
|
|||
/// Provides an implementation of a recolor brush for painting color changes.
|
|||
/// </summary>
|
|||
public class RecolorBrush : RecolorBrush<Color> |
|||
{ |
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="RecolorBrush" /> class.
|
|||
/// </summary>
|
|||
/// <param name="sourceColor">Color of the source.</param>
|
|||
/// <param name="targetColor">Color of the target.</param>
|
|||
/// <param name="threashold">The threashold.</param>
|
|||
public RecolorBrush(Color sourceColor, Color targetColor, float threashold) |
|||
: base(sourceColor, targetColor, threashold) |
|||
{ |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,129 @@ |
|||
// <copyright file="RecolorBrush{TColor}.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageSharp.Drawing.Brushes |
|||
{ |
|||
using System; |
|||
using System.Numerics; |
|||
|
|||
using Processors; |
|||
|
|||
/// <summary>
|
|||
/// Provides an implementation of a brush that can recolor an image
|
|||
/// </summary>
|
|||
/// <typeparam name="TColor">The pixel format.</typeparam>
|
|||
public class RecolorBrush<TColor> : IBrush<TColor> |
|||
where TColor : struct, IPackedPixel, IEquatable<TColor> |
|||
{ |
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="RecolorBrush{TColor}" /> class.
|
|||
/// </summary>
|
|||
/// <param name="sourceColor">Color of the source.</param>
|
|||
/// <param name="targetColor">Color of the target.</param>
|
|||
/// <param name="threashold">The threashold as a value between 0 and 1.</param>
|
|||
public RecolorBrush(TColor sourceColor, TColor targetColor, float threashold) |
|||
{ |
|||
this.SourceColor = sourceColor; |
|||
this.Threashold = threashold; |
|||
this.TargetColor = targetColor; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the threashold.
|
|||
/// </summary>
|
|||
/// <value>
|
|||
/// The threashold.
|
|||
/// </value>
|
|||
public float Threashold { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the source color.
|
|||
/// </summary>
|
|||
/// <value>
|
|||
/// The color of the source.
|
|||
/// </value>
|
|||
public TColor SourceColor { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the target color.
|
|||
/// </summary>
|
|||
/// <value>
|
|||
/// The color of the target.
|
|||
/// </value>
|
|||
public TColor TargetColor { get; } |
|||
|
|||
/// <inheritdoc />
|
|||
public IBrushApplicator<TColor> CreateApplicator(IReadonlyPixelAccessor<TColor> sourcePixels, RectangleF region) |
|||
{ |
|||
return new RecolorBrushApplicator(sourcePixels, this.SourceColor, this.TargetColor, this.Threashold); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// The recolor brush applicator.
|
|||
/// </summary>
|
|||
private class RecolorBrushApplicator : IBrushApplicator<TColor> |
|||
{ |
|||
/// <summary>
|
|||
/// The source pixel accessor.
|
|||
/// </summary>
|
|||
private readonly IReadonlyPixelAccessor<TColor> source; |
|||
private readonly Vector4 sourceColor; |
|||
private readonly Vector4 targetColor; |
|||
private readonly float threashold; |
|||
private readonly float totalDistance; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="RecolorBrushApplicator" /> class.
|
|||
/// </summary>
|
|||
/// <param name="sourcePixels">The source pixels.</param>
|
|||
/// <param name="sourceColor">Color of the source.</param>
|
|||
/// <param name="targetColor">Color of the target.</param>
|
|||
/// <param name="threashold">The threashold .</param>
|
|||
public RecolorBrushApplicator(IReadonlyPixelAccessor<TColor> sourcePixels, TColor sourceColor, TColor targetColor, float threashold) |
|||
{ |
|||
this.source = sourcePixels; |
|||
this.sourceColor = sourceColor.ToVector4(); |
|||
this.targetColor = targetColor.ToVector4(); |
|||
|
|||
// lets hack a min max extreams for a color space by letteing the IPackedPixle clamp our values to something in the correct spaces :)
|
|||
TColor maxColor = default(TColor); |
|||
maxColor.PackFromVector4(new Vector4(float.MaxValue)); |
|||
TColor minColor = default(TColor); |
|||
minColor.PackFromVector4(new Vector4(float.MinValue)); |
|||
this.totalDistance = Vector4.DistanceSquared(maxColor.ToVector4(), minColor.ToVector4()); |
|||
this.threashold = this.totalDistance * threashold; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the color for a single pixel.
|
|||
/// </summary>
|
|||
/// <param name="point">The point.</param>
|
|||
/// <returns>
|
|||
/// The color
|
|||
/// </returns>
|
|||
public TColor GetColor(Vector2 point) |
|||
{ |
|||
// Offset the requested pixel by the value in the rectangle (the shapes position)
|
|||
TColor result = this.source[(int)point.X, (int)point.Y]; |
|||
Vector4 background = result.ToVector4(); |
|||
float distance = Vector4.DistanceSquared(background, this.sourceColor); |
|||
if (distance <= this.threashold) |
|||
{ |
|||
var lerpAmount = (this.threashold - distance) / this.threashold; |
|||
Vector4 blended = Vector4BlendTransforms.PremultipliedLerp(background, this.targetColor, lerpAmount); |
|||
result.PackFromVector4(blended); |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
|
|||
/// <inheritdoc />
|
|||
public void Dispose() |
|||
{ |
|||
// we didn't make the lock on the PixelAccessor we shouldn't release it.
|
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,50 @@ |
|||
// <copyright file="IReadonlyPixelAccessor{TColor}.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageSharp |
|||
{ |
|||
using System; |
|||
using System.Diagnostics; |
|||
using System.Runtime.CompilerServices; |
|||
using System.Runtime.InteropServices; |
|||
|
|||
/// <summary>
|
|||
/// Provides per-pixel readonly access to generic <see cref="Image{TColor}"/> pixels.
|
|||
/// </summary>
|
|||
/// <typeparam name="TColor">The pixel format.</typeparam>
|
|||
public interface IReadonlyPixelAccessor<TColor> |
|||
{ |
|||
/// <summary>
|
|||
/// Gets the size of a single pixel in the number of bytes.
|
|||
/// </summary>
|
|||
int PixelSize { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the width of one row in the number of bytes.
|
|||
/// </summary>
|
|||
int RowStride { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the width of the image.
|
|||
/// </summary>
|
|||
int Width { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the height of the image.
|
|||
/// </summary>
|
|||
int Height { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the pixel at the specified position.
|
|||
/// </summary>
|
|||
/// <param name="x">The x-coordinate of the pixel. Must be greater than zero and smaller than the width of the pixel.</param>
|
|||
/// <param name="y">The y-coordinate of the pixel. Must be greater than zero and smaller than the width of the pixel.</param>
|
|||
/// <returns>The <see typeparam="TColor"/> at the specified position.</returns>
|
|||
TColor this[int x, int y] |
|||
{ |
|||
get; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,55 @@ |
|||
// <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 ImageSharp.Tests |
|||
{ |
|||
using ImageSharp.Drawing.Brushes; |
|||
using System.IO; |
|||
using System.Linq; |
|||
|
|||
using Xunit; |
|||
|
|||
public class RecolorImageTest : FileTestBase |
|||
{ |
|||
[Fact] |
|||
public void ImageShouldRecolorYellowToHotPink() |
|||
{ |
|||
string path = CreateOutputDirectory("Drawing", "RecolorImage"); |
|||
|
|||
var brush = new RecolorBrush(Color.Yellow, Color.HotPink, 0.2f); |
|||
|
|||
foreach (TestFile file in Files) |
|||
{ |
|||
Image image = file.CreateImage(); |
|||
|
|||
using (FileStream output = File.OpenWrite($"{path}/{file.FileName}")) |
|||
{ |
|||
image.Fill(brush) |
|||
.Save(output); |
|||
} |
|||
} |
|||
} |
|||
|
|||
[Fact] |
|||
public void ImageShouldRecolorYellowToHotPinkInARectangle() |
|||
{ |
|||
string path = CreateOutputDirectory("Drawing", "RecolorImage"); |
|||
|
|||
var brush = new RecolorBrush(Color.Yellow, Color.HotPink, 0.2f); |
|||
|
|||
foreach (TestFile file in Files) |
|||
{ |
|||
Image image = file.CreateImage(); |
|||
|
|||
using (FileStream output = File.OpenWrite($"{path}/Shaped_{file.FileName}")) |
|||
{ |
|||
var imageHeight = image.Height; |
|||
image.Fill(brush, new Rectangle(0, imageHeight/2 - imageHeight/4, image.Width, imageHeight/2)) |
|||
.Save(output); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue