Processing the crop
Initially I was frustrated with the data that was being sent. I knew the center of the image in relation to this 200x200 pixel canvas and its width... but what could I do with that. Well I could just recreate what I saw in the GUI. I needed to create a 200x200 pixel image first, place my original avatar resized (and resampled) at the precise coordinates and then cut out the center most 80x80 pixels to become the final avatar image.
If you note in our template above for cropSuccess.php we submit our form back to the crop action. Let's look at the action:
public function executeCrop()
{
if ($this->getRequestParameter('file')&&$this->getRequestParameter('width')) { // we are saving our cropped image
// Load the original avatar into a GD image so we can manipulate it with GD
$o_filename = $this->getRequestParameter('file'); // we'll use this to find the file on our system
$o_filename = sfConfig::get('sf_root_dir').'/web' . $o_filename;
$o_im = @imagecreatetruecolor(80, 80) or die("Cannot Initialize new GD image stream");
$o_imagetype = exif_imagetype($o_filename); // is this gif/jpeg/png
// appropriately create the GD image
switch ($o_imagetype) {
case 1: // gif
$o_im = imagecreatefromgif($o_filename);
break;
case 2: // jpeg
$o_im = imagecreatefromjpeg($o_filename);
break;
case 3: // png
$o_im = imagecreatefrompng($o_filename);
break;
}
// Let's create our canvas
$im = @imagecreatetruecolor(200, 200) or die("Cannot Initialize new GD image stream");
imagecolortransparent ( $im, 127 ); // set the transparency color to 127
imagefilledrectangle( $im, 0, 0, 200, 200, 127 ); // fill the canvas with a transparent rectangle
// let's get the new dimension for our image
$new_width = $this->getRequestParameter('width');
$o_width = imageSX($o_im);
$o_height = imageSY($o_im);
$new_height = $o_height/$o_width * $new_width;
// we place the image at the xy coordinate and then shift it so that the image is now centered at the xy coordinate
$x = $this->getRequestParameter('x') - $new_width/2;
$y = $this->getRequestParameter('y') - $new_height/2;
// copy the original image resized and resampled onto the canvas
imagecopyresampled($im,$o_im,$x,$y,0,0,$new_width,$new_height,$o_width,$o_height);
imagedestroy($o_im);
// $final will be our final image, we will chop $im and take out the 80x80 center
$final = @imagecreatetruecolor(80, 80) or die("Cannot Initialize new GD image stream");
imagecolortransparent ( $final, 127 ); // maintain transparency
//copy the center of our original image and store it here
imagecopyresampled ( $final, $im, 0, 0, 60, 60, 80, 80, 80, 80 );
imagedestroy($im);
//save our new user pic
$p = new Userpic();
$p->setUser($this->getUser()->getUser());
$p->setGD2($final);
$p->save();
imagedestroy($final);
$this->userpic = $p;
return "Finished";
}
$this->getResponse()->addJavascript("dom-drag");
$this->getResponse()->addJavascript('/sf/js/prototype/prototype');
$this->getResponse()->addJavascript('/sf/js/prototype/effects');
$this->image = '/images/userpics/originals/' . $this->getRequestParameter('file');
}
It's doing exactly what the paragraph above explains when the image dimensions are given. The code is well commented so it should be easy enough to follow.
[GD image functions in PHP][gd2php] are fairly robust and can help you do a lot of tricks with image data. Note the code to save the image, we'll cover it in detail soon.
The Model
$p = new Userpic();
$p->setUser($this->getUser()->getUser());
$p->setGD2($final);
$p->save();
First some clarification the second line. myUser::getUser() gets the User object associated with the currently logged in user. The third line, however, is where the magic happens. Before we look at it, let's have a quick look at our model:
userpic:
_attributes: { phpName: Userpic }
id:
user_id:
image: blob
thumb: blob
created_at:
updated_at:
We have an image attribute and a thumb property to our Userpic object. This is where we store PNG versions of each icon and their 16x16 thumbnails respectively. We do this in Userpic::setGD2():
public function setGD2($gd2_image)
{
//convert to PNG
ob_start();
imagepng($gd2_image);
$png = ob_get_clean();
//save 16x16
$gd2_tn = @imagecreatetruecolor(16, 16) or die("Cannot Initialize new GD image stream");
imagealphablending( $gd2_tn, true );
imagecolortransparent ( $gd2_tn, 127 );
imagecopyresampled ( $gd2_tn, $gd2_image, 0, 0, 0, 0, 16, 16, 80, 80 );
ob_start();
imagepng($gd2_tn);
$tn = ob_get_clean();
$this->setImage($png);
$this->setThumb($tn);
}
We capture the output of the full size PNG, then we scale it again and capture the output of the thumbnail and set them.
Conclusion
When it comes to web apps, having a relatively simple GUI for people to resize images can go a long way in terms of adoption rate of avatars and custom user pictures by non technical users.
Enjoy, and if you found this useful (or better implemented it) let me know.
Pages: 1 2



