Skip to content

Instantly share code, notes, and snippets.

@zllovesuki
Last active August 29, 2015 14:02
Show Gist options
  • Save zllovesuki/5113b95d599faa830fa9 to your computer and use it in GitHub Desktop.
Save zllovesuki/5113b95d599faa830fa9 to your computer and use it in GitHub Desktop.
<?php
require_once(dirname(__FILE__) . '/icc.php');
class Darkroom {
public static $presets = array(
'tiny' => array(
'width' => 60,
'height' => 60
),
'small' => array(
'width' => 100,
'height' => 100
),
'medium' => array(
'width' => 480,
'height' => 480
),
'medium_large' => array(
'width' => 800,
'height' => 800
),
'large' => array(
'width' => 1024,
'height' => 1024
),
'xlarge' => array(
'width' => 1600,
'height' => 1600
),
'huge' => array(
'width' => 2048,
'height' => 2048
)
);
private static function is_animated_gif($filename)
{
$raw = file_get_contents( $filename );
$offset = 0;
$frames = 0;
while ($frames < 2)
{
$where1 = strpos($raw, "\x00\x21\xF9\x04", $offset);
if ( $where1 === false )
{
break;
}
else
{
$offset = $where1 + 1;
$where2 = strpos( $raw, "\x00\x2C", $offset );
if ( $where2 === false )
{
break;
}
else
{
if ( $where1 + 8 == $where2 )
{
$frames ++;
}
$offset = $where2 + 1;
}
}
}
return $frames > 1;
}
////
// The workhorse develop function
////
static function develop($options, $kokenData = array()) {
$defaults = array(
'square' => false,
'force' => false,
'hires' => false,
'source_width' => false,
'source_height' => false,
'strip' => true,
'hooks' => true,
);
$o = array_merge($defaults, $options);
$old_mask = umask(0);
if ($o['hires'])
{
$o['width'] *= 2;
$o['height'] *= 2;
$o['quality'] = max(50, $o['quality'] - ($o['quality']*0.15));
$o['sharpening'] /= 2;
}
$o['sharpening'] = min(1, $o['sharpening']);
list($im_version, $lib) = self::processing_library_version();
if (!$o['source_width'] || !$o['source_height'])
{
list($o['source_width'], $o['source_height'], $source_image_type) = getimagesize($o['source']);
}
$original_aspect = $o['source_width']/$o['source_height'];
$new_aspect = $o['width']/$o['height'];
$pathinfo = pathinfo($o['source']);
$midsize = preg_replace('/\.' . $pathinfo['extension'] . '$/', '.1600.' . $pathinfo['extension'], $o['source']);
$has_midsize = file_exists($midsize);
if ($has_midsize)
{
if ($original_aspect >= 1)
{
$mid_w = 1600;
$mid_h = round(1600 / $original_aspect);
}
else
{
$mid_h = 1600;
$mid_h = round(1600 * $original_aspect);
}
}
if ($o['sharpening'] > 0 && ($lib === 'GD' || version_compare($im_version, '6.2.9', '>=')))
{
$sigma = $o['sharpening'] *= 1.1;
}
else
{
$o['sharpening'] = false;
}
if (!$o['strip'] && $options['width'] < 480 && $options['height'] < 480)
{
$o['strip'] = true;
}
if ($lib !== 'GD' && version_compare($im_version, '6.0.0') && $o['strip'])
{
$strip = ' -strip';
}
else
{
$strip = '';
}
$thread = false;
if ($lib === false)
{
exec(str_replace('convert', 'identify', MAGICK_PATH_FINAL) . ' -list resource', $limits);
if (strpos(strtolower($limits[0]), 'thread') !== false)
{
$thread = true;
$strip .= ' -limit thread 8';
}
}
else
{
// Up mem limit for Imagick and GD
$memory_limit = (int) ini_get('memory_limit');
if (is_numeric($memory_limit) && $memory_limit < 256)
{
@ini_set('memory_limit', '256M');
}
}
// TODO: If sufficient demand, implement this for Imagick and GD
// Imagick: http://us1.php.net/manual/ru/imagick.coalesceimages.php
// GD: http://stackoverflow.com/questions/718491/resize-animated-gif-file-without-destroying-animation
if (preg_match('/\.gif$/i', $o['source']) && $lib === false)
{
$coalesced = preg_replace('/\.gif$/i', '.coalesced.gif', $o['source']);
$has_c = file_exists($coalesced);
if ($has_c)
{
$o['source'] = $coalesced;
}
else if (self::is_animated_gif($o['source']))
{
exec(MAGICK_PATH_FINAL . ' ' . $o['source'] . ($thread ? ' -limit thread 8' : '') . ' -limit memory 20MB -limit map 20MB -coalesce ' . $coalesced);
$o['source'] = $coalesced;
}
}
if ((!empty($strip) || $lib === 'GD') && preg_match('/\.jpe?g$/i', $o['source']))
{
$icc = new JPEG_ICC;
$icc->LoadFromJpeg($o['source']);
}
if ($lib === 'Imagick')
{
$image = new Imagick();
$image->setResourceLimit(6, 8); // Threads
$image->setResourceLimit(IMagick::RESOURCETYPE_MAP, 33554432);
$image->setResourceLimit(IMagick::RESOURCETYPE_MEMORY, 67108864);
}
else if ($lib === 'GD')
{
if (!isset($source_image_type))
{
list(,,$source_image_type) = getimagesize($o['source']);
}
switch($source_image_type) {
case IMAGETYPE_JPEG:
$src_img = imagecreatefromjpeg($o['source']);
break;
case IMAGETYPE_PNG:
$src_img = imagecreatefrompng($o['source']);
break;
case IMAGETYPE_GIF:
$src_img = imagecreatefromgif($o['source']);
break;
}
if (!isset($src_img))
{
return false;
}
}
if ($o['square'])
{
$resize_h = $resize_w = 0;
if ($original_aspect >= $new_aspect) {
$resize_h = $o['height'];
$size_str = 'x' . $o['height'];
$int_w = ($o['source_width']*$o['height'])/$o['source_height'];
$int_h = $o['height'];
$pos_x = $int_w * ($o['focal_x']/100);
$pos_y = $o['height'] * ($o['focal_y']/100);
$hint_w = $int_w;
$hint_h = $o['height'];
} else {
$resize_w = $o['width'];
$size_str = $o['width'] . 'x';
$int_h = ($o['source_height']*$o['width'])/$o['source_width'];
$int_w = $o['width'];
$pos_x = $o['width'] * ($o['focal_x']/100);
$pos_y = $int_h * ($o['focal_y']/100);
$hint_w = $o['width'];
$hint_h = $int_w;
}
if ($has_midsize && $resize_w <= $mid_w && $resize_h <= $mid_h)
{
$o['source'] = $midsize;
}
$crop_y = $pos_y - ($o['height']/2);
$crop_x = $pos_x - ($o['width']/2);
if ($crop_y < 0) {
$crop_y = 0;
} else if (($crop_y+$o['height']) > $int_h) {
$crop_y = $int_h - $o['height'];
}
if ($crop_x < 0) {
$crop_x = 0;
} else if (($crop_x+$o['width']) > $int_w) {
$crop_x = $int_w - $o['width'];
}
if ($lib === 'Imagick')
{
$image->readImage($o['source']);
if ($o['strip'])
{
$image->stripImage();
}
$image->setImageCompressionQuality($o['quality']);
$image->scaleImage($resize_w, $resize_h, $bestfit);
$image->cropImage($o['width'], $o['height'], $crop_x, $crop_y);
$image->setImagePage($o['width'], $o['height'], 0, 0);
}
else if ($lib === 'GD')
{
$temp_img = self::create_gd_proportional($int_w, $int_h, $o['source_width'], $o['source_height'], $src_img, $source_image_type);
$final_img = imagecreatetruecolor($o['width'], $o['height']);
if ($source_image_type == IMAGETYPE_PNG)
{
$transparency = imagecolorallocatealpha($final_img, 0, 0, 0, 127);
imagefill($final_img, 0, 0, $transparency);
}
imagecopy($final_img, $temp_img, 0, 0, $crop_x, $crop_y, $o['width'], $o['height']);
imagedestroy($temp_img);
}
else
{
$cmd = MAGICK_PATH_FINAL . $strip . " -size {$hint_w}x{$hint_h} \"{$o['source']}\" -limit memory 33554432 -limit map 67108864 -depth 8 -density 72 -quality {$o['quality']} -resize $size_str -crop {$o['width']}x{$o['height']}+{$crop_x}+{$crop_y} -page 0+0";
}
$o['final_width'] = $o['width'];
$o['final_height'] = $o['height'];
}
else
{
$bestfit = true;
if ($o['width'] == 0 || $o['height'] == 0)
{
$hint_w = $hint_h = max($o['width'], $o['height']);
if ($lib !== 'GD')
{
if ($lib === false && version_compare($im_version, '6.3.8-2') < 0)
{
if ($o['width'] == 0)
{
$o['width'] = 10000;
}
else
{
$o['height'] = 10000;
}
}
else
{
$o['width'] = max($o['width'], 0);
$o['height'] = max($o['height'], 0) . '^';
}
$bestfit = false;
}
if ($o['width'] == 0)
{
$o['final_height'] = $o['height'];
$o['final_width'] = ($o['height']*$o['source_width'])/$o['source_height'];
}
else
{
$o['final_width'] = $o['width'];
$o['final_height'] = ($o['width']*$o['source_height'])/$o['source_width'];
}
}
else
{
if ((($original_aspect >= $new_aspect && $o['width'] > $o['source_width']) || ($original_aspect < $new_aspect && $o['height'] > $o['source_height'])) && !$o['force']) {
$o['width'] = $o['final_width'] = $hint_w = $o['source_width'];
$o['height'] = $o['final_height'] = $hint_h = $o['source_height'];
} else {
$hint_w = $o['width'];
$hint_h = $o['height'];
if ($original_aspect >= $new_aspect)
{
$o['final_width'] = $o['width'];
$o['final_height'] = ($o['width']*$o['source_height'])/$o['source_width'];
}
else
{
$o['final_width'] = ($o['height']*$o['source_width'])/$o['source_height'];
$o['final_height'] = $o['height'];
}
}
}
$o['final_width'] = round($o['final_width']);
$o['final_height'] = round($o['final_height']);
if ($has_midsize && $hint_w <= $mid_w && $hint_h <= $mid_h)
{
$o['source'] = $midsize;
}
if ($lib === 'Imagick')
{
$image->setSize($hint_w, $hint_h);
$image->readImage($o['source']);
if ($o['strip'])
{
$image->stripImage();
}
$image->setImageCompressionQuality($o['quality']);
$image->scaleImage( $o['width'], $o['height'], $bestfit);
}
else if ($lib === 'GD')
{
$final_img = self::create_gd_proportional($o['final_width'], $o['final_height'], $o['source_width'], $o['source_height'], $src_img, $source_image_type);
}
else
{
$cmd = MAGICK_PATH_FINAL . $strip . " -size {$hint_w}x{$hint_h} \"{$o['source']}\" -limit memory 33554432 -limit map 67108864 -depth 8 -density 72 -quality {$o['quality']} -resize {$o['width']}x{$o['height']}";
}
}
if ($o['sharpening'] > 0)
{
// Threshold setting applied to Imagick and ImageMagick
$threshold = 0.05;
if ($lib !== 'GD')
{
// Set radius to 0 to let IM decide the best value based on sigma
$amount = $o['sharpening'];
$o['sharpening'] = 0;
}
if ($lib === 'Imagick')
{
$image->unsharpMaskImage( $o['sharpening'], $sigma, $amount, $threshold );
}
else if ($lib === 'GD')
{
if (function_exists('imageconvolution'))
{
if ($o['sharpening'] != 1)
{
$o['sharpening'] = abs(1 - $o['sharpening']);
}
if ($o['sharpening'] > 0)
{
$matrix = array
(
array(-1, -1, -1),
array(-1, ceil($o['sharpening']*60), -1),
array(-1, -1, -1),
);
$divisor = array_sum(array_map('array_sum', $matrix));
imageconvolution($final_img, $matrix, $divisor, 0);
}
}
}
else
{
$cmd .= " -unsharp {$o['sharpening']}x{$sigma}+$amount+$threshold";
}
}
if ($o['hooks'])
{
if (!class_exists('Shutter'))
{
require dirname(dirname(__FILE__)) . '/application/libraries/shutter.php';
$api_cache_dir = dirname(dirname(dirname(__FILE__))) .
DIRECTORY_SEPARATOR . 'storage' .
DIRECTORY_SEPARATOR . 'cache' .
DIRECTORY_SEPARATOR . 'api';
$stamp = $api_cache_dir . DIRECTORY_SEPARATOR . 'stamp';
$cache_file = $api_cache_dir . DIRECTORY_SEPARATOR . md5('/plugins') . '.cache';
if (file_exists($cache_file) && filemtime($cache_file) >= filemtime($stamp))
{
$plugin_data = json_decode( file_get_contents($cache_file), true );
}
else
{
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, 'http://' . $_SERVER['HTTP_HOST'] . preg_replace('~/i\.php.*~', '', $_SERVER['SCRIPT_NAME']) . '/api.php?/plugins');
curl_setopt($curl, CURLOPT_HEADER, 0);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
$plugin_data = json_decode( curl_exec($curl), true );
}
Shutter::finalize_plugins($plugin_data['plugins']);
}
if ($lib === 'Imagick')
{
$image = Shutter::filter('darkroom.render.imagick', array($image, $o, $kokenData));
}
else if ($lib === 'GD')
{
$final_img = Shutter::filter('darkroom.render.gd', array($final_img, $o, $kokenData));
}
else
{
$cmd = Shutter::filter('darkroom.render.imagemagick', array($cmd, $o, $kokenData));
}
}
if ($lib === 'Imagick')
{
$image->writeImage( $o['destination'] );
$image->clear();
$image->destroy();
}
else if ($lib === 'GD')
{
if ($source_image_type === IMAGETYPE_PNG) {
imagealphablending($final_img, false);
imagesavealpha($final_img, true);
imagepng($final_img, $o['destination']);
} elseif ($source_image_type === IMAGETYPE_GIF) {
imagegif($final_img, $o['destination']);
} else {
imagejpeg($final_img, $o['destination'], min($o['quality'], 99));
}
imagedestroy($final_img);
imagedestroy($src_img);
}
else
{
$cmd .= " \"{$o['destination']}\"";
exec($cmd);
}
if (isset($icc))
{
$icc->SaveToJPEG($o['destination']);
}
if ($o['hooks'])
{
Shutter::hook('darkroom.render.complete', array($o['destination']));
}
}
private static function create_gd_proportional($w, $h, $source_w, $source_h, $src_img, $source_image_type)
{
$final_img = imagecreatetruecolor($w, $h);
if ($source_image_type == IMAGETYPE_PNG)
{
$transparency = imagecolorallocatealpha($final_img, 0, 0, 0, 127);
imagefill($final_img, 0, 0, $transparency);
}
imagecopyresampled($final_img, $src_img, 0, 0, 0, 0, $w, $h, $source_w, $source_h);
return $final_img;
}
private static function is_really_callable($function_name)
{
$disabled_functions = explode(',', str_replace(' ', '', ini_get('disable_functions')));
if (ini_get('suhosin.executor.func.blacklist'))
{
$disabled_functions = array_merge($disabled_functions, explode(',', str_replace(' ', '', ini_get('suhosin.executor.func.blacklist'))));
}
if (in_array($function_name, $disabled_functions))
{
return false;
}
else
{
return is_callable($function_name);
}
}
////
// Check ImageMagick version
////
static function processing_library_version() {
return array('6.8.9-3', false);
$version_string = $version = $lib = false;
if (function_exists('gd_info')) {
$gd = gd_info();
$version = preg_replace('/[a-zA-Z\s\(\)]/', '', $gd['GD Version']);
$lib = 'GD';
}
if (MAGICK_PATH !== 'gd')
{
if (in_array('imagick', get_loaded_extensions()) && class_exists('Imagick'))
{
$im = new Imagick;
$v = $im->getVersion();
$version_string = $v['versionString'];
$imagick = 'Imagick';
}
else if (self::is_really_callable('exec') && (DIRECTORY_SEPARATOR == '/' || (DIRECTORY_SEPARATOR == '\\' && MAGICK_PATH_FINAL != 'convert'))) {
exec(MAGICK_PATH_FINAL . ' -version', $out);
if (count($out)) {
$version_string = $out[0];
$imagick = false;
}
}
if ($version_string)
{
preg_match('/\d+\.\d+\.\d+([^\s]+)?/', $version_string, $matches);
if ($matches)
{
$version = $matches[0];
$lib = $imagick;
}
}
}
return array($version, $lib);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment