Showing posts with label Image Processing. Show all posts
Showing posts with label Image Processing. Show all posts

Friday, October 29, 2010

Use Imagick to create images with transparent canvas

update: just found an easier way to create a transparent canvas ;-)
so, you can pretty much ignore all these options listed below now




$im = new Imagick();
$im->newImage(100, 200, new ImagickPixel('transparent')); // use this predefined transparent color string
$im->setImageFormat('png32');
//$im->setImageFormat('gif');
//$im->setImageFormat('jpg'); // note that jpg does not support transparency, so you see a default black background



==== ignore the rest of the post ====



According to ImageMagick Examples canvas page, there are multiple ways to create a transparent canvas.



It is a pity that Imagick PHP library lacks enough documentation, so I had to try my luck with it. For example, I want to create a transparent background, then copy an image with transparency on top of it.




< ?php
try
{
$icon = new Imagick('play.png');



$im = new Imagick();


// put code here to create the transparent canvas, use options below



$im->compositeImage($icon, Imagick::COMPOSITE_COPY, 0, 0);

header('Content-Type: image/png');
echo $im;


}
catch (Exception $ex)
{
echo ‘Exception: ‘ . $ex->getMessage() . “\n”;
echo $ex->getTraceAsString();
}
?>



a) setImageOpacity

$im->newImage(200, 200, '#000000', 'png');
$im->setImageOpacity(0.0);



b) paintTransparentImage

$transparent = new ImagickPixel('#000000');
$im->newImage(200, 200, '#000000', 'png');
$im->paintTransparentImage($transparent, 0, 10);



c) matteFloodfillImage

$transparent = new ImagickPixel('#000000');
$im->newImage(200, 200, '#000000', 'png');
$im->setImageBorderColor($transparent);
$im->matteFloodfillImage(0.0, 0.0, $transparent, 200, 200);



d) thresholdImage

$im->newImage(200, 200, '#000000', 'png');
$im->thresholdImage(-1, Imagick::CHANNEL_ALPHA);



e) fxImage

$im->newImage(200, 200, '#000000', 'png');
$im = $im->fxImage('0', Imagick::CHANNEL_ALPHA);



For ImageMagick 6.2.9 12/17/07 Q16, Imagick 2.2.2RC1
a) does not work at all
e) gives the correct result, transparent background for play.png and transparent background for the whole image
others give transparent background for play.png, but black background for the whole image



For ImageMagick 6.4.9-6 2009-03-25 Q16, Imagick 2.2.2
a) b) gives transparent background for play.png and whole image
c) gives an exception
others give white background for play.png, black background for whole image



Images (note that you may want to save the images and open it in a viewer with non-white background to see the difference, especially the last two):




  1. Desired ouptput: transparent play button background and transparent canvas:
    desired output

  2. White play button background and black canvas
    white play bg, black canvas

  3. Transparent play button background and black canvas
    transparent play bg, black canvas

Transparent gif with smooth edge using Imagick

If you have a PNG with transparency and then you want to convert it to a GIF with transparency preserved. Since GIF has only 1-bit transparency, the edge will look jacky. However, if you know a color that will be used as solid background color or an average color for a gradient background, you can still achieve a smooth edge for the transparent gif. Here is how (assuming the original PNG is circle.png, the edge antialias color is RED):

<?php $red = new ImagickPixel(’red’); $transparent = new ImagickPixel(’transparent’); $im = new Imagick(’circle.png’); $im->setImageBorderColor($red); $im->frameImage($red, 0, 0, 0, 0); //or use $im->borderImage($red, 0.0, 0.0); $im->paintOpaqueImage($red, $transparent, 0.0); //replacing RED color with transparent color $im->setImageFormat(’gif’); header(’Content-Type: image/gif’); echo $im; ?>

 

ICO image processing with Imagick

Used to have an issue with Imagick when I convert ICO image into png. Somehow when I create the Imagick object from image content string, the final result is a crappy image. But when I create the Imagick object directly from a file, it works fine, with only the problem of a different size.



