Skip to content

Commit

Permalink
FEAT: new blur native function for fast Gaussian blur of images
Browse files Browse the repository at this point in the history
Based on Ivan Kuckir's Fastest Gaussian blur implementation algorithm from article: http://blog.ivank.net/fastest-gaussian-blur.html

Usage example:
```
image: load %some-image.jpg
blur image 15                 ;@@ image is modified!
save %blured-image.png image

```
  • Loading branch information
Oldes committed Jun 29, 2021
1 parent 23e2b46 commit c756498
Show file tree
Hide file tree
Showing 6 changed files with 259 additions and 2 deletions.
3 changes: 2 additions & 1 deletion make/rebol3.nest
Original file line number Diff line number Diff line change
Expand Up @@ -313,11 +313,12 @@ config: [ ;- this is list of configuration (optional) defines
;- image related natives and codecs:
include-image-natives: [
; image related native functions like:
; `hsv-to-rgb`, `rgb-to-hsv`, `tint`, `resize`, `premultiply`
; `hsv-to-rgb`, `rgb-to-hsv`, `tint`, `resize`, `premultiply`, `blur`
; on Windows also: `image` as common entry to os image codec (if enabled)
config: INCLUDE_IMAGE_NATIVES
core-files: %core/n-image.c
core-files: %core/u-image-resize.c
core-files: %core/u-image-blur.c
]

include-native-bmp-codec: [config: INCLUDE_BMP_CODEC core-files: %core/u-bmp.c]
Expand Down
22 changes: 21 additions & 1 deletion src/core/n-image.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
** REBOL [R3] Language Interpreter and Run-time Environment
**
** Copyright 2012 REBOL Technologies
** Copyright 2012-2019 Rebol Open Source Contributors
** Copyright 2012-2021 Rebol Open Source Contributors
** REBOL is a trademark of REBOL Technologies
**
** Licensed under the Apache License, Version 2.0 (the "License");
Expand Down Expand Up @@ -34,6 +34,7 @@

#include "reb-codec.h"
#include "sys-magick.h" // used for `resize` native
#include "sys-blur.h" // used for `blur` native
#if defined(TO_WINDOWS) && defined(INCLUDE_IMAGE_OS_CODEC)
#include "winerror.h" // used for WINCODEC_ERR_COMPONENTNOTFOUND
#endif
Expand Down Expand Up @@ -339,6 +340,25 @@ typedef struct REBCLR {
return R_ARG1;
}

/***********************************************************************
**
*/ REBNATIVE(blur)
/*
// blur: native [
// "Blur (Gaussian) given image"
// image [image!] "Image to blur (modified)"
// radius [number!] "Blur amount"
// ]
***********************************************************************/
{
REBVAL* val_img = D_ARG(1);
REBVAL* val_rad = D_ARG(2);
REBINT radius = IS_INTEGER(val_rad) ? VAL_INT32(val_rad) : (REBINT)round(VAL_DECIMAL(val_rad));

BlurImage(VAL_SERIES(val_img), radius);
return R_ARG1;
}

/***********************************************************************
**
*/ REBNATIVE(image)
Expand Down
203 changes: 203 additions & 0 deletions src/core/u-image-blur.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
/***********************************************************************
**
** REBOL [R3] Language Interpreter and Run-time Environment
**
** Copyright 2012 REBOL Technologies
** Copyright 2012-2021 Rebol Open Source Developers
** REBOL is a trademark of REBOL Technologies
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
**
************************************************************************
**
** Module: u-image-blur.c
** Summary: image blur
** Section: utility
** Author: Oldes
**
** Note:
** Based on Ivan Kuckir's Fastest Gaussian blur implementation algorithm
** from article: http://blog.ivank.net/fastest-gaussian-blur.html and
** SilverWin's C port of it: https://github.com/SilverWin/blur_lib
**
***********************************************************************/

#include "sys-core.h"

#ifdef INCLUDE_IMAGE_NATIVES
#include "sys-blur.h"

static void boxes_for_gauss(REBDEC sigma, REBINT sizes[3])
{
REBDEC wIdeal;
REBINT wl;
REBINT wu;
REBDEC mIdeal;
REBINT m;
REBINT n = 3;

wIdeal = sqrt((REBDEC)(12 * sigma * sigma / n) + 1); /* Ideal averaging filter width */
wl = (REBINT)floor(wIdeal);
if (wl % 2 == 0)
wl--;
wu = wl + 2;

mIdeal = (12 * sigma * sigma - (REBDEC)wl * wl * n - 4.0 * n * wl - 3.0 * n) / (-4.0 * wl - 4);
m = (REBINT)round(mIdeal);

for (REBINT i = 0; i < n; i++)
{
if (i < m)
{
sizes[i] = wl;
}
else
{
sizes[i] = wu;
}
}
}

static void box_blur_H(REBYTE *scl, REBYTE*tcl, REBINT w, REBINT h, REBINT r, REBINT bpp)
{
for (REBINT i = 0; i < h; i++)
{
for (REBINT k = 0; k < bpp; k++)
{
REBINT ti = i * w * bpp + k;
REBINT li = ti;
REBINT ri = ti + r * bpp;
REBINT fv = scl[li];
REBINT lv = scl[ti + (w - 1) * bpp];
REBINT val = (r + 1) * fv;
for (REBINT j = 0; j < r; j++)
{
val += scl[ti + (j * bpp)];
}
for (REBINT j = 0; j <= r; j++)
{
val += scl[ri] - fv;
tcl[ti] = (REBYTE)round(val / (r + r + 1));
ri += bpp;
ti += bpp;
}
for (REBINT j = r + 1; j < (w - r); j++)
{
val += scl[ri] - scl[li];
tcl[ti] = (REBYTE)round(val / (r + r + 1));
li += bpp;
ri += bpp;
ti += bpp;
}
for (REBINT j = w - r; j < w; j++)
{
val += lv - scl[li];
tcl[ti] = (REBYTE)round(val / (r + r + 1));
li += bpp;
ti += bpp;
}
}
}
}

static void box_blur_T(REBYTE*scl, REBYTE*tcl, REBINT w, REBINT h, REBINT r, REBINT bpp)
{
REBINT i, j, k, ti, li, ri, fv, lv, val;
for (i = 0; i < w; i++)
{
for (k = 0; k < bpp; k++)
{
ti = i * bpp + k;
li = ti;
ri = ti + r * w * bpp;
fv = scl[ti];
lv = scl[ti + w * (h - 1) * bpp];
val = (r + 1) * fv;
for (j = 0; j < r; j++)
{
val += scl[ti + j * w * bpp];
}
for (j = 0; j <= r; j++)
{
val += scl[ri] - fv;
tcl[ti] = (REBYTE)round(val / (r + r + 1));
ri += w * bpp;
ti += w * bpp;
}
for (j = r + 1; j < (h - r); j++)
{
val += scl[ri] - scl[li];
tcl[ti] = (REBYTE)round(val / (r + r + 1));
li += w * bpp;
ri += w * bpp;
ti += w * bpp;
}
for (j = h - r; j < h; j++)
{
val += lv - scl[li];
tcl[ti] = (REBYTE)round(val / (r + r + 1));
li += w * bpp;
ti += w * bpp;
}
}
}
}

static void box_blur(REBYTE*scl, REBYTE*tcl, REBINT w, REBINT h, REBINT r, REBINT bpp)
{
for (REBINT i = 0; i < (h * w * bpp); i++)
{
tcl[i] = scl[i];
}
box_blur_H(tcl, scl, w, h, r, bpp);
box_blur_T(scl, tcl, w, h, r, bpp);
}

void fast_gauss_blur(REBYTE*scl, REBYTE*tcl, REBINT w, REBINT h, REBINT r, REBINT bpp)
{
REBINT bxs[3];
boxes_for_gauss(r, bxs);
box_blur(scl, tcl, w, h, (bxs[0] - 1) / 2, bpp);
box_blur(tcl, scl, w, h, (bxs[1] - 1) / 2, bpp);
box_blur(scl, tcl, w, h, (bxs[2] - 1) / 2, bpp);
// result would be in tcl, so copy it back to source, as it is modified anyway
for (REBINT i = 0; i < (h * w * bpp); i++)
{
scl[i] = tcl[i];
}
}

void BlurImage(REBSER *image, REBINT radius)
{
REBSER *temp_image;

// Validate input.
if ( image == NULL
|| (IMG_WIDE(image) == 0UL)
|| (IMG_HIGH(image) == 0UL)
|| radius <= 0
) return;

radius = MIN(radius, IMG_WIDE(image) / 2);
radius = MIN(radius, IMG_HIGH(image) / 2);

// Prepare temporary image.

temp_image = Make_Image(IMG_WIDE(image), IMG_HIGH(image), TRUE);

// Resize image.

fast_gauss_blur(IMG_DATA(image), IMG_DATA(temp_image), IMG_WIDE(image), IMG_HIGH(image), radius, 4);
}

#endif // INCLUDE_IMAGE_NATIVES
6 changes: 6 additions & 0 deletions src/include/sys-blur.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#ifndef __INCLUDE_BLUR_H__
#define __INCLUDE_BLUR_H__

void BlurImage(REBSER *image, REBINT radius);

#endif
Binary file added src/tests/units/files/flower.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
27 changes: 27 additions & 0 deletions src/tests/units/image-test.r3
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,33 @@ FFFFFFDC1616212121212121
===end-group===


===start-group=== "BLUR"
if value? 'blur [
--test-- "blur"
i: load %units/files/flower.png
c: checksum to binary! i 'crc32
t: copy i
--assert all [
image? blur t 0
c = checksum to binary! t 'crc32
]
--assert all [
image? blur t 5
-1700743341 = checksum to binary! t 'crc32
]
--assert all [
image? blur t 5
-583506697 = checksum to binary! t 'crc32
]
--assert all [
image? blur i 100000
1523895462 = checksum to binary! i 'crc32
]
t: i: none
]
===end-group===


===start-group=== "Save/load image"
if find codecs 'png [
--test-- "save/load PNG"
Expand Down

0 comments on commit c756498

Please # to comment.