From 4e2a511e1e805f8edce00cbd1f7d48bb3b43d12b Mon Sep 17 00:00:00 2001 From: cxl Date: Sun, 19 Apr 2020 15:07:57 +0000 Subject: [PATCH] Draw: GaussianBlur git-svn-id: svn://ultimatepp.org/upp/trunk@14334 f0d560ea-af0d-0410-9eb7-867de7ffcac7 --- uppsrc/Core/CoWork.h | 3 +- uppsrc/Draw/ImageOp.cpp | 139 ++++++++++++++++++++++++++ uppsrc/Draw/ImageOp.h | 1 + uppsrc/Draw/src.tpp/ImageOp_en-us.tpp | 7 ++ 4 files changed, 148 insertions(+), 2 deletions(-) diff --git a/uppsrc/Core/CoWork.h b/uppsrc/Core/CoWork.h index 439876fd3..824446778 100644 --- a/uppsrc/Core/CoWork.h +++ b/uppsrc/Core/CoWork.h @@ -134,8 +134,7 @@ void CoDo_ST(Function&& fn) template void CoFor(int n, Fn iterator) { - std::atomic ii; - ii = 0; + std::atomic ii(0); CoDo([&] { for(int i = ii++; i < n; i = ii++) iterator(i); diff --git a/uppsrc/Draw/ImageOp.cpp b/uppsrc/Draw/ImageOp.cpp index ad0399dca..bfc54d675 100644 --- a/uppsrc/Draw/ImageOp.cpp +++ b/uppsrc/Draw/ImageOp.cpp @@ -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 sizes; + Image t = img; + for (int i = 0; i < 3; i++) + t = ApplyBoxBlur(t, ((i < m ? wl : wu) - 1) / 2); + return pick(t); +} + } diff --git a/uppsrc/Draw/ImageOp.h b/uppsrc/Draw/ImageOp.h index aeb692c4d..718f1b681 100644 --- a/uppsrc/Draw/ImageOp.h +++ b/uppsrc/Draw/ImageOp.h @@ -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); diff --git a/uppsrc/Draw/src.tpp/ImageOp_en-us.tpp b/uppsrc/Draw/src.tpp/ImageOp_en-us.tpp index 3fe615e24..b86cb7f7f 100644 --- a/uppsrc/Draw/src.tpp/ImageOp_en-us.tpp +++ b/uppsrc/Draw/src.tpp/ImageOp_en-us.tpp @@ -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.&]