src/Service/AnimatedGif.php line 127

Open in your IDE?
  1. <?php
  2. namespace App\Service;
  3. /*
  4.   :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  5.   :: Formerly known as:::
  6.   :: GIFEncoder Version 2.0 by László Zsidi, http://gifs.hu
  7.   ::
  8.   :: This class is a rewritten 'GifMerge.class.php' version.
  9.   ::
  10.   :: Modification:
  11.   :: - Simplified and easy code,
  12.   :: - Ultra fast encoding,
  13.   :: - Built-in errors,
  14.   :: - Stable working
  15.   ::
  16.   ::
  17.   :: Updated at 2007. 02. 13. '00.05.AM'
  18.   ::
  19.   ::
  20.   ::
  21.   :: Try on-line GIFBuilder Form demo based on GIFEncoder.
  22.   ::
  23.   :: http://gifs.hu/phpclasses/demos/GifBuilder/
  24.   ::
  25.   :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
  26.  */
  27. use Exception;
  28. /**
  29.  * Encode animated gifs
  30.  */
  31. class AnimatedGif
  32. {
  33.     /**
  34.      * The built gif image
  35.      * @var resource
  36.      */
  37.     private $image '';
  38.     /**
  39.      * The array of images to stack
  40.      * @var array
  41.      */
  42.     private $buffer = Array();
  43.     /**
  44.      * How many times to loop? 0 = infinite
  45.      * @var int
  46.      */
  47.     private $number_of_loops 0;
  48.     /**
  49.      *
  50.      * @var int
  51.      */
  52.     private $DIS 2;
  53.     /**
  54.      * Which colour is transparent
  55.      * @var int
  56.      */
  57.     private $transparent_colour = -1;
  58.     /**
  59.      * Is this the first frame
  60.      * @var int
  61.      */
  62.     private $first_frame true;
  63.     /**
  64.      * Encode an animated gif
  65.      * @param array $source_images An array of binary source images
  66.      * @param array $image_delays The delays associated with the source images
  67.      * @param type $number_of_loops The number of times to loop
  68.      * @param int $transparent_colour_red
  69.      * @param int $transparent_colour_green
  70.      * @param int $transparent_colour_blue
  71.      * @throws Exception
  72.      */
  73.     function __construct(
  74.         array $source_images,
  75.         array $image_delays,
  76.         $number_of_loops,
  77.         $transparent_colour_red = -1,
  78.         $transparent_colour_green = -1,
  79.         $transparent_colour_blue = -1
  80.     ) {
  81.         /**
  82.          * I have no idea what these even do, they appear to do nothing to the image so far
  83.          */
  84.         $transparent_colour_red 0;
  85.         $transparent_colour_green 0;
  86.         $transparent_colour_blue 0;
  87.         $this->number_of_loops = ($number_of_loops > -1) ? $number_of_loops 0;
  88.         $this->set_transparent_colour($transparent_colour_red$transparent_colour_green$transparent_colour_blue);
  89.         $this->buffer_images($source_images);
  90.         $this->addHeader();
  91.         for ($i 0$i count($this->buffer); $i++) {
  92.             $this->addFrame($i$image_delays [$i]);
  93.         }
  94.     }
  95.     /**
  96.      * Set the transparent colour
  97.      * @param int $red
  98.      * @param int $green
  99.      * @param int $blue
  100.      */
  101.     private function set_transparent_colour($red$green$blue)
  102.     {
  103.         $this->transparent_colour = ($red > -&& $green > -&& $blue > -1) ?
  104.             ($red | ($green << 8) | ($blue << 16)) : -1;
  105.     }
  106.     /**
  107.      * Buffer the images and check to make sure they are vaild
  108.      * @param array $source_images the array of source images
  109.      * @throws Exception
  110.      */
  111.     private function buffer_images($source_images)
  112.     {
  113.         for ($i 0$i count($source_images); $i++) {
  114.             $this->buffer [] = $source_images [$i];
  115.             if (substr($this->buffer [$i], 06) != "GIF87a" && substr($this->buffer [$i], 06) != "GIF89a") {
  116.                 throw new Exception('Image at position '.$i.' is not a gif');
  117.             }
  118.             for ($j = (13 * (<< (ord($this->buffer [$i]{10}) & 0x07))), $k true$k$j++) {
  119.                 switch ($this->buffer [$i]{$j}) {
  120.                     case "!":
  121.                         if ((substr($this->buffer [$i], ($j 3), 8)) == "NETSCAPE") {
  122.                             throw new Exception('You cannot make an animation from an animated gif.');
  123.                         }
  124.                         break;
  125.                     case ";":
  126.                         $k false;
  127.                         break;
  128.                 }
  129.             }
  130.         }
  131.     }
  132.     /**
  133.      * Add the gif header to the image
  134.      */
  135.     private function addHeader()
  136.     {
  137.         $cmap 0;
  138.         $this->image 'GIF89a';
  139.         if (ord($this->buffer [0]{10}) & 0x80) {
  140.             $cmap * (<< (ord($this->buffer [0]{10}) & 0x07));
  141.             $this->image .= substr($this->buffer [0], 67);
  142.             $this->image .= substr($this->buffer [0], 13$cmap);
  143.             $this->image .= "!\377\13NETSCAPE2.0\3\1".$this->word($this->number_of_loops)."\0";
  144.         }
  145.     }
  146.     /**
  147.      * Add a frame to the animation
  148.      * @param int $frame The frame to be added
  149.      * @param int $delay The delay associated with the frame
  150.      */
  151.     private function addFrame($frame$delay)
  152.     {
  153.         $Locals_str 13 * (<< (ord($this->buffer [$frame]{10}) & 0x07));
  154.         $Locals_end strlen($this->buffer [$frame]) - $Locals_str 1;
  155.         $Locals_tmp substr($this->buffer [$frame], $Locals_str$Locals_end);
  156.         $Global_len << (ord($this->buffer [0]{10}) & 0x07);
  157.         $Locals_len << (ord($this->buffer [$frame]{10}) & 0x07);
  158.         $Global_rgb substr($this->buffer [0], 13* (<< (ord($this->buffer [0]{10}) & 0x07)));
  159.         $Locals_rgb substr($this->buffer [$frame], 13* (<< (ord($this->buffer [$frame]{10}) & 0x07)));
  160.         $Locals_ext "!\xF9\x04".chr(($this->DIS << 2) + 0).
  161.             chr(($delay >> 0) & 0xFF).chr(($delay >> 8) & 0xFF)."\x0\x0";
  162.         if ($this->transparent_colour > -&& ord($this->buffer [$frame]{10}) & 0x80) {
  163.             for ($j 0$j < (<< (ord($this->buffer [$frame]{10}) & 0x07)); $j++) {
  164.                 if (
  165.                     ord($Locals_rgb{$j 0}) == (($this->transparent_colour >> 16) & 0xFF) &&
  166.                     ord($Locals_rgb{$j 1}) == (($this->transparent_colour >> 8) & 0xFF) &&
  167.                     ord($Locals_rgb{$j 2}) == (($this->transparent_colour >> 0) & 0xFF)
  168.                 ) {
  169.                     $Locals_ext "!\xF9\x04".chr(($this->DIS << 2) + 1).
  170.                         chr(($delay >> 0) & 0xFF).chr(($delay >> 8) & 0xFF).chr($j)."\x0";
  171.                     break;
  172.                 }
  173.             }
  174.         }
  175.         switch ($Locals_tmp{0}) {
  176.             case "!":
  177.                 $Locals_img substr($Locals_tmp810);
  178.                 $Locals_tmp substr($Locals_tmp18strlen($Locals_tmp) - 18);
  179.                 break;
  180.             case ",":
  181.                 $Locals_img substr($Locals_tmp010);
  182.                 $Locals_tmp substr($Locals_tmp10strlen($Locals_tmp) - 10);
  183.                 break;
  184.         }
  185.         if (ord($this->buffer [$frame]{10}) & 0x80 && $this->first_frame === false) {
  186.             if ($Global_len == $Locals_len) {
  187.                 if ($this->blockCompare($Global_rgb$Locals_rgb$Global_len)) {
  188.                     $this->image .= ($Locals_ext.$Locals_img.$Locals_tmp);
  189.                 } else {
  190.                     $byte ord($Locals_img{9});
  191.                     $byte |= 0x80;
  192.                     $byte &= 0xF8;
  193.                     $byte |= (ord($this->buffer [0]{10}) & 0x07);
  194.                     $Locals_img{9} = chr($byte);
  195.                     $this->image .= ($Locals_ext.$Locals_img.$Locals_rgb.$Locals_tmp);
  196.                 }
  197.             } else {
  198.                 $byte ord($Locals_img{9});
  199.                 $byte |= 0x80;
  200.                 $byte &= 0xF8;
  201.                 $byte |= (ord($this->buffer [$frame]{10}) & 0x07);
  202.                 $Locals_img{9} = chr($byte);
  203.                 $this->image .= ($Locals_ext.$Locals_img.$Locals_rgb.$Locals_tmp);
  204.             }
  205.         } else {
  206.             $this->image .= ($Locals_ext.$Locals_img.$Locals_tmp);
  207.         }
  208.         $this->first_frame false;
  209.     }
  210.     /**
  211.      * Add the gif footer
  212.      */
  213.     private function addFooter()
  214.     {
  215.         $this->image .= ";";
  216.     }
  217.     /**
  218.      * Compare gif blocks? What is a block?
  219.      * @param type $GlobalBlock
  220.      * @param type $LocalBlock
  221.      * @param type $Len
  222.      * @return type
  223.      */
  224.     private function blockCompare($GlobalBlock$LocalBlock$Len)
  225.     {
  226.         for ($i 0$i $Len$i++) {
  227.             if (
  228.                 $GlobalBlock{$i 0} != $LocalBlock{$i 0} ||
  229.                 $GlobalBlock{$i 1} != $LocalBlock{$i 1} ||
  230.                 $GlobalBlock{$i 2} != $LocalBlock{$i 2}
  231.             ) {
  232.                 return (0);
  233.             }
  234.         }
  235.         return (1);
  236.     }
  237.     /**
  238.      * No clue
  239.      * @param int $int
  240.      * @return string the char you meant?
  241.      */
  242.     private function word($int)
  243.     {
  244.         return (chr($int 0xFF).chr(($int >> 8) & 0xFF));
  245.     }
  246.     /**
  247.      * Return the animated gif
  248.      * @return resource
  249.      */
  250.     function getAnimation()
  251.     {
  252.         return $this->image;
  253.     }
  254.     /**
  255.      * Return the animated gif
  256.      * @return type
  257.      */
  258.     function display()
  259.     {
  260.         //late footer add
  261.         $this->addFooter();
  262.         header('Content-type:image/gif');
  263.         echo $this->image;
  264.     }
  265. }