People who dont have exif turned on in PHP can use this:
Just a note,
there is a small bug
as things currently are, you must move the slider (even if you are just moving it to the zero position), otherwise the image will not be cropped at the correct correctly
to fix this, add the following line into the SetupAva function
ava_width.value = new_w;apart from that.. great code, very helpful! EXACTLY what i was looking for
Helper Guy,
Thanks for the help, I edited your original post to preserve underscores… this site uses markdown which likes to turn underscores into italics… I’ll need to make the editing form more clear.
-d
Transparency doesn’t work properly, even with the default image you are using
any idea how to fix that?
Are you using Firefox or IE?
If you’re using Firefox, it should work.
If you’re using IE then there is an issue with IE’s lack of support for alpha transparncies.
Luckily there is a PNG transparency fix that’ll use some IE specific magic to make it work. Hope that helps.
I meant the PHP GD image stuff
it creates an image with a black background, rather than transparent
Unfortunately my knowledge of GD is limited, when I’ve used this cropper in environments running PHP5 with GD2 my transparencies worked.
Looking back at the code:
We already initialize the canvas with a 200×200 rectangle that has the same color as the transparent pixel. So the code does what I expect it to on my end, beyond that I’m not sure what to say.
alright thanks for your help, it behaves differently for me for some reason.. i’m looking into the imagecopypallete function
if i get it working i’ll post the solution here
Very Nice Job, I’ve been looking for something like this for a while. Though I can’t seem to get it to work fully, would anyone have a working version they could zip to me. It would be greatly appreciated.
vjz@hotmail.com
Hey Vince,
Where are you getting stuck?
Unfortunately I don’t have anything zippable (code that’s used in closed-source production projects) at the moment, but I’m sure it can be debugged.
Hi,
this is the very best at least the more elegant cropping solution I have ever seen on web. Far. Bravo !
Unfortunatly my poor english and programming knowledge don’t give me the opportunity to make it working fine for my users.
The link to dom-drag.js is dead but I could get it using Coogle search (dom-drag.js download).
The only way I found to get mac design images was to copy them from demo. I think they are only two images (handle.png and slider_back.png), right ?
If you could put on line a full “kit” to download with the differents files (php, JS, CSS) as someone already asked for I believe, it would be great.
Best regards
Hi Dave,
Thanks so much for sharing this example. I just got done implementing it in C#. Let’s just say I’m pretty stoked for figure it all out.
I did find one bug in the javascript “function setupAva”
The problem occurs when a user uploads a landscape image (width is greater than height), and does not use the handle.onDrag . If they don’t the ava_width returns 80, which will throw off the proportion.
So what I did to fix this bug was set the was to set: avawidth.value = neww;
Here’s a portion of the function setupAva that shows where I’ve added “avawidth.value = neww;”
var ratio = (starth / startw); var newh; var neww; if (ratio > 1) { neww = 80; newh = (80starth)/startw; } else { newh = 80; neww = (80startw)/starth; }
Anyway, thanks again and hopefully this helps someone.
omg, i’m crazing with it
here’s my problem
http://www.sitepoint.com/forums/showthread.php?t=475618
i can’t create image ok with 48×48 pixels
I passed 2 days trying to change this… I don’t want 80×80, I want 160×160, nothing happen…
I did it! imagecopyresampled ( $final, $im, 0, 0, 17, 17, 160, 160, 160, 160 ); 17 = Margin of the overlay, sure!
Could you please make a archive with the full functional code? I’m really having a hard time setting this up.
Can you please upload the corresponding php file(s)? Or am I missing something? I really digg your script. There are a couple others around, but I like the idea of the interface.
Just for others to be up to date of what is around and works in most browsers:
http://199.199.212.26/demo/ http://mondaybynoon.com/examples/imagecropresize/
Hi, thank you very much autor for this work. Very introsting. But this havn’t been working in IE6.
. Problem in transparency for “”. Maybe somebody solved this proble.
Problem in transparency for “ava_overlay”
Because there’s some demand out there for a packaged version of this, I’ve made one! My package wraps this in a class that isn’t symfony dependent.
http://www.tanabi.com/projects/cropper