2021-05-05 22:54:03 +08:00
|
|
|
import { Factory } from '../Factory';
|
|
|
|
import { Node, Filter } from '../Node';
|
|
|
|
import { getNumberValidator } from '../Validators';
|
2019-01-02 04:59:27 +08:00
|
|
|
/*
|
|
|
|
the Gauss filter
|
|
|
|
master repo: https://github.com/pavelpower/kineticjsGaussFilter
|
|
|
|
*/
|
|
|
|
/*
|
|
|
|
|
|
|
|
StackBlur - a fast almost Gaussian Blur For Canvas
|
|
|
|
|
|
|
|
Version: 0.5
|
|
|
|
Author: Mario Klingemann
|
|
|
|
Contact: mario@quasimondo.com
|
|
|
|
Website: http://www.quasimondo.com/StackBlurForCanvas
|
|
|
|
Twitter: @quasimondo
|
|
|
|
|
|
|
|
In case you find this class useful - especially in commercial projects -
|
|
|
|
I am not totally unhappy for a small donation to my PayPal account
|
|
|
|
mario@quasimondo.de
|
|
|
|
|
|
|
|
Or support me on flattr:
|
|
|
|
https://flattr.com/thing/72791/StackBlur-a-fast-almost-Gaussian-Blur-Effect-for-CanvasJavascript
|
|
|
|
|
|
|
|
Copyright (c) 2010 Mario Klingemann
|
|
|
|
|
|
|
|
Permission is hereby granted, free of charge, to any person
|
|
|
|
obtaining a copy of this software and associated documentation
|
|
|
|
files (the "Software"), to deal in the Software without
|
|
|
|
restriction, including without limitation the rights to use,
|
|
|
|
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
|
|
copies of the Software, and to permit persons to whom the
|
|
|
|
Software is furnished to do so, subject to the following
|
|
|
|
conditions:
|
|
|
|
|
|
|
|
The above copyright notice and this permission notice shall be
|
|
|
|
included in all copies or substantial portions of the Software.
|
|
|
|
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
|
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|
|
|
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
|
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|
|
|
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
|
|
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
|
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
|
|
OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
*/
|
|
|
|
|
2023-09-10 05:39:57 +08:00
|
|
|
function BlurStack(this: any) {
|
2019-01-02 04:59:27 +08:00
|
|
|
this.r = 0;
|
|
|
|
this.g = 0;
|
|
|
|
this.b = 0;
|
|
|
|
this.a = 0;
|
|
|
|
this.next = null;
|
|
|
|
}
|
|
|
|
|
2024-10-05 22:18:32 +08:00
|
|
|
const mul_table = [
|
2023-08-28 22:23:57 +08:00
|
|
|
512, 512, 456, 512, 328, 456, 335, 512, 405, 328, 271, 456, 388, 335, 292,
|
|
|
|
512, 454, 405, 364, 328, 298, 271, 496, 456, 420, 388, 360, 335, 312, 292,
|
|
|
|
273, 512, 482, 454, 428, 405, 383, 364, 345, 328, 312, 298, 284, 271, 259,
|
|
|
|
496, 475, 456, 437, 420, 404, 388, 374, 360, 347, 335, 323, 312, 302, 292,
|
|
|
|
282, 273, 265, 512, 497, 482, 468, 454, 441, 428, 417, 405, 394, 383, 373,
|
|
|
|
364, 354, 345, 337, 328, 320, 312, 305, 298, 291, 284, 278, 271, 265, 259,
|
|
|
|
507, 496, 485, 475, 465, 456, 446, 437, 428, 420, 412, 404, 396, 388, 381,
|
|
|
|
374, 367, 360, 354, 347, 341, 335, 329, 323, 318, 312, 307, 302, 297, 292,
|
|
|
|
287, 282, 278, 273, 269, 265, 261, 512, 505, 497, 489, 482, 475, 468, 461,
|
|
|
|
454, 447, 441, 435, 428, 422, 417, 411, 405, 399, 394, 389, 383, 378, 373,
|
|
|
|
368, 364, 359, 354, 350, 345, 341, 337, 332, 328, 324, 320, 316, 312, 309,
|
|
|
|
305, 301, 298, 294, 291, 287, 284, 281, 278, 274, 271, 268, 265, 262, 259,
|
|
|
|
257, 507, 501, 496, 491, 485, 480, 475, 470, 465, 460, 456, 451, 446, 442,
|
|
|
|
437, 433, 428, 424, 420, 416, 412, 408, 404, 400, 396, 392, 388, 385, 381,
|
|
|
|
377, 374, 370, 367, 363, 360, 357, 354, 350, 347, 344, 341, 338, 335, 332,
|
|
|
|
329, 326, 323, 320, 318, 315, 312, 310, 307, 304, 302, 299, 297, 294, 292,
|
|
|
|
289, 287, 285, 282, 280, 278, 275, 273, 271, 269, 267, 265, 263, 261, 259,
|
2019-01-02 04:59:27 +08:00
|
|
|
];
|
|
|
|
|
2024-10-05 22:18:32 +08:00
|
|
|
const shg_table = [
|
2023-08-28 22:23:57 +08:00
|
|
|
9, 11, 12, 13, 13, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 17, 17, 17, 17, 17,
|
|
|
|
17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, 19, 19, 19, 19,
|
|
|
|
19, 19, 19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,
|
|
|
|
20, 20, 20, 20, 20, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
|
|
|
|
21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22,
|
|
|
|
22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
|
|
|
|
22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23,
|
|
|
|
23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
|
|
|
|
23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
|
|
|
|
23, 23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
|
|
|
|
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
|
|
|
|
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
|
|
|
|
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
|
|
|
|
24, 24, 24, 24, 24, 24, 24,
|
2019-01-02 04:59:27 +08:00
|
|
|
];
|
|
|
|
|
|
|
|
function filterGaussBlurRGBA(imageData, radius) {
|
2024-10-05 22:18:32 +08:00
|
|
|
const pixels = imageData.data,
|
2019-01-02 04:59:27 +08:00
|
|
|
width = imageData.width,
|
|
|
|
height = imageData.height;
|
|
|
|
|
2025-04-01 00:30:12 +08:00
|
|
|
let p,
|
2019-01-02 04:59:27 +08:00
|
|
|
yi,
|
|
|
|
yw,
|
|
|
|
r_sum,
|
|
|
|
g_sum,
|
|
|
|
b_sum,
|
|
|
|
a_sum,
|
|
|
|
r_out_sum,
|
|
|
|
g_out_sum,
|
|
|
|
b_out_sum,
|
|
|
|
a_out_sum,
|
|
|
|
r_in_sum,
|
|
|
|
g_in_sum,
|
|
|
|
b_in_sum,
|
|
|
|
a_in_sum,
|
|
|
|
pr,
|
|
|
|
pg,
|
|
|
|
pb,
|
|
|
|
pa,
|
|
|
|
rbs;
|
|
|
|
|
2024-10-11 19:11:27 +08:00
|
|
|
const div = radius + radius + 1,
|
2019-01-02 04:59:27 +08:00
|
|
|
widthMinus1 = width - 1,
|
|
|
|
heightMinus1 = height - 1,
|
|
|
|
radiusPlus1 = radius + 1,
|
|
|
|
sumFactor = (radiusPlus1 * (radiusPlus1 + 1)) / 2,
|
|
|
|
stackStart = new BlurStack(),
|
|
|
|
mul_sum = mul_table[radius],
|
|
|
|
shg_sum = shg_table[radius];
|
|
|
|
|
2024-10-11 19:11:27 +08:00
|
|
|
let stackEnd = null,
|
|
|
|
stack = stackStart,
|
|
|
|
stackIn: any = null,
|
|
|
|
stackOut: any = null;
|
|
|
|
|
2025-04-01 00:30:12 +08:00
|
|
|
for (let i = 1; i < div; i++) {
|
2019-01-02 04:59:27 +08:00
|
|
|
stack = stack.next = new BlurStack();
|
|
|
|
if (i === radiusPlus1) {
|
|
|
|
stackEnd = stack;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
stack.next = stackStart;
|
|
|
|
|
|
|
|
yw = yi = 0;
|
|
|
|
|
2025-04-01 00:30:12 +08:00
|
|
|
for (let y = 0; y < height; y++) {
|
2023-08-28 22:23:57 +08:00
|
|
|
r_in_sum =
|
|
|
|
g_in_sum =
|
|
|
|
b_in_sum =
|
|
|
|
a_in_sum =
|
|
|
|
r_sum =
|
|
|
|
g_sum =
|
|
|
|
b_sum =
|
|
|
|
a_sum =
|
|
|
|
0;
|
2019-01-02 04:59:27 +08:00
|
|
|
|
|
|
|
r_out_sum = radiusPlus1 * (pr = pixels[yi]);
|
|
|
|
g_out_sum = radiusPlus1 * (pg = pixels[yi + 1]);
|
|
|
|
b_out_sum = radiusPlus1 * (pb = pixels[yi + 2]);
|
|
|
|
a_out_sum = radiusPlus1 * (pa = pixels[yi + 3]);
|
|
|
|
|
|
|
|
r_sum += sumFactor * pr;
|
|
|
|
g_sum += sumFactor * pg;
|
|
|
|
b_sum += sumFactor * pb;
|
|
|
|
a_sum += sumFactor * pa;
|
|
|
|
|
|
|
|
stack = stackStart;
|
|
|
|
|
2025-04-01 00:30:12 +08:00
|
|
|
for (let i = 0; i < radiusPlus1; i++) {
|
2019-01-02 04:59:27 +08:00
|
|
|
stack.r = pr;
|
|
|
|
stack.g = pg;
|
|
|
|
stack.b = pb;
|
|
|
|
stack.a = pa;
|
|
|
|
stack = stack.next;
|
|
|
|
}
|
|
|
|
|
2025-04-01 00:30:12 +08:00
|
|
|
for (let i = 1; i < radiusPlus1; i++) {
|
2019-01-02 04:59:27 +08:00
|
|
|
p = yi + ((widthMinus1 < i ? widthMinus1 : i) << 2);
|
|
|
|
r_sum += (stack.r = pr = pixels[p]) * (rbs = radiusPlus1 - i);
|
|
|
|
g_sum += (stack.g = pg = pixels[p + 1]) * rbs;
|
|
|
|
b_sum += (stack.b = pb = pixels[p + 2]) * rbs;
|
|
|
|
a_sum += (stack.a = pa = pixels[p + 3]) * rbs;
|
|
|
|
|
|
|
|
r_in_sum += pr;
|
|
|
|
g_in_sum += pg;
|
|
|
|
b_in_sum += pb;
|
|
|
|
a_in_sum += pa;
|
|
|
|
|
|
|
|
stack = stack.next;
|
|
|
|
}
|
|
|
|
|
|
|
|
stackIn = stackStart;
|
|
|
|
stackOut = stackEnd;
|
2025-04-01 00:30:12 +08:00
|
|
|
for (let x = 0; x < width; x++) {
|
2019-01-02 04:59:27 +08:00
|
|
|
pixels[yi + 3] = pa = (a_sum * mul_sum) >> shg_sum;
|
|
|
|
if (pa !== 0) {
|
|
|
|
pa = 255 / pa;
|
|
|
|
pixels[yi] = ((r_sum * mul_sum) >> shg_sum) * pa;
|
|
|
|
pixels[yi + 1] = ((g_sum * mul_sum) >> shg_sum) * pa;
|
|
|
|
pixels[yi + 2] = ((b_sum * mul_sum) >> shg_sum) * pa;
|
|
|
|
} else {
|
|
|
|
pixels[yi] = pixels[yi + 1] = pixels[yi + 2] = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
r_sum -= r_out_sum;
|
|
|
|
g_sum -= g_out_sum;
|
|
|
|
b_sum -= b_out_sum;
|
|
|
|
a_sum -= a_out_sum;
|
|
|
|
|
|
|
|
r_out_sum -= stackIn.r;
|
|
|
|
g_out_sum -= stackIn.g;
|
|
|
|
b_out_sum -= stackIn.b;
|
|
|
|
a_out_sum -= stackIn.a;
|
|
|
|
|
|
|
|
p = (yw + ((p = x + radius + 1) < widthMinus1 ? p : widthMinus1)) << 2;
|
|
|
|
|
|
|
|
r_in_sum += stackIn.r = pixels[p];
|
|
|
|
g_in_sum += stackIn.g = pixels[p + 1];
|
|
|
|
b_in_sum += stackIn.b = pixels[p + 2];
|
|
|
|
a_in_sum += stackIn.a = pixels[p + 3];
|
|
|
|
|
|
|
|
r_sum += r_in_sum;
|
|
|
|
g_sum += g_in_sum;
|
|
|
|
b_sum += b_in_sum;
|
|
|
|
a_sum += a_in_sum;
|
|
|
|
|
|
|
|
stackIn = stackIn.next;
|
|
|
|
|
|
|
|
r_out_sum += pr = stackOut.r;
|
|
|
|
g_out_sum += pg = stackOut.g;
|
|
|
|
b_out_sum += pb = stackOut.b;
|
|
|
|
a_out_sum += pa = stackOut.a;
|
|
|
|
|
|
|
|
r_in_sum -= pr;
|
|
|
|
g_in_sum -= pg;
|
|
|
|
b_in_sum -= pb;
|
|
|
|
a_in_sum -= pa;
|
|
|
|
|
|
|
|
stackOut = stackOut.next;
|
|
|
|
|
|
|
|
yi += 4;
|
|
|
|
}
|
|
|
|
yw += width;
|
|
|
|
}
|
|
|
|
|
2025-04-01 00:30:12 +08:00
|
|
|
for (let x = 0; x < width; x++) {
|
2023-08-28 22:23:57 +08:00
|
|
|
g_in_sum =
|
|
|
|
b_in_sum =
|
|
|
|
a_in_sum =
|
|
|
|
r_in_sum =
|
|
|
|
g_sum =
|
|
|
|
b_sum =
|
|
|
|
a_sum =
|
|
|
|
r_sum =
|
|
|
|
0;
|
2019-01-02 04:59:27 +08:00
|
|
|
|
|
|
|
yi = x << 2;
|
|
|
|
r_out_sum = radiusPlus1 * (pr = pixels[yi]);
|
|
|
|
g_out_sum = radiusPlus1 * (pg = pixels[yi + 1]);
|
|
|
|
b_out_sum = radiusPlus1 * (pb = pixels[yi + 2]);
|
|
|
|
a_out_sum = radiusPlus1 * (pa = pixels[yi + 3]);
|
|
|
|
|
|
|
|
r_sum += sumFactor * pr;
|
|
|
|
g_sum += sumFactor * pg;
|
|
|
|
b_sum += sumFactor * pb;
|
|
|
|
a_sum += sumFactor * pa;
|
|
|
|
|
|
|
|
stack = stackStart;
|
|
|
|
|
2025-04-01 00:30:12 +08:00
|
|
|
for (let i = 0; i < radiusPlus1; i++) {
|
2019-01-02 04:59:27 +08:00
|
|
|
stack.r = pr;
|
|
|
|
stack.g = pg;
|
|
|
|
stack.b = pb;
|
|
|
|
stack.a = pa;
|
|
|
|
stack = stack.next;
|
|
|
|
}
|
|
|
|
|
2025-04-01 00:30:12 +08:00
|
|
|
let yp = width;
|
2019-01-02 04:59:27 +08:00
|
|
|
|
2025-04-01 00:30:12 +08:00
|
|
|
for (let i = 1; i <= radius; i++) {
|
2019-01-02 04:59:27 +08:00
|
|
|
yi = (yp + x) << 2;
|
|
|
|
|
|
|
|
r_sum += (stack.r = pr = pixels[yi]) * (rbs = radiusPlus1 - i);
|
|
|
|
g_sum += (stack.g = pg = pixels[yi + 1]) * rbs;
|
|
|
|
b_sum += (stack.b = pb = pixels[yi + 2]) * rbs;
|
|
|
|
a_sum += (stack.a = pa = pixels[yi + 3]) * rbs;
|
|
|
|
|
|
|
|
r_in_sum += pr;
|
|
|
|
g_in_sum += pg;
|
|
|
|
b_in_sum += pb;
|
|
|
|
a_in_sum += pa;
|
|
|
|
|
|
|
|
stack = stack.next;
|
|
|
|
|
|
|
|
if (i < heightMinus1) {
|
|
|
|
yp += width;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
yi = x;
|
|
|
|
stackIn = stackStart;
|
|
|
|
stackOut = stackEnd;
|
2025-04-01 00:30:12 +08:00
|
|
|
for (let y = 0; y < height; y++) {
|
2019-01-02 04:59:27 +08:00
|
|
|
p = yi << 2;
|
|
|
|
pixels[p + 3] = pa = (a_sum * mul_sum) >> shg_sum;
|
|
|
|
if (pa > 0) {
|
|
|
|
pa = 255 / pa;
|
|
|
|
pixels[p] = ((r_sum * mul_sum) >> shg_sum) * pa;
|
|
|
|
pixels[p + 1] = ((g_sum * mul_sum) >> shg_sum) * pa;
|
|
|
|
pixels[p + 2] = ((b_sum * mul_sum) >> shg_sum) * pa;
|
|
|
|
} else {
|
|
|
|
pixels[p] = pixels[p + 1] = pixels[p + 2] = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
r_sum -= r_out_sum;
|
|
|
|
g_sum -= g_out_sum;
|
|
|
|
b_sum -= b_out_sum;
|
|
|
|
a_sum -= a_out_sum;
|
|
|
|
|
|
|
|
r_out_sum -= stackIn.r;
|
|
|
|
g_out_sum -= stackIn.g;
|
|
|
|
b_out_sum -= stackIn.b;
|
|
|
|
a_out_sum -= stackIn.a;
|
|
|
|
|
|
|
|
p =
|
|
|
|
(x +
|
|
|
|
((p = y + radiusPlus1) < heightMinus1 ? p : heightMinus1) * width) <<
|
|
|
|
2;
|
|
|
|
|
|
|
|
r_sum += r_in_sum += stackIn.r = pixels[p];
|
|
|
|
g_sum += g_in_sum += stackIn.g = pixels[p + 1];
|
|
|
|
b_sum += b_in_sum += stackIn.b = pixels[p + 2];
|
|
|
|
a_sum += a_in_sum += stackIn.a = pixels[p + 3];
|
|
|
|
|
|
|
|
stackIn = stackIn.next;
|
|
|
|
|
|
|
|
r_out_sum += pr = stackOut.r;
|
|
|
|
g_out_sum += pg = stackOut.g;
|
|
|
|
b_out_sum += pb = stackOut.b;
|
|
|
|
a_out_sum += pa = stackOut.a;
|
|
|
|
|
|
|
|
r_in_sum -= pr;
|
|
|
|
g_in_sum -= pg;
|
|
|
|
b_in_sum -= pb;
|
|
|
|
a_in_sum -= pa;
|
|
|
|
|
|
|
|
stackOut = stackOut.next;
|
|
|
|
|
|
|
|
yi += width;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Blur Filter
|
|
|
|
* @function
|
|
|
|
* @name Blur
|
|
|
|
* @memberof Konva.Filters
|
|
|
|
* @param {Object} imageData
|
|
|
|
* @example
|
|
|
|
* node.cache();
|
|
|
|
* node.filters([Konva.Filters.Blur]);
|
|
|
|
* node.blurRadius(10);
|
|
|
|
*/
|
2019-06-19 04:29:07 +08:00
|
|
|
export const Blur: Filter = function Blur(imageData) {
|
2024-10-05 22:18:32 +08:00
|
|
|
const radius = Math.round(this.blurRadius());
|
2019-01-02 04:59:27 +08:00
|
|
|
|
|
|
|
if (radius > 0) {
|
|
|
|
filterGaussBlurRGBA(imageData, radius);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
Factory.addGetterSetter(
|
|
|
|
Node,
|
|
|
|
'blurRadius',
|
|
|
|
0,
|
2019-02-25 01:06:04 +08:00
|
|
|
getNumberValidator(),
|
2019-01-02 04:59:27 +08:00
|
|
|
Factory.afterSetFilter
|
|
|
|
);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* get/set blur radius. Use with {@link Konva.Filters.Blur} filter
|
2019-01-06 16:01:20 +08:00
|
|
|
* @name Konva.Node#blurRadius
|
2019-01-02 04:59:27 +08:00
|
|
|
* @method
|
|
|
|
* @param {Integer} radius
|
|
|
|
* @returns {Integer}
|
|
|
|
*/
|