1
0
mirror of https://github.com/processwire/processwire.git synced 2025-08-15 11:14:12 +02:00

Add new module ImageSizerEngineAnimatedGif by @horst-n for animated GIF support in image resizes

This commit is contained in:
Ryan Cramer
2018-01-31 09:13:21 -05:00
parent 03660974ee
commit bbca8f5669
4 changed files with 974 additions and 0 deletions

View File

@@ -0,0 +1,357 @@
<?php namespace ProcessWire;
/**
* ImageSizer Engine Animated GIF by Horst
*
* This module supports resizing and cropping of animated GIFs when using GD-Library
* (The GD-Library does not support this)
*
* This module is based upon the work of
*
* * László Zsidi (initial classes)
* http://www.gifs.hu/
* http://www.phpclasses.org/gifsplit
* http://www.phpclasses.org/gifmerge
* (License: Apache 2.0)
*
* * xurei (enhanced classes)
* https://github.com/xurei/GIFDecoder_optimized
* (License: Apache 2.0)
*
* Ported first to ProcessWire module & then to ImageSizerEngine module by Horst Nogajski
*
* https://processwire.com/talk/topic/8386-image-animated-gif/
*/
class ImageSizerEngineAnimatedGif extends ImageSizerEngine {
public static function getModuleInfo() {
return array(
'title' => 'Animated GIF Image Sizer',
'version' => 1,
'summary' => "Upgrades image manipulations for animated GIFs.",
'author' => 'Horst Nogajski',
);
}
/**
* Class constructor
*
*/
public function __construct() {
parent::__construct();
$this->set('enginePriority', 9); // use a late priority so that optional other installed engines get the job (first) if they support animated gifs
}
/**
* Get valid image source formats
*
* @return array
*
*/
protected function validSourceImageFormats() {
return array('GIF');
}
/**
* Get valid target image formats
*
* @return array
*
*/
protected function validTargetImageFormats() {
return $this->validSourceImageFormats();
}
/**
* Is GD supported?
*
* @param string $action
* @return bool
*
*/
public function supported($action = 'imageformat') {
// first we check parts that are mandatory for all $actions
if(!function_exists('gd_info')) return false;
// and if it passes the mandatory requirements, we check particularly aspects here
switch($action) {
case 'imageformat':
// compare current imagefile infos fetched from ImageInspector
$requested = $this->getImageInfo(false);
switch($requested) {
case 'gif-anim':
case 'gif-trans-anim':
return true;
default:
return false;
}
break;
case 'install':
return true;
default:
return false;
}
}
/**
* Process the image resize
*
* Processing is as follows:
* 1. first do a check if the given image(type) can be processed, if not do an early return false
* 2. than (try) to process all required steps, if one failes, return false
* 3. if all is successful, finally return true
*
* @param string $srcFilename Source file
* @param string $dstFilename Destination file
* @param int $fullWidth Current width
* @param int $fullHeight Current height
* @param int $finalWidth Requested final width
* @param int $finalHeight Requested final height
* @return bool True if successful, false if not
* @throws WireException
*
*/
protected function processResize($srcFilename, $dstFilename, $fullWidth, $fullHeight, $finalWidth, $finalHeight) {
$this->modified = false;
if(isset($this->info['bits'])) $this->imageDepth = $this->info['bits'];
$this->imageFormat = strtoupper(str_replace('image/', '', $this->info['mime']));
if(!in_array($this->imageFormat, $this->validSourceImageFormats())) {
throw new WireException(sprintf($this->_("loaded file '%s' is not in the list of valid images"), basename($dstFilename)));
}
require_once(__DIR__ . '/gif_encoder.php');
require_once(__DIR__ . '/gif_decoder.php');
$this->setTimeLimit(120);
// if extra crop manipulation is requested, it is processed first
if(is_array($this->cropExtra) && 4 == count($this->cropExtra)) { // crop before resize
list($cropX, $cropY, $cropWidth, $cropHeight) = $this->cropExtra;
$bg = null;
$gif = new ISEAG_GIFDecoder(file_get_contents($srcFilename));
$originalFramesMeta = $gif->GIFGetFramesMeta();
if(count($originalFramesMeta) <= 0) return false;
$this->meta = array(
'delays' => $gif->GIFGetDelays(),
'loops' => $gif->GIFGetLoop(),
'disposal' => $gif->GIFGetDisposal(),
'tr' => $gif->GIFGetTransparentR(),
'tg' => $gif->GIFGetTransparentG(),
'tb' => $gif->GIFGetTransparentB(),
'trans' => (0 == $gif->GIFGetTransparentI() ? false : true)
);
$originalFrames = $gif->GIFGetFrames();
$newFrames = array();
foreach($originalFrames as $k => $v) {
$frame = @imagecreatefromstring($v);
if(!is_resource($frame)) continue;
if(!is_resource($bg)) {
$bg = imagecreatetruecolor($fullWidth, $fullHeight);
$this->prepareGDimage($bg);
}
$srcX = 0;
$srcY = 0;
$srcW = imagesx($frame);
$srcH = imagesy($frame);
$dstX = $originalFramesMeta[$k]['left'];
$dstY = $originalFramesMeta[$k]['top'];
$dstW = $originalFramesMeta[$k]['width'];
$dstH = $originalFramesMeta[$k]['height'];
imagecopy($bg, $frame, $dstX, $dstY, $srcX, $srcY, $srcW, $srcH);
$newimg = imagecreatetruecolor($cropWidth, $cropHeight);
$this->prepareGDimage($newimg);
imagecopy($newimg, $bg, 0, 0, $cropX, $cropY, $cropWidth, $cropHeight);
array_push($newFrames, $newimg);
$originalFrames[$k] = null;
}
if(count($newFrames) > 0) {
$frames = array();
foreach($newFrames as $nf) {
if(!is_resource($nf)) continue;
ob_start();
imagegif($nf);
$gifdata = ob_get_clean();
array_push($frames, $gifdata);
@imagedestroy($nf);
}
$gifmerge = new ISEAG_GIFEncoder(
$frames,
$this->meta['delays'],
$this->meta['loops'],
$this->meta['disposal'],
$this->meta['tr'], $this->meta['tg'], $this->meta['tb'],
'bin'
);
$result = false === fwrite(fopen($srcFilename, 'wb'), $gifmerge->GetAnimation()) ? false : true;
if($result) {
$fullWidth = $cropWidth;
$fullHeight = $cropHeight;
$this->image = array('width' => $fullWidth, 'height' => $fullHeight);
}
} else {
// $result = false;
}
if(isset($bg) && is_resource($bg)) @imagedestroy($bg);
if(isset($frame) && is_resource($frame)) @imagedestroy($frame);
if(isset($newimg) && is_resource($newimg)) @imagedestroy($newimg);
unset($gif, $gifmerge, $originalFrames, $originalFramesMeta, $newFrames, $cropHeight, $cropWidth, $cropX, $cropY, $dstH, $dstW, $dstX, $dstY, $frames, $nf, $srcH, $srcW, $srcX, $srcY);
$this->meta = null;
}
// regular resize / crop manipulation starts here
$bgX = $bgY = 0;
$bgWidth = $fullWidth;
$bgHeight = $fullHeight;
$resizemethod = $this->getResizeMethod($bgWidth, $bgHeight, $finalWidth, $finalHeight, $bgX, $bgY);
if(0 == $resizemethod) return true; // if same size or disallowed greater size is requested, we stop here and leave the original copy as is
$gif = new ISEAG_GIFdecoder(file_get_contents($srcFilename));
$originalFramesMeta = $gif->GIFGetFramesMeta();
if(count($originalFramesMeta) <= 0) return false;
$this->meta = array(
'delays' => $gif->GIFGetDelays(),
'loops' => $gif->GIFGetLoop(),
'disposal' => $gif->GIFGetDisposal(),
'tr' => $gif->GIFGetTransparentR(),
'tg' => $gif->GIFGetTransparentG(),
'tb' => $gif->GIFGetTransparentB(),
'trans' => (0 == $gif->GIFGetTransparentI() ? false : true)
);
$originalFrames = $gif->GIFGetFrames();
$newFrames = array();
if(2 == $resizemethod) { // 2 = resize with aspect ratio
$bg = null;
// $ratio = 1.0;
$ratio_w = $fullWidth / $finalWidth;
$ratio_h = $fullHeight / $finalHeight;
$ratio = ($ratio_h > $ratio_w ? $ratio_h : $ratio_w);
foreach($originalFrames as $k => $v) {
$frame = @imagecreatefromstring($v);
if(!is_resource($frame)) continue;
$newimg = imagecreatetruecolor($finalWidth, $finalHeight);
$this->prepareGDimage($newimg);
if(is_resource($bg)) {
imagecopy($newimg, $bg, 0, 0, 0, 0, $finalWidth, $finalHeight);
}
$srcX = 0;
$srcY = 0;
$srcW = imagesx($frame);
$srcH = imagesy($frame);
$dstX = floor($originalFramesMeta[$k]['left'] / $ratio);
$dstY = floor($originalFramesMeta[$k]['top'] / $ratio);
$dstW = ceil($originalFramesMeta[$k]['width'] / $ratio);
$dstH = ceil($originalFramesMeta[$k]['height'] / $ratio);
imagecopyresampled($newimg, $frame, $dstX, $dstY, $srcX, $srcY, $dstW, $dstH, $srcW, $srcH);
array_push($newFrames, $newimg);
if(!is_resource($bg)) {
$bg = imagecreatetruecolor($finalWidth, $finalHeight);
$this->prepareGDimage($bg);
}
imagecopy($bg, $newimg, 0, 0, 0, 0, $finalWidth, $finalHeight);
$originalFrames[$k] = null;
}
}
if(4 == $resizemethod) { // 4 = resize and crop from center with aspect ratio
$bg = null;
// $ratio = 1.0;
$ratio_w = $fullWidth / $bgWidth;
$ratio_h = $fullHeight / $bgHeight;
$ratio = ($ratio_h > $ratio_w ? $ratio_h : $ratio_w);
foreach($originalFrames as $k => $v) {
$frame = @imagecreatefromstring($v);
if(!is_resource($frame)) continue;
$newimg = imagecreatetruecolor($bgWidth, $bgHeight);
$this->prepareGDimage($newimg);
if(is_resource($bg)) {
imagecopy($newimg, $bg, 0, 0, 0, 0, $bgWidth, $bgHeight);
}
$srcX = 0;
$srcY = 0;
$srcW = imagesx($frame);
$srcH = imagesy($frame);
$dstX = floor($originalFramesMeta[$k]['left'] / $ratio);
$dstY = floor($originalFramesMeta[$k]['top'] / $ratio);
$dstW = ceil($originalFramesMeta[$k]['width'] / $ratio);
$dstH = ceil($originalFramesMeta[$k]['height'] / $ratio);
imagecopyresampled($newimg, $frame, $dstX, $dstY, $srcX, $srcY, $dstW, $dstH, $srcW, $srcH);
if(!is_resource($bg)) {
$bg = imagecreatetruecolor($bgWidth, $bgHeight);
$this->prepareGDimage($bg);
}
imagecopy($bg, $newimg, 0, 0, 0, 0, $bgWidth, $bgHeight);
$newimg = imagecreatetruecolor($finalWidth, $finalHeight);
$this->prepareGDimage($newimg);
imagecopy($newimg, $bg, 0, 0, $bgX, $bgY, $finalWidth, $finalHeight);
array_push($newFrames, $newimg);
$originalFrames[$k] = null;
}
}
if(count($newFrames) > 0) {
$frames = array();
foreach($newFrames as $nf) {
if(!is_resource($nf)) continue;
ob_start();
imagegif($nf);
$gifdata = ob_get_clean();
array_push($frames, $gifdata);
@imagedestroy($nf);
}
$gifmerge = new ISEAG_GIFEncoder(
$frames,
$this->meta['delays'],
$this->meta['loops'],
$this->meta['disposal'],
$this->meta['tr'], $this->meta['tg'], $this->meta['tb'],
'bin'
);
$result = false === fwrite(fopen($dstFilename, 'wb'), $gifmerge->GetAnimation()) ? false : true;
} else {
$result = false;
}
if(isset($bg) && is_resource($bg)) @imagedestroy($bg);
if(isset($frame) && is_resource($frame)) @imagedestroy($frame);
if(isset($newimg) && is_resource($newimg)) @imagedestroy($newimg);
if(!$result) {
return false;
}
$this->modified = true;
return true;
}
/**
* Helper for transparent background preparation
*
* @param resource $gdimage by reference $gdimage
* @return void
*
*/
protected function prepareGDimage(&$gdimage) {
if(!$this->meta['trans']) return;
$transparentNew = imagecolorallocate($gdimage, $this->meta['tr'], $this->meta['tg'], $this->meta['tb']);
$transparentNewIndex = imagecolortransparent($gdimage, $transparentNew);
imagefill($gdimage, 0, 0, $transparentNewIndex);
}
/**
* Module install
*
* @throws WireException
*
*/
public function ___install() {
if(!$this->supported('install')) {
throw new WireException("This module requires that you have PHP's GD image library bundled or installed");
}
}
}

