I had a task: to write a program that makes text from animated ‘gif’ images of letters. For example, the following image:

Usually, the PHP extension ‘GD’ is good enough to perform the job. GD is an image processing extension, that allows a developer to create canvasses, place images of common formats in there, save your picture to an image file of your favorite format or to a string, and other operations. This time, GD generated the big image, but without the animation. But GD is still useful if you learn about the structure of a GIF file, and you can get all the relevant information in this text file.
The Structure of a GIF File Made Simpler
A GIF File consists of the following:
- File Header, begins with ‘GIF’ followed by ’87a’ or ’89a’ and 7 more bytes.
- There may be a global color map, depending on byte 10. It’s size is calculated according to the 3 rightmost bytes of byte 10.
- Extensions that begin with a byte of value 21h (“\x21”), including graphic control (“\x21\xf9”), text (“\x21\x01”), app data(“\x21\xff”), and comments (“\x21\xfe”).
- Image Separator (“\x2c”).
- A file Terminator (‘|x3b”).
The file can be processed by reading the header and using a state machine as follows:
// Read the header $gif_string = my_fread($fd, 13); // Get the packed field; $value = ord(substr($gif_string, 10, 1)); $globalMapExists = $value & 0x80; if ($globalMapExists){ $tmp=($value & 0x07) + 1; $globalMapSize = 3 * (1 << $tmp); } else $globalMapSize = 0; // Read Global Color Map if ($globalMapExists){ $globalColorMap = my_fread($fd, $globalMapSize ); $gif_string .= $globalColorMap; } // Data begins here. // Data blocks begin with: // 0x2C - Image Descriptor // 0x21 - Extensions: Graphic Control, Comment, Text, Application. $curr_frame = -1; $gc_encountered = FALSE; while (($chr = my_fread($fd, 1)) != "\x3B"){ switch (ord($chr)){ case 0x21: $next_chr = my_fread($fd, 1); switch (ord($next_chr)){ case 0xf9: $curr_frame++; $frames[] = $gif_string . chr(0x21) . chr(0xf9) . read_frame($fd, $curr_frame); $gc_encountered = TRUE; break; case 0x01: if (!$gc_encountered){ $frames[0] = $gif_string; $curr_frame = 0; } $frames[$curr_frame] .= chr(0x21) . chr(0x01) . read_text($fd); break; case 0xff: $gif_string .= chr(0x21) . chr(0xff) . read_app_data($fd); break; case 0xfe: $gif_string .= chr(0x21) . chr(0xfe) . read_comment($fd); break; } break; case 0x2c: if (!$gc_encountered){ $frames[0] = $gif_string; $curr_frame = 0; $gc_encountered=TRUE; } $frames[$curr_frame] .= chr(0x2c) . read_image($fd, $curr_frame); break; } }
Doing it With GD
The steps of the program are as follows:
- Create an image resource for each letter using the function $res=imagecreatefromgif($url).
- Get the width and height of each letter using imagesx($res) and imagesy($res). Use these values to find the size of the output image.
- Split each image into frames using the state machine.
- Create the output frames using ‘imagecreate($total_width, $total_height);’Note: the image created by GD is not stored in GIF format.
- If the frame is new set its transparent background using the following commands:
- $tci = imagecolortransparent($frame_res); // Get the transparent color index from the original frams.
- imagepalettecopy($out_frames[$curr_frame_no],$frame_res); // Copy the palette to the output frame.
- imagefill($out_frames[$curr_frame_no], 0, 0, $tci); // Fill the output frame with the transparent color
- imagecolortransparent($out_frames[$curr_frame_no], $tci); // Set the output frame’s transparent color.
- Place each letter’s frame in the output frame using the command:
imagecopymerge($out_frames[$curr_frame_no], // Destination image $frame_res, // Source image $pos_in_pic + $left_positions[$curr_frame_no], // Destination x, $top_positions[$curr_frame_no], // Destination y, 0, //$left_positions[$curr_frame_no], // Source x, 0, //$top_positions[$curr_frame_no], // source y, $width, $height,100);
- Now, use a state machine similar to the one described above to create the output GIF file.
Great site. Plenty of useful info here. I’m sending it to a few pals ans additionally sharing in delicious. And obviously, thanks for your sweat! isabel marant online
The next time I read a weblog, I hope that it doesnt disappoint me as significantly as this one. I mean, I know it was my selection to read, but I truly thought youd have some thing interesting to say. All I hear is usually a bunch of whining about something which you could fix if you happen to werent too busy searching for attention.
— Editor Comment: this response shows lack of reading comprehension. See what your URL is now. Satisfied?
An impressive share, I just given this onto a colleague who was doing a little analysis on this. And he in fact bought me breakfast because I found it for him.. smile. So let me reword that: Thnx for the treat! But yeah Thnkx for spending the time to discuss this, I feel strongly about it and love reading more on this topic. If possible, as you become expertise, would you mind updating your blog with more details? It is highly helpful for me. Big thumb up for this blog post!
You should take part in a contest for one of the best blogs on the web. I will recommend this site!