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