php imagick add round corners to a jpg image

One might think this would be an easy task, and it is, unless you want it to look good. Unfortunately, imagick really only performs well when processing images with an alpha channel. I’ve found that PNG format works best. So, to add rounded corners to a jpg image with php’s imagick object, there are a few steps involved.

First, you have to create an imagick object from the source image. Then, convert it to PNG format before applying the rounded corners. Once in PNG format, the script applies the rounded corners. If you dont care about having the large size of a png file, you can simply output the the browser at this point, but, if you want the smaller file size of a jpg, you must take a few more steps.

After adding the rounded corners to your now PNG file, you must create another imagick image object of the same size as your original image, with your desired background color, of image type jpg. Then, you composite the rounded corner png over the new solid background color image you just created.

Now that you have your rounded corner image over the correct background and in jpg format, you can set the image compression type and quality. Once this is done, you are ready to output the resulting jpg to the browser. There is cache code commented out if you would like to save the resulting jpg to disk.

<?
 
$background = "#FFFFFF";
$imagefile = "/path/to/your/image.jpg";
$cornerradius_x = 50;
$cornerradius_y = 50;
$quality = 70;
 
//$cachefile = '/path/to/your/cachedimage.jpg';
//if (!file_exists($cachefile)) {
 
 
	try{
 
		$canvas = new Imagick($imagefile);
 
		$canvas->setImageFormat('PNG');
		$canvas->roundCorners($cornerradius_x,$cornerradius_y);
 
		$canvas2 = new Imagick();
		$canvas2->newimage($canvas->getImageWidth(),  $canvas->getImageHeight(), $background, 'jpg');
 
		$canvas2->compositeImage($canvas, imagick::COMPOSITE_OVER, 0, 0);
 
		$canvas2->setImageFormat('JPG');
		$canvas2->setImageCompression(Imagick::COMPRESSION_JPEG);
		$canvas2->setImageCompressionQuality($quality); 
 
		//$canvas2->writeImage($cachefile);
 
		header( "Content-Type: image/jpg" );
		echo $canvas2;
 
		$canvas->clear();
		$canvas->destroy();
		$canvas2->clear();
		$canvas2->destroy();	
 
	}catch(Exception $e){
		echo 'Error: ',  $e->getMessage(), "";
	}
 
/*
}else{
 
	$canvas = new imagick($cachefile);
	header("Content-Type: image/png");
	echo $canvas;
 
}
*/
 
?>

NOTE: your “cachefile.jpg” folder must be writable by the server.

php imagick rounded rectangle with border

In this post I will show you how to “draw” a rounded rectangle with a border. This will involve creating a new ImagickDraw object, then applying your draw settings to an Imagick object.

First, set up all your colors, image size, border width, and corner radius. After you have set all the input variables, the script will calculate the dimensions of the rectangle you are going to draw within the size of your image. To do this you must subtract the size of the border from the height and width of your desired image size, otherwise the rectangle will get cut off.

Then the script creates a new Imagick object, and applies your background color. The next step is to create your ImagickDraw object and set your fill color. If you have specified a border width greater than 0, the script will set the stroke color and width. Now the script sets up the rounded rectangle with all the settings you specified in the first step and what the script had calculated for the rectangle’s height and width.

Once the ImagickDraw object is all set up, the script will “draw” it onto the Imagick canvas previously created. And Voila! You have a rounded rectangle with a border. Uncomment the caching code if you want to have the script save the resulting image to disk.

<?
 
$background = "transparent";
$fillcolor = "#C5E3FF";
$bordercolor = "#226FB6";
$borderwidth = 2;
$cornerradius = 10;
 
$height = 300;
$width = 300;
 
