Many designers (and the people that hire them) don't realize or appreciate what happens on the development side of their web projects. Tweaking things a pixel this way or that for them is a matter of dragging it around in Photoshop until it looks good. They then hand it off to a developer expecting it's done, when in reality we not only need to do the same thing they did (at least in terms of positioning, if not aesthetics), but we have to do it by typing in code that they're typically completely incapable of producing themselves. Regardless of having to reproduce their designs in code, we frequently need at least rudimentary skills with design tools like Photoshop both to open their files and prepare their designs for the web, and the overlap is such that the only things we're really missing are 4 years of design classes (trivial compared to what we're forced by our profession to learn almost daily), and that harder to obtain ineffable sense of what "looks good".

In addition to converting their designs to code, we often need to produce, install, or at least troubleshoot a back end that lets someone create content, make it account in some way for the fact that those content creators are going to screw up the designer's pixel-perfect vision for the site with poorly-formed content, and code it all so that it scales over hundreds of pages that individually vary the one or two designs they've so elegantly produced. And too often, we're left to explain issues to the client of why the site doesn't do anything interactive (because there's no design for it), or why their navigation colors won't appear on top of the image that the client swaps in later (because it doesn't magically change from low-contrast black to high-contrast white), or why search engines will never find that paragraph of text that absolutely must be in that bizzaro font in that weird texture pattern in front of that stock art I've seen lately on your competitor's site.

I recognize that I do not have a designer's design skill. Nonetheless, what I do is not only a completely congruent skill set - in my estimation, requiring more than just the designer-like natural ability to be creative and persistent competence, but also real, continuous learning and refinement of technology and technique - but it is also an art in that it takes creativity to solve all of the problems that these graphic designers carelessly cast to us to solve without a clue themselves for how to do it, or even in most cases that it's a problem at all.

Yes, it's work, and it takes time, and it's every bit as complex as as design, and takes just as much creativity. Just because you can't see it or understand it doesn't mean you should not appreciate it; doesn't mean it is not so. I'll never be belittled by a designer again. My accumulation of years of experience applied to turning your pretty pictures into a working, breathing web site demands your respect, and I'll have it.

And to any designer that says "that's not me", sure. You're right. That's not you. But if you're saying, "I don't see what's so hard," then you're the one with the problem. But you're not reading my site anyway, are you?

I wanted to get some thumbnail creation code in PHP to use for a little project I was working on, and so I traipsed over to Google and asked for "php thumbnail". I took a peek at the first search result, snagged the code, inserted it in my test app, and went merrily on my way. And that will be the last time I trust Google to find me code.

There are many problems with the code that I got. It's not malicious, but it does many easy things wrong. And the one thing that it's supposed to do well - the thing for which Google found it - it doesn't do correctly.

I can't entirely blame the code author, because this particular bit of code is very commonly written incorrectly. But what's a guy to do when you just want a simple PHP function (as opposed to a whole class) that generates thumbnails? Well, it's about time someone did something about that. Here we go.

Here's what most people do wrong. You can't simply compare whether the width of the original image is greater than the height of the original image. You need to compare the aspect ratio (the ratio of width to height) of the source size to the destination (thumbnail) size. Why?

Assume the destination size is a square, 100 pixels by 100 pixels. The aspect ratio of the destination size is 1 (100/100). If the source image is wider than it is tall, then you need to maximize the width of the output. If the source image is taller than it is wide, then you need to maximize the height of the output. In either of these cases, you're fitting the larger of the two sides into the 100×100 pixel space. That the comparison of original width to original height works out is pure coincidence. To see where that evaluation fails, consider a different set of dimensions.

Assume the destination size is a rectangle, 100 pixels wide by 50 pixels high. If you have an image that is 50×50, it should obviously not be resized. According to the algorithm I was using, you would end up resizing the image to 100×50, which is obviously not right.

Worse yet, if your source image was 75×50, it should still not be resized because it is already at the maximum output height. You end up with a very oddly resized image using the old, incorrect algorithm.

If instead of comparing the size of the source image dimensions to itself, you compare the ratio of those dimensions to the ratio of the dimensions of the output image, then you know which way the source image will better fit into the destination size. You can then use that information to anchor your dimensions for the thumbnail output.

When you're done playing mental math with how to use those dimensions, there are a few other things that we should clean up in the PHP, too.

First, you don't explode() a file extension off the end of the file. If you already have the image file, use the built-in getimagesize() image function to determine the dimensions of the file and its file type all in one pass. When you have to determine filetype based on extension, use PHP's pathinfo() function. It returns other information that might be useful later.

For the love of all that is good, will people please start using switch/case? There is a point when you gain enough experience in coding to know that you're going to be making comparisons to a bunch of things and that you'll have to handle each differently. Even if you only have to deal with one thing to start off (like a jpeg, for example) you still get that feeling that maybe this shouldn't be an if(), but a switch(). Given the option, use the switch(). There's not much worse than a bunch of ifelse(){} statements stacked thoughtlessly onto one another.

Let's also try returning some values on success or failure instead of die()ing. Sure, we might not use the return value. There might be nothing we can do if the thumbnail creation fails, but at least we can try to let someone know if they care. I often care.

PHP GD now supports GIF input and output, so there is no need to omit it. There are also a few tricky functions in PHP that let you apply matrix transformations to your images, so you can do things like sharpen the thumbnail before it's saved. I stole borrowed the code for sharpening directly from the online PHP manual.

Granted, this function to create thumbnails isn't perfect, but in my opinion, it's a big improvement over what came before, and it's still small enough to include as a clip of commonly used code.

Here is the function, with a decent amount of inline and phpdoc comments:

/** * Creates a resized image from a source image, saves resized image to file * @param string $src_filename Filename of the image to resize * @param string $dst_filename Filename of the resized image to output * @param integer $max_width Maximum width of resized image * @param integer $max_height Maximum height of resized image * @return boolean true on success, false on failure */ function createthumb( $src_filename, $dst_filename, $max_width, $max_height ) { // Get information about the image list($src_width, $src_height, $type, $attr) = getimagesize( $src_filename ); // Load the image based on filetype switch( $type ) { case IMAGETYPE_JPEG: $src_img = imagecreatefromjpeg( $src_filename ); break; case IMAGETYPE_PNG: $src_img = imagecreatefrompng( $src_filename ); break; case IMAGETYPE_GIF: $src_img = imagecreatefromgif( $src_filename ); break; default: return false; } // Did the image fail to load? if ( !$src_img ) { return false; } // Calculate the output size based on the original's aspect ratio if ( $src_width / $src_height > $max_width / $max_height ) { $thumb_w = $max_width; $thumb_h = $src_height * $max_width / $src_width; } else { $thumb_w = $src_width * $max_height / $src_height; $thumb_h = $max_height; } // Create the output image and copy to source to it $dst_img = ImageCreateTrueColor( $thumb_w, $thumb_h ); imagecopyresampled( $dst_img, $src_img, 0, 0, 0, 0, $thumb_w, $thumb_h, $src_width, $src_height ); /* Sharpen before save? $sharpenMatrix = array( array(-1, -1, -1), array(-1, 16, -1), array(-1, -1, -1) ); $divisor = 8; $offset = 0; imageconvolution( $dst_img, $sharpenMatrix, $divisor, $offset ); //*/ // Get information about the output image $path_info = pathinfo($dst_filename); // Setup default return info $return = true; // Load the image based on filetype switch ( strtolower( $path_info['extension'] ) ) { case 'jpg': case 'jpeg': imagejpeg( $dst_img, $dst_filename ); break; case 'png': imagepng( $dst_img, $dst_filename ); break; case 'gif': imagegif( $dst_img, $dst_filename ); break; default: $return = $false; } // Clean up memory imagedestroy( $dst_img ); imagedestroy( $src_img ); return $return; }

There is so much stuff going on, I'm not even sure where to begin.

If you missed it, yesterday's April Fools Day activities went off quite well. Thanks a bunch to skippy, moeffju, and chrisjdavis for helping out and playing along.

Also, I'd like to apologize to all the WordPress users who found the ForkPress link on their Dashboards yesterday. I was really tired when I entered the post, and I hadn't intended to tag it "WordPress" when I was scanning through the text. I hope it was entertaining just the same.

Obviously, part of the plan is to use ForkPress as a jumping point to announce the Habari DR release. We've gotten the code wrapped up to a point where we'd like to have some other developers see what we've done so far. We want people to start playing with Habari, see what it can do and what it lacks, and continue to make the software better. All of the sites in yesterday's joke were running Habari, and we did production and setup of all three entirely on Saturday. Most of the work was getting the logos and photos together - Habari installation was a snap, as expected.

This release isn't for production use (although you are reading a Habari install in production currently), but it is the first release available to users outside of source control. That is, you can download it as a zip or tgz file.

Oh, and there's more on my plate after this weekend's shuffling the software out the door and getting the AFD stuff online...

I'm going to Los Angeles tomorrow for work. I'll be in town all week. I don't know what my free time is like, and I don't have a car. I don't know what that means. Are there people in L.A. who want to chat about Habari? I hope I can escape the $work$ for a few hours here and there, though I expect they'll be locking me in a closet with my notebook the whole time I'm there.

So I'll be away from home until Saturday. Easter is Sunday - there's much family planning there. And then on Monday, Berta leaves for Atlanta and her $work$ for a few days, leaving me alone at home with the kids.

I shouldn't report being alone with the kids on this blog because then people might get wise and send over social services. Popcorn for lunch! Pizza for every other meal! Bed time at 3am!

Berta's birthday is Wednesday. I have no idea what to do about that.

And I guess that's all I can talk about for now. Hopefully have more stories for you from LA. I hope the hotel has wifi, or it's dialup via bluetooth via cell for me.