While working on Mozilla Community Sites project, I encountered an interesting challenge.
This Poll Box may look nice, but there’s a hidden trap for anyone trying to use this on his website. How to make the cute bars look nice and present real value in it? The intuitive response is to create a small PHP script that will be drawing the bar depending on the input value.
It would be easy to do if only the bar itself is a simple green/red dual color rectangle but in this case, it’s something more sophisticated. Because the bar can be used on different backgrounds, I had to extract it from the source and create a translucent PNG file with representation of blends, shadows and smooth edges. The file looks like this:
Great. Now we have the bar, and we can present it on any customized background, but it’s somehow static and cannot be used to present real values, right?
So the next step is to take it into PHP world and here were the trap is hidden. How to take a complex PNG image into PHP file? Unfortunately Google failed to help me here, so I started playing around with GD library (after an hour of searching for how to take alpha value from pixel in Gimp – it’s impossible) and crafted a plan.
I could create two dimension array of pixels (rgba) representing left edge, right edge, green line and red line and bundle it into a bar like this.
Alternative approach would be to cut PNG file and load elements into PHP script on each load, but it will have to cost much more (load file, decode, operations, encode, write) so I sticked to the pixel array map idea.
I was also initially considering trying to create such bar algorithmically (take green, red rectangles, then do the math for shadows, rounded corners, opacity etc.) but even ignoring how much time it would take to write it, the performance of such script would be definitely not satisfying.
So I needed two scripts. First, to take PNG file and build a PHP Array map of pixels. Second, to take such array and paint it on PNG file.
The script with those two functions is my svn repo.
At the top of the script file is an example Array map that will paint the example bar. drawpixelmap() is a function that can take such map as an argument and create PNG image from it.
getpixelmap() is a function that takes PNG image and creates string with PHP Array map.
In result you can take PNG image, save it in PHP, operate on it and draw the final result.
In case of my quest I had to cut the Array map into pieces (left edge, right edge etc.) and do a little bit of math.
The result looks like this:
http://labs.braniecki.net/pollbar.png.php?v=50
http://labs.braniecki.net/pollbar.png.php?v=30
http://labs.braniecki.net/pollbar.png.php?v=75
Of course, any other value from 0-100 range for $v is possible.
So, if you will ever need to dynamically modify PNG file by pixels, those scripts may be useful for you 🙂
btw. I’m writing it from Victoria, where it is snowing for the third day in a row. Not sure how to explain it, but the snow in Canada is different from the one in Europe. Is it possible that its a kind of eggnog influence? 🙂
13 replies on “PHP pixel mapper story”
Or you could do it on the client side:
(div>
(div style=”float: left; background-image:url(http://labs.braniecki.net/pollbar.png.php?v=100);height:11px;width:30px”>(/div>
(div style=”background-image:url(http://labs.braniecki.net/pollbar.png.php?v=0);background-position:right;height:11px;width:22px;margin-left:30px”>(/div>
(/div>
The way I would do it is to create a with a width of x pixels and a red background.
Then either another with green at y pixels, or use a green 1×1 image and set the width and height appropriately.
That way you can scale to whatever and keep that image generation stuff off your server.
I would have gone a step further and had two images… one of an empty bar, one of a full bar. Then I load the empty one and the full one, and crop the full one at the correct width, and paint it on top of the empty one.
Considering how small the images are, you could also have the script output N 1 images (where N is the width of the image) one for each possible bar image, and then the live PHP script can just select the “correct” image instead of having to recreate it 1000s of times for different visitors.
A good way to think about these kinds of problems is that PHP is basically automating what you would do in a tool like Photoshop.
If I was going to need a new progress bar value every day, I would keep premade empty and full images and just crop and overlay them for every new image (which is my first idea). Or I would be sneaky and just make all possible images at once, and then just find the right one when I needed a new progress bar image (solution #2).
Solution #2 has the additional advantage of not needing to run a PHP script for every image… the PHP script serving the main page can generate the urls to the static images instead.
Sure would be easy with or SVG. 🙂
Hmm, I wonder if it would be easy to construct a client-side data: URI for the image. Perhaps as a hacky uncompressed PNG, or some other file format that’s easy to manipulate.
@justin: That’s a neat idea. I did not consider it, but I could offer SVG for SVG enabled browsers, and PHP generated PNG for others…
@all: Thanks for your ideas. I did not consider doing it with CSS for two reasons:
1) I’m a bit afraid of who it could cut off from seeing it properly
2) I’m trying to create the website in 100% non-hacky web standard style. No dirty hacks, everything as near as possible to HTML5/XHTML1.0 etc. I would like the website to be an example of how clean can the website code be.
Unfortunately I think your concept with two cropped images positioned over one another is the *cheapest* for the user and server, so I think I will use it instead of my PHP generator despite my disgust to such dirty hacks. (because it proves that web technologies are not yet there…)
I will also try to play with SVG concept with a fallback to such thing…
Thanks for your help! 🙂
I’d have gone with the CSS approach myself – we use a similar approach extensively on our product for doing things like graphical buttons, and I don’t consider it at all hacky. It’s a perfectly good solution to the problem, entirely standards compliant, and works fine on IE7 (we don’t care about 6) and any standards-compliant browser.
From memory we actually go slightly further, and tile the different images into the same png file, using offsets to display the correct part. Works just as well, and reduces the number of files needing to be downloaded.
Why would using CSS to position single parts of the bar be hacky?
(BTW: My idea would have been to use 4 DIVs, one for round edges at the beginning, green part (stretched to the right size), red part, round edges — exceptions (that is static images) for 0 and 100%)
@zbraniecki: I don’t really think it’s a hack. You could do it in 100% valid HTML:
<div style=”width: 100px; background: red;”>
<img src=”pixel.png” width=”60″ height=”5″/>
</div>
Then again, what constitutes a hack or not in html/css is all a matter of perspective. There’s *much* worse out there.
I didn’t even see Jesper’s idea, his is probably the best yet. Only two images, no extra PHP scripts.
well, the basic problem with all CSS approaches is that it’s counter semantic.
There is no semantic reason to have two IMG’s there. (There is hardly reason to have any btw. – it’s just graphical representation of the data. Not content itself), and if we go with Robert’s approach (pretty neat I must say) we still have a green bar that has no semantic meaning in the XHTML code. (in other words – there is IMG with a green bar there with no reason but to blend with other pieces to suddenly look good on the modern web browser because of hacks).
The optimal way would be to present the value (75%) and replace it with the bar in CSS, but that’s not possible at all 🙁
A List Apart: Accessible Data Visualization with Web Standards
http://alistapart.com/articles/accessibledatavisualization
Just to quote the evil, I wonder if you could have just called into the google charts API 😉
I tried to go with Robert’s approach but it will not work. The bars have translucent shadows that overlap when put one over another and the result looks bad.
I can go with one of two ways now:
1) SVG with fallback to PHP
2) <table> with two <td>’s with the bar.
A List Apart idea is great for plain simple bars. but it will not work in my case for the reasons mentioned above (shadows, translucency etc.)