//$cachefile = 'path/to/your/cachefile.png';
//if (!file_exists($cachefile)) {
 
	try{
 
		$rectwidth = ($borderwidth>0?($width-($borderwidth+1)):$width-1);
		$rectheight = ($borderwidth>0?($height-($borderwidth+1)):$height-1);
 
		$canvas = new Imagick();
		$canvas->newImage($width, $height, $background, "png" );
 
		$draw = new ImagickDraw();
		$draw->setFillColor($fillcolor);
		if($borderwidth>0){
			$draw->setStrokeColor($bordercolor);
			$draw->setStrokeWidth($borderwidth);
		}
		$draw->roundRectangle($borderwidth, $borderwidth, $rectheight, $rectwidth, $cornerradius, $cornerradius);
 
		$canvas->drawImage($draw);
 
		//$canvas->writeImage($cachefile);
 
		header("Content-Type: image/png");
		echo $canvas;
 
		$canvas->clear();
		$sanvas->destroy();
 
	}catch(Exception $e){
		echo 'Error: ',  $e->getMessage(), "";
	}
 
/*
}else{
 
	$canvas = new imagick($cachefile);
	header("Content-Type: image/png");
	echo $canvas;
 
}
*/
 
?>

NOTE: “$cachefile” path must be writable by the server if you want to save the image to disk.

php imagick simple vertical or horizontal gradient image

In this post I will show you how to create a simple vertical gradient with php’s imagick object.

First, set some variables for the colors you want. In this example we are creating a simple 2 color gradient on a opaque background. Once you have specified which colors you want for the background, the top of the gradient, and the bottom of the gradient, the script will create a new imagick object using the png image type with the background color you have specified.

Next the script creates another imagick object which will be our gradient. On this new blank canvas, the script uses imagick’s “PseudoImage” function which can take a gradient color as it’s background color when creating the image. The first color is the top color of the gradient and the second color is the bottom color of the gradient.

Once this gradient image is created, the script then composites the gradient image on top if the first image you created. I have tried many different constants for imagick’s “compositeImage” function, but the one we will use for this example is “imagick::COMPOSITE_OVER” as we are just combining the two images without any fancyness. You can experiment with other constants such as “imagick::COMPOSITE_SCREEN” or “imagick::COMPOSITE_MULTIPLY” which behave much like photoshop layers. You can find all the available composite constants here.

Once the two images are combined, you can spit out the resulting image to the browser. The image in this example is opaque, but if you were to set the background color to the string “transparent” and one of the two gradient images to “transparent” you would get a png image with a transparent gradient.

If you want a horizontal gradient, simply uncomment one of the two “rotateImage” lines. The first will rotate the image counterclockwise, placing the color specified for the “top” of the gradient to the right, the second will rotate the image clockwise, placing the “top” color to the left side of the image.

If you wanted to create a gradient that was at an arbitrary angle, you would have to first make the image larger than you wanted, change the second parameter of the “rotateImage” function to the angle you want, and then use the “cropImage” function to get the gradient inside the bounds of the rotated rectangle which was just rotated. It might take some trial and error to get your crop bounds right, but that’s the general concept.

<?
 
$background = "#FFFFFF";
$top = "#73AEE5";
$bottom = "#003C74";
 
//$cachefile = 'path/to/your/cachefile.png';
//if (!file_exists($cachefile)) {
 
	try{
 
		$canvas = new Imagick();
		$canvas->newImage(80, 80, $background, "png");
 
		$gradient = new Imagick();
		$gradient->newPseudoImage(80, 80, "gradient:".$top."-".$bottom);
		$canvas->compositeImage( $gradient, imagick::COMPOSITE_OVER, 0, 0 );
 
		//$canvas->rotateImage($background, 90);
		//$canvas->rotateImage($background, -90);
		//$canvas->rotateImage($background, 45);
 
		//$canvas->cropImage(height, width, x, y);
		//$canvas->cropImage(50, 50, 30, 30);
 
		header( "Content-Type: image/png" );
		echo $canvas;
 
		//$canvas->writeImage($cachefile);
 
		$canvas->clear();
		$canvas->destroy();
 
		$gradient->clear();
		$gradient->destroy();
 
	}catch(Exception $e){
		echo 'Error: ',  $e->getMessage(), "";
	}
 
/*
}else{
 
	$canvas = new Imagick($cachefile);
	header("Content-Type: image/png");
	echo $canvas;
 
}
*/
?>

NOTE: I have commented out the code which will cache the image, but you can uncomment it if you want to save CPU cycles =) “$cachefile” path must be writable by the server.