View File

@@ -0,0 +1,352 @@
<?php namespace ProcessWire;
/*
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
::
:: GIFDecoder Version 2.0 by László Zsidi
::
:: Created at 2007. 02. 01. '07.47.AM'
::
:: Updated at 2009. 06. 23. '06.00.AM'
::
:: * Optimized version by xurei
:: https://github.com/xurei/GIFDecoder_optimized
:: Updated at 2015-04-13
::
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
LICENSE:
All versions specify Apache 2.0 license (2018-01-31):
https://github.com/jacoka/GIFDecoder/blob/master/LICENSE
https://github.com/xurei/GIFDecoder_optimized/blob/master/LICENSE
Namespace added by Horst for ProcessWire
*/
class ISEAG_GIFDecoder {
var $GIF_TransparentR = -1;
var $GIF_TransparentG = -1;
var $GIF_TransparentB = -1;
var $GIF_TransparentI = 0;
var $GIF_buffer = null;
var $GIF_arrays = Array ( );
var $GIF_delays = Array ( );
var $GIF_dispos = Array ( );
var $GIF_stream = "";
var $GIF_string = "";
var $GIF_bfseek = 0;
var $GIF_anloop = 0;
//xurei - frames metadata
var $GIF_frames_meta = Array();
var $GIF_screen = Array ( );
var $GIF_global = Array ( ); //global color map
var $GIF_sorted;
var $GIF_colorS; //
var $GIF_colorC; //Size of global color table
var $GIF_colorF; //if 1, global color table follows image descriptor
/*
:::::::::::::::::::::::::::::::::::::::::::::::::::
::
:: GIFDecoder ( $GIF_pointer )
::
*/
function __construct ( $GIF_pointer ) {
$this->GIF_stream = $GIF_pointer;
ISEAG_GIFDecoder::GIFGetByte ( 6 );
ISEAG_GIFDecoder::GIFGetByte ( 7 );
$this->GIF_screen = $this->GIF_buffer;
$this->GIF_colorF = $this->GIF_buffer [ 4 ] & 0x80 ? 1 : 0;
$this->GIF_sorted = $this->GIF_buffer [ 4 ] & 0x08 ? 1 : 0;
$this->GIF_colorC = $this->GIF_buffer [ 4 ] & 0x07;
$this->GIF_colorS = 2 << $this->GIF_colorC;
if ( $this->GIF_colorF == 1 ) {
ISEAG_GIFDecoder::GIFGetByte ( 3 * $this->GIF_colorS );
$this->GIF_global = $this->GIF_buffer;
}
for ( $cycle = 1; $cycle; ) {
if ( ISEAG_GIFDecoder::GIFGetByte ( 1 ) ) {
switch ( $this->GIF_buffer [ 0 ] ) {
case 0x21:
ISEAG_GIFDecoder::GIFReadExtensions ( );
break;
case 0x2C:
ISEAG_GIFDecoder::GIFReadDescriptor ( );
break;
case 0x3B:
$cycle = 0;
break;
}
}
else {
$cycle = 0;
}
}
}
/*
:::::::::::::::::::::::::::::::::::::::::::::::::::
::
:: GIFReadExtension ( )
::
*/
function GIFReadExtensions ( ) {
ISEAG_GIFDecoder::GIFGetByte ( 1 );
if ( $this->GIF_buffer [ 0 ] == 0xff ) {
for ( ; ; ) {
ISEAG_GIFDecoder::GIFGetByte ( 1 );
if ( ( $u = $this->GIF_buffer [ 0 ] ) == 0x00 ) {
break;
}
ISEAG_GIFDecoder::GIFGetByte ( $u );
if ( $u == 0x03 ) {
$this->GIF_anloop = ( $this->GIF_buffer [ 1 ] | $this->GIF_buffer [ 2 ] << 8 );
}
}
}
else {
for ( ; ; ) {
ISEAG_GIFDecoder::GIFGetByte ( 1 );
if ( ( $u = $this->GIF_buffer [ 0 ] ) == 0x00 ) {
break;
}
ISEAG_GIFDecoder::GIFGetByte ( $u );
if ( $u == 0x04 ) {
$buf4 = count($this->GIF_buffer) >= 5 ? $this->GIF_buffer [ 4 ] : 0;
if ( $buf4 & 0x80 ) {
$this->GIF_dispos [ ] = ( $this->GIF_buffer [ 0 ] >> 2 ) - 1;
}
else {
$this->GIF_dispos [ ] = ( $this->GIF_buffer [ 0 ] >> 2 ) - 0;
}
$this->GIF_delays [ ] = ( $this->GIF_buffer [ 1 ] | $this->GIF_buffer [ 2 ] << 8 );
if ( $this->GIF_buffer [ 3 ] ) {
$this->GIF_TransparentI = $this->GIF_buffer [ 3 ];
}
}
}
}
}
/*
:::::::::::::::::::::::::::::::::::::::::::::::::::
::
:: GIFReadDescriptor ( )
::
*/
function GIFReadDescriptor ( ) {
$GIF_screen = Array ( );
ISEAG_GIFDecoder::GIFGetByte ( 9 );
//xurei - metadata saving
$this->GIF_frames_meta[] = array(
'left'=>$this->GIF_buffer[0] + ($this->GIF_buffer[1] << 8),
'top'=>$this->GIF_buffer[2] + ($this->GIF_buffer[3] << 8),
'width'=>$this->GIF_buffer[4] + ($this->GIF_buffer[5] << 8),
'height'=>$this->GIF_buffer[6] + ($this->GIF_buffer[7] << 8),
);
$GIF_screen = $this->GIF_buffer;
$GIF_colorF = $this->GIF_buffer [ 8 ] & 0x80 ? 1 : 0;
if ( $GIF_colorF ) {
$GIF_code = $this->GIF_buffer [ 8 ] & 0x07;
$GIF_sort = $this->GIF_buffer [ 8 ] & 0x20 ? 1 : 0;
}
else {
$GIF_code = $this->GIF_colorC;
$GIF_sort = $this->GIF_sorted;
}
$GIF_size = 2 << $GIF_code;
$this->GIF_screen [ 4 ] &= 0x70;
$this->GIF_screen [ 4 ] |= 0x80;
$this->GIF_screen [ 4 ] |= $GIF_code;
if ( $GIF_sort ) {
$this->GIF_screen [ 4 ] |= 0x08;
}
/*
*
* GIF Data Begin
*
*/
if ( $this->GIF_TransparentI ) {
$this->GIF_string = "GIF89a";
}
else {
$this->GIF_string = "GIF87a";
}
ISEAG_GIFDecoder::GIFPutByte ( $this->GIF_screen );
if ( $GIF_colorF == 1 ) {
ISEAG_GIFDecoder::GIFGetByte ( 3 * $GIF_size );
if ( $this->GIF_TransparentI ) {
$this->GIF_TransparentR = $this->GIF_buffer [ 3 * $this->GIF_TransparentI + 0 ];
$this->GIF_TransparentG = $this->GIF_buffer [ 3 * $this->GIF_TransparentI + 1 ];
$this->GIF_TransparentB = $this->GIF_buffer [ 3 * $this->GIF_TransparentI + 2 ];
}
ISEAG_GIFDecoder::GIFPutByte ( $this->GIF_buffer );
}
else {
if ( $this->GIF_TransparentI ) {
$this->GIF_TransparentR = $this->GIF_global [ 3 * $this->GIF_TransparentI + 0 ];
$this->GIF_TransparentG = $this->GIF_global [ 3 * $this->GIF_TransparentI + 1 ];
$this->GIF_TransparentB = $this->GIF_global [ 3 * $this->GIF_TransparentI + 2 ];
}
ISEAG_GIFDecoder::GIFPutByte ( $this->GIF_global );
}
if ( $this->GIF_TransparentI ) {
$this->GIF_string .= "!\xF9\x04\x1\x0\x0". chr ( $this->GIF_TransparentI ) . "\x0";
}
$this->GIF_string .= chr ( 0x2C );
$GIF_screen [ 8 ] &= 0x40;
ISEAG_GIFDecoder::GIFPutByte ( $GIF_screen );
ISEAG_GIFDecoder::GIFGetByte ( 1 );
ISEAG_GIFDecoder::GIFPutByte ( $this->GIF_buffer );
for ( ; ; ) {
ISEAG_GIFDecoder::GIFGetByte ( 1 );
ISEAG_GIFDecoder::GIFPutByte ( $this->GIF_buffer );
if ( ( $u = $this->GIF_buffer [ 0 ] ) == 0x00 ) {
break;
}
/*for ($i=0; $i!=$u; ++$i)
{
$this->GIF_string .= $this->GIF_stream { $this->GIF_bfseek++ };
}*/
$this->GIF_string .= substr($this->GIF_stream, $this->GIF_bfseek, $u);
$this->GIF_bfseek += $u;
//GIFDecoder::GIFGetByte ( $u );
//GIFDecoder::GIFPutByte ( $this->GIF_buffer );
}
$this->GIF_string .= chr ( 0x3B );
/*
*
* GIF Data End
*
*/
$this->GIF_arrays [ ] = $this->GIF_string;
}
/*
:::::::::::::::::::::::::::::::::::::::::::::::::::
::
:: GIFGetByte ( $len )
::
*/
function GIFGetByte ( $len ) {
$this->GIF_buffer = new \SplFixedArray($len);
$l = strlen ( $this->GIF_stream );
for ( $i = 0; $i < $len; $i++ ) {
if ( $this->GIF_bfseek > $l ) {
$this->GIF_buffer->setSize($i);
return 0;
}
$this->GIF_buffer [$i] = ord ( $this->GIF_stream { $this->GIF_bfseek++ } );
}
return 1;
}
/*
:::::::::::::::::::::::::::::::::::::::::::::::::::
::
:: GIFPutByte ( $bytes )
::
*/
function GIFPutByte ( $bytes ) {
$out = '';
foreach ( $bytes as $byte ) {
$out .= chr($byte);
}
$this->GIF_string .= $out;
}
/*
:::::::::::::::::::::::::::::::::::::::::::::::::::
::
:: PUBLIC FUNCTIONS
::
::
:: GIFGetFrames ( )
::
*/
function GIFGetFrames ( ) {
return ( $this->GIF_arrays );
}
/*
:::::::::::::::::::::::::::::::::::::::::::::::::::
::
:: GIFGetFramesMeta ( )
::
:: xurei - returns metadata as an array of arrays
*/
function GIFGetFramesMeta ( ) {
return ( $this->GIF_frames_meta );
}
/*
:::::::::::::::::::::::::::::::::::::::::::::::::::
::
:: GIFGetDelays ( )
::
*/
function GIFGetDelays ( ) {
return ( $this->GIF_delays );
}
/*
:::::::::::::::::::::::::::::::::::::::::::::::::::
::
:: GIFGetLoop ( )
::
*/
function GIFGetLoop ( ) {
return ( $this->GIF_anloop );
}
/*
:::::::::::::::::::::::::::::::::::::::::::::::::::
::
:: GIFGetDisposal ( )
::
*/
function GIFGetDisposal ( ) {
return ( $this->GIF_dispos );
}
/*
:::::::::::::::::::::::::::::::::::::::::::::::::::
::
:: GIFGetTransparentR ( )
::
*/
function GIFGetTransparentR ( ) {
return ( $this->GIF_TransparentR );
}
/*
:::::::::::::::::::::::::::::::::::::::::::::::::::
::
:: GIFGetTransparentG ( )
::
*/
function GIFGetTransparentG ( ) {
return ( $this->GIF_TransparentG );
}
/*
:::::::::::::::::::::::::::::::::::::::::::::::::::
::
:: GIFGetTransparentB ( )
::
*/
function GIFGetTransparentB ( ) {
return ( $this->GIF_TransparentB );
}
/*
:::::::::::::::::::::::::::::::::::::::::::::::::::
::
:: GIFGetTransparentI ( )
::
*/
function GIFGetTransparentI ( ) {
return ( $this->GIF_TransparentI );
}
}

