const r00 = p00 & 0xFF,
g00 = (p00 >>> 8) & 0xFF,
b00 = (p00 >>> 16) & 0xFF,
a00 = (p00 >>> 24) & 0xFF;
const r10 = p10 & 0xFF,
g10 = (p10 >>> 8) & 0xFF,
b10 = (p10 >>> 16) & 0xFF,
a10 = (p10 >>> 24) & 0xFF;
const r01 = p01 & 0xFF,
g01 = (p01 >>> 8) & 0xFF,
b01 = (p01 >>> 16) & 0xFF,
a01 = (p01 >>> 24) & 0xFF;
const r11 = p11 & 0xFF,
g11 = (p11 >>> 8) & 0xFF,
b11 = (p11 >>> 16) & 0xFF,
a11 = (p11 >>> 24) & 0xFF;
bilinearResult[0] = r00 * w00 + r10 * w10 + r01 * w01 + r11 * w11;
bilinearResult[1] = g00 * w00 + g10 * w10 + g01 * w01 + g11 * w11;
bilinearResult[2] = b00 * w00 + b10 * w10 + b01 * w01 + b11 * w11;
bilinearResult[3] = a00 * w00 + a10 * w10 + a01 * w01 + a11 * w11;
return bilinearResult;
};
const pack4 = (r, g, b, a) => {
r = r < 0 ? 0 : r > 255 ? 255 : r | 0;
g = g < 0 ? 0 : g > 255 ? 255 : g | 0;
b = b < 0 ? 0 : b > 255 ? 255 : b | 0;
a = a < 0 ? 0 : a > 255 ? 255 : a | 0;
return (a << 24) | (b << 16) | (g << 8) | r;
};
const rotateIntoAngleFrame = (src32, dst32, width, height, theta) => {
const c = _cos(theta),
s = _sin(theta),
cx = (width - 1) * 0.5,
cy = (height - 1) * 0.5;
let v, dv, u, du, x, y, r, g, b, a;
for (v = 0; v < height; v++) {
dv = v - cy;
for (u = 0; u < width; u++) {
du = u - cx;
x = du * c - dv * s + cx;
y = du * s + dv * c + cy;
[r,g,b,a] = sampleRGBA_bilinear_u32(src32, width, height, x, y);
dst32[v * width + u] = pack4(r, g, b, a);
}
}
};
const rotateBackToImageFrame = (src32, dst32, width, height, theta) => {
const c = _cos(theta),
s = _sin(theta),
cx = (width - 1) * 0.5,
cy = (height - 1) * 0.5;
let y, dy, x, dx, u, v, r, g, b, a;
for (y = 0; y < height; y++) {
dy = y - cy;
for (x = 0; x < width; x++) {
dx = x - cx;
u = dx * c + dy * s + cx;
v = -dx * s + dy * c + cy;
[r,g,b,a] = sampleRGBA_bilinear_u32(src32, width, height, u, v);
dst32[y * width + x] = pack4(r, g, b, a);
}
}
};
const getExpandedBlurBounds = (width, height, theta, radiusHorizontal, radiusVertical) => {
const c = _abs(_cos(theta)),
s = _abs(_sin(theta));
const rotatedWidth = Math.ceil(width * c + height * s),
rotatedHeight = Math.ceil(width * s + height * c);
const pad = Math.ceil(_max(radiusHorizontal, radiusVertical) * 3);
return {
width: rotatedWidth + pad * 2,
height: rotatedHeight + pad * 2,
};
};
const rotateIntoExpandedAngleFrame = (src32, dst32, width, height, expWidth, expHeight, theta) => {
const c = _cos(theta),
s = _sin(theta),
cx = (width - 1) * 0.5,
cy = (height - 1) * 0.5,
ecx = (expWidth - 1) * 0.5,
ecy = (expHeight - 1) * 0.5;
let v, dv, u, du, x, y, r, g, b, a;
for (v = 0; v < expHeight; v++) {
dv = v - ecy;
for (u = 0; u < expWidth; u++) {
du = u - ecx;
x = du * c - dv * s + cx;
y = du * s + dv * c + cy;
[r, g, b, a] = sampleRGBA_bilinear_u32(src32, width, height, x, y);
dst32[v * expWidth + u] = pack4(r, g, b, a);
}
}
};
const rotateBackFromExpandedAngleFrame = (src32, dst32, width, height, expWidth, expHeight, theta) => {
const c = _cos(theta),
s = _sin(theta),
cx = (width - 1) * 0.5,
cy = (height - 1) * 0.5,
ecx = (expWidth - 1) * 0.5,
ecy = (expHeight - 1) * 0.5;
let y, dy, x, dx, u, v, r, g, b, a;
for (y = 0; y < height; y++) {
dy = y - cy;
for (x = 0; x < width; x++) {
dx = x - cx;
u = dx * c + dy * s + ecx;
v = -dx * s + dy * c + ecy;
[r, g, b, a] = sampleRGBA_bilinear_u32(src32, expWidth, expHeight, u, v);
dst32[y * width + x] = pack4(r, g, b, a);
}
}
};
const transpose_u32 = (src32, dst32, width, height) => {
let y, baseY, x;
for (y = 0; y < height; y++) {
baseY = y * width;
for (x = 0; x < width; x++) {
dst32[baseY + x] = src32[x * height + y];
}
}
};
const runRotatedPath = (angleR) => {
rotateIntoAngleFrame(src32, bufA32, width, height, angleR);
if (doH && doV) {
convolveRGBA(bufA32, bufB32, tmpLineF32, hCoeff, width, height);
convolveRGBA(bufB32, bufA32, tmpLineF32, vCoeff, height, width);
}
else if (doH && !doV) {
convolveRGBA(bufA32, bufB32, tmpLineF32, hCoeff, width, height);
transpose_u32(bufB32, bufA32, width, height);
}
else if (!doH && doV) {
transpose_u32(bufA32, bufB32, width, height);
convolveRGBA(bufB32, bufA32, tmpLineF32, vCoeff, height, width);
}
else bufA32.set(bufA32);
rotateBackToImageFrame(bufA32, bufB32, width, height, angleR);
return bufB32;
};
const runExpandedRotatedPath = (angleR, expWidth, expHeight) => {
rotateIntoExpandedAngleFrame(src32, expA32, width, height, expWidth, expHeight, angleR);
if (doH && doV) {
convolveRGBA(expA32, expB32, tmpLineF32, hCoeff, expWidth, expHeight);
convolveRGBA(expB32, expA32, tmpLineF32, vCoeff, expHeight, expWidth);
}
else if (doH && !doV) {
convolveRGBA(expA32, expB32, tmpLineF32, hCoeff, expWidth, expHeight);
transpose_u32(expB32, expA32, expWidth, expHeight);
}
else if (!doH && doV) {
transpose_u32(expA32, expB32, expWidth, expHeight);
convolveRGBA(expB32, expA32, tmpLineF32, vCoeff, expHeight, expWidth);
}
else expA32.set(expA32);
rotateBackFromExpandedAngleFrame(expA32, expOut32, width, height, expWidth, expHeight, angleR);
return expOut32;
};
const convolveRGBA = (src, out, line, coeff, width, height) => {
const sat8 = (x) => x < 0 ? 0 : (x > 255 ? 255 : x);
const c_a0L = coeff[0],
c_a1L = coeff[1],
c_a0R = coeff[2],
c_a1R = coeff[3],
c_b1 = coeff[4],
c_b2 = coeff[5],
c_lc = coeff[6],
c_rc = coeff[7];
let i, j,
src_index, out_index, line_index, rgba,
prev_src_r, prev_src_g, prev_src_b, prev_src_a,
prev_prev_out_r, prev_prev_out_g, prev_prev_out_b, prev_prev_out_a,
prev_out_r, prev_out_g, prev_out_b, prev_out_a,
curr_src_r, curr_src_g, curr_src_b, curr_src_a,
curr_out_r, curr_out_g, curr_out_b, curr_out_a,
pr, pg, pb, pa;
for (i = 0; i < height; i++) {
src_index = i * width;
out_index = i;
line_index = 0;
rgba = src[src_index];
prev_src_r = rgba & 0xff;
prev_src_g = (rgba >>> 8) & 0xff;
prev_src_b = (rgba >>> 16) & 0xff;
prev_src_a = (rgba >>> 24) & 0xff;
prev_prev_out_r = prev_src_r * c_lc;
prev_prev_out_g = prev_src_g * c_lc;
prev_prev_out_b = prev_src_b * c_lc;
prev_prev_out_a = prev_src_a * c_lc;
prev_out_r = prev_prev_out_r;
prev_out_g = prev_prev_out_g;
prev_out_b = prev_prev_out_b;
prev_out_a = prev_prev_out_a;
for (j = 0; j < width; j++) {
rgba = src[src_index];
curr_src_r = rgba & 0xff;
curr_src_g = (rgba >>> 8) & 0xff;
curr_src_b = (rgba >>> 16) & 0xff;
curr_src_a = (rgba >>> 24) & 0xff;
curr_out_r = curr_src_r * c_a0L + prev_src_r * c_a1L + prev_out_r * c_b1 + prev_prev_out_r * c_b2;
curr_out_g = curr_src_g * c_a0L + prev_src_g * c_a1L + prev_out_g * c_b1 + prev_prev_out_g * c_b2;
curr_out_b = curr_src_b * c_a0L + prev_src_b * c_a1L + prev_out_b * c_b1 + prev_prev_out_b * c_b2;
curr_out_a = curr_src_a * c_a0L + prev_src_a * c_a1L + prev_out_a * c_b1 + prev_prev_out_a * c_b2;
prev_prev_out_r = prev_out_r;
prev_out_r = curr_out_r;
prev_src_r = curr_src_r;
prev_prev_out_g = prev_out_g;
prev_out_g = curr_out_g;
prev_src_g = curr_src_g;
prev_prev_out_b = prev_out_b;
prev_out_b = curr_out_b;
prev_src_b = curr_src_b;
prev_prev_out_a = prev_out_a;
prev_out_a = curr_out_a;
prev_src_a = curr_src_a;
line[line_index] = prev_out_r;
line[line_index + 1] = prev_out_g;
line[line_index + 2] = prev_out_b;
line[line_index + 3] = prev_out_a;
line_index += 4;
src_index++;
}
src_index--;
line_index -= 4;
out_index += height * (width - 1);
rgba = src[src_index];
prev_src_r = rgba & 0xff;
prev_src_g = (rgba >>> 8) & 0xff;
prev_src_b = (rgba >>> 16) & 0xff;
prev_src_a = (rgba >>> 24) & 0xff;
prev_prev_out_r = prev_src_r * c_rc;
prev_prev_out_g = prev_src_g * c_rc;
prev_prev_out_b = prev_src_b * c_rc;
prev_prev_out_a = prev_src_a * c_rc;
prev_out_r = prev_prev_out_r;
prev_out_g = prev_prev_out_g;
prev_out_b = prev_prev_out_b;
prev_out_a = prev_prev_out_a;
curr_src_r = prev_src_r;
curr_src_g = prev_src_g;
curr_src_b = prev_src_b;
curr_src_a = prev_src_a;
for (j = width - 1; j >= 0; j--) {
curr_out_r = curr_src_r * c_a0R + prev_src_r * c_a1R + prev_out_r * c_b1 + prev_prev_out_r * c_b2;
curr_out_g = curr_src_g * c_a0R + prev_src_g * c_a1R + prev_out_g * c_b1 + prev_prev_out_g * c_b2;
curr_out_b = curr_src_b * c_a0R + prev_src_b * c_a1R + prev_out_b * c_b1 + prev_prev_out_b * c_b2;
curr_out_a = curr_src_a * c_a0R + prev_src_a * c_a1R + prev_out_a * c_b1 + prev_prev_out_a * c_b2;
prev_prev_out_r = prev_out_r;
prev_out_r = curr_out_r;
prev_src_r = curr_src_r;
prev_prev_out_g = prev_out_g;
prev_out_g = curr_out_g;
prev_src_g = curr_src_g;
prev_prev_out_b = prev_out_b;
prev_out_b = curr_out_b;
prev_src_b = curr_src_b;
prev_prev_out_a = prev_out_a;
prev_out_a = curr_out_a;
prev_src_a = curr_src_a;
rgba = src[src_index];
curr_src_r = rgba & 0xff;
curr_src_g = (rgba >>> 8) & 0xff;
curr_src_b = (rgba >>> 16) & 0xff;
curr_src_a = (rgba >>> 24) & 0xff;
pr = sat8((line[line_index] + prev_out_r) | 0);
pg = sat8((line[line_index + 1] + prev_out_g) | 0);
pb = sat8((line[line_index + 2] + prev_out_b) | 0);
pa = sat8((line[line_index + 3] + prev_out_a) | 0);
out[out_index] = pr | (pg << 8) | (pb << 16) | (pa << 24);
src_index--;
line_index -= 4;
out_index -= height;
}
}
};
const near = (t) => _abs(_sin(t)) < 1e-6 || _abs(_cos(t)) < 1e-6;
const [input, output] = getInputAndOutputLines(requirements);
const iData = input.data,
oData = output.data;
const { width, height } = input;
const {
opacity = 1,
radiusHorizontal = 1,
radiusVertical = 1,
angle = 0,
includeRed = true,
includeGreen = true,
includeBlue = true,
includeAlpha = true,
excludeTransparentPixels = false,
premultiply = false,
lineOut,
} = requirements;
const angleRad = angle * _radian;
const pixels = (iData.length >>> 2),
maxSide4 = _max(width, height) * 4;
const src32 = new Uint32Array(iData.buffer, iData.byteOffset, pixels),
out32 = new Uint32Array(oData.buffer, oData.byteOffset, pixels);
const RM = includeRed ? 0x000000FF : 0,
GM = includeGreen ? 0x0000FF00 : 0,
BM = includeBlue ? 0x00FF0000 : 0,
AM = includeAlpha ? 0xFF000000 : 0;
const CHMASK = (RM | GM | BM | AM) >>> 0;
if ((radiusHorizontal <= 0) && (radiusVertical <= 0)) {
if (CHMASK === 0xFFFFFFFF && !excludeTransparentPixels) out32.set(src32);
else if (!excludeTransparentPixels) {
let s, p;
for (p = 0; p < pixels; p++) {
s = src32[p];
out32[p] = (s & CHMASK) | (s & ~CHMASK);
}
}
else {
let s, p;
for (p = 0; p < pixels; p++) {
s = src32[p];
if ((s >>> 24) === 0) out32[p] = s;
else out32[p] = (s & CHMASK) | (s & ~CHMASK);
}
}
if (lineOut) processResults(output, input, 1 - opacity);
else processResults(cache.work, output, opacity);
return;
}
const k = _round(angleRad / _piHalf),
snapped = k * _piHalf,
useExpandedPath = !near(angleRad);
const expandedBounds = useExpandedPath ?
getExpandedBlurBounds(width, height, angleRad, radiusHorizontal, radiusVertical) :
null;
const expandedPixels = expandedBounds ? expandedBounds.width * expandedBounds.height : 0,
expandedMaxSide4 = expandedBounds ? _max(expandedBounds.width, expandedBounds.height) * 4 : 0;
const {
bufA32,
bufB32,
expA32,
expB32,
expOut32,
tmpLineF32,
} = getWorkspace(pixels, maxSide4, expandedPixels, expandedMaxSide4);
const doH = radiusHorizontal > 0,
doV = radiusVertical > 0;
const hCoeff = doH ? getCoeffs(radiusHorizontal) : null,
vCoeff = doV ? getCoeffs(radiusVertical) : null;
if (premultiply) premultiply_u32(src32, pixels);
let blurred32;
if (!useExpandedPath) blurred32 = runRotatedPath(snapped);
else blurred32 = runExpandedRotatedPath(angleRad, expandedBounds.width, expandedBounds.height);
if (premultiply) unpremultiply_u32(blurred32, pixels);
if (CHMASK === 0xFFFFFFFF && !excludeTransparentPixels) out32.set(blurred32);
else if (!excludeTransparentPixels) {
let p, s, b;
for (p = 0; p < pixels; p++) {
s = src32[p];
b = blurred32[p];
out32[p] = (b & CHMASK) | (s & ~CHMASK);
}
}
else {
let p, s, b;
for (p = 0; p < pixels; p++) {
s = src32[p];
if ((s >>> 24) === 0) {
out32[p] = s;
continue;
}
b = blurred32[p];
out32[p] = (b & CHMASK) | (s & ~CHMASK);
}
}
if (lineOut) processResults(output, input, 1 - opacity);
else processResults(cache.work, output, opacity);
},