in native-filters/src/main/jni/filters/blur_filter.c [159:268]
static void BlurFilter_iterativeBoxBlur(
JNIEnv* env,
jclass clazz,
jobject bitmap,
jint iterations,
jint radius) {
UNUSED(clazz);
if (iterations <= 0 || iterations > BLUR_MAX_ITERATIONS) {
safe_throw_exception(env, "BlurFilter_iterativeBoxBlur: Iterations argument out of bounds");
return;
}
if (radius <= 0 || radius > BLUR_MAX_RADIUS) {
safe_throw_exception(env, "BlurFilter_iterativeBoxBlur: Blur radius argument out of bounds");
return;
}
AndroidBitmapInfo bitmapInfo;
int rc = AndroidBitmap_getInfo(env, bitmap, &bitmapInfo);
if (rc != ANDROID_BITMAP_RESULT_SUCCESS) {
safe_throw_exception(env, "BlurFilter_iterativeBoxBlur: Failed to get Bitmap info");
return;
}
if (bitmapInfo.format != ANDROID_BITMAP_FORMAT_RGBA_8888) {
safe_throw_exception(env, "BlurFilter_iterativeBoxBlur: Unexpected bitmap format");
return;
}
pixel_t* pixelPtr;
const int w = bitmapInfo.width;
const int h = bitmapInfo.height;
if (w > BITMAP_MAX_DIMENSION || h > BITMAP_MAX_DIMENSION) {
safe_throw_exception(env, "BlurFilter_iterativeBoxBlur: Bitmap dimensions too large");
return;
}
// locking pixels such that they will not get moved around during processing
rc = AndroidBitmap_lockPixels(env, bitmap, (void*) &pixelPtr);
if (rc != ANDROID_BITMAP_RESULT_SUCCESS) {
safe_throw_exception(env, "BlurFilter_iterativeBoxBlur: Failed to lock Bitmap pixels");
return;
}
// the information written to an output pixels `x` are from `[x-radius, x+radius]` (inclusive)
const int diameter = radius + 1 + radius;
// pre-compute division table: speed-up by factor 5(!)
uint8_t* div = (uint8_t*) malloc(256 * diameter * sizeof(uint8_t));
if (!div) {
AndroidBitmap_unlockPixels(env, bitmap);
safe_throw_exception(env, "BlurFilter_iterativeBoxBlur: Failed to allocate memory: div");
return;
}
// the following lines will fill-up at least the first `255 * diameter` entries with the mapping
// `div[x] = (x + r) / d` (i.e. division of x by d rounded to the nearest number).
uint8_t* ptr = div;
for (int r = 0; r <= radius; r++) {
*(ptr++) = 0;
}
for (int b = 1; b <= 255; b++) {
for (int d = 0; d < diameter; d++) {
*(ptr++) = b;
}
}
// temporary array for the output of the currently blurred row OR column
pixel_t* tempRowOrColumn = (pixel_t*) malloc(max(w, h) * sizeof(pixel_t));
if (!tempRowOrColumn) {
free(div);
AndroidBitmap_unlockPixels(env, bitmap);
safe_throw_exception(env, "BlurFilter_iterativeBoxBlur: Failed to allocate memory: tempRowOrColumn");
return;
}
for (int i = 0; i < iterations; i++) {
// blur rows one-by-one
for (int row = 0; row < h; row++) {
internalHorizontalBlur(pixelPtr, tempRowOrColumn, w, row, diameter, div);
// copy output row pixels back to bitmap
memcpy(pixelPtr + w * row, tempRowOrColumn, w * sizeof(pixel_t));
}
// blur columns one-by-one
for (int col = 0; col < w; col++) {
internalVerticalBlur(pixelPtr, tempRowOrColumn, w, h, col, diameter, div);
// copy output column pixels back to bitmap
pixel_t* ptr = pixelPtr + col;
for (int row = 0; row < h; row++) {
*ptr = tempRowOrColumn[row];
ptr += w;
}
}
}
free(tempRowOrColumn);
free(div);
rc = AndroidBitmap_unlockPixels(env, bitmap);
if (rc != ANDROID_BITMAP_RESULT_SUCCESS) {
safe_throw_exception(env, "BlurFilter_iterativeBoxBlur: Failed to unlock Bitmap pixels");
}
}