View File

@@ -0,0 +1,265 @@
<?php namespace ProcessWire;
/*
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
::
:: ISEAG_GIFEncoder Version 2.0 by László Zsidi, http://gifs.hu
::
:: This class is a rewritten 'GifMerge.class.php' version.
::
:: Modification:
:: - Simplified and easy code,
:: - Ultra fast encoding,
:: - Built-in errors,
:: - Stable working
::
::
:: Updated at 2007. 02. 13. '00.05.AM'
::
:: * Enhanced version by xurei (https://github.com/xurei/GIFDecoder_optimized)
::
:: Try on-line GIFBuilder Form demo based on ISEAG_GIFEncoder.
::
:: http://gifs.hu/phpclasses/demos/GifBuilder/
::
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
LICENSE:
All versions specify Apache 2.0 license (2018-01-31):
https://github.com/jacoka/GIFDecoder/blob/master/LICENSE
https://github.com/xurei/GIFDecoder_optimized/blob/master/LICENSE
Namespace added by Horst for ProcessWire
*/
class ISEAG_GIFEncoder {
var $GIF = "GIF89a"; /* GIF header 6 bytes */
var $VER = "ISEAG_GIFEncoder V2.05"; /* Encoder version */
var $BUF = Array ( );
var $LOP = 0;
var $DIS = 2;
//var $COL = -1;
var $TRANSPARENT_R = -1;
var $TRANSPARENT_G = -1;
var $TRANSPARENT_B = -1;
var $IMG = -1;
var $ERR = Array (
'ERR00'=>"Does not supported function for only one image!",
'ERR01'=>"Source is not a GIF image!",
'ERR02'=>"Unintelligible flag ",
'ERR03'=>"Does not make animation from animated GIF source",
);
/*
:::::::::::::::::::::::::::::::::::::::::::::::::::
::
:: ISEAG_GIFEncoder...
::
*/
function __construct (
$GIF_src, $GIF_dly, $GIF_lop, $GIF_dis,
$GIF_red, $GIF_grn, $GIF_blu, $GIF_mod
) {
if ( ! is_array ( $GIF_src ) && ! is_array ( $GIF_dly ) ) {
printf ( "%s: %s", $this->VER, $this->ERR [ 'ERR00' ] );
exit ( 0 );
}
$this->LOP = ( $GIF_lop > -1 ) ? $GIF_lop : 0;
$GIF_dis_out = $GIF_dis;
foreach ($GIF_dis_out as &$dis)
{
$dis = ( $dis > -1 ) ? ( ( $dis < 3 ) ? $dis : 3 ) : 2;
}
$this->DIS = $GIF_dis_out;//( $GIF_dis > -1 ) ? ( ( $GIF_dis < 3 ) ? $GIF_dis : 3 ) : 2;
/*$this->COL = ( $GIF_red > -1 && $GIF_grn > -1 && $GIF_blu > -1 ) ?
( $GIF_red | ( $GIF_grn << 8 ) | ( $GIF_blu << 16 ) ) : -1;*/
if ( $GIF_red > -1 && $GIF_grn > -1 && $GIF_blu > -1 )
{
$this->TRANSPARENT_R = $GIF_red & 0xFF;
$this->TRANSPARENT_G = $GIF_grn & 0xFF;
$this->TRANSPARENT_B = $GIF_blu & 0xFF;
}
//Filling in the buffer
for ( $i = 0; $i < count ( $GIF_src ); $i++ ) {
if ( strToLower ( $GIF_mod ) == "url" ) {
$this->BUF [ ] = fread ( fopen ( $GIF_src [ $i ], "rb" ), filesize ( $GIF_src [ $i ] ) );
}
else if ( strToLower ( $GIF_mod ) == "bin" ) {
$this->BUF [ ] = $GIF_src [ $i ];
}
else {
printf ( "%s: %s ( %s )!", $this->VER, $this->ERR [ 'ERR02' ], $GIF_mod );
exit ( 0 );
}
if ( substr ( $this->BUF [ $i ], 0, 6 ) != "GIF87a" && substr ( $this->BUF [ $i ], 0, 6 ) != "GIF89a" ) {
printf ( "%s: %d %s", $this->VER, $i, $this->ERR [ 'ERR01' ] );
exit ( 0 );
}
for ( $j = ( 13 + 3 * ( 2 << ( ord ( $this->BUF [ $i ] { 10 } ) & 0x07 ) ) ), $k = TRUE; $k; $j++ ) {
switch ( $this->BUF [ $i ] { $j } ) {
case "!":
if ( ( substr ( $this->BUF [ $i ], ( $j + 3 ), 8 ) ) == "NETSCAPE" ) {
printf ( "%s: %s ( %s source )!", $this->VER, $this->ERR [ 'ERR03' ], ( $i + 1 ) );
exit ( 0 );
}
break;
case ";":
$k = FALSE;
break;
}
}
}
ISEAG_GIFEncoder::GIFAddHeader ( );
for ( $i = 0; $i < count ( $this->BUF ); $i++ ) {
ISEAG_GIFEncoder::GIFAddFrames ( $i, $GIF_dly[$i] );
}
ISEAG_GIFEncoder::GIFAddFooter ( );
}
/*
:::::::::::::::::::::::::::::::::::::::::::::::::::
::
:: GIFAddHeader...
::
*/
function GIFAddHeader ( ) {
$cmap = 0;
if ( ord ( $this->BUF [ 0 ] { 10 } ) & 0x80 ) {
$cmap = 3 * ( 2 << ( ord ( $this->BUF [ 0 ] { 10 } ) & 0x07 ) );
$this->GIF .= substr ( $this->BUF [ 0 ], 6, 7 );
$this->GIF .= substr ( $this->BUF [ 0 ], 13, $cmap );
$this->GIF .= "!\377\13NETSCAPE2.0\3\1" . ISEAG_GIFEncoder::GIFWord ( $this->LOP ) . "\0";
}
}
/*
:::::::::::::::::::::::::::::::::::::::::::::::::::
::
:: GIFAddFrames...
::
*/
function GIFAddFrames ( $i, $d ) {
$Locals_str = 13 + 3 * ( 2 << ( ord ( $this->BUF [ $i ] { 10 } ) & 0x07 ) );
$Locals_end = strlen ( $this->BUF [ $i ] ) - $Locals_str - 1;
$Locals_tmp = substr ( $this->BUF [ $i ], $Locals_str, $Locals_end );
$Global_len = 2 << ( ord ( $this->BUF [ 0 ] { 10 } ) & 0x07 );
$Locals_len = 2 << ( ord ( $this->BUF [ $i ] { 10 } ) & 0x07 );
$Global_rgb = substr ( $this->BUF [ 0 ], 13,
3 * ( 2 << ( ord ( $this->BUF [ 0 ] { 10 } ) & 0x07 ) ) );
$Locals_rgb = substr ( $this->BUF [ $i ], 13,
3 * ( 2 << ( ord ( $this->BUF [ $i ] { 10 } ) & 0x07 ) ) );
$Locals_ext = "!\xF9\x04" . chr ( ( $this->DIS[$i] << 2 ) + 0 ) .
chr ( ( $d >> 0 ) & 0xFF ) . chr ( ( $d >> 8 ) & 0xFF ) . "\x0\x0";
if ( $this->TRANSPARENT_R > -1 && ord ( $this->BUF [ $i ] { 10 } ) & 0x80 ) {
for ( $j = 0; $j < ( 2 << ( ord ( $this->BUF [ $i ] { 10 } ) & 0x07 ) ); $j++ ) {
if (
ord ( $Locals_rgb { 3 * $j + 0 } ) == $this->TRANSPARENT_R &&
ord ( $Locals_rgb { 3 * $j + 1 } ) == $this->TRANSPARENT_G &&
ord ( $Locals_rgb { 3 * $j + 2 } ) == $this->TRANSPARENT_B )
{
$Locals_ext = "!\xF9\x04" . chr ( ( $this->DIS[$i] << 2 ) ) .
chr ( ( $d >> 0 ) & 0xFF ) . chr ( ( $d >> 8 ) & 0xFF ) . chr ( $j ) . "\x0";
break;
}
}
}
switch ( $Locals_tmp { 0 } ) {
case "!":
$Locals_img = substr ( $Locals_tmp, 8, 10 );
$Locals_ext[3] = chr((ord($Locals_ext[3]) & 0xFE) | (ord($Locals_tmp[3]) & 0x01));
$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->BUF [ $i ] { 10 } ) & 0x80 && $this->IMG > -1 ) {
if ( $Global_len == $Locals_len ) {
if ( ISEAG_GIFEncoder::GIFBlockCompare ( $Global_rgb, $Locals_rgb, $Global_len ) ) {
$this->GIF .= ( $Locals_ext . $Locals_img . $Locals_tmp );
}
else {
$byte = ord ( $Locals_img { 9 } );
$byte |= 0x80;
$byte &= 0xF8;
$byte |= ( ord ( $this->BUF [ 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 |= 0x80;
$byte &= 0xF8;
$byte |= ( ord ( $this->BUF [ $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->IMG = 1;
}
/*
:::::::::::::::::::::::::::::::::::::::::::::::::::
::
:: GIFAddFooter...
::
*/
function GIFAddFooter ( ) {
$this->GIF .= ";";
}
/*
:::::::::::::::::::::::::::::::::::::::::::::::::::
::
:: GIFBlockCompare...
::
*/
function GIFBlockCompare ( $GlobalBlock, $LocalBlock, $Len ) {
for ( $i = 0; $i < $Len; $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 );
}
/*
:::::::::::::::::::::::::::::::::::::::::::::::::::
::
:: GIFWord...
::
*/
function GIFWord ( $int ) {
return ( chr ( $int & 0xFF ) . chr ( ( $int >> 8 ) & 0xFF ) );
}
/*
:::::::::::::::::::::::::::::::::::::::::::::::::::
::
:: GetAnimation...
::
*/
function GetAnimation ( ) {
return ( $this->GIF );
}
}