Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
166 changes: 83 additions & 83 deletions src/GifCreator/AnimGif.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@

/**
* Create an animated GIF from multiple images
*
*
* @link https://github.com/lunakid/AnimGif
* @author Sybio (Clément Guillemain / @Sybio01), lunakid (@GitHub, @Gmail, @SO etc.)
* @license http://opensource.org/licenses/gpl-license.php GNU Public License
Expand Down Expand Up @@ -94,10 +94,10 @@ class AnimGif
* @var array
*/
private static $errors;

// Methods
// ===================================================================================

public function __construct()
{
$this->reset();
Expand All @@ -114,20 +114,20 @@ public function __construct()

/**
* Create animated GIF from source images
*
* @param array $frames The source iamges: can be a local dir path, or an array
*
* @param array $frames The source iamges: can be a local dir path, or an array
* of file paths, resource image variables, binary data or image URLs.
* @param array|number $durations The duration (in 1/100s) of the individual frames,
* @param array|number $durations The duration (in 1/100s) of the individual frames,
* or a single integer for each one.
* @param integer $loop Number of loops before stopping the animation (set to 0 for infinite looping).
*
*
* @return string The resulting GIF binary data.
*/
public function create($frames, $durations = self::DEFAULT_DURATION, $loop = 0)
{
$last_duration = self::DEFAULT_DURATION; // used only if $durations is an array
$this->loop = ($loop > -1) ? $loop : 0; // Negatives would be fine for the GIF itself (all unsigned!),

$this->loop = ($loop > -1) ? $loop : 0; // Negatives would be fine for the GIF itself (all unsigned!),
// but better not take a chance with the PHP bit-ops...
$this->dis = 2; // "reset to bgnd." (http://www.matthewflickinger.com/lab/whatsinagif/animation_and_transparency.asp)

Expand All @@ -136,16 +136,16 @@ public function create($frames, $durations = self::DEFAULT_DURATION, $loop = 0)
$frames_dir = $frames;
if (@is_dir($frames_dir)) {
if ($frames = scandir($frames_dir)) {
$frames = array_filter($frames, function($x) {
$frames = array_filter($frames, function($x) {
// Should these two below be selectable?
return $x[0] != "."; // Or: $x != "." && $x != "..";
});

array_walk($frames, function(&$x, $i) use ($frames_dir) {
array_walk($frames, function(&$x, $i) use ($frames_dir) {
$x = "$frames_dir/$x"; });
}
}

if (!is_array($frames)) { // i.e. scandir() failed above!
throw new \Exception(VERSION.': '
. sprintf(self::$errors['ERR05'], $frames_dir)); // $frames is expected to be a string here; see the other ERR05 case!
Expand All @@ -172,11 +172,11 @@ public function create($frames, $durations = self::DEFAULT_DURATION, $loop = 0)
if (substr($this->frameSources[$i], 0, 6) != 'GIF87a' && substr($this->frameSources[$i], 0, 6) != 'GIF89a') {
throw new \Exception(VERSION.': '.$i.' '.self::$errors['ERR01']);
}

} elseif (is_string($frame)) { // file path, URL or binary data

if (@is_readable($frame)) { // file path
$bin = file_get_contents($frame);
$bin = file_get_contents($frame);
} else if (filter_var($frame, FILTER_VALIDATE_URL)) {
if (ini_get('allow_url_fopen')) {
$bin = @file_get_contents($frame);
Expand All @@ -186,18 +186,18 @@ public function create($frames, $durations = self::DEFAULT_DURATION, $loop = 0)
} else {
$bin = $frame;
}

if (! ($bin && ($resourceImg = imagecreatefromstring($bin))) )
{
throw new \Exception(VERSION.': '.$i.' '
throw new \Exception(VERSION.': '.$i.' '
. sprintf(self::$errors['ERR05'], substr($frame, 0, 200))); //!! $frame may be binary data, not a name!
}

ob_start();
imagegif($resourceImg);
$this->frameSources[] = ob_get_contents();
ob_end_clean();

} else { // Fail
throw new \Exception(VERSION.': '.self::$errors['ERR02']);
}
Expand All @@ -206,18 +206,18 @@ public function create($frames, $durations = self::DEFAULT_DURATION, $loop = 0)
$this->transparent_color = imagecolortransparent($resourceImg);
}

for ($j = (13 + 3 * (2 << (ord($this->frameSources[$i] { 10 }) & 0x07))), $k = TRUE; $k; $j++) {
switch ($this->frameSources[$i] { $j }) {
for ($j = (13 + 3 * (2 << (ord($this->frameSources[$i] [ 10 ]) & 0x07))), $k = TRUE; $k; $j++) {

switch ($this->frameSources[$i] [ $j ]) {

case '!':
if ((substr($this->frameSources[$i], ($j + 3), 8)) == 'NETSCAPE') {

throw new \Exception(VERSION.': '.self::$errors['ERR03'].' ('.($i + 1).' source).');
}

break;

case ';':
$k = false;
break;
Expand Down Expand Up @@ -252,26 +252,26 @@ public function create($frames, $durations = self::DEFAULT_DURATION, $loop = 0)

/**
* Get the resulting GIF image binary
*
*
* @return string
*/
public function get()
{
return $this->gif;
}

/**
* Save the resulting GIF to a file.
*
*
* @param $filename String Target file path
*
*
* @return that of file_put_contents($filename)
*/
public function save($filename)
{
return file_put_contents($filename, $this->gif);
}

/**
* Clean-up the current object (also used by the ctor.)
*/
Expand All @@ -284,7 +284,7 @@ public function reset()
$this->dis = 2;
$this->transparent_color = -1;
}

// Internals
// ===================================================================================

Expand All @@ -295,144 +295,144 @@ protected function gifAddHeader()
{
$cmap = 0;

if (ord($this->frameSources[0] { 10 }) & 0x80) {
$cmap = 3 * (2 << (ord($this->frameSources[0] { 10 }) & 0x07));
if (ord($this->frameSources[0] [ 10 ]) & 0x80) {

$cmap = 3 * (2 << (ord($this->frameSources[0] [ 10 ]) & 0x07));

$this->gif .= substr($this->frameSources[0], 6, 7);
$this->gif .= substr($this->frameSources[0], 13, $cmap);
if ($this->loop !== 1) // Only add the looping extension if really looping
$this->gif .= "!\xFF\x0BNETSCAPE2.0\x03\x01".word2bin($this->loop==0?0:$this->loop-1)."\x0";
}
}

/**
* Add frame to the GIF data
*
*
* @param integer $i: index of frame source
* @param integer $d: delay time (frame display duration)
*/
protected function addGifFrames($i, $d)
{
$Locals_str = 13 + 3 * (2 << (ord($this->frameSources[ $i ] { 10 }) & 0x07));
$Locals_str = 13 + 3 * (2 << (ord($this->frameSources[ $i ] [ 10 ]) & 0x07));

$Locals_end = strlen($this->frameSources[$i]) - $Locals_str - 1;
$Locals_tmp = substr($this->frameSources[$i], $Locals_str, $Locals_end);

$Global_len = 2 << (ord($this->frameSources[0 ] { 10 }) & 0x07);
$Locals_len = 2 << (ord($this->frameSources[$i] { 10 }) & 0x07);
$Global_len = 2 << (ord($this->frameSources[0 ] [ 10 ]) & 0x07);
$Locals_len = 2 << (ord($this->frameSources[$i] [ 10 ]) & 0x07);

$Global_rgb = substr($this->frameSources[ 0], 13, 3 * (2 << (ord($this->frameSources[ 0] { 10 }) & 0x07)));
$Locals_rgb = substr($this->frameSources[$i], 13, 3 * (2 << (ord($this->frameSources[$i] { 10 }) & 0x07)));
$Global_rgb = substr($this->frameSources[ 0], 13, 3 * (2 << (ord($this->frameSources[ 0] [ 10 ]) & 0x07)));
$Locals_rgb = substr($this->frameSources[$i], 13, 3 * (2 << (ord($this->frameSources[$i] [ 10 ]) & 0x07)));

$Locals_ext = "!\xF9\x04" . chr(($this->dis << 2) + 0) . word2bin($d) . "\x0\x0";

if ($this->transparent_color > -1 && ord($this->frameSources[$i] { 10 }) & 0x80) {
for ($j = 0; $j < (2 << (ord($this->frameSources[$i] { 10 } ) & 0x07)); $j++) {
if (ord($Locals_rgb { 3 * $j + 0 }) == (($this->transparent_color >> 16) & 0xFF) &&
ord($Locals_rgb { 3 * $j + 1 }) == (($this->transparent_color >> 8) & 0xFF) &&
ord($Locals_rgb { 3 * $j + 2 }) == (($this->transparent_color >> 0) & 0xFF)
if ($this->transparent_color > -1 && ord($this->frameSources[$i] [ 10 ]) & 0x80) {

for ($j = 0; $j < (2 << (ord($this->frameSources[$i] [ 10 ] ) & 0x07)); $j++) {

if (ord($Locals_rgb [ 3 * $j + 0 ]) == (($this->transparent_color >> 16) & 0xFF) &&
ord($Locals_rgb [ 3 * $j + 1 ]) == (($this->transparent_color >> 8) & 0xFF) &&
ord($Locals_rgb [ 3 * $j + 2 ]) == (($this->transparent_color >> 0) & 0xFF)
) {
$Locals_ext = "!\xF9\x04".chr(($this->dis << 2) + 1).chr(($d >> 0) & 0xFF).chr(($d >> 8) & 0xFF).chr($j)."\x0";
break;
}
}
}
switch ($Locals_tmp { 0 }) {

switch ($Locals_tmp [ 0 ]) {

case '!':

$Locals_img = substr($Locals_tmp, 8, 10);
$Locals_tmp = substr($Locals_tmp, 18, strlen($Locals_tmp) - 18);

break;

case ',':

$Locals_img = substr($Locals_tmp, 0, 10);
$Locals_tmp = substr($Locals_tmp, 10, strlen($Locals_tmp) - 10);

break;
}
if (ord($this->frameSources[$i] { 10 }) & 0x80 && $this->imgBuilt) {

if (ord($this->frameSources[$i] [ 10 ]) & 0x80 && $this->imgBuilt) {

if ($Global_len == $Locals_len) {

if ($this->gifBlockCompare($Global_rgb, $Locals_rgb, $Global_len)) {

$this->gif .= $Locals_ext.$Locals_img.$Locals_tmp;

} else {
$byte = ord($Locals_img { 9 });

$byte = ord($Locals_img [9]);
$byte |= 0x80;
$byte &= 0xF8;
$byte |= (ord($this->frameSources[0] { 10 }) & 0x07);
$Locals_img { 9 } = chr($byte);
$byte |= (ord($this->frameSources[0] [ 10 ]) & 0x07);
$Locals_img [9] = chr($byte);
$this->gif .= $Locals_ext.$Locals_img.$Locals_rgb.$Locals_tmp;
}

} else {
$byte = ord($Locals_img { 9 });

$byte = ord($Locals_img [9]);
$byte |= 0x80;
$byte &= 0xF8;
$byte |= (ord($this->frameSources[$i] { 10 }) & 0x07);
$Locals_img { 9 } = chr($byte);
$byte |= (ord($this->frameSources[$i] [ 10 ]) & 0x07);
$Locals_img [9] = chr($byte);
$this->gif .= $Locals_ext.$Locals_img.$Locals_rgb.$Locals_tmp;
}

} else {

$this->gif .= $Locals_ext.$Locals_img.$Locals_tmp;
}

$this->imgBuilt = true;
}

/**
* Add the gif string footer char
*/
protected function gifAddFooter()
{
$this->gif .= ';';
}

/**
* Compare two blocks and return 1 if they are equal, 0 if differ.
*
*
* @param string $globalBlock
* @param string $localBlock
* @param integer $length
*
*
* @return integer
*/
protected function gifBlockCompare($globalBlock, $localBlock, $length)
{
for ($i = 0; $i < $length; $i++) {

if ($globalBlock [ 3 * $i + 0 ] != $localBlock [ 3 * $i + 0 ] ||
$globalBlock [ 3 * $i + 1 ] != $localBlock [ 3 * $i + 1 ] ||
$globalBlock [ 3 * $i + 2 ] != $localBlock [ 3 * $i + 2 ]) {

return 0;
}
}

return 1;
}

}

/**
* Convert an integer to 2-byte little-endian binary data
*
*
* @param integer $word Number to encode
*
*
* @return string of 2 bytes representing @word as binary data
*/
function word2bin($word)
Expand Down