Draw: GaussianBlur

git-svn-id: svn://ultimatepp.org/upp/trunk@14334 f0d560ea-af0d-0410-9eb7-867de7ffcac7
This commit is contained in:
cxl 2020-04-19 15:07:57 +00:00
parent 53e8512d7a
commit 4e2a511e1e
4 changed files with 148 additions and 2 deletions

View file

@ -134,8 +134,7 @@ void CoDo_ST(Function<void ()>&& fn)
template <typename Fn>
void CoFor(int n, Fn iterator)
{
std::atomic<int> ii;
ii = 0;
std::atomic<int> ii(0);
CoDo([&] {
for(int i = ii++; i < n; i = ii++)
iterator(i);

View file

@ -952,4 +952,143 @@ Image Dither(const Image& m, int dival)
return ib;
}
Image GaussianBlur(const Image& img, int radius, bool co)
{
// This code is adapted from Ivan Kutskir's fast blur implementation, published under MIT license.
// See: http://blog.ivank.net/fastest-gaussian-blur.html
// An implementation of well known fast box and gaussian blur
// approximation algorithms by Wojciech Jarosz and Peter Kovesi.
// See: https://elynxsdk.free.fr/ext-docs/Blur/Fast_box_blur.pdf
// See: https://www.peterkovesi.com/papers/FastGaussianSmoothing.pdf
auto ApplyBoxBlur = [&](const Image& src, int r) -> Image
{
double avg = 1.0 / (r + r + 1);
Size sz = src.GetSize();
ImageBuffer tmp(sz);
ImageBuffer out(sz);
auto DoLine = [&](int i) {
int ti = 0;
int li = ti;
int ri = r;
const RGBA& fv = src[i][0];
const RGBA& lv = src[i][sz.cx - 1];
dword rsum = fv.r * (r + 1);
dword gsum = fv.g * (r + 1);
dword bsum = fv.b * (r + 1);
dword asum = fv.a * (r + 1);
for(int j = 0; j < r; j++) {
const RGBA& p = src[i][j];
rsum += p.r;
gsum += p.g;
bsum += p.b;
asum += p.a;
}
for(int j = 0; j <= r; j++) {
const RGBA& p = src[i][ri++];
RGBA& q = tmp[i][ti++];
q.r = (rsum += p.r - fv.r) * avg;
q.g = (gsum += p.g - fv.g) * avg;
q.b = (bsum += p.b - fv.b) * avg;
q.a = (asum += p.a - fv.a) * avg;
}
for(int j = r + 1; j < sz.cx - r; j++) {
const RGBA& p = src[i][ri++];
const RGBA& q = src[i][li++];
RGBA& t = tmp[i][ti++];
t.r = (rsum += p.r - q.r) * avg;
t.g = (gsum += p.g - q.g) * avg;
t.b = (bsum += p.b - q.b) * avg;
t.a = (asum += p.a - q.a) * avg;
}
for(int j = sz.cx - r; j < sz.cx ; j++) {
const RGBA& p = src[i][li++];
RGBA& q = tmp[i][ti++];
q.r = (rsum += lv.r - p.r) * avg;
q.g = (gsum += lv.g - p.g) * avg;
q.b = (bsum += lv.b - p.b) * avg;
q.a = (asum += lv.a - p.a) * avg;
}
};
if(co)
CoFor(sz.cy, [&](int i) { DoLine(i); });
else
for(int i = 0; i < sz.cy; i++)
DoLine(i);
auto DoColumn = [&](int i) {
int ti = 0;
int li = ti;
int ri = r;
const RGBA& fv = tmp[ti][i];
const RGBA& lv = tmp[sz.cy - 1][i];
dword rsum = fv.r * (r + 1);
dword gsum = fv.g * (r + 1);
dword bsum = fv.b * (r + 1);
dword asum = fv.a * (r + 1);
for(int j = 0; j < r; j++) {
const RGBA& p = tmp[j][i];
rsum += p.r;
gsum += p.g;
bsum += p.b;
asum += p.a;
}
for(int j = 0; j <= r; j++) {
const RGBA& p = tmp[ri++][i];
RGBA& q = out[ti++][i];
q.r = (rsum += p.r - fv.r) * avg;
q.g = (gsum += p.g - fv.g) * avg;
q.b = (bsum += p.b - fv.b) * avg;
q.a = (asum += p.a - fv.a) * avg;
}
for(int j = r + 1; j < sz.cy - r; j++) {
const RGBA& p = tmp[ri++][i];
const RGBA& q = tmp[li++][i];
RGBA& t = out[ti++][i];
t.r = (rsum += p.r - q.r) * avg;
t.g = (gsum += p.g - q.g) * avg;
t.b = (bsum += p.b - q.b) * avg;
t.a = (asum += p.a - q.a) * avg;
}
for(int j = sz.cy - r; j < sz.cy; j++) {
const RGBA& p = tmp[li++][i];
RGBA& q = out[ti++][i];
q.r = (rsum += lv.r - p.r) * avg;
q.g = (gsum += lv.g - p.g) * avg;
q.b = (bsum += lv.b - p.b) * avg;
q.a = (asum += lv.a - p.a) * avg;
}
};
if(co)
CoFor(sz.cx, [&](int i) { DoColumn(i); });
else
for(int i = 0; i < sz.cx; i++)
DoColumn(i);
out.SetHotSpots(src);
out.SetResolution(src.GetResolution());
return out;
};
if(radius < 1 || IsNull(img))
return img;
double wl = ffloor(sqrt((12 * sqr(radius) / 3) + 1));
if(fmod(wl, 2) == 0) wl--;
double wu = wl + 2;
double m = fround((12 * sqr(radius) - 3 * sqr(wl) - 4 * 3 * wl - 3 * 3) / (-4 * wl - 4));
Vector<int> sizes;
Image t = img;
for (int i = 0; i < 3; i++)
t = ApplyBoxBlur(t, ((i < m ? wl : wu) - 1) / 2);
return pick(t);
}
}

View file

@ -106,6 +106,7 @@ void Filter(RasterEncoder& target, Raster& src, ImageFilter9& filter);
Image Etched(const Image& img);
Image Sharpen(const Image& img, int amount = 100);
Image Dither(const Image& m, int dival = 394);
Image GaussianBlur(const Image& img, int radius, bool co = false);
Image RotateClockwise(const Image& img);
Image RotateAntiClockwise(const Image& img);

View file

@ -258,6 +258,13 @@ dithering is using for printing on purely monochromatic (without
half`-toning support) printers.&]
[s3;%% &]
[s4; &]
[s5;:Upp`:`:GaussianBlur`(const Upp`:`:Image`&`,int`): [_^Upp`:`:Image^ Image]_[* Gaussia
nBlur]([@(0.0.255) const]_[_^Upp`:`:Image^ Image][@(0.0.255) `&]_[*@3 img],
[@(0.0.255) int]_[*@3 radius])&]
[s2; [@N An implementation of well known fast box and gaussian blur
approximation algorithms by Wojciech Jarosz and Peter Kovesi.]&]
[s3;%% &]
[s4; &]
[s5;:Etched`(const Image`&`): [_^Image^ Image]_[* Etched]([@(0.0.255) const]_[_^Image^ Image][@(0.0.255) `&
]_[*@3 img])&]
[s2;%% `"Etching`" effect.&]