Animated GIF Manipulating With GD

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

hw

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:

  1. Create an image resource for each letter using the function $res=imagecreatefromgif($url).
  2. 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.
  3. Split each image into frames using the state machine.
  4. Create the output frames using ‘imagecreate($total_width, $total_height);’Note: the image created by GD is not stored in GIF format.
  5. If the frame is new set its transparent background using the following commands:
    1. $tci = imagecolortransparent($frame_res);  // Get the transparent color index from the original frams.
    2. imagepalettecopy($out_frames[$curr_frame_no],$frame_res); // Copy the palette to the output frame.
    3. imagefill($out_frames[$curr_frame_no], 0, 0, $tci);  // Fill the output frame with the transparent color
    4. imagecolortransparent($out_frames[$curr_frame_no], $tci);  // Set the output frame’s transparent color.
  6. Place each letter’s frame in the output frame using tbe 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);
  7. Now, use a state machine similar to the one described above to create the output GIF file.
Advertisements