processing.js:
I used the processing.js library for my canvas drawing. This was my first project with processing.js, and in fact my first project drawing in JS. Processingjs works great! However, it's a little funky. The following are my impressions:
The first oddity is the way transformations work. Anybody is free to correct me on this (e-mail matt@pagefoundry.net) but it seems like transformations (scale, etc) are done globally. That is to say if I want to scale something, I set a global scale ratio, draw my object, and set it back. If you view the source for the above, that is how I scale the image. This set me up for quite the headache, as I describe later. Also you'll notice that when dragging a scaled image, the image does not "stick" to your mouse and will in fact move less (scaled down) or more (scaled up) than your mouse moves. This method, while perfectly valid and intuitive (works just like setting color in most drawing apis), it took some getting used to.
Next was the fact that you write processing.js scripts as their own applications. I'm a newbie to advanced javascript, Ajax, etc. so I don't fully understand the implications of this but briefly what this meant for me was that my first notion of how to pass the scaling/cropping information to PHP was impossible. To explain, what I wanted to do was use a form. I would have the thumbnail app update a set of hidden fields with the x, y offest and scaling factor. Once the user was happy with his cropping he would hit the submit button on the form which would post the information to the next page. I wanted to do this because I figured that would be the most common way to include this fucntionality (e.g. setting an avatar thumbnail while setting up a forum account, etc.). However, I found that I couldn't call regular javascript functions (i.e. document.hiddenfield.value = xpos;) from within the processing.js application. If anybody knows how to do this please let me know. The closest alternative I could find was to link directly to the next page using the link() method and sending the parameters via GET. This works just as well, of course, but may potentially pose problems for other practical uses.
Image.php:
The Image is resized by a PHP plugin called GD. To use these thumbnails you can do one of two things:
a. Right-click and save the image for use on your page--quick and dirty.
b. Use the image.php script (please put it on your own server to keep the load off mine :). Simply store the x, y, and s parameters (e.g. to your database) and to embed the image, use the tag from renderthumb.php:
<img src="image.php?image=Metaeye_lg.jpg&x=<?php echo GET['x'];?>&y=<?php echo GET['y'];?>&s=<?php echo GET['s'];?>" alt="image" />
(note: i had to remove the "$_" before GET so that PHP didn't get angry at me)
image.php returns the image itself, not an html page, so even though it is a script, the data returned has a jpg header. Nifty, huh?
I haven't installed imagemagick on my server, and you can feel free to use IM if you prefer. In fact you'll probably have a better time doing the resizing if IM has a more convenient resize function. Regardless, in GD the resize function works as follows:
imagecopyresampled(dst_img, src_img, dst_x, dst_y, src_x, src_y, dst_w, dst_h, src_w, src_h)
That is, its parameters are the output image, source image, coordinates of the upper left corner of the subimage in the output (if this is > 0,0 it will fill in the empty space with black), the coordinates of the same in the source, and the width, height of the destination and source image. This method of indirectly specifying scale would be fine in most cases, but the data coming from the cropping application was quite inconvenient. It took me a few re-thinks to figure out the formula for converting the x, y offset values to the subimage coordinats for GD.
The function I finally discovered was: $src_x = (100 - $_GET['x'] * $scl) / $scl;
First of all, we are interested in where the image intersects the corner of the gray area, not the origin. This 100-pixel padding must be taken into account, along with the scaling factor. In other words, if the image is scaled by half, the line x = 100 through the source image will actually appear as x = 50 in the scaled image. Conversely, if we lined the the left padding up with the line x = 50 in our scaled view, the real padding (which will be the data sent to image.php) will be 100.
Still with us? Good! Now that we've changed our coordinate system, we now have to divide by the scaling factor. If we've dragged our 50% scaled image 100 pixels to the left of the origin (which is now defined by the gray border, not the corner of the canvas), the highlighted are of the scaled image will be located 200 pixels in to the source image (which is twice the size).
If this is confusing don't worry, it was for me too. If it's not confusing at all then maybe I had a harder time than I should have had, but I'm sure we can all appreciate the situation where a seemingly difficult problem appears trivial once solved.
Upgrades for 2.0:
Obviously there is room for improvement! For the next iteration, I will fix the mouse "drift" issue, and maybe include a clever-er way to scale and/or rotate the image inspired by multi-touch pinch-zoom features. Also on the horizon: parameterize the canvas size and padding for easier integration. Also support for other image types (I've just hard-coded jpeg support for now, but any format supported by processing.js is theoreticaly possible). Hope you like my app!
Questions? coments? concerns? helpful hints to fix browser compatibility?
e-mail me: matt@pagefoundry.net