Google Community Chat
 
Dynamic Image Generation with PHP

You're visiting Google Community as a guest.
In order to post, you'll need to register and log in.


(If you were registered and logged in, these advertisements wouldn't be here)
Post new topic   Reply to topic    Google Community Forum Index // Web Design, Coding & Programming Forum
   
Author Message
puccavn
Noogle
Noogle


Joined: 12 Apr 2005

1021.15 GC$

Items

PostPosted: Thu May 05, 2005 11:42 pm    Post subject: Dynamic Image Generation with PHP Reply with quote
Target Audience
This tutorial is designed for the intermediate PHP user.

Introduction
PHP lets you do a lot more than simply generating HTML content, it is also a great tool to dynamically generate images on-the-fly. For example, you can

create buttons with text from a database
create grapical displays of statistics from log files
enhance business data with graphs and diagrams

This tutorial shows you how to use the GD library to dynamically create images on your site. The GD library is an external module which is accessed by a native PHP module.

Goals of the Tutorial
In this tutorial you will learn the following:

About headers
How to use the basic GD command set to dynamically generate images with PHP.
How to make use of the HTML header fields to trick your browser into thinking your PHP files are actually images

Background Information
The key to controlling how data is displayed is the HTML header. Before sending out the actual data of your webpage, each Web server sends a header which is formatted according to the HTTP protocol rules. It denotes the kind of data, the last modification date, information about the document size and such (see for example RFC 2068, which can be retrieved at any RFC archive like ftp://ftp.isi.edu/in-notes/rfc2068.txt).

Creating a Header

By default, PHP creates at least one header field for you: the Content-type header which marks the output of your script as HTML data.

<?php
print("Hello world!");
?>

$> php hello.php
Content-type: text/html

Hello world!
$> _

As you can see, PHP prints Content-type: text/html and a blank line before starting to print the output of this script. Everything before the blank line is defined as being the HTTP header. The header is interpreted by your browser and doesn't show up in the page content.

The default header Content-type: text/html is a MIME-type, an element of a standardized catalog of content types, and denotes the following content as HTML that your browser is supposed to render. If we'd change this to "Content-type: text/plain" (another MIME-type), the browser would not render the data but interprete it as plain text - and simply display it "as is".

PHP allows you to change the content type field in the header with the header() function.

header("Content-type: image/jpeg"); // mark the following content as JPEG image
header("Content-type: application/zip"); // mark the following content as ZIP file

Of course, this makes sense only if the data you send is of the specified type. Sending a header for a ZIP file and printing "Hello world!" after that would confuse your browser or any other application handling your ZIPs.

But the point is that choosing your own content type headers opens the door to another dimension of server-side scripting. You can make PHP generate any form of data which is specified in the catalogue of MIME types (a huge variety of sound, video and text formats), This tutorial is focused on the dynamic generation of images.

Note
Optionally, your webserver may add a few fields to the header, but these are not interesting at the moment.


Preparing Images for PHP
Image generation in PHP can easily be done by using Thomas Boutell's GD library. (see http://www.boutell.com/gd) Support for this library can be compiled into PHP using the --with-gd option after which you can access the GD functionality with a large set of native functions. In case you would also like to use the FreeType functions, you require the FreeType library (for ImageTTFText() and ImageTTFBox()) that you can retrieve at http://www.freetype.org. The installation of both should be straight forward when following the documentation.

Generating Images with PHP
Basically, image generation is done in three steps:

allocating an image
drawing into the allocated space
releasing the allocated data in picture format to the browser

Allocating an image is done using GD's ImageCreate() function. As parameters this function takes the image width and height in pixels, the return value consists of a handle that you require to refer to this allocated image when doing further drawing (or similar) operations.

After allocating an image, you must allocate colors. The GD library needs to know which colors are in use before it can generate the final image data, thus it requires you to register all colors that you want to use. Registering colors can be done using ImageColorAllocate(). As arguments this function takes the handle of the image for which you want to register a color and the RGB (red, green, blue) values of the specific color you would like to add. The return value is a handle for the color which allows you to reference it later on when drawing.

Example:

$colorHandle = imageColorAllocate($image, 192, 192, 192); // allocate color
imageFilledRectangle($image, 0, 0, $width - 1, $height - 1, $colorHandle); // use it for drawing

There are a large number of PHP functions for drawing the image. Covering all the functions is beyond the scope of this tutorial. To get a complete list of the drawing function, take a look at the appropriate section in the PHP manual. (direct link is here)

Finally, releasing the picture to the browser is done with a single call to either ImagePNG() or ImageGIF(). The latter is not supported in current versions of the GD library due to licensing problems with the compression code. ImagePNG() converts the internal image data to a PNG file and writes it directly to the client. ImageGIF() does the same, except for that it creates a GIF image.

Before calling either ImageGIF() or ImagePNG(), set the content header as shown in the following table.

Image Type Content Header
PNG "Content-type: image/png"
GIF "Content-type: image/gif"

NOTE
The content type headers refer to the entire document. This means that once you have specified an image content type, you cannot print regular text from that point on. Thus, after the first byte has been sent out, you cannot change the header anymore! This means you must first call header() and then do the rest of your script processing, otherwise you receive a warning message. If you do not specify a content type header at all, PHP automatically sends the header type "text/html" for you as soon as the first character is sent to the browser.

How It Works
To show you how to actually make use of the GD library, this tutorial shows a sample script to print out your biorythm. Following, each step of the source is commented and explained.

The theory of the biorythm says that the emotional, physical and intellectual state of each person has low and high passes within regular intervals. With birth, all three curves start at zero and then swing with a certain cycle through your life. If a curve is above the middle line, it is in an active phase, if it is below it is in a passive phase. If it crosses the middle line this means a critical day: you're prone to intellectual, physical or emotional "catastrophes". When all three curves cross the line you should therefore try to avoid to work on important PHP projects.

Each curve has different cycles:

Physical: 23 days
Emotional: 28 days
Intellectual: 33 days
All curves are sinusoid, so drawing them can be reduced to a simple call to sin() with a little bit of math around it.

Setting the Birthday

This step retrieves the user's birthdate and calculates the number of days the person has been alive to date. The result can then be used to directly calculate the phase of the sine curves for the biorhythm.

Code Flow

Checks whether a birthdate has been specified.
If not, it displays a form to allow the user to input one.
After checking date validity and specifying diagram parameters, calculates the number of days the person has been alive to date using the Julian calendar.

if(!isset($birthdate))
{
/* */
}

$daysGone = abs(gregorianToJD($birthMonth, $birthDay, $birthYear)
- gregorianToJD(date( "m"), date( "d"), date( "Y")));

Tips
The date calculation uses Julian dates, which count absolute number of days, not a split date into years, months and days like Gregorian dates do. In order to determine the number of days the person is alive, both today's date and the person's birthdate have to be converted to Julian dates. Calculating the difference gives the correct result.

Note
The function gregorianToJD() has been reimplemented in the PHP source code. Usually, this function is also available in PHP's calendaring module, but at the time of this writing, it did not work in the most up-to-date version of PHP 4. So, in order to assure a working program with all PHP versions, this function is now "handcrafted". If you still want to use the calendaring module, uncomment the function here.


Preparing the Image

Code Flow
Preparing the image is done with a few calls to a set of GD's functions. The following code:


Names and creates a new $image of the desired size (in pixels).
Allocate the colors for the drawing $image.
Creates a rectangle with a background color to draw the image. (Clears the image space.)

// create image
$image = imageCreate($diagramWidth, $diagramHeight);

// allocate all required colors
$colorBackgr = imageColorAllocate($image, 192, 192, 192);
$colorForegr = imageColorAllocate($image, 255, 255, 255);
$colorGrid = imageColorAllocate($image, 0, 0, 0);
$colorCross = imageColorAllocate($image, 0, 0, 0);
$colorPhysical = imageColorAllocate($image, 0, 0, 255);
$colorEmotional = imageColorAllocate($image, 255, 0, 0);
$colorIntellectual = imageColorAllocate($image, 0, 255, 0);

// clear the image space with the background color
imageFilledRectangle($image, 0, 0, $width - 1, $height - 1, $colorBackgr);

Tips
It's a good idea to create a colored rectangle to serve as a background for drawing the picture. This ensures that the image will be drawn on a blank space of the color you want. It is also useful for creating a transparency effect, as this tutorial discusses later.

Note
This tutorial does not include any error checking, which is bad coding style, but is used for clarity in the tutorial. In a "real" script, there should be error checking and exception handling to make sure that everything works as expected, and to give some sort of notification to the user if there is an error.


Drawing Boundaries and Adding Text

ImageString() writes the specified text string into the image using a position and size. The first two strings will appear at the top of the diagram and display the birthdate and today's date, while the other three will appear at the bottom and give color information for the three curves. Working with the predefined color handles makes it easy to denote which color you want to use.

Code Flow

Draws the diagram's boundaries using a rectangle and the middle cross using two simple lines.
Places descriptive text in the diagram.

// draw rectangle around diagram (marks its boundaries)
imageRectangle($image, 0, 0, $diagramWidth - 1, $diagramHeight - 20,
$colorGrid);

// draw middle cross
imageLine($image, 0, ($diagramHeight - 20) / 2, $diagramWidth,
($diagramHeight - 20) / 2, $colorCross);
imageLine($image, $diagramWidth / 2, 0, $diagramWidth / 2, $diagramHeight - 20,
$colorCross);
imageString($image, 3, 10, 10, "Birthday: $birthDay.$birthMonth.$birthYear",
$colorCross);
imageString($image, 3, 10, 26, "Today: ". date( "d.m.Y"), $colorCross);
imageString($image, 3, 10, $diagramHeight - 42, "Physical", $colorPhysical);
imageString($image, 3, 10, $diagramHeight - 58, "Emotional", $colorEmotional);
imageString($image, 3, 10, $diagramHeight - 74, "Intellectual",
$colorIntellectual);

Figure 1. The final biorhythm image




Drawing the Curves

All the curves are sine waves differing only in their period, drawing a "rhythm" has been abstracted into the function drawRythm(). The curve is drawn by calculating the biorhythm values on each day from the start date to the current date, and drawing a line from the value on one day to the value on the next day. The biorhthm value is a Y position on the diagram. The lines are drawing in intervals of one day, using as connection points the X/Y coordinates calculated for each day.

Code Flow

Fixes the start angle of the sine wave based on the number of days the person is alive ($daysGone) and the period of the curve.
Calls the drawRhythm function to draw the curve three times: one with each parameter set.
Within for loop, draws a line from each point to the next point and saves the coordinates for the next run of the for loop.

drawRhythm($daysGone, 23, $colorPhysical);
drawRhythm($daysGone, 28, $colorEmotional);
drawRhythm($daysGone, 33, $colorIntellectual);

for($x = 0; $x <= $daysToShow; $x++)
{
// calculate phase of curve at this day, then Y value
// within diagram
$phase = (($centerDay + $x) % $period) / $period * 2 * pi();
$y = 1 - sin($phase) * (float)$plotScale + (float)$plotCenter;

// draw line from last point to current point
if($x > 0)
imageLine($image, $oldX, $oldY, $x * $diagramWidth / $daysToShow,
$y, $color);

// save current X/Y coordinates as start point for next line
$oldX = $x * $diagramWidth / $daysToShow;
$oldY = $y;
}

Tips
It would be better to not draw lines in day intervals on the diagram but to use smaller steps and interpolate between the coordinates, eventually even draw pixel-based. This would give a much smoother curve, however, the compromise in speed and simplicity wasn't worth it for the purpose of this tutorial.


Sending the Image to the Client

Now that the image has been created, it needs to be sent to the client.

Code Flow

Send the header, specifying either GIF or PNG as the content type.
Sets imageGIF or imagePNG command. Either command converts the picture data to the target format and immediately sends the information to the client. Note that two lines are commented out, so you can select the appropriate command.
Creates an interlaced image.
Sets a transparent background color.

header("Content-type: image/gif");
//header("Content-type: image/png");

imageGIF($image);
//imagePNG($image);

// create an interlaced image for better loading in the browser
imageInterlace($image, 1);
// mark background color as being transparent
imageColorTransparent($image, $colorBackgr);

Tips
Once you have used the imageGIF or imagePNG, you cannot make further modifications to the image, as it has already been sent to the client.

ImageInterlace() allows you to turn interlacing on or off. This call marks the target image as being an interlaced image (if the output format supports it). Interlaced images are stored in a different way, which enables the client browser to show a "preview" of the image while it is being loaded. At first, you only see a rough "sketch" of the picture, but the more data is available, the finer and clearer the picture will become - a very handy feature for large diagrams, loaded across a slow network connection.

ImageColorTransparent() sets the color which should generate a transparency effect (again, only if the output format supports it). This command sets $colorBackgr, the background color, as transparent color, which creates a transparent "see-through" diagram background. The background rectangle is being used to create the transparency effect, meaning, the rectangle is being made transparent - all spots on the image still having the original rectangle color will be transparent, allowing to see through to the background color of the HTML page.


Using the Image
Now that you can make PHP generate image data, you need to know how to use it. This is actually straightforward: you can use the PHP script just like a regular image in your HTML files. Because this sample script is "interactive", requesting user input first, you need to take a look at it in your browser the regular http://my.server.net/script.php way. However, for a non-interactive script, for example, creating an image generated from database inforamtion, all you'd need to do is to put the following tag into your HTML:

<img src="http://my.server.net/script.php">

Complete Code

<?php

//
// Print a standard page header
//
function pageHeader()
{

print("<html><head>");
print("<title>Biorhythm with PHP3</title>");
print("</head><body>");

}

//
// Print a standard page footer
//
function pageFooter()
{

print("</body></html>");

}

//
// Function to convert a Gregorian date to a Julian date (number of days)
//
function gregorianToJD($month, $day, $year)
{
// Note: The following algorithm is based
// on a paper by Peter Baum, pbaum@capecod.net

if($month < 3)
{
$month = $month + 12;
$year = $year - 1;
}

$jd = $day + floor((153 * $month - 457) / 5) + 365 * $year
+ floor($year / 4) - floor($year / 100)
+ floor($year / 400) + 1721118.5;

return($jd);

}

//
// Function to draw a curve of the biorythm
// Parameters are the day number for which to draw,
// period of the specific curve and its color
//
function drawRhythm($daysAlive, $period, $color)
{
global $daysToShow, $image, $diagramWidth, $diagramHeight;

// get day on which to center
$centerDay = $daysAlive - ($daysToShow / 2);

// calculate diagram parameters
$plotScale = ($diagramHeight - 25) / 2;
$plotCenter = ($diagramHeight - 25) / 2;

// draw the curve
for($x = 0; $x <= $daysToShow; $x++)
{
// calculate phase of curve at this day, then Y value
// within diagram
$phase = (($centerDay + $x) % $period) / $period * 2 * pi();
$y = 1 - sin($phase) * (float)$plotScale + (float)$plotCenter;

// draw line from last point to current point
if($x > 0)
imageLine($image, $oldX, $oldY,
$x * $diagramWidth / $daysToShow, $y, $color);

// save current X/Y coordinates as start point for next line
$oldX = $x * $diagramWidth / $daysToShow;
$oldY = $y;
}

}

//
// ---- MAIN PROGRAM START ----


// check if we already have a date to work with,
// if not display a form for the user to enter one
if(!isset($birthdate))
{
pageHeader();

?>
<form method="post" action="<?php print(basename($PHP_SELF)); ?>">
Please enter your birthday:<br>
<input type="text" name="birthdate"
value="MM/DD/YYYY"><input type="submit" value="OK!">
</form>
<?php

pageFooter();

exit();
}

// get different parts of the date
$birthMonth = substr($birthdate, 0, 2);
$birthDay = substr($birthdate, 3, 2);
$birthYear = substr($birthdate, 6, 4);

// check date for validity, display error message if invalid
if(!checkDate($birthMonth, $birthDay, $birthYear))
{
pageHeader();

print("The date '$birthMonth/$birthDay/$birthYear' is invalid.");

pageFooter();

exit();
}

// specify diagram parameters (these are global)
$diagramWidth = 710;
$diagramHeight = 400;
$daysToShow = 30;

// calculate the number of days this person is alive
// this works because Julian dates specify an absolute number
// of days -> the difference between Julian birthday and
// "Julian today" gives the number of days alive
$daysGone = abs(gregorianToJD($birthMonth, $birthDay, $birthYear)
- gregorianToJD(date( "m"), date( "d"), date( "Y")));

// create image
$image = imageCreate($diagramWidth, $diagramHeight);

// allocate all required colors
$colorBackgr = imageColorAllocate($image, 192, 192, 192);
$colorForegr = imageColorAllocate($image, 255, 255, 255);
$colorGrid = imageColorAllocate($image, 0, 0, 0);
$colorCross = imageColorAllocate($image, 0, 0, 0);
$colorPhysical = imageColorAllocate($image, 0, 0, 255);
$colorEmotional = imageColorAllocate($image, 255, 0, 0);
$colorIntellectual = imageColorAllocate($image, 0, 255, 0);

// clear the image with the background color
imageFilledRectangle($image, 0, 0, $width - 1, $height - 1, $colorBackgr);

// calculate start date for diagram and start drawing
$nrSecondsPerDay = 60 * 60 * 24;
$diagramDate = time() - ($daysToShow / 2 * $nrSecondsPerDay)
+ $nrSecondsPerDay;

for ($i = 1; $i < $daysToShow; $i++)
{
$thisDate = getDate($diagramDate);
$xCoord = ($diagramWidth / $daysToShow) * $i;

// draw day mark and day number
imageLine($image, $xCoord, $diagramHeight - 25, $xCoord,
$diagramHeight - 20, $colorGrid);
imageString($image, 3, $xCoord - 5, $diagramHeight - 16,
$thisDate[ "mday"], $colorGrid);

$diagramDate += $nrSecondsPerDay;
}

// draw rectangle around diagram (marks its boundaries)
imageRectangle($image, 0, 0, $diagramWidth - 1, $diagramHeight - 20,
$colorGrid);

// draw middle cross
imageLine($image, 0, ($diagramHeight - 20) / 2, $diagramWidth,
($diagramHeight - 20) / 2, $colorCross);
imageLine($image, $diagramWidth / 2, 0, $diagramWidth / 2, $diagramHeight - 20,
$colorCross);

// print descriptive text into the diagram
imageString($image, 3, 10, 10, "Birthday: $birthDay.$birthMonth.$birthYear",
$colorCross);
imageString($image, 3, 10, 26, "Today: ". date( "d.m.Y"), $colorCross);
imageString($image, 3, 10, $diagramHeight - 42, "Physical", $colorPhysical);
imageString($image, 3, 10, $diagramHeight - 58, "Emotional", $colorEmotional);
imageString($image, 3, 10, $diagramHeight - 74, "Intellectual",
$colorIntellectual);

// now draw each curve with its appropriate parameters
drawRhythm($daysGone, 23, $colorPhysical);
drawRhythm($daysGone, 28, $colorEmotional);
drawRhythm($daysGone, 33, $colorIntellectual);

// set the content type
header("Content-type: image/gif");
//header("Content-type: image/png");

// create an interlaced image for better loading in the browser
imageInterlace($image, 1);

// mark background color as being transparent
imageColorTransparent($image, $colorBackgr);

// now send the picture to the client (this outputs all image data directly)
imageGIF($image);
//imagePNG($image);

?>

This article has shown the basics of dynamic image generation with PHP in combination with the GD library. You have learned how to setup PHP, how to deal with the output data and what basic steps you need to perform in order to generate your images. A fairly complex example has shown you how to draw diagrams based on user input making use of GD's standard functionality. Based on this code, you are able to perform real life dynamic image generation now

Very Happy Very Happy Very Happy
Back to top
View user's profile Send private message
Sponsored Links
Posted: 5 Dec 2008 10:19 am    Post subject: Advertisements
Back to top
Post new topic   Reply to topic    Google Community Forum Index // Web Design, Coding & Programming Forum All times are GMT - 8 Hours
Page 1 of 1


 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
Host your free forums with Invision Plus.net forum web hosting with your own subdomain.

alexisBlue v1.2 // Theme Created By: Andrew Charron // Icons in Part By: Travis Carden

© 2005-2006 Google Community

Powered by phpBB

Privacy Policy | Contact Us

Powered by Google Search blog

This website is not affiliated in any way with Google, Inc.
Google™ is a registered trademark of Google, Inc.