php imagick simple font rendering with automatic image canvas size

I have been working with the image imagick library for PHP (ImageMagick) for some time now. I figure its time to share what I have discovered and figured out. There is very little documentation on this amazing library for PHP, but hopefully my hours of trial and error can help those who are interested in using it. For my first post, I will share a script I wrote which will render a png image for text using a ttf font.

The script uses imagick’s ImagickDraw object to first load the font, set the font size, orientation (GRAVITY_CENTER in this example, but you could set it to GRAVITY_NORTHWEST, or GRAVITY_SOUTHEAST), and fill color. Then the script gets the proper canvas size for the Imagick image object before the image is created, so you don’t need to worry about having to “know” what size the image needs to be. I have found that the metrics ImagickDraw outputs is not always perfect, so I have added a bit of padding to the image size that is calculated.

Next, the script creates a new Imagick image object and sets it’s size to what was calculated in the first step. It then “draws” the font that was set up in the first step. Once this is done, the script sets the image format (“PNG” in this example, but it could be a gif, or jpg if you want). The script finally saves the cache file, then sets the content type header and outputs the image to the browser.

<?php
 
$fontpath = "path/to/your/fontfile.ttf";
if(!file_exists($fontpath)) die("Error!  Font file does not exist at '".$fontpath."'");
 
$cachefile = "path/to/your/cachefile.png";
$text = "ABCD abcd 1234";
$fillcolor = "#999999";
 
 
if (!file_exists($cachefile)) {
 
	try{
 
		$draw = new ImagickDraw();
		$draw->setFont($fontpath);
		$draw->setFontSize(20);
		$draw->setGravity(Imagick::GRAVITY_CENTER);
		$draw->setFillColor($fillcolor);
 
		$canvas = new Imagick();
 
		$metrics = $canvas->queryFontMetrics($draw,$text);
 
		$canvas->newImage($metrics['textWidth'], $metrics['textHeight'], "transparent", "png");
		$canvas->annotateImage($draw,0,0,0,$text);
 
		$canvas->setImageFormat('PNG');
		$canvas->writeImage($cachefile);
 
		header("Content-Type: image/png");
		echo $canvas;
 
		$canvas->clear();
		$canvas->destroy();
 
		$draw->clear();
		$draw->destroy();
 
	}catch(Exception $e){
		echo 'Error: ',  $e->getMessage(), "";
	}
 
} else {
 
	$canvas = new Imagick($cachefile);
	header("Content-Type: image/png");
	echo $canvas;
 
}
 
die();
 
?>

NOTES: Your cache file path must be writable by the server.

OSX Finder New File Contextual Menu Item Using Automator

I have always been annoyed that there is no “New File” contextual menu item in OS X’s Finder. I mean, common Apple, why no new file? Anyway, after looking around I found some packages that don’t work with Snow Leopard. So I decided to see what I could do with Automator. Low and behold, there are many options for writing “services” which appear in a the “Services” sub menu of the contextual menu for a folder. I found this simple shell script which creates an empty text document when saved in Automator as a “Service”. (NOTE: there is a better solution below…)

touch "$@/NewTextFile.txt"

I tried messing around with apple script to get a dialog input from the user for the new file’s extension (UPDATE: figured this out, see below), but got incredibly frustrated trying to turn the input folder into a string that could be run as a shell script with the a fore mentioned script. So I gave up and just made a few of the most common blank files I wanted.

Click Here to download a zip of the Automator Workflow files I created. Hopefully this helps someone who is as frustrated as I am with the lack of a “New File” button or contextual menu in OS X.

Please Note: that you must click on the actual folder (with the icon) to get the “Services” menu to appear in Finder’s contextual menu. The “Services” menu does not show when you click on the “background” of the folder in Finder. Annoying.

A Better Solution

UPDATE: Actually got this working the next day when a co-worker mentioned that the folder string i was looking for was called a POSIX (aka Unix) style folder path string. Anyway, I now have a script which is called from the contextual menu, which prompts you for a filename, then creates it in the folder you have clicked on! Yay!

