Skip to content

Instantly share code, notes, and snippets.

@Alanaktion
Created December 15, 2016 23:33
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Alanaktion/105863a8f1c45acbc0037b25088f6b56 to your computer and use it in GitHub Desktop.
Save Alanaktion/105863a8f1c45acbc0037b25088f6b56 to your computer and use it in GitHub Desktop.
Updated blur.php for PHP's gd library with full RGB
<?php
/**
* Blur values in both directions
* @param resource $src
* @param integer $radius
* @return resource
*/
function blur($src, $radius) {
return bVert(bHoriz($src, $radius), $radius);
}
/**
* Blur values horizontally
* @param resource $src
* @param integer $radius
* @return resource
*/
function bHoriz($src, $radius) {
$height = imagesy($src);
$width = imagesx($src);
$dest = imagecreatetruecolor($width, $height);
for ($y = 0; $y < $height; ++$y) {
for ($x = 0; $x < $width; ++$x) {
$r = 0;
$g = 0;
$b = 0;
for ($kx = -$radius; $kx <= $radius; ++$kx) {
if ($x + $kx >= 0 && $x + $kx < $width) {
$pixel = imagecolorat($src, $x + $kx, $y);
$r += ($pixel >> 16) & 0xFF;
$g += ($pixel >> 8) & 0xFF;
$b += $pixel & 0xFF;
}
}
$color = imagecolorallocate(
$dest,
$r / ($radius * 2 + 1),
$g / ($radius * 2 + 1),
$b / ($radius * 2 + 1)
);
imagesetpixel($dest, $x, $y, $color);
}
}
return $dest;
}
/**
* Blur values vertically
* @param resource $src
* @param integer $radius
* @return resource
*/
function bVert($src, $radius) {
$height = imagesy($src);
$width = imagesx($src);
$dest = imagecreatetruecolor($width, $height);
for ($x = 0; $x < $width; ++$x) {
for ($y = 0; $y < $height; ++$y) {
$r = 0;
$g = 0;
$b = 0;
for ($ky = -$radius; $ky <= $radius; ++$ky) {
if ($y + $ky >= 0 && $y + $ky < $height) {
$pixel = imagecolorat($src, $x, $y + $ky);
$r += ($pixel >> 16) & 0xFF;
$g += ($pixel >> 8) & 0xFF;
$b += $pixel & 0xFF;
}
}
$color = imagecolorallocate(
$dest,
$r / ($radius * 2 + 1),
$g / ($radius * 2 + 1),
$b / ($radius * 2 + 1)
);
imagesetpixel($dest, $x, $y, $color);
}
}
return $dest;
}
/**
* Blur values in both directions, optimized
* @param resource $img
* @param integer $radius
* @return resource
*/
function blurFast($src, $radius) {
return bVertFast(bHorizFast($src, $radius), $radius);
}
/**
* Blur values horizontally, reusing values where possible
* @param resource $src
* @param integer $radius
* @return resource
*/
function bHorizFast($src, $radius) {
$height = imagesy($src);
$width = imagesx($src);
$dest = imagecreatetruecolor($width, $height);
for ($y = 0; $y < $height; ++$y) {
$r = 0;
$g = 0;
$b = 0;
// Process entire window for first pixel
for ($x = 0; $x <= $radius; ++$x) {
$pixel = imagecolorat($src, $x, $y);
$r += ($pixel >> 16) & 0xFF;
$g += ($pixel >> 8) & 0xFF;
$b += $pixel & 0xFF;
}
$color = imagecolorallocate(
$dest,
$r / ($radius * 2 + 1),
$g / ($radius * 2 + 1),
$b / ($radius * 2 + 1)
);
imagesetpixel($dest, 0, $y, $color);
// Subsequent pixels just update window total
for ($x = 1; $x < $width; ++$x) {
// Subtract pixel leaving window
if ($x - $radius - 1 >= 0 && $x - $radius - 1 < $width) {
$pixel = imagecolorat($src, $x - $radius - 1, $y);
$r -= ($pixel >> 16) & 0xFF;
$g -= ($pixel >> 8) & 0xFF;
$b -= $pixel & 0xFF;
}
// Add pixel entering window
if ($x + $radius >= 0 && $x + $radius < $width) {
$pixel = imagecolorat($src, $x + $radius, $y);
$r += ($pixel >> 16) & 0xFF;
$g += ($pixel >> 8) & 0xFF;
$b += $pixel & 0xFF;
}
// Keep RGB values positive
if ($r < 0) {
$r = 0;
}
if ($g < 0) {
$g = 0;
}
if ($b < 0) {
$b = 0;
}
$color = imagecolorallocate(
$dest,
$r / ($radius * 2 + 1),
$g / ($radius * 2 + 1),
$b / ($radius * 2 + 1)
);
imagesetpixel($dest, $x, $y, $color);
}
}
return $dest;
}
/**
* Blur values vertically, reusing values where possible
* @param resource $src
* @param integer $radius
* @return resource
*/
function bVertFast($src, $radius) {
$height = imagesy($src);
$width = imagesx($src);
$dest = imagecreatetruecolor($width, $height);
for ($x = 0; $x < $width; ++$x) {
$r = 0;
$g = 0;
$b = 0;
// Process entire window for first pixel
for ($ky = 0; $ky <= $radius; ++$ky) {
$pixel = imagecolorat($src, $x, $ky);
$r += ($pixel >> 16) & 0xFF;
$g += ($pixel >> 8) & 0xFF;
$b += $pixel & 0xFF;
}
$color = imagecolorallocate(
$dest,
$r / ($radius * 2 + 1),
$g / ($radius * 2 + 1),
$b / ($radius * 2 + 1)
);
imagesetpixel($dest, $x, 0, $color);
// Subsequent pixels just update window total
for ($y = 1; $y < $height; ++$y) {
// Subtract pixel leaving window
if ($y - $radius - 1 >= 0 && $y - $radius - 1 < $height) {
$pixel = imagecolorat($src, $x, $y - $radius - 1);
$r -= ($pixel >> 16) & 0xFF;
$g -= ($pixel >> 8) & 0xFF;
$b -= $pixel & 0xFF;
}
// Add pixel entering window
if ($y + $radius >= 0 && $y + $radius < $height) {
$pixel = imagecolorat($src, $x, $y + $radius);
$r += ($pixel >> 16) & 0xFF;
$g += ($pixel >> 8) & 0xFF;
$b += $pixel & 0xFF;
}
// Keep RGB values positive
if ($r < 0) {
$r = 0;
}
if ($g < 0) {
$g = 0;
}
if ($b < 0) {
$b = 0;
}
$color = imagecolorallocate(
$dest,
$r / ($radius * 2 + 1),
$g / ($radius * 2 + 1),
$b / ($radius * 2 + 1)
);
imagesetpixel($dest, $x, $y, $color);
}
}
return $dest;
}
// Generate a test image
$src = imagecreatetruecolor(64, 64);
$w = imagecolorallocate($src, 255, 255, 255);
imagefill($src, 0, 0, $w);
$f1 = imagecolorallocate($src, 255, 0, 0);
imagefilledrectangle($src, 8, 8, 24, 24, $f1);
$f2 = imagecolorallocate($src, 0, 255, 0);
imagefilledrectangle($src, 32, 24, 48, 40, $f2);
$f3 = imagecolorallocate($src, 0, 0, 255);
imagefilledrectangle($src, 16, 40, 32, 56, $f3);
// Blur image
$dest = blurFast($src, 6);
// Blurring with a lower radius a second time smoothes the result nicely
$dest = blurFast($dest, 3);
header('Content-Type: image/png');
imagepng($dest);
/*echo "speed test, 10 ops...\n";
$s1 = microtime(true);
for($i=0; $i<9; $i++) {
blur($src, 32);
}
$s2 = microtime(true);
for($i=0; $i<9; $i++) {
blurFast($src, 32);
}
$s3 = microtime(true);
echo "base: " . round(($s2 - $s1) / 10, 4) . "s/op\n";
echo "fast: " . round(($s3 - $s2) / 10, 4) . "s/op\n";*/
@Alanaktion
Copy link
Author

PHP 7.0 performance on A10-5800K:

10 ops, 64x64, radius 5px
base: 0.1527s/op
fast: 0.0492s/op

10 ops, 256x256, radius 5px
base: 1.4526s/op
fast: 0.6051s/op

10 ops, 512x512, radius 5px
base: 8.6355s/op
fast: 2.5312s/op

3 ops, 1024x1024, radius 5px
base: 26.2904s/op
fast: 7.8087s/op

10 ops, 64x64, radius 16px
base: 0.3338s/op
fast: 0.0431s/op

10 ops, 256x256, radius 16px
base: 5.8077s/op
fast: 0.616s/op

10 ops, 64x64, radius 32px
base: 0.542s/op
fast: 0.0321s/op

10 ops, 256x256, radius 32px
base: 10.8564s/op
fast: 0.6088s/op

Basically, you'll want to use blurFast. It's much faster at O(size^2) vs blur's O(size^2 * 2 * blur radius)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment