GaussianBlur.java
package io.brunoborges.jairosvg.draw;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferInt;
/**
* Gaussian blur via 3-pass box blur approximation (W3C recommended).
*
* @see <a href="http://blog.ivank.net/fastest-gaussian-blur.html">Fastest
* Gaussian Blur</a>
*/
final class GaussianBlur {
private GaussianBlur() {
}
static BufferedImage apply(BufferedImage input, double stdDeviation, BufferedImage temp, BufferedImage output) {
if (stdDeviation <= 0) {
return input;
}
int w = input.getWidth();
int h = input.getHeight();
int[] radii = boxRadii(stdDeviation);
int[] src = ((DataBufferInt) input.getRaster().getDataBuffer()).getData();
int[] tmp = ((DataBufferInt) temp.getRaster().getDataBuffer()).getData();
int[] dst = ((DataBufferInt) output.getRaster().getDataBuffer()).getData();
boxBlurH(src, tmp, w, h, radii[0]);
boxBlurV(tmp, dst, w, h, radii[0]);
boxBlurH(dst, tmp, w, h, radii[1]);
boxBlurV(tmp, dst, w, h, radii[1]);
boxBlurH(dst, tmp, w, h, radii[2]);
boxBlurV(tmp, dst, w, h, radii[2]);
return output;
}
private static int[] boxRadii(double sigma) {
double ideal = Math.sqrt((12 * sigma * sigma / 3) + 1);
int wl = (int) Math.floor(ideal);
if (wl % 2 == 0) {
wl--;
}
int wu = wl + 2;
int m = (int) Math.round((12 * sigma * sigma - 3.0 * wl * wl - 12.0 * wl - 9) / (-4.0 * wl - 4));
return new int[]{(m > 0 ? wl : wu) / 2, (m > 1 ? wl : wu) / 2, (m > 2 ? wl : wu) / 2};
}
private static void boxBlurH(int[] src, int[] dst, int w, int h, int r) {
if (r <= 0) {
System.arraycopy(src, 0, dst, 0, src.length);
return;
}
int boxSize = r + r + 1;
int scale = (1 << 24) / boxSize;
for (int y = 0; y < h; y++) {
int off = y * w;
int sa = 0, sr = 0, sg = 0, sb = 0;
for (int i = -r; i <= r; i++) {
int px = src[off + Math.min(Math.max(i, 0), w - 1)];
sa += (px >>> 24);
sr += (px >> 16) & 0xFF;
sg += (px >> 8) & 0xFF;
sb += px & 0xFF;
}
int prev = dst[off] = ((sa * scale) & 0xFF000000) | (((sr * scale) & 0xFF000000) >>> 8)
| (((sg * scale) & 0xFF000000) >>> 16) | (((sb * scale) & 0xFF000000) >>> 24);
for (int x = 1; x < w; x++) {
int addPx = src[off + Math.min(x + r, w - 1)];
int remPx = src[off + Math.max(x - r - 1, 0)];
if (addPx == remPx) {
dst[off + x] = prev;
} else {
sa += (addPx >>> 24) - (remPx >>> 24);
sr += ((addPx >> 16) & 0xFF) - ((remPx >> 16) & 0xFF);
sg += ((addPx >> 8) & 0xFF) - ((remPx >> 8) & 0xFF);
sb += (addPx & 0xFF) - (remPx & 0xFF);
prev = dst[off + x] = ((sa * scale) & 0xFF000000) | (((sr * scale) & 0xFF000000) >>> 8)
| (((sg * scale) & 0xFF000000) >>> 16) | (((sb * scale) & 0xFF000000) >>> 24);
}
}
}
}
private static void boxBlurV(int[] src, int[] dst, int w, int h, int r) {
if (r <= 0) {
System.arraycopy(src, 0, dst, 0, src.length);
return;
}
int boxSize = r + r + 1;
int scale = (1 << 24) / boxSize;
for (int x = 0; x < w; x++) {
int sa = 0, sr = 0, sg = 0, sb = 0;
for (int i = -r; i <= r; i++) {
int px = src[Math.min(Math.max(i, 0), h - 1) * w + x];
sa += (px >>> 24);
sr += (px >> 16) & 0xFF;
sg += (px >> 8) & 0xFF;
sb += px & 0xFF;
}
int prev = dst[x] = ((sa * scale) & 0xFF000000) | (((sr * scale) & 0xFF000000) >>> 8)
| (((sg * scale) & 0xFF000000) >>> 16) | (((sb * scale) & 0xFF000000) >>> 24);
for (int y = 1; y < h; y++) {
int addPx = src[Math.min(y + r, h - 1) * w + x];
int remPx = src[Math.max(y - r - 1, 0) * w + x];
if (addPx == remPx) {
dst[y * w + x] = prev;
} else {
sa += (addPx >>> 24) - (remPx >>> 24);
sr += ((addPx >> 16) & 0xFF) - ((remPx >> 16) & 0xFF);
sg += ((addPx >> 8) & 0xFF) - ((remPx >> 8) & 0xFF);
sb += (addPx & 0xFF) - (remPx & 0xFF);
prev = dst[y * w + x] = ((sa * scale) & 0xFF000000) | (((sr * scale) & 0xFF000000) >>> 8)
| (((sg * scale) & 0xFF000000) >>> 16) | (((sb * scale) & 0xFF000000) >>> 24);
}
}
}
}
}