To create this work flow, you can download the service workflow , then put it in the ~/Library/Services/ folder for your user. Alternatively, you can create your own new “Service” with Automator. Open a new workflow as a “Service”, then search for “applescript” in the actions library. Drag the “Run AppleScript” action into your workflow, select “files and folders” in the “service recieves” drop down, then paste this code into the action:

on run {input}
	display dialog "File Name?" default answer "blank.txt"
	set filename to the text returned of result
	set filepath to POSIX path of input
	do shell script "touch " & quoted form of filepath & filename
	return input
end run

Save the service workflow, and blamo, there it is in your contextual menu when clicking on a folder.

Yet another Prototype Colorpicker

And then there was another…..color….picker…. I have been dissatisfied with all the prototype color pickers out there, so when I found this jQuery colorpicker, I decided to port it to prototype. Yay!

It has lots of options. Examples/Demo available here, download available here. This color picker allows you to not only select your color, but it also enables the following features:

  • specify a preview element
  • specify an input element that’s not the actionable element (ie, you can specify an action element, like a clickable icon, and have the selected color applied to a hidden input)
  • allows for target input element and preview element’s background color to be updated on change of the new color in the picker, or not (only will update on the commit of the color)
  • ability to control hide color picker on commit color, or not
  • ability to customize onShow, onBeforeShow, onChange, onHide, onLoad, and onSubmit functions
  • HSB, RGB, and Hex numbers available
  • Custom Event type on the “show color picker target”, such as onClick or onFocus
  • has an “extra info” button which can allow you to put in a custom color palette, or anything else you might want to put into a color picker. Color palette code can be found at the demo page.
Select Your Color: #  

	var cp = new colorPicker('colorpicker',{
		color:'#008a20',
		previewElement:'colorpicker-preview',
		eventName: 'focus',
		onShow:function(picker){
			new Effect.Appear(picker.cp);
			return false;
		},
		onHide:function(picker){
			new Effect.Fade(picker.cp);
			return false;
		},
		origColor:'#000000',
		livePreview: true,
		hideOnSubmit:false,
		updateOnChange:true,
		flat: false,
		hasExtraInfo:true,
		extraInfo:function(picker){
			var colors = $A([
				  '000000', '993300', '333300', '003300', '003366', '000080', '333399', '333333',
				  '800000', 'FF6600', '808000', '008000', '008080', '0000FF', '666699', '808080',
				  'FF0000', 'FF9900', '99CC00', '339966', '33CCCC', '3366FF', '800080', '969696',
				  'FF00FF', 'FFCC00', 'FFFF00', '00FF00', '00FFFF', '00CCFF', '993366', 'C0C0C0',
				  'FF99CC', 'FFCC99', 'FFFF99', 'CCFFCC', 'CCFFFF', '99CCFF', 'CC99FF', 'FFFFFF'
			]);
 
			var div = Builder.node('DIV').setStyle({padding:'10px 12px'});
			colors.each(function(color){
				var div_inner = Builder.node('DIV').setStyle({backgroundColor:'#'+color,cursor:'pointer',width:'10px',height:'10px','float':'left',border:'2px solid #'+color,margin:'1px'});
				div.insert(div_inner);
				div_inner.observe('click',function(ev){picker.setColor(color);});
				div_inner.observe('mouseover',function(ev){ev.element().setStyle({border:'2px solid #000'});});
				div_inner.observe('mouseout',function(ev){ev.element().setStyle({border:'2px solid #'+color});});		
			});
			picker.extraInfo.update(div);
		}
	});

Textarea auto re-size function….

