Reputation: 5256
I need a script which makes rounded transparent corners on supplied image. I've found one and it works good except the one thing: the applied corners do not look smooth. The imageantialias()
throws Fatal Error since PHP is running on Debian and re-compiling it is not an option.
The trick I've found to make those corners look smooth is resizing the image with imagecopyresampled()
as the following:
But here comes the problem: the corners of the result image (after step 5) are smooth, but not transparent. When sending to output the image after step 4 (i.e. before decreasing it's size) – everything's as it should be.
Here's the part of the code responsible for making corners rounded:
// $dest = image resource $q=10; // making everything 10x bigger $new_width=$width*$q; $new_height=$height*$q; $radius=$radius*$q; $magnified=imagecreatetruecolor($new_width, $new_height); imagecopyresampled($magnified, $dest, 0,0, 0,0, $new_width,$new_height, ($new_width/$q),($new_height/$q)); // picking the unique colour $found = false; while($found == false) { $r = rand(0, 255); $g = rand(0, 255); $b = rand(0, 255); if(imagecolorexact($magnified, $r, $g, $b) != (-1)) { $found = true; } } $colorcode = imagecolorallocate($magnified, $r, $g, $b); // drawing corners imagearc($magnified, $radius-1, $radius-1, $radius*2, $radius*2, 180, 270, $colorcode); imagefilltoborder($magnified, 0, 0, $colorcode, $colorcode); imagearc($magnified, $new_width-$radius, $radius-1, $radius*2, $radius*2, 270, 0, $colorcode); imagefilltoborder($magnified, $new_width-1, 0, $colorcode, $colorcode); imagearc($magnified, $radius-1, $new_height-$radius, $radius*2, $radius*2, 90, 180, $colorcode); imagefilltoborder($magnified, 0, $new_height-1, $colorcode, $colorcode); imagearc($magnified, $new_width-$radius, $new_height-$radius, $radius*2, $radius*2, 0, 90, $colorcode); imagefilltoborder($magnified, $new_width-1, $new_height-1, $colorcode, $colorcode); // making the unique colour transparent imagecolortransparent($magnified, $colorcode); // scaling down the enlarged image to it's original size // expecting corners to remain transparent imagecopyresampled($dest, $magnified, 0,0, 0,0, ($new_width/$q),($new_height/$q), $new_width,$new_height); // but they're not // sending $magnified to output for testing purposes $dest=$magnified; // outputting $dest as image/png
So as you can see, the problem occurs when enlarged image is being imagecopyresampled to it's original size. The transparent corners get filled with the $colorcode
colour. I've been playing with imagesavealpha()
and imagealphablending()
as advised, but no result.
Please help me to make this work.
P.S. This may be useful: when uploaded the large PNG to imgur.com it had it converted to JPG and as you can see all corners got filled with that very restored $colorcode.
P.S. Hope I won't get banned for overusing the word "enlargement" :)
Upvotes: 15
Views: 12106
Reputation: 4420
Improved code from @Wh1T3h4Ck5. This function take image and make round corners for it without waste memory. Also it must work faster on huge images. For example 1024*1024 image need 400MB temp image in original code. Now only 230 KB. (if you use radius 10 px).
Function take GD image object, radius in px and return GD image object. Currently it is same as original GD image object.
Function assume size of your image is bigger from radius. Exactly it need to be greater (or equal) from ($radius + 2)*2
on any side.
Also function set imagealphablending
to true
for this image. If you need save into png, do not forgot set imagesavealpha
to true
.
function roundCorners($source, $radius) {
$ws = imagesx($source);
$hs = imagesy($source);
$corner = $radius + 2;
$s = $corner*2;
$src = imagecreatetruecolor($s, $s);
imagecopy($src, $source, 0, 0, 0, 0, $corner, $corner);
imagecopy($src, $source, $corner, 0, $ws - $corner, 0, $corner, $corner);
imagecopy($src, $source, $corner, $corner, $ws - $corner, $hs - $corner, $corner, $corner);
imagecopy($src, $source, 0, $corner, 0, $hs - $corner, $corner, $corner);
$q = 8; # change this if you want
$radius *= $q;
# find unique color
do {
$r = rand(0, 255);
$g = rand(0, 255);
$b = rand(0, 255);
} while (imagecolorexact($src, $r, $g, $b) < 0);
$ns = $s * $q;
$img = imagecreatetruecolor($ns, $ns);
$alphacolor = imagecolorallocatealpha($img, $r, $g, $b, 127);
imagealphablending($img, false);
imagefilledrectangle($img, 0, 0, $ns, $ns, $alphacolor);
imagefill($img, 0, 0, $alphacolor);
imagecopyresampled($img, $src, 0, 0, 0, 0, $ns, $ns, $s, $s);
imagedestroy($src);
imagearc($img, $radius - 1, $radius - 1, $radius * 2, $radius * 2, 180, 270, $alphacolor);
imagefilltoborder($img, 0, 0, $alphacolor, $alphacolor);
imagearc($img, $ns - $radius, $radius - 1, $radius * 2, $radius * 2, 270, 0, $alphacolor);
imagefilltoborder($img, $ns - 1, 0, $alphacolor, $alphacolor);
imagearc($img, $radius - 1, $ns - $radius, $radius * 2, $radius * 2, 90, 180, $alphacolor);
imagefilltoborder($img, 0, $ns - 1, $alphacolor, $alphacolor);
imagearc($img, $ns - $radius, $ns - $radius, $radius * 2, $radius * 2, 0, 90, $alphacolor);
imagefilltoborder($img, $ns - 1, $ns - 1, $alphacolor, $alphacolor);
imagealphablending($img, true);
imagecolortransparent($img, $alphacolor);
# resize image down
$dest = imagecreatetruecolor($s, $s);
imagealphablending($dest, false);
imagefilledrectangle($dest, 0, 0, $s, $s, $alphacolor);
imagecopyresampled($dest, $img, 0, 0, 0, 0, $s, $s, $ns, $ns);
imagedestroy($img);
# output image
imagealphablending($source, false);
imagecopy($source, $dest, 0, 0, 0, 0, $corner, $corner);
imagecopy($source, $dest, $ws - $corner, 0, $corner, 0, $corner, $corner);
imagecopy($source, $dest, $ws - $corner, $hs - $corner, $corner, $corner, $corner, $corner);
imagecopy($source, $dest, 0, $hs - $corner, 0, $corner, $corner, $corner);
imagealphablending($source, true);
imagedestroy($dest);
return $source;
}
Upvotes: 8
Reputation: 71
...
/* rounded corner */
$radius = 20;
// find ghost color
do
{
$r = rand(0, 255);
$g = rand(0, 255);
$b = rand(0, 255);
} while (imagecolorexact($img_resource, $r, $g, $b) < 0);
$ghost_color = imagecolorallocate($img_resource, $r, $g, $b);
imagearc($img_resource, $radius-1, $radius-1, $radius*2, $radius*2, 180, 270, $ghost_color);
imagefilltoborder($img_resource, 0, 0, $ghost_color, $ghost_color);
imagearc($img_resource, $img_width-$radius, $radius-1, $radius*2, $radius*2, 270, 0, $ghost_color);
imagefilltoborder($img_resource, $img_width-1, 0, $ghost_color, $ghost_color);
imagearc($img_resource, $radius-1, $img_height-$radius, $radius*2, $radius*2, 90, 180, $ghost_color);
imagefilltoborder($img_resource, 0, $img_height-1, $ghost_color, $ghost_color);
imagearc($img_resource, $img_width-$radius, $img_height-$radius, $radius*2, $radius*2, 0, 90, $ghost_color);
imagefilltoborder($img_resource, $img_width-1, $img_height-1, $ghost_color, $ghost_color);
imagecolortransparent($img_resource, $ghost_color);
...
try this one ...
Upvotes: 2
Reputation: 8509
After couple of hours of testing and kicking my head against the wall, I think I've found solution. Problem was about allocating transparent color using imagecolorallocate()
. I did not get it at first sight. It was totally wrong approach. However, imagecolorallocatealpha()
has helped me alot.
Also, alpha blending must be off before saving alpha channel on working layer. However, it must be done right after creating blank true-color image, like
$im = imagecreatetruecolor($w, $h);
$alphacolor = imagecolorallocatealpha($img, $r, $g, $b, 127);
imagealphablending($im, false);
imagesavealpha($im, true);
This code is a key for getting smooth corners in transparent area after resize-down.
After all, I've wrote this function
function imageCreateCorners($sourceImageFile, $radius) {
# function body
}
I've tested it with couple of images and it returned image with smooth corners for every bg color.
imagepng(imageCreateCorners('jan_vesely_and_james_gist.jpg', 9), 'test.png');
Original image
It finally returns fully transparent alpha channel so you can use that image on every background you want.
I almost forgot to post function code :)
function imageCreateCorners($sourceImageFile, $radius) {
# test source image
if (file_exists($sourceImageFile)) {
$res = is_array($info = getimagesize($sourceImageFile));
}
else $res = false;
# open image
if ($res) {
$w = $info[0];
$h = $info[1];
switch ($info['mime']) {
case 'image/jpeg': $src = imagecreatefromjpeg($sourceImageFile);
break;
case 'image/gif': $src = imagecreatefromgif($sourceImageFile);
break;
case 'image/png': $src = imagecreatefrompng($sourceImageFile);
break;
default:
$res = false;
}
}
# create corners
if ($res) {
$q = 10; # change this if you want
$radius *= $q;
# find unique color
do {
$r = rand(0, 255);
$g = rand(0, 255);
$b = rand(0, 255);
}
while (imagecolorexact($src, $r, $g, $b) < 0);
$nw = $w*$q;
$nh = $h*$q;
$img = imagecreatetruecolor($nw, $nh);
$alphacolor = imagecolorallocatealpha($img, $r, $g, $b, 127);
imagealphablending($img, false);
imagesavealpha($img, true);
imagefilledrectangle($img, 0, 0, $nw, $nh, $alphacolor);
imagefill($img, 0, 0, $alphacolor);
imagecopyresampled($img, $src, 0, 0, 0, 0, $nw, $nh, $w, $h);
imagearc($img, $radius-1, $radius-1, $radius*2, $radius*2, 180, 270, $alphacolor);
imagefilltoborder($img, 0, 0, $alphacolor, $alphacolor);
imagearc($img, $nw-$radius, $radius-1, $radius*2, $radius*2, 270, 0, $alphacolor);
imagefilltoborder($img, $nw-1, 0, $alphacolor, $alphacolor);
imagearc($img, $radius-1, $nh-$radius, $radius*2, $radius*2, 90, 180, $alphacolor);
imagefilltoborder($img, 0, $nh-1, $alphacolor, $alphacolor);
imagearc($img, $nw-$radius, $nh-$radius, $radius*2, $radius*2, 0, 90, $alphacolor);
imagefilltoborder($img, $nw-1, $nh-1, $alphacolor, $alphacolor);
imagealphablending($img, true);
imagecolortransparent($img, $alphacolor);
# resize image down
$dest = imagecreatetruecolor($w, $h);
imagealphablending($dest, false);
imagesavealpha($dest, true);
imagefilledrectangle($dest, 0, 0, $w, $h, $alphacolor);
imagecopyresampled($dest, $img, 0, 0, 0, 0, $w, $h, $nw, $nh);
# output image
$res = $dest;
imagedestroy($src);
imagedestroy($img);
}
return $res;
}
Function returns GD object or false.
Function works with solid JPEG, GIF and PNG images. Also, it works great with transparent PNGs and GIFs.
Upvotes: 26