So, I filed a bug (http://pecl.php.net/bugs/bug.php?id=15701), but still got no resolution.



Got some time recently and did a bit research, found that the problem is with the ICO format: it may contain the same image with different sizes. So, when you convert the format directly, it will not work. It is similar in the case when you try to convert an animated GIF image into JPG or PNG, you get one of the frames instead of an animated image.



There are two solutions:




  1. use foreach($ico as $im) loop to get one image and just convert that image

  2. use flattenImages function before the format conversion



Note that after the format conversion, the image dimensions might be different from when you directly open the ICO image from a browser or image viewer. You probably need to resize the image manually.



The bug report has some code snippets if you want to try it out.

Creating shadows with ImageMagick

1. Shadows for an image (create a shadow using Imagick::shadowImage and overlay the original image on top)

<?php
/* Read the image into the object /
$im = new Imagick( ‘totoro.png’ );
$im->setImageFormat("png");

/ Make the image a little smaller, maintain aspect ratio /
$im->thumbnailImage( 200, null );

/ Clone the current object /
$shadow = $im->clone();

/ Set image background color to black (the color of the shadow) /
$shadow->setImageBackgroundColor( new ImagickPixel( ‘black’ ) );

/ Create the shadow /
$shadow->shadowImage( 80, 3, 5, 5 );

/ Imagick::shadowImage only creates the shadow. That is why the original image is composited over it /
$shadow->compositeImage( $im, Imagick::COMPOSITE_OVER, 0, 0 );
$content = $shadow->getImageBlob();

$shadow->destroy();
$im->destroy(); 

/ Display the image */
header( "Content-Type: image/jpeg" );
header(’Content-Length: ‘ . strlen($content)); 
echo $content;
?>

2. Shadow for text

Note that the trick for multi-line text is to insert a new line character (\n) in your string. You can also use the rotation degree to control the layout, but make sure you use the correct size text canvas image because when you queryFontMetrics, it is based on the horizontal layout.

<?php
// overlay the text
$draw = new ImagickDraw();
$draw->setFont(’Helvetica’);
$draw->setFontSize(13);
$draw->setFillColor(new ImagickPixel(’#B8B8B8′));

$text = "HELLO\nWORLD!"; // for multi-line text, insert new line char

$textcanvas = new Imagick();
$textprops = $textcanvas->queryFontMetrics($draw, $text, true); //you need to turn multiline on, the last param
$textw = intval($textprops[’textWidth’]);
$texth = intval($textprops[’textHeight’]);
$textcanvas->newImage($textw+20, $texth+20, new ImagickPixel(’transparent’)); //give 20 pixel spaces around the text
$textcanvas->setImageFormat(’png’);
$textcanvas->annotateImage($draw, 10, $texth, 0, $text);

// create shadow
$shadow = $textcanvas->clone();
$shadow->setImageBackgroundColor(new ImagickPixel(’black’));
$shadow->shadowImage(80, 3, 5, 5);
$shadow->compositeImage($textcanvas, Imagick::COMPOSITEOVER, 0, 0);
$shadoww = $shadow->getImageWidth();
$shadowh = $shadow->getImageHeight();

// overlay the shadowed text on the given background image
$bg = new Imagick(’background.png’);
$bg->setImageFormat(’png’);
$bgw = $bg->getImageWidth();
$bgh = $bg->getImageHeight();

//$bg->compositeImage($shadow, Imagick::COMPOSITEOVER, 0, ($bgh-$shadowh));
$bg->compositeImage($shadow, Imagick::COMPOSITEOVER, 0, 0);

// let’s show the image
$content = $bg->getImageBlob();
$textcanvas->destroy();
$shadow->destroy();
$bg->destroy();

header(’Content-Type: image/png’);
header(’Content-Length: ‘ . strlen($content));
echo $content;
?> 


 

Somehow, I still don’t know how to adjust the line spacing between multi-lines. I also found that ImagickDraw::setFontWeight did not work for me. Anyone knows how to fix those two issues? 

 

Reference:

1. Mikko’s blog (the best place for ImageMagick samples, who can beat the IM developer himself)