So I had to create an auto re-size function for textareas. I looked around on the net and found a few things, but nothing really did what i wanted…. so I wrote this quick and dirty function to determine the pixel height a textarea should be based on the number of new lines and length of the content in that textarea. You pass the function the id of the textarea and it returns a number which you should resize the textarea’s pixel height to. The function uses prototype element functions, but these could easily be ported to another framework or replaced with crossbrowser javascript code. I’m just too lazy…..you get the idea =)

 
sizeTextarea = function(textarea){ 
	textarea = $(textarea);
	var hard_lines = 0;
	var soft_lines = 0;
	var lines = 0;
	var str = textarea.value;
	var w = textarea.getWidth();
	var fs = parseInt(textarea.getStyle('font-size').replace('px',''));
	var lh = parseInt(textarea.getStyle('line-height').replace('px',''));
	var tmp_char_cnt = 0;
	var tmp_s = '';
	var sarr = str.split("\n");
	hard_lines = sarr.length;
	if(sarr.length>0){
		for(i=0;i<sarr.length;i++) {
			tmp_s = sarr[i].split('');
			tmp_char_cnt = (tmp_s.length*6)/w;
			if(tmp_char_cnt > 1){
				soft_lines += tmp_char_cnt;
			}
		}
	}
 
	lines = hard_lines+soft_lines+1;
	return (lines*lh); 
}

You may need to change this line:

tmp_char_cnt = (tmp_s.length*6)/w;

as I found the number 6 just seemed to work for me with the font size in my textarea…..good luck! I hope someone finds this useful….

String Functions for Javascript – trim, to camel case, to dashed, and to underscore

So I was messing with a little template builder, and decided to try and dynamically read and write CSS to elements on a page. This brought up the difference between JavaScript having camel case representations of the dashed css properties. Soooooo, I had to write little string functions to convert camel case to dashed strings and visa versa. Heres what I came up with:

Trim String

1
2
3
String.prototype.trim = function(){
	return this.replace(/^\s+|\s+$/g, "");
};

To Camel Case

1
2
3
String.prototype.toCamel = function(){
	return this.replace(/(\-[a-z])/g, function($1){return $1.toUpperCase().replace('-','');});
};

To Dashed from Camel Case

1
2
3
String.prototype.toDash = function(){
	return this.replace(/([A-Z])/g, function($1){return "-"+$1.toLowerCase();});
};

To Underscore from Camel Case

1
2
3
String.prototype.toUnderscore = function(){
	return this.replace(/([A-Z])/g, function($1){return "_"+$1.toLowerCase();});
};

Enjoy!

simplexml to array

Sometimes dealing with PHP’s simplexml object can be annoying, and you just wish it was a simple array. There are a ton of functions out there for that, but I’ve found this one works best =)

Source: http://www.php.net/manual/en/ref.simplexml.php

function simplexml2array($xml) {
	if (get_class($xml) == 'SimpleXMLElement') {
		$attributes = $xml->attributes();
		foreach($attributes as $k=>$v) {
			if ($v) $a[$k] = (string) $v;
		}
		$x = $xml;
		$xml = get_object_vars($xml);
	}
	if (is_array($xml)) {
		if (count($xml) == 0) return (string) $x; // for CDATA
		foreach($xml as $key=>$value) {
			$r[$key] = $this->simplexml2array($value);
		}
		if (isset($a)) $r['@attributes'] = $a;    // Attributes
		return $r;
	}
	return (string) $xml;
}

Polar Arc Animation Using Canvas and Prototype

This is a little class I created to use Canvas to create a polar arc animation. A while ago I came across a Polar Clock using canvas and I thought it would be cool to create a randomized background using the same concepts. I would link back to the original script I based this on, but it appears to have been taken down =/ My script randomizes the number of arcs, the width, radius, and length of each arc. Options allow you to change the colors of the gradient and the arc, as well as the animation frame rate and arc opacity. Try reloading the demo a few times to see the randomness in action.

Anyway, you can see a demo here, or download the script….

I thought about making it compatible use IE using exCanvas, but then I got lazy….

You can see I am using the script for my background on this site. I went a little further with it and made it customizable. If you click on the little color wheel icon in the top right, you can select a color you want the background to be, and it will automatically create a darker color and lighter color for the background gradient. Pretty neat!

Once you have downloaded the script, you initialize it like so:

1
2
3
4
5
6
7
8
9
10
11
12
13
document.observe("dom:loaded", function() { 
	var useCanvas = !!( typeof($('canvas').getContext) == 'function'); 
	if(useCanvas){
		var pc = new polar("canvas",{
			bgColorBtm:"#000000",
			bgColorTop:"#666666",
			arcColor:"#FFFFFF",
			frameRate:12,
			arcOpacity:0.05
		});
		pc.start();
	}
});