/
InkSketchEffect.cs
144 lines (116 loc) · 4.07 KB
/
InkSketchEffect.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
/////////////////////////////////////////////////////////////////////////////////
// Paint.NET //
// Copyright (C) dotPDN LLC, Rick Brewster, Tom Jackson, and contributors. //
// Portions Copyright (C) Microsoft Corporation. All Rights Reserved. //
// See license-pdn.txt for full licensing and attribution details. //
// //
// Ported to Pinta by: Jonathan Pobst <monkey@jpobst.com> //
/////////////////////////////////////////////////////////////////////////////////
using System;
using System.Collections.Generic;
using Pinta.ImageManipulation.UnaryPixelOperations;
using Pinta.ImageManipulation.PixelBlendOperations;
namespace Pinta.ImageManipulation.Effects
{
public class InkSketchEffect : BaseEffect
{
private static readonly int[][] conv;
private const int size = 5;
private const int radius = (size - 1) / 2;
private GlowEffect glow_effect;
private DesaturateOp desaturate_op;
private DarkenBlendOp darken_op;
private int ink_outline;
private int coloring;
static InkSketchEffect ()
{
conv = new int[5][];
for (var i = 0; i < conv.Length; ++i)
conv[i] = new int[5];
conv[0] = new int[] { -1, -1, -1, -1, -1 };
conv[1] = new int[] { -1, -1, -1, -1, -1 };
conv[2] = new int[] { -1, -1, 30, -1, -1 };
conv[3] = new int[] { -1, -1, -1, -1, -1 };
conv[4] = new int[] { -1, -1, -5, -1, -1 };
}
/// <summary>
/// Creates a new effect that will make an image look like an ink sketch.
/// </summary>
/// <param name="inkOutline">Size of the ink outline. Valid range is 0 - 99.</param>
/// <param name="coloring">Amount of color to keep. Valid range is 0 - 100.</param>
public InkSketchEffect (int inkOutline = 50, int coloring = 50)
{
if (inkOutline < 0 || inkOutline > 99)
throw new ArgumentOutOfRangeException ("inkOutline");
if (coloring < 0 || coloring > 100)
throw new ArgumentOutOfRangeException ("coloring");
ink_outline = inkOutline;
this.coloring = coloring;
glow_effect = new GlowEffect (6, -(coloring - 50) * 2, -(coloring - 50) * 2);
desaturate_op = new DesaturateOp ();
darken_op = new DarkenBlendOp ();
}
#region Algorithm Code Ported From PDN
protected unsafe override void RenderLine (ISurface src, ISurface dest, Rectangle roi)
{
// Glow backgound
glow_effect.Render (src, dest, roi);
// Create black outlines by finding the edges of objects
for (int y = roi.Top; y <= roi.Bottom; ++y) {
int top = y - radius;
int bottom = y + radius + 1;
if (top < 0) {
top = 0;
}
if (bottom > dest.Height) {
bottom = dest.Height;
}
ColorBgra* srcPtr = src.GetPointAddress (roi.X, y);
ColorBgra* dstPtr = dest.GetPointAddress (roi.X, y);
for (int x = roi.Left; x <= roi.Right; ++x) {
int left = x - radius;
int right = x + radius + 1;
if (left < 0) {
left = 0;
}
if (right > dest.Width) {
right = dest.Width;
}
int r = 0;
int g = 0;
int b = 0;
for (int v = top; v < bottom; v++) {
ColorBgra* pRow = src.GetRowAddress (v);
int j = v - y + radius;
for (int u = left; u < right; u++) {
int i1 = u - x + radius;
int w = conv[j][i1];
ColorBgra* pRef = pRow + u;
r += pRef->R * w;
g += pRef->G * w;
b += pRef->B * w;
}
}
ColorBgra topLayer = ColorBgra.FromBgr (
Utility.ClampToByte (b),
Utility.ClampToByte (g),
Utility.ClampToByte (r));
// Desaturate
topLayer = this.desaturate_op.Apply (topLayer);
// Adjust Brightness and Contrast
if (topLayer.R > (ink_outline * 255 / 100)) {
topLayer = ColorBgra.FromBgra (255, 255, 255, topLayer.A);
} else {
topLayer = ColorBgra.FromBgra (0, 0, 0, topLayer.A);
}
// Change Blend Mode to Darken
ColorBgra myPixel = this.darken_op.Apply (topLayer, *dstPtr);
*dstPtr = myPixel;
++srcPtr;
++dstPtr;
}
}
}
#endregion
}
}