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:
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
352
wire/modules/Image/ImageSizerEngineAnimatedGif/gif_decoder.php
Executable file
352
wire/modules/Image/ImageSizerEngineAnimatedGif/gif_decoder.php
Executable 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 );
|
||||
}
|
||||
}
|
||||
|
||||
|
265
wire/modules/Image/ImageSizerEngineAnimatedGif/gif_encoder.php
Executable file
265
wire/modules/Image/ImageSizerEngineAnimatedGif/gif_encoder.php
Executable 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 );
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user