New Graphics extension
Added constants to several extensions
This commit is contained in:
parent
3dce134fe9
commit
3859e27839
@ -58,7 +58,7 @@ class BaseThemelet
|
|||||||
$tsize = get_thumbnail_size($image->width, $image->height);
|
$tsize = get_thumbnail_size($image->width, $image->height);
|
||||||
} else {
|
} else {
|
||||||
//Use max thumbnail size if using thumbless filetype
|
//Use max thumbnail size if using thumbless filetype
|
||||||
$tsize = get_thumbnail_size($config->get_int('thumb_width'), $config->get_int('thumb_height'));
|
$tsize = get_thumbnail_size($config->get_int(ImageConfig::THUMB_WIDTH), $config->get_int(ImageConfig::THUMB_WIDTH));
|
||||||
}
|
}
|
||||||
|
|
||||||
$custom_classes = "";
|
$custom_classes = "";
|
||||||
|
@ -463,7 +463,7 @@ class Image
|
|||||||
*/
|
*/
|
||||||
public function get_image_link(): string
|
public function get_image_link(): string
|
||||||
{
|
{
|
||||||
return $this->get_link('image_ilink', '_images/$hash/$id%20-%20$tags.$ext', 'image/$id.$ext');
|
return $this->get_link(ImageConfig::ILINK, '_images/$hash/$id%20-%20$tags.$ext', 'image/$id.$ext');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -480,8 +480,8 @@ class Image
|
|||||||
public function get_thumb_link(): string
|
public function get_thumb_link(): string
|
||||||
{
|
{
|
||||||
global $config;
|
global $config;
|
||||||
$ext = $config->get_string("thumb_type");
|
$ext = $config->get_string(ImageConfig::THUMB_TYPE);
|
||||||
return $this->get_link('image_tlink', '_thumbs/$hash/thumb.'.$ext, 'thumb/$id.'.$ext);
|
return $this->get_link(ImageConfig::TLINK, '_thumbs/$hash/thumb.'.$ext, 'thumb/$id.'.$ext);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -512,7 +512,7 @@ class Image
|
|||||||
public function get_tooltip(): string
|
public function get_tooltip(): string
|
||||||
{
|
{
|
||||||
global $config;
|
global $config;
|
||||||
$tt = $this->parse_link_template($config->get_string('image_tip'), "no_escape");
|
$tt = $this->parse_link_template($config->get_string(ImageConfig::TIP), "no_escape");
|
||||||
|
|
||||||
// Removes the size tag if the file is an mp3
|
// Removes the size tag if the file is an mp3
|
||||||
if ($this->ext === 'mp3') {
|
if ($this->ext === 'mp3') {
|
||||||
|
@ -126,23 +126,30 @@ function get_thumbnail_size(int $orig_width, int $orig_height, bool $use_dpi_sca
|
|||||||
|
|
||||||
|
|
||||||
if($use_dpi_scaling) {
|
if($use_dpi_scaling) {
|
||||||
$max_size = get_thumbnail_max_size_scaled();
|
list($max_width, $max_height) = get_thumbnail_max_size_scaled();
|
||||||
$max_width = $max_size[0];
|
|
||||||
$max_height = $max_size[1];
|
|
||||||
} else {
|
} else {
|
||||||
$max_width = $config->get_int('thumb_width');
|
$max_width = $config->get_int(ImageConfig::THUMB_WIDTH);
|
||||||
$max_height = $config->get_int('thumb_height');
|
$max_height = $config->get_int(ImageConfig::THUMB_HEIGHT);
|
||||||
}
|
}
|
||||||
|
|
||||||
$xscale = ($max_height / $orig_height);
|
$output = get_scaled_by_aspect_ratio($orig_width, $orig_height, $max_width, $max_height);
|
||||||
$yscale = ($max_width / $orig_width);
|
|
||||||
$scale = ($xscale < $yscale) ? $xscale : $yscale;
|
|
||||||
|
|
||||||
if ($scale > 1 && $config->get_bool('thumb_upscale')) {
|
if ($output[2] > 1 && $config->get_bool('thumb_upscale')) {
|
||||||
return [(int)$orig_width, (int)$orig_height];
|
return [(int)$orig_width, (int)$orig_height];
|
||||||
} else {
|
} else {
|
||||||
return [(int)($orig_width*$scale), (int)($orig_height*$scale)];
|
return $output;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function get_scaled_by_aspect_ratio(int $original_width, int $original_height, int $max_width, int $max_height) : array
|
||||||
|
{
|
||||||
|
$xscale = ($max_width/ $original_width);
|
||||||
|
$yscale = ($max_height/ $original_height);
|
||||||
|
|
||||||
|
$scale = ($yscale < $xscale) ? $yscale : $xscale ;
|
||||||
|
|
||||||
|
return [(int)($original_width*$scale), (int)($original_height*$scale), $scale];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -154,355 +161,37 @@ function get_thumbnail_max_size_scaled(): array
|
|||||||
{
|
{
|
||||||
global $config;
|
global $config;
|
||||||
|
|
||||||
$scaling = $config->get_int("thumb_scaling");
|
$scaling = $config->get_int(ImageConfig::THUMB_SCALING);
|
||||||
$max_width = $config->get_int('thumb_width') * ($scaling/100);
|
$max_width = $config->get_int(ImageConfig::THUMB_WIDTH) * ($scaling/100);
|
||||||
$max_height = $config->get_int('thumb_height') * ($scaling/100);
|
$max_height = $config->get_int(ImageConfig::THUMB_HEIGHT) * ($scaling/100);
|
||||||
return [$max_width, $max_height];
|
return [$max_width, $max_height];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a thumbnail file using ImageMagick's convert command.
|
function create_image_thumb(string $hash, string $type, string $engine = null) {
|
||||||
*
|
|
||||||
* @param $hash
|
|
||||||
* @param string $input_type Optional, allows specifying the input format. Usually not necessary.
|
|
||||||
* @return bool true is successful, false if not.
|
|
||||||
*/
|
|
||||||
function create_thumbnail_convert($hash, $input_type = ""): bool
|
|
||||||
{
|
|
||||||
global $config;
|
global $config;
|
||||||
|
|
||||||
$inname = warehouse_path(Image::IMAGE_DIR, $hash);
|
$inname = warehouse_path(Image::IMAGE_DIR, $hash);
|
||||||
$outname = warehouse_path(Image::THUMBNAIL_DIR, $hash);
|
$outname = warehouse_path(Image::THUMBNAIL_DIR, $hash);
|
||||||
|
$tsize = get_thumbnail_max_size_scaled();
|
||||||
|
|
||||||
$q = $config->get_int("thumb_quality");
|
if(empty($engine)) {
|
||||||
$convert = $config->get_string("thumb_convert_path");
|
$engine = $config->get_string(ImageConfig::THUMB_ENGINE);
|
||||||
|
|
||||||
if ($convert==null||$convert=="") {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ffff imagemagick fails sometimes, not sure why
|
send_event(new GraphicResizeEvent(
|
||||||
//$format = "'%s' '%s[0]' -format '%%[fx:w] %%[fx:h]' info:";
|
$engine,
|
||||||
//$cmd = sprintf($format, $convert, $inname);
|
$inname,
|
||||||
//$size = shell_exec($cmd);
|
$type,
|
||||||
//$size = explode(" ", trim($size));
|
$outname,
|
||||||
list($w, $h) = get_thumbnail_max_size_scaled();
|
$tsize[0],
|
||||||
|
$tsize[1],
|
||||||
|
false,
|
||||||
// running the call with cmd.exe requires quoting for our paths
|
$config->get_string(ImageConfig::THUMB_TYPE),
|
||||||
$type = $config->get_string('thumb_type');
|
$config->get_int(ImageConfig::THUMB_QUALITY),
|
||||||
|
true,
|
||||||
$options = "";
|
$config->get_bool('thumb_upscale', false)
|
||||||
if (!$config->get_bool('thumb_upscale')) {
|
));
|
||||||
$options .= "\>";
|
|
||||||
}
|
|
||||||
|
|
||||||
$bg = "black";
|
|
||||||
if ($type=="webp") {
|
|
||||||
$bg = "none";
|
|
||||||
}
|
|
||||||
if (!empty($input_type)) {
|
|
||||||
$input_type = $input_type.":";
|
|
||||||
}
|
|
||||||
$format = '"%s" -flatten -strip -thumbnail %ux%u%s -quality %u -background %s %s"%s[0]" %s:"%s" 2>&1';
|
|
||||||
$cmd = sprintf($format, $convert, $w, $h, $options, $q, $bg, $input_type, $inname, $type, $outname);
|
|
||||||
$cmd = str_replace("\"convert\"", "convert", $cmd); // quotes are only needed if the path to convert contains a space; some other times, quotes break things, see github bug #27
|
|
||||||
exec($cmd, $output, $ret);
|
|
||||||
if ($ret!=0) {
|
|
||||||
log_warning('imageboard/misc', "Generating thumbnail with command `$cmd`, returns $ret, outputting ".implode("\r\n", $output));
|
|
||||||
} else {
|
|
||||||
log_debug('imageboard/misc', "Generating thumbnail with command `$cmd`, returns $ret");
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($config->get_bool("thumb_optim", false)) {
|
|
||||||
exec("jpegoptim $outname", $output, $ret);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a thumbnail using ffmpeg.
|
|
||||||
*
|
|
||||||
* @param $hash
|
|
||||||
* @return bool true if successful, false if not.
|
|
||||||
*/
|
|
||||||
function create_thumbnail_ffmpeg($hash): bool
|
|
||||||
{
|
|
||||||
global $config;
|
|
||||||
|
|
||||||
$ffmpeg = $config->get_string("thumb_ffmpeg_path");
|
|
||||||
if ($ffmpeg==null||$ffmpeg=="") {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$inname = warehouse_path(Image::IMAGE_DIR, $hash);
|
|
||||||
$outname = warehouse_path(Image::THUMBNAIL_DIR, $hash);
|
|
||||||
|
|
||||||
$orig_size = video_size($inname);
|
|
||||||
$scaled_size = get_thumbnail_size($orig_size[0], $orig_size[1], true);
|
|
||||||
|
|
||||||
$codec = "mjpeg";
|
|
||||||
$quality = $config->get_int("thumb_quality");
|
|
||||||
if ($config->get_string("thumb_type")=="webp") {
|
|
||||||
$codec = "libwebp";
|
|
||||||
} else {
|
|
||||||
// mjpeg quality ranges from 2-31, with 2 being the best quality.
|
|
||||||
$quality = floor(31 - (31 * ($quality/100)));
|
|
||||||
if ($quality<2) {
|
|
||||||
$quality = 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$args = [
|
|
||||||
escapeshellarg($ffmpeg),
|
|
||||||
"-y", "-i", escapeshellarg($inname),
|
|
||||||
"-vf", "thumbnail,scale={$scaled_size[0]}:{$scaled_size[1]}",
|
|
||||||
"-f", "image2",
|
|
||||||
"-vframes", "1",
|
|
||||||
"-c:v", $codec,
|
|
||||||
"-q:v", $quality,
|
|
||||||
escapeshellarg($outname),
|
|
||||||
];
|
|
||||||
|
|
||||||
$cmd = escapeshellcmd(implode(" ", $args));
|
|
||||||
|
|
||||||
exec($cmd, $output, $ret);
|
|
||||||
|
|
||||||
if ((int)$ret == (int)0) {
|
|
||||||
log_debug('imageboard/misc', "Generating thumbnail with command `$cmd`, returns $ret");
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
log_error('imageboard/misc', "Generating thumbnail with command `$cmd`, returns $ret");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determines the dimensions of a video file using ffmpeg.
|
|
||||||
*
|
|
||||||
* @param string $filename
|
|
||||||
* @return array [width, height]
|
|
||||||
*/
|
|
||||||
function video_size(string $filename): array
|
|
||||||
{
|
|
||||||
global $config;
|
|
||||||
$ffmpeg = $config->get_string("thumb_ffmpeg_path");
|
|
||||||
$cmd = escapeshellcmd(implode(" ", [
|
|
||||||
escapeshellarg($ffmpeg),
|
|
||||||
"-y", "-i", escapeshellarg($filename),
|
|
||||||
"-vstats"
|
|
||||||
]));
|
|
||||||
$output = shell_exec($cmd . " 2>&1");
|
|
||||||
// error_log("Getting size with `$cmd`");
|
|
||||||
|
|
||||||
$regex_sizes = "/Video: .* ([0-9]{1,4})x([0-9]{1,4})/";
|
|
||||||
if (preg_match($regex_sizes, $output, $regs)) {
|
|
||||||
if (preg_match("/displaymatrix: rotation of (90|270).00 degrees/", $output)) {
|
|
||||||
$size = [$regs[2], $regs[1]];
|
|
||||||
} else {
|
|
||||||
$size = [$regs[1], $regs[2]];
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$size = [1, 1];
|
|
||||||
}
|
|
||||||
log_debug('imageboard/misc', "Getting video size with `$cmd`, returns $output -- $size[0], $size[1]");
|
|
||||||
return $size;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check Memory usage limits
|
|
||||||
*
|
|
||||||
* Old check: $memory_use = (filesize($image_filename)*2) + ($width*$height*4) + (4*1024*1024);
|
|
||||||
* New check: $memory_use = $width * $height * ($bits_per_channel) * channels * 2.5
|
|
||||||
*
|
|
||||||
* It didn't make sense to compute the memory usage based on the NEW size for the image. ($width*$height*4)
|
|
||||||
* We need to consider the size that we are GOING TO instead.
|
|
||||||
*
|
|
||||||
* The factor of 2.5 is simply a rough guideline.
|
|
||||||
* http://stackoverflow.com/questions/527532/reasonable-php-memory-limit-for-image-resize
|
|
||||||
*
|
|
||||||
* @param array $info The output of getimagesize() for the source file in question.
|
|
||||||
* @return int The number of bytes an image resize operation is estimated to use.
|
|
||||||
*/
|
|
||||||
function calc_memory_use(array $info): int
|
|
||||||
{
|
|
||||||
if (isset($info['bits']) && isset($info['channels'])) {
|
|
||||||
$memory_use = ($info[0] * $info[1] * ($info['bits'] / 8) * $info['channels'] * 2.5) / 1024;
|
|
||||||
} else {
|
|
||||||
// If we don't have bits and channel info from the image then assume default values
|
|
||||||
// of 8 bits per color and 4 channels (R,G,B,A) -- ie: regular 24-bit color
|
|
||||||
$memory_use = ($info[0] * $info[1] * 1 * 4 * 2.5) / 1024;
|
|
||||||
}
|
|
||||||
return (int)$memory_use;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Performs a resize operation on an image file using GD.
|
|
||||||
*
|
|
||||||
* @param String $image_filename The source file to be resized.
|
|
||||||
* @param array $info The output of getimagesize() for the source file.
|
|
||||||
* @param int $new_width
|
|
||||||
* @param int $new_height
|
|
||||||
* @param string $output_filename
|
|
||||||
* @param string|null $output_type If set to null, the output file type will be automatically determined via the $info parameter. Otherwise an exception will be thrown.
|
|
||||||
* @param int $output_quality Defaults to 80.
|
|
||||||
* @throws ImageResizeException
|
|
||||||
* @throws InsufficientMemoryException if the estimated memory usage exceeds the memory limit.
|
|
||||||
*/
|
|
||||||
function image_resize_gd(
|
|
||||||
String $image_filename,
|
|
||||||
array $info,
|
|
||||||
int $new_width,
|
|
||||||
int $new_height,
|
|
||||||
string $output_filename,
|
|
||||||
string $output_type=null,
|
|
||||||
int $output_quality = 80
|
|
||||||
) {
|
|
||||||
$width = $info[0];
|
|
||||||
$height = $info[1];
|
|
||||||
|
|
||||||
if ($output_type==null) {
|
|
||||||
/* If not specified, output to the same format as the original image */
|
|
||||||
switch ($info[2]) {
|
|
||||||
case IMAGETYPE_GIF: $output_type = "gif"; break;
|
|
||||||
case IMAGETYPE_JPEG: $output_type = "jpeg"; break;
|
|
||||||
case IMAGETYPE_PNG: $output_type = "png"; break;
|
|
||||||
case IMAGETYPE_WEBP: $output_type = "webp"; break;
|
|
||||||
case IMAGETYPE_BMP: $output_type = "bmp"; break;
|
|
||||||
default: throw new ImageResizeException("Failed to save the new image - Unsupported image type.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$memory_use = calc_memory_use($info);
|
|
||||||
$memory_limit = get_memory_limit();
|
|
||||||
if ($memory_use > $memory_limit) {
|
|
||||||
throw new InsufficientMemoryException("The image is too large to resize given the memory limits. ($memory_use > $memory_limit)");
|
|
||||||
}
|
|
||||||
|
|
||||||
$image = imagecreatefromstring(file_get_contents($image_filename));
|
|
||||||
$image_resized = imagecreatetruecolor($new_width, $new_height);
|
|
||||||
try {
|
|
||||||
if ($image===false) {
|
|
||||||
throw new ImageResizeException("Could not load image: ".$image_filename);
|
|
||||||
}
|
|
||||||
if ($image_resized===false) {
|
|
||||||
throw new ImageResizeException("Could not create output image with dimensions $new_width c $new_height ");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle transparent images
|
|
||||||
switch ($info[2]) {
|
|
||||||
case IMAGETYPE_GIF:
|
|
||||||
$transparency = imagecolortransparent($image);
|
|
||||||
$palletsize = imagecolorstotal($image);
|
|
||||||
|
|
||||||
// If we have a specific transparent color
|
|
||||||
if ($transparency >= 0 && $transparency < $palletsize) {
|
|
||||||
// Get the original image's transparent color's RGB values
|
|
||||||
$transparent_color = imagecolorsforindex($image, $transparency);
|
|
||||||
|
|
||||||
// Allocate the same color in the new image resource
|
|
||||||
$transparency = imagecolorallocate($image_resized, $transparent_color['red'], $transparent_color['green'], $transparent_color['blue']);
|
|
||||||
if ($transparency===false) {
|
|
||||||
throw new ImageResizeException("Unable to allocate transparent color");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Completely fill the background of the new image with allocated color.
|
|
||||||
if (imagefill($image_resized, 0, 0, $transparency)===false) {
|
|
||||||
throw new ImageResizeException("Unable to fill new image with transparent color");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the background color for new image to transparent
|
|
||||||
imagecolortransparent($image_resized, $transparency);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case IMAGETYPE_PNG:
|
|
||||||
case IMAGETYPE_WEBP:
|
|
||||||
//
|
|
||||||
// More info here: http://stackoverflow.com/questions/279236/how-do-i-resize-pngs-with-transparency-in-php
|
|
||||||
//
|
|
||||||
if (imagealphablending($image_resized, false)===false) {
|
|
||||||
throw new ImageResizeException("Unable to disable image alpha blending");
|
|
||||||
}
|
|
||||||
if (imagesavealpha($image_resized, true)===false) {
|
|
||||||
throw new ImageResizeException("Unable to enable image save alpha");
|
|
||||||
}
|
|
||||||
$transparent_color = imagecolorallocatealpha($image_resized, 255, 255, 255, 127);
|
|
||||||
if ($transparent_color===false) {
|
|
||||||
throw new ImageResizeException("Unable to allocate transparent color");
|
|
||||||
}
|
|
||||||
if (imagefilledrectangle($image_resized, 0, 0, $new_width, $new_height, $transparent_color)===false) {
|
|
||||||
throw new ImageResizeException("Unable to fill new image with transparent color");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Actually resize the image.
|
|
||||||
if (imagecopyresampled(
|
|
||||||
$image_resized,
|
|
||||||
$image,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
$new_width,
|
|
||||||
$new_height,
|
|
||||||
$width,
|
|
||||||
$height
|
|
||||||
)===false) {
|
|
||||||
throw new ImageResizeException("Unable to copy resized image data to new image");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
switch ($output_type) {
|
|
||||||
case "bmp":
|
|
||||||
$result = imagebmp($image_resized, $output_filename, true);
|
|
||||||
break;
|
|
||||||
case "webp":
|
|
||||||
$result = imagewebp($image_resized, $output_filename, $output_quality);
|
|
||||||
break;
|
|
||||||
case "jpg":
|
|
||||||
case "jpeg":
|
|
||||||
$result = imagejpeg($image_resized, $output_filename, $output_quality);
|
|
||||||
break;
|
|
||||||
case "png":
|
|
||||||
$result = imagepng($image_resized, $output_filename, 9);
|
|
||||||
break;
|
|
||||||
case "gif":
|
|
||||||
$result = imagegif($image_resized, $output_filename);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new ImageResizeException("Failed to save the new image - Unsupported image type: $output_type");
|
|
||||||
}
|
|
||||||
if ($result==false) {
|
|
||||||
throw new ImageResizeException("Failed to save the new image, function returned false when saving type: $output_type");
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
imagedestroy($image);
|
|
||||||
imagedestroy($image_resized);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determines if a file is an animated gif.
|
|
||||||
*
|
|
||||||
* @param String $image_filename The path of the file to check.
|
|
||||||
* @return bool true if the file is an animated gif, false if it is not.
|
|
||||||
*/
|
|
||||||
function is_animated_gif(String $image_filename)
|
|
||||||
{
|
|
||||||
$is_anim_gif = 0;
|
|
||||||
if (($fh = @fopen($image_filename, 'rb'))) {
|
|
||||||
//check if gif is animated (via http://www.php.net/manual/en/function.imagecreatefromgif.php#104473)
|
|
||||||
while (!feof($fh) && $is_anim_gif < 2) {
|
|
||||||
$chunk = fread($fh, 1024 * 100);
|
|
||||||
$is_anim_gif += preg_match_all('#\x00\x21\xF9\x04.{4}\x00(\x2C|\x21)#s', $chunk, $matches);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ($is_anim_gif == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
function image_to_id(Image $image): int
|
|
||||||
{
|
|
||||||
return $image->id;
|
|
||||||
}
|
|
||||||
|
@ -40,7 +40,7 @@ _d("SEARCH_ACCEL", false); // boolean use search accelerator
|
|||||||
_d("WH_SPLITS", 1); // int how many levels of subfolders to put in the warehouse
|
_d("WH_SPLITS", 1); // int how many levels of subfolders to put in the warehouse
|
||||||
_d("VERSION", '2.7-beta'); // string shimmie version
|
_d("VERSION", '2.7-beta'); // string shimmie version
|
||||||
_d("TIMEZONE", null); // string timezone
|
_d("TIMEZONE", null); // string timezone
|
||||||
_d("CORE_EXTS", "bbcode,user,mail,upload,image,view,handle_pixel,ext_manager,setup,upgrade,handle_404,handle_static,comment,tag_list,index,tag_edit,alias_editor"); // extensions to always enable
|
_d("CORE_EXTS", "bbcode,user,mail,upload,image,view,handle_pixel,ext_manager,setup,upgrade,handle_404,handle_static,comment,tag_list,index,tag_edit,alias_editor,graphics"); // extensions to always enable
|
||||||
_d("EXTRA_EXTS", ""); // string optional extra extensions
|
_d("EXTRA_EXTS", ""); // string optional extra extensions
|
||||||
_d("BASE_URL", null); // string force a specific base URL (default is auto-detect)
|
_d("BASE_URL", null); // string force a specific base URL (default is auto-detect)
|
||||||
_d("MIN_PHP_VERSION", '7.1');// string minimum supported PHP version
|
_d("MIN_PHP_VERSION", '7.1');// string minimum supported PHP version
|
||||||
|
@ -79,7 +79,7 @@ function get_memory_limit(): int
|
|||||||
|
|
||||||
// thumbnail generation requires lots of memory
|
// thumbnail generation requires lots of memory
|
||||||
$default_limit = 8*1024*1024; // 8 MB of memory is PHP's default.
|
$default_limit = 8*1024*1024; // 8 MB of memory is PHP's default.
|
||||||
$shimmie_limit = parse_shorthand_int($config->get_int("thumb_mem_limit"));
|
$shimmie_limit = parse_shorthand_int($config->get_int(GraphicsConfig::MEM_LIMIT));
|
||||||
|
|
||||||
if ($shimmie_limit < 3*1024*1024) {
|
if ($shimmie_limit < 3*1024*1024) {
|
||||||
// we aren't going to fit, override
|
// we aren't going to fit, override
|
||||||
|
@ -53,13 +53,16 @@ class ET extends Extension
|
|||||||
to_shorthand_int(disk_total_space("./"));
|
to_shorthand_int(disk_total_space("./"));
|
||||||
$info['sys_server'] = isset($_SERVER["SERVER_SOFTWARE"]) ? $_SERVER["SERVER_SOFTWARE"] : 'unknown';
|
$info['sys_server'] = isset($_SERVER["SERVER_SOFTWARE"]) ? $_SERVER["SERVER_SOFTWARE"] : 'unknown';
|
||||||
|
|
||||||
$info['thumb_engine'] = $config->get_string("thumb_engine");
|
$info[GraphicsConfig::FFMPEG_PATH] = $config->get_string(GraphicsConfig::FFMPEG_PATH);
|
||||||
$info['thumb_quality'] = $config->get_int('thumb_quality');
|
$info[GraphicsConfig::CONVERT_PATH] = $config->get_string(GraphicsConfig::CONVERT_PATH);
|
||||||
$info['thumb_width'] = $config->get_int('thumb_width');
|
$info[GraphicsConfig::MEM_LIMIT] = $config->get_int(GraphicsConfig::MEM_LIMIT);
|
||||||
$info['thumb_height'] = $config->get_int('thumb_height');
|
|
||||||
$info['thumb_scaling'] = $config->get_int('thumb_scaling');
|
$info[ImageConfig::THUMB_ENGINE] = $config->get_string(ImageConfig::THUMB_ENGINE);
|
||||||
$info['thumb_type'] = $config->get_string('thumb_type');
|
$info[ImageConfig::THUMB_QUALITY] = $config->get_int(ImageConfig::THUMB_QUALITY);
|
||||||
$info['thumb_mem'] = $config->get_int("thumb_mem_limit");
|
$info[ImageConfig::THUMB_WIDTH] = $config->get_int(ImageConfig::THUMB_WIDTH);
|
||||||
|
$info[ImageConfig::THUMB_HEIGHT] = $config->get_int(ImageConfig::THUMB_HEIGHT);
|
||||||
|
$info[ImageConfig::THUMB_SCALING] = $config->get_int(ImageConfig::THUMB_SCALING);
|
||||||
|
$info[ImageConfig::THUMB_TYPE] = $config->get_string(ImageConfig::THUMB_TYPE);
|
||||||
|
|
||||||
$info['stat_images'] = $database->get_one("SELECT COUNT(*) FROM images");
|
$info['stat_images'] = $database->get_one("SELECT COUNT(*) FROM images");
|
||||||
$info['stat_comments'] = $database->get_one("SELECT COUNT(*) FROM comments");
|
$info['stat_comments'] = $database->get_one("SELECT COUNT(*) FROM comments");
|
||||||
|
@ -35,14 +35,16 @@ Database: {$info['sys_db']}
|
|||||||
Server: {$info['sys_server']}
|
Server: {$info['sys_server']}
|
||||||
Disk use: {$info['sys_disk']}
|
Disk use: {$info['sys_disk']}
|
||||||
|
|
||||||
|
Graphics System:
|
||||||
|
Memory Limit: {$info[GraphicsConfig::MEM_LIMIT]}
|
||||||
|
|
||||||
Thumbnail Generation:
|
Thumbnail Generation:
|
||||||
Engine: {$info['thumb_engine']}
|
Engine: {$info[ImageConfig::THUMB_ENGINE]}
|
||||||
Type: {$info['thumb_type']}
|
Type: {$info[ImageConfig::THUMB_TYPE]}
|
||||||
Memory: {$info['thumb_mem']}
|
Quality: {$info[ImageConfig::THUMB_QUALITY]}
|
||||||
Quality: {$info['thumb_quality']}
|
Width: {$info[ImageConfig::THUMB_WIDTH]}
|
||||||
Width: {$info['thumb_width']}
|
Height: {$info[ImageConfig::THUMB_HEIGHT]}
|
||||||
Height: {$info['thumb_height']}
|
Scaling: {$info[ImageConfig::THUMB_SCALING]}
|
||||||
Scaling: {$info['thumb_scaling']}
|
|
||||||
|
|
||||||
Shimmie stats:
|
Shimmie stats:
|
||||||
Images: {$info['stat_images']}
|
Images: {$info['stat_images']}
|
||||||
|
808
ext/graphics/main.php
Normal file
808
ext/graphics/main.php
Normal file
@ -0,0 +1,808 @@
|
|||||||
|
<?php
|
||||||
|
/*
|
||||||
|
* Name: Graphics
|
||||||
|
* Author: Matthew Barbour <matthew@darkholme.net>
|
||||||
|
* Description: Provides common functions and settings used for graphic operations.
|
||||||
|
* License: MIT
|
||||||
|
* Version: 1.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This is used by the graphics code when there is an error
|
||||||
|
*/
|
||||||
|
|
||||||
|
use FFMpeg\FFMpeg;
|
||||||
|
|
||||||
|
abstract class GraphicsConfig
|
||||||
|
{
|
||||||
|
const FFMPEG_PATH = "graphics_ffmpeg_path";
|
||||||
|
const FFPROBE_PATH = "graphics_ffprobe_path";
|
||||||
|
const CONVERT_PATH = "graphics_convert_path";
|
||||||
|
const VERSION = "ext_graphics_version";
|
||||||
|
const MEM_LIMIT = 'graphics_mem_limit';
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class GraphicsException extends SCoreException
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
class GraphicResizeEvent extends Event
|
||||||
|
{
|
||||||
|
public $engine;
|
||||||
|
public $input_path;
|
||||||
|
public $input_type;
|
||||||
|
public $output_path;
|
||||||
|
public $target_format;
|
||||||
|
public $target_width;
|
||||||
|
public $target_height;
|
||||||
|
public $target_quality;
|
||||||
|
public $minimize;
|
||||||
|
public $ignore_aspect_ratio;
|
||||||
|
public $allow_upscale;
|
||||||
|
|
||||||
|
public function __construct(String $engine, string $input_path, string $input_type, string $output_path,
|
||||||
|
int $target_width, int $target_height,
|
||||||
|
bool $ignore_aspect_ratio = false,
|
||||||
|
string $target_format = null,
|
||||||
|
int $target_quality = 80,
|
||||||
|
bool $minimize = false,
|
||||||
|
bool $allow_upscale = true)
|
||||||
|
{
|
||||||
|
assert(in_array($engine, Graphics::GRAPHICS_ENGINES));
|
||||||
|
$this->engine = $engine;
|
||||||
|
$this->input_path = $input_path;
|
||||||
|
$this->input_type = $input_type;
|
||||||
|
$this->output_path = $output_path;
|
||||||
|
$this->target_height = $target_height;
|
||||||
|
$this->target_width = $target_width;
|
||||||
|
$this->target_format = $target_format;
|
||||||
|
$this->target_quality = $target_quality;
|
||||||
|
$this->minimize = $minimize;
|
||||||
|
$this->ignore_aspect_ratio = $ignore_aspect_ratio;
|
||||||
|
$this->allow_upscale = $allow_upscale;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Graphics extends Extension
|
||||||
|
{
|
||||||
|
const WEBP_LOSSY = "webp-lossy";
|
||||||
|
const WEBP_LOSSLESS = "webp-lossless";
|
||||||
|
|
||||||
|
const FFMPEG_ENGINE = "ffmpeg";
|
||||||
|
const GD_ENGINE = "gd";
|
||||||
|
const IMAGICK_ENGINE = "convert";
|
||||||
|
|
||||||
|
const GRAPHICS_ENGINES = [
|
||||||
|
self::GD_ENGINE,
|
||||||
|
self::FFMPEG_ENGINE,
|
||||||
|
self::IMAGICK_ENGINE
|
||||||
|
];
|
||||||
|
|
||||||
|
const IMAGE_GRAPHICS_ENGINES = [
|
||||||
|
"GD" => self::GD_ENGINE,
|
||||||
|
"ImageMagick" => self::IMAGICK_ENGINE,
|
||||||
|
];
|
||||||
|
|
||||||
|
const ENGINE_INPUT_SUPPORT = [
|
||||||
|
self::GD_ENGINE => [
|
||||||
|
"bmp",
|
||||||
|
"gif",
|
||||||
|
"jpg",
|
||||||
|
"png",
|
||||||
|
"webp",
|
||||||
|
],
|
||||||
|
self::IMAGICK_ENGINE => [
|
||||||
|
"bmp",
|
||||||
|
"gif",
|
||||||
|
"jpg",
|
||||||
|
"png",
|
||||||
|
"psd",
|
||||||
|
"tiff",
|
||||||
|
"webp",
|
||||||
|
"ico",
|
||||||
|
],
|
||||||
|
self::FFMPEG_ENGINE => [
|
||||||
|
"avi",
|
||||||
|
"mkv",
|
||||||
|
"webm",
|
||||||
|
"mp4",
|
||||||
|
"mov",
|
||||||
|
"flv"
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
const ENGINE_OUTPUT_SUPPORT = [
|
||||||
|
self::GD_ENGINE => [
|
||||||
|
"gif",
|
||||||
|
"jpg",
|
||||||
|
"png",
|
||||||
|
"webp",
|
||||||
|
self::WEBP_LOSSY,
|
||||||
|
],
|
||||||
|
self::IMAGICK_ENGINE => [
|
||||||
|
"gif",
|
||||||
|
"jpg",
|
||||||
|
"png",
|
||||||
|
"webp",
|
||||||
|
self::WEBP_LOSSY,
|
||||||
|
self::WEBP_LOSSLESS,
|
||||||
|
],
|
||||||
|
self::FFMPEG_ENGINE => [
|
||||||
|
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
const LOSSLESS_FORMATS = [
|
||||||
|
self::WEBP_LOSSLESS,
|
||||||
|
"png",
|
||||||
|
];
|
||||||
|
|
||||||
|
const ALPHA_FORMATS = [
|
||||||
|
self::WEBP_LOSSLESS,
|
||||||
|
self::WEBP_LOSSY,
|
||||||
|
"png",
|
||||||
|
];
|
||||||
|
|
||||||
|
const FORMAT_ALIASES = [
|
||||||
|
"tif" => "tiff",
|
||||||
|
"jpeg" => "jpg",
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
static function imagick_available(): bool
|
||||||
|
{
|
||||||
|
return extension_loaded("imagick");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* High priority just so that it can be early in the settings
|
||||||
|
*/
|
||||||
|
public function get_priority(): int
|
||||||
|
{
|
||||||
|
return 30;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onInitExt(InitExtEvent $event)
|
||||||
|
{
|
||||||
|
global $config;
|
||||||
|
$config->set_default_string(GraphicsConfig::FFPROBE_PATH, 'ffprobe');
|
||||||
|
|
||||||
|
|
||||||
|
if ($config->get_int(GraphicsConfig::VERSION) < 1) {
|
||||||
|
$current_value = $config->get_string("thumb_ffmpeg_path");
|
||||||
|
if(!empty($current_value)) {
|
||||||
|
$config->set_string(GraphicsConfig::FFMPEG_PATH, $current_value);
|
||||||
|
} elseif ($ffmpeg = shell_exec((PHP_OS == 'WINNT' ? 'where' : 'which') . ' ffmpeg')) {
|
||||||
|
//ffmpeg exists in PATH, check if it's executable, and if so, default to it instead of static
|
||||||
|
if (is_executable(strtok($ffmpeg, PHP_EOL))) {
|
||||||
|
$config->set_default_string(GraphicsConfig::FFMPEG_PATH, 'ffmpeg');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$config->set_default_string(GraphicsConfig::FFMPEG_PATH, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
$current_value = $config->get_string("thumb_convert_path");
|
||||||
|
if(!empty($current_value)) {
|
||||||
|
$config->set_string(GraphicsConfig::CONVERT_PATH, $current_value);
|
||||||
|
} elseif ($convert = shell_exec((PHP_OS == 'WINNT' ? 'where' : 'which') . ' convert')) {
|
||||||
|
//ffmpeg exists in PATH, check if it's executable, and if so, default to it instead of static
|
||||||
|
if (is_executable(strtok($convert, PHP_EOL))) {
|
||||||
|
$config->set_default_string(GraphicsConfig::CONVERT_PATH, 'convert');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$config->set_default_string(GraphicsConfig::CONVERT_PATH, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
$current_value = $config->get_int("thumb_mem_limit");
|
||||||
|
if(!empty($current_value)) {
|
||||||
|
$config->set_int(GraphicsConfig::MEM_LIMIT, $current_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
$config->set_int(GraphicsConfig::VERSION, 1);
|
||||||
|
log_info("graphics", "extension installed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onSetupBuilding(SetupBuildingEvent $event)
|
||||||
|
{
|
||||||
|
$sb = new SetupBlock("Graphics");
|
||||||
|
|
||||||
|
// if (self::imagick_available()) {
|
||||||
|
// try {
|
||||||
|
// $image = new Imagick(realpath('tests/favicon.png'));
|
||||||
|
// $image->clear();
|
||||||
|
// $sb->add_label("ImageMagick detected");
|
||||||
|
// } catch (ImagickException $e) {
|
||||||
|
// $sb->add_label("<b style='color:red'>ImageMagick not detected</b>");
|
||||||
|
// }
|
||||||
|
// } else {
|
||||||
|
$sb->add_text_option(GraphicsConfig::CONVERT_PATH, "convert command: ");
|
||||||
|
// }
|
||||||
|
|
||||||
|
$sb->add_text_option(GraphicsConfig::FFMPEG_PATH, "<br/>ffmpeg command: ");
|
||||||
|
|
||||||
|
$sb->add_shorthand_int_option(GraphicsConfig::MEM_LIMIT, "<br />Max memory use: ");
|
||||||
|
|
||||||
|
$event->panel->add_block($sb);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param GraphicResizeEvent $event
|
||||||
|
* @throws GraphicsException
|
||||||
|
* @throws InsufficientMemoryException
|
||||||
|
*/
|
||||||
|
public function onGraphicResize(GraphicResizeEvent $event)
|
||||||
|
{
|
||||||
|
switch ($event->engine) {
|
||||||
|
case self::GD_ENGINE:
|
||||||
|
$info = getimagesize($event->input_path);
|
||||||
|
if ($info === false) {
|
||||||
|
throw new GraphicsException("getimagesize failed for " . $event->input_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
self::image_resize_gd(
|
||||||
|
$event->input_path,
|
||||||
|
$info,
|
||||||
|
$event->target_width,
|
||||||
|
$event->target_height,
|
||||||
|
$event->output_path,
|
||||||
|
$event->target_format,
|
||||||
|
$event->ignore_aspect_ratio,
|
||||||
|
$event->target_quality,
|
||||||
|
$event->allow_upscale);
|
||||||
|
|
||||||
|
break;
|
||||||
|
case self::IMAGICK_ENGINE:
|
||||||
|
// if (self::imagick_available()) {
|
||||||
|
// } else {
|
||||||
|
self::image_resize_convert(
|
||||||
|
$event->input_path,
|
||||||
|
$event->input_type,
|
||||||
|
$event->target_width,
|
||||||
|
$event->target_height,
|
||||||
|
$event->output_path,
|
||||||
|
$event->target_format,
|
||||||
|
$event->ignore_aspect_ratio,
|
||||||
|
$event->target_quality,
|
||||||
|
$event->minimize,
|
||||||
|
$event->allow_upscale);
|
||||||
|
//}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new GraphicsException("Engine not supported for resize: " . $event->engine);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Get output optimization tools working better
|
||||||
|
// if ($config->get_bool("thumb_optim", false)) {
|
||||||
|
// exec("jpegoptim $outname", $output, $ret);
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check Memory usage limits
|
||||||
|
*
|
||||||
|
* Old check: $memory_use = (filesize($image_filename)*2) + ($width*$height*4) + (4*1024*1024);
|
||||||
|
* New check: $memory_use = $width * $height * ($bits_per_channel) * channels * 2.5
|
||||||
|
*
|
||||||
|
* It didn't make sense to compute the memory usage based on the NEW size for the image. ($width*$height*4)
|
||||||
|
* We need to consider the size that we are GOING TO instead.
|
||||||
|
*
|
||||||
|
* The factor of 2.5 is simply a rough guideline.
|
||||||
|
* http://stackoverflow.com/questions/527532/reasonable-php-memory-limit-for-image-resize
|
||||||
|
*
|
||||||
|
* @param array $info The output of getimagesize() for the source file in question.
|
||||||
|
* @return int The number of bytes an image resize operation is estimated to use.
|
||||||
|
*/
|
||||||
|
static function calc_memory_use(array $info): int
|
||||||
|
{
|
||||||
|
if (isset($info['bits']) && isset($info['channels'])) {
|
||||||
|
$memory_use = ($info[0] * $info[1] * ($info['bits'] / 8) * $info['channels'] * 2.5) / 1024;
|
||||||
|
} else {
|
||||||
|
// If we don't have bits and channel info from the image then assume default values
|
||||||
|
// of 8 bits per color and 4 channels (R,G,B,A) -- ie: regular 24-bit color
|
||||||
|
$memory_use = ($info[0] * $info[1] * 1 * 4 * 2.5) / 1024;
|
||||||
|
}
|
||||||
|
return (int)$memory_use;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a thumbnail using ffmpeg.
|
||||||
|
*
|
||||||
|
* @param $hash
|
||||||
|
* @return bool true if successful, false if not.
|
||||||
|
* @throws GraphicsException
|
||||||
|
*/
|
||||||
|
static function create_thumbnail_ffmpeg($hash): bool
|
||||||
|
{
|
||||||
|
global $config;
|
||||||
|
|
||||||
|
$ffmpeg = $config->get_string(GraphicsConfig::FFMPEG_PATH);
|
||||||
|
if ($ffmpeg == null || $ffmpeg == "") {
|
||||||
|
throw new GraphicsException("ffmpeg command configured");
|
||||||
|
}
|
||||||
|
|
||||||
|
$inname = warehouse_path(Image::IMAGE_DIR, $hash);
|
||||||
|
$outname = warehouse_path(Image::THUMBNAIL_DIR, $hash);
|
||||||
|
|
||||||
|
$orig_size = self::video_size($inname);
|
||||||
|
$scaled_size = get_thumbnail_size($orig_size[0], $orig_size[1], true);
|
||||||
|
|
||||||
|
$codec = "mjpeg";
|
||||||
|
$quality = $config->get_int(ImageConfig::THUMB_QUALITY);
|
||||||
|
if ($config->get_string(ImageConfig::THUMB_TYPE) == "webp") {
|
||||||
|
$codec = "libwebp";
|
||||||
|
} else {
|
||||||
|
// mjpeg quality ranges from 2-31, with 2 being the best quality.
|
||||||
|
$quality = floor(31 - (31 * ($quality / 100)));
|
||||||
|
if ($quality < 2) {
|
||||||
|
$quality = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$args = [
|
||||||
|
escapeshellarg($ffmpeg),
|
||||||
|
"-y", "-i", escapeshellarg($inname),
|
||||||
|
"-vf", "thumbnail,scale={$scaled_size[0]}:{$scaled_size[1]}",
|
||||||
|
"-f", "image2",
|
||||||
|
"-vframes", "1",
|
||||||
|
"-c:v", $codec,
|
||||||
|
"-q:v", $quality,
|
||||||
|
escapeshellarg($outname),
|
||||||
|
];
|
||||||
|
|
||||||
|
$cmd = escapeshellcmd(implode(" ", $args));
|
||||||
|
|
||||||
|
exec($cmd, $output, $ret);
|
||||||
|
|
||||||
|
if ((int)$ret == (int)0) {
|
||||||
|
log_debug('graphics', "Generating thumbnail with command `$cmd`, returns $ret");
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
log_error('graphics', "Generating thumbnail with command `$cmd`, returns $ret");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function determine_ext(String $format): String
|
||||||
|
{
|
||||||
|
$format = self::normalize_format($format);
|
||||||
|
switch ($format) {
|
||||||
|
case self::WEBP_LOSSLESS:
|
||||||
|
case self::WEBP_LOSSY:
|
||||||
|
return "webp";
|
||||||
|
default:
|
||||||
|
return $format;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// private static function image_save_imagick(Imagick $image, string $path, string $format, int $output_quality = 80, bool $minimize)
|
||||||
|
// {
|
||||||
|
// switch ($format) {
|
||||||
|
// case "png":
|
||||||
|
// $result = $image->setOption('png:compression-level', 9);
|
||||||
|
// if ($result !== true) {
|
||||||
|
// throw new GraphicsException("Could not set png compression option");
|
||||||
|
// }
|
||||||
|
// break;
|
||||||
|
// case Graphics::WEBP_LOSSLESS:
|
||||||
|
// $result = $image->setOption('webp:lossless', true);
|
||||||
|
// if ($result !== true) {
|
||||||
|
// throw new GraphicsException("Could not set lossless webp option");
|
||||||
|
// }
|
||||||
|
// break;
|
||||||
|
// default:
|
||||||
|
// $result = $image->setImageCompressionQuality($output_quality);
|
||||||
|
// if ($result !== true) {
|
||||||
|
// throw new GraphicsException("Could not set compression quality for $path to $output_quality");
|
||||||
|
// }
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if (self::supports_alpha($format)) {
|
||||||
|
// $result = $image->setImageBackgroundColor(new \ImagickPixel('transparent'));
|
||||||
|
// } else {
|
||||||
|
// $result = $image->setImageBackgroundColor(new \ImagickPixel('black'));
|
||||||
|
// }
|
||||||
|
// if ($result !== true) {
|
||||||
|
// throw new GraphicsException("Could not set background color");
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// if ($minimize) {
|
||||||
|
// $profiles = $image->getImageProfiles("icc", true);
|
||||||
|
// $result = $image->stripImage();
|
||||||
|
// if ($result !== true) {
|
||||||
|
// throw new GraphicsException("Could not strip information from image");
|
||||||
|
// }
|
||||||
|
// if (!empty($profiles)) {
|
||||||
|
// $image->profileImage("icc", $profiles['icc']);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// $ext = self::determine_ext($format);
|
||||||
|
//
|
||||||
|
// $result = $image->writeImage($ext . ":" . $path);
|
||||||
|
// if ($result !== true) {
|
||||||
|
// throw new GraphicsException("Could not write image to $path");
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// public static function image_resize_imagick(
|
||||||
|
// String $input_path,
|
||||||
|
// String $input_type,
|
||||||
|
// int $new_width,
|
||||||
|
// int $new_height,
|
||||||
|
// string $output_filename,
|
||||||
|
// string $output_type = null,
|
||||||
|
// bool $ignore_aspect_ratio = false,
|
||||||
|
// int $output_quality = 80,
|
||||||
|
// bool $minimize = false,
|
||||||
|
// bool $allow_upscale = true
|
||||||
|
// ): void
|
||||||
|
// {
|
||||||
|
// global $config;
|
||||||
|
//
|
||||||
|
// if (!empty($input_type)) {
|
||||||
|
// $input_type = self::determine_ext($input_type);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// try {
|
||||||
|
// $image = new Imagick($input_type . ":" . $input_path);
|
||||||
|
// try {
|
||||||
|
// $result = $image->flattenImages();
|
||||||
|
// if ($result !== true) {
|
||||||
|
// throw new GraphicsException("Could not flatten image $input_path");
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// $height = $image->getImageHeight();
|
||||||
|
// $width = $image->getImageWidth();
|
||||||
|
// if (!$allow_upscale &&
|
||||||
|
// ($new_width > $width || $new_height > $height)) {
|
||||||
|
// $new_height = $height;
|
||||||
|
// $new_width = $width;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// $result = $image->resizeImage($new_width, $new_width, Imagick::FILTER_LANCZOS, 0, !$ignore_aspect_ratio);
|
||||||
|
// if ($result !== true) {
|
||||||
|
// throw new GraphicsException("Could not perform image resize on $input_path");
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// if (empty($output_type)) {
|
||||||
|
// $output_type = $input_type;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// self::image_save_imagick($image, $output_filename, $output_type, $output_quality);
|
||||||
|
//
|
||||||
|
// } finally {
|
||||||
|
// $image->destroy();
|
||||||
|
// }
|
||||||
|
// } catch (ImagickException $e) {
|
||||||
|
// throw new GraphicsException("Error while resizing with Imagick: " . $e->getMessage(), $e->getCode(), $e);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
public static function image_resize_convert(
|
||||||
|
String $input_path,
|
||||||
|
String $input_type,
|
||||||
|
int $new_width,
|
||||||
|
int $new_height,
|
||||||
|
string $output_filename,
|
||||||
|
string $output_type = null,
|
||||||
|
bool $ignore_aspect_ratio = false,
|
||||||
|
int $output_quality = 80,
|
||||||
|
bool $minimize = false,
|
||||||
|
bool $allow_upscale = true
|
||||||
|
): void
|
||||||
|
{
|
||||||
|
global $config;
|
||||||
|
|
||||||
|
$convert = $config->get_string(GraphicsConfig::CONVERT_PATH);
|
||||||
|
|
||||||
|
if ($convert == null || $convert == "") {
|
||||||
|
throw new GraphicsException("convert command not configured");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($output_type)) {
|
||||||
|
$output_type = $input_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
$bg = "black";
|
||||||
|
if (self::supports_alpha($output_type)) {
|
||||||
|
$bg = "none";
|
||||||
|
}
|
||||||
|
if (!empty($input_type)) {
|
||||||
|
$input_type = $input_type . ":";
|
||||||
|
}
|
||||||
|
$args = "";
|
||||||
|
if ($minimize) {
|
||||||
|
$args = " -strip -thumbnail";
|
||||||
|
}
|
||||||
|
|
||||||
|
$resize_args = "";
|
||||||
|
if (!$allow_upscale) {
|
||||||
|
$resize_args .= "\>";
|
||||||
|
}
|
||||||
|
if ($ignore_aspect_ratio) {
|
||||||
|
$resize_args .= "\!";
|
||||||
|
}
|
||||||
|
|
||||||
|
$format = '"%s" -flatten %s %ux%u%s -quality %u -background %s %s"%s[0]" %s:"%s" 2>&1';
|
||||||
|
$cmd = sprintf($format, $convert, $args, $new_width, $new_height, $resize_args, $output_quality, $bg, $input_type, $input_path, $output_type, $output_filename);
|
||||||
|
$cmd = str_replace("\"convert\"", "convert", $cmd); // quotes are only needed if the path to convert contains a space; some other times, quotes break things, see github bug #27
|
||||||
|
exec($cmd, $output, $ret);
|
||||||
|
if ($ret != 0) {
|
||||||
|
throw new GraphicsException("Resizing image with command `$cmd`, returns $ret, outputting " . implode("\r\n", $output));
|
||||||
|
} else {
|
||||||
|
log_debug('graphics', "Generating thumbnail with command `$cmd`, returns $ret");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs a resize operation on an image file using GD.
|
||||||
|
*
|
||||||
|
* @param String $image_filename The source file to be resized.
|
||||||
|
* @param array $info The output of getimagesize() for the source file.
|
||||||
|
* @param int $new_width
|
||||||
|
* @param int $new_height
|
||||||
|
* @param string $output_filename
|
||||||
|
* @param string|null $output_type If set to null, the output file type will be automatically determined via the $info parameter. Otherwise an exception will be thrown.
|
||||||
|
* @param int $output_quality Defaults to 80.
|
||||||
|
* @throws GraphicsException
|
||||||
|
* @throws InsufficientMemoryException if the estimated memory usage exceeds the memory limit.
|
||||||
|
*/
|
||||||
|
public static function image_resize_gd(
|
||||||
|
String $image_filename,
|
||||||
|
array $info,
|
||||||
|
int $new_width,
|
||||||
|
int $new_height,
|
||||||
|
string $output_filename,
|
||||||
|
string $output_type = null,
|
||||||
|
bool $ignore_aspect_ratio = false,
|
||||||
|
int $output_quality = 80,
|
||||||
|
bool $allow_upscale = true
|
||||||
|
)
|
||||||
|
{
|
||||||
|
$width = $info[0];
|
||||||
|
$height = $info[1];
|
||||||
|
|
||||||
|
if ($output_type == null) {
|
||||||
|
/* If not specified, output to the same format as the original image */
|
||||||
|
switch ($info[2]) {
|
||||||
|
case IMAGETYPE_GIF:
|
||||||
|
$output_type = "gif";
|
||||||
|
break;
|
||||||
|
case IMAGETYPE_JPEG:
|
||||||
|
$output_type = "jpeg";
|
||||||
|
break;
|
||||||
|
case IMAGETYPE_PNG:
|
||||||
|
$output_type = "png";
|
||||||
|
break;
|
||||||
|
case IMAGETYPE_WEBP:
|
||||||
|
$output_type = "webp";
|
||||||
|
break;
|
||||||
|
case IMAGETYPE_BMP:
|
||||||
|
$output_type = "bmp";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new GraphicsException("Failed to save the new image - Unsupported image type.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$memory_use = self::calc_memory_use($info);
|
||||||
|
$memory_limit = get_memory_limit();
|
||||||
|
if ($memory_use > $memory_limit) {
|
||||||
|
throw new InsufficientMemoryException("The image is too large to resize given the memory limits. ($memory_use > $memory_limit)");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$ignore_aspect_ratio) {
|
||||||
|
list($new_width, $new_height) = get_scaled_by_aspect_ratio($width, $height, $new_width, $new_height);
|
||||||
|
}
|
||||||
|
if (!$allow_upscale &&
|
||||||
|
($new_width > $width || $new_height > $height)) {
|
||||||
|
$new_height = $height;
|
||||||
|
$new_width = $width;
|
||||||
|
}
|
||||||
|
|
||||||
|
$image = imagecreatefromstring(file_get_contents($image_filename));
|
||||||
|
$image_resized = imagecreatetruecolor($new_width, $new_height);
|
||||||
|
try {
|
||||||
|
if ($image === false) {
|
||||||
|
throw new GraphicsException("Could not load image: " . $image_filename);
|
||||||
|
}
|
||||||
|
if ($image_resized === false) {
|
||||||
|
throw new GraphicsException("Could not create output image with dimensions $new_width c $new_height ");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle transparent images
|
||||||
|
switch ($info[2]) {
|
||||||
|
case IMAGETYPE_GIF:
|
||||||
|
$transparency = imagecolortransparent($image);
|
||||||
|
$pallet_size = imagecolorstotal($image);
|
||||||
|
|
||||||
|
// If we have a specific transparent color
|
||||||
|
if ($transparency >= 0 && $transparency < $pallet_size) {
|
||||||
|
// Get the original image's transparent color's RGB values
|
||||||
|
$transparent_color = imagecolorsforindex($image, $transparency);
|
||||||
|
|
||||||
|
// Allocate the same color in the new image resource
|
||||||
|
$transparency = imagecolorallocate($image_resized, $transparent_color['red'], $transparent_color['green'], $transparent_color['blue']);
|
||||||
|
if ($transparency === false) {
|
||||||
|
throw new GraphicsException("Unable to allocate transparent color");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Completely fill the background of the new image with allocated color.
|
||||||
|
if (imagefill($image_resized, 0, 0, $transparency) === false) {
|
||||||
|
throw new GraphicsException("Unable to fill new image with transparent color");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the background color for new image to transparent
|
||||||
|
imagecolortransparent($image_resized, $transparency);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case IMAGETYPE_PNG:
|
||||||
|
case IMAGETYPE_WEBP:
|
||||||
|
//
|
||||||
|
// More info here: http://stackoverflow.com/questions/279236/how-do-i-resize-pngs-with-transparency-in-php
|
||||||
|
//
|
||||||
|
if (imagealphablending($image_resized, false) === false) {
|
||||||
|
throw new GraphicsException("Unable to disable image alpha blending");
|
||||||
|
}
|
||||||
|
if (imagesavealpha($image_resized, true) === false) {
|
||||||
|
throw new GraphicsException("Unable to enable image save alpha");
|
||||||
|
}
|
||||||
|
$transparent_color = imagecolorallocatealpha($image_resized, 255, 255, 255, 127);
|
||||||
|
if ($transparent_color === false) {
|
||||||
|
throw new GraphicsException("Unable to allocate transparent color");
|
||||||
|
}
|
||||||
|
if (imagefilledrectangle($image_resized, 0, 0, $new_width, $new_height, $transparent_color) === false) {
|
||||||
|
throw new GraphicsException("Unable to fill new image with transparent color");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Actually resize the image.
|
||||||
|
if (imagecopyresampled(
|
||||||
|
$image_resized,
|
||||||
|
$image,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
$new_width,
|
||||||
|
$new_height,
|
||||||
|
$width,
|
||||||
|
$height
|
||||||
|
) === false) {
|
||||||
|
throw new GraphicsException("Unable to copy resized image data to new image");
|
||||||
|
}
|
||||||
|
|
||||||
|
switch ($output_type) {
|
||||||
|
case "bmp":
|
||||||
|
$result = imagebmp($image_resized, $output_filename, true);
|
||||||
|
break;
|
||||||
|
case "webp":
|
||||||
|
case Graphics::WEBP_LOSSY:
|
||||||
|
$result = imagewebp($image_resized, $output_filename, $output_quality);
|
||||||
|
break;
|
||||||
|
case "jpg":
|
||||||
|
case "jpeg":
|
||||||
|
$result = imagejpeg($image_resized, $output_filename, $output_quality);
|
||||||
|
break;
|
||||||
|
case "png":
|
||||||
|
$result = imagepng($image_resized, $output_filename, 9);
|
||||||
|
break;
|
||||||
|
case "gif":
|
||||||
|
$result = imagegif($image_resized, $output_filename);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new GraphicsException("Failed to save the new image - Unsupported image type: $output_type");
|
||||||
|
}
|
||||||
|
if ($result === false) {
|
||||||
|
throw new GraphicsException("Failed to save the new image, function returned false when saving type: $output_type");
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
@imagedestroy($image);
|
||||||
|
@imagedestroy($image_resized);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines if a file is an animated gif.
|
||||||
|
*
|
||||||
|
* @param String $image_filename The path of the file to check.
|
||||||
|
* @return bool true if the file is an animated gif, false if it is not.
|
||||||
|
*/
|
||||||
|
public static function is_animated_gif(String $image_filename)
|
||||||
|
{
|
||||||
|
$is_anim_gif = 0;
|
||||||
|
if (($fh = @fopen($image_filename, 'rb'))) {
|
||||||
|
//check if gif is animated (via http://www.php.net/manual/en/function.imagecreatefromgif.php#104473)
|
||||||
|
while (!feof($fh) && $is_anim_gif < 2) {
|
||||||
|
$chunk = fread($fh, 1024 * 100);
|
||||||
|
$is_anim_gif += preg_match_all('#\x00\x21\xF9\x04.{4}\x00(\x2C|\x21)#s', $chunk, $matches);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ($is_anim_gif == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function supports_alpha(string $format)
|
||||||
|
{
|
||||||
|
return in_array(self::normalize_format($format), self::ALPHA_FORMATS);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function is_input_supported($engine, $format): bool
|
||||||
|
{
|
||||||
|
$format = self::normalize_format($format);
|
||||||
|
if (!in_array($format, Graphics::ENGINE_INPUT_SUPPORT[$engine])) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function is_output_supported($engine, $format): bool
|
||||||
|
{
|
||||||
|
$format = self::normalize_format($format);
|
||||||
|
if (!in_array($format, Graphics::ENGINE_OUTPUT_SUPPORT[$engine])) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if a format (normally a file extension) is a variant name of another format (ie, jpg and jpeg).
|
||||||
|
* If one is found, then the maine name that the Graphics extension will recognize is returned,
|
||||||
|
* otherwise the incoming format is returned.
|
||||||
|
*
|
||||||
|
* @param $format
|
||||||
|
* @return string|null The format name that the graphics extension will recognize.
|
||||||
|
*/
|
||||||
|
static public function normalize_format($format): ?string
|
||||||
|
{
|
||||||
|
if (array_key_exists($format, Graphics::FORMAT_ALIASES)) {
|
||||||
|
return self::FORMAT_ALIASES[$format];
|
||||||
|
}
|
||||||
|
return $format;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines the dimensions of a video file using ffmpeg.
|
||||||
|
*
|
||||||
|
* @param string $filename
|
||||||
|
* @return array [width, height]
|
||||||
|
*/
|
||||||
|
static public function video_size(string $filename): array
|
||||||
|
{
|
||||||
|
global $config;
|
||||||
|
$ffmpeg = $config->get_string(GraphicsConfig::FFMPEG_PATH);
|
||||||
|
$cmd = escapeshellcmd(implode(" ", [
|
||||||
|
escapeshellarg($ffmpeg),
|
||||||
|
"-y", "-i", escapeshellarg($filename),
|
||||||
|
"-vstats"
|
||||||
|
]));
|
||||||
|
$output = shell_exec($cmd . " 2>&1");
|
||||||
|
// error_log("Getting size with `$cmd`");
|
||||||
|
|
||||||
|
$regex_sizes = "/Video: .* ([0-9]{1,4})x([0-9]{1,4})/";
|
||||||
|
if (preg_match($regex_sizes, $output, $regs)) {
|
||||||
|
if (preg_match("/displaymatrix: rotation of (90|270).00 degrees/", $output)) {
|
||||||
|
$size = [$regs[2], $regs[1]];
|
||||||
|
} else {
|
||||||
|
$size = [$regs[1], $regs[2]];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$size = [1, 1];
|
||||||
|
}
|
||||||
|
log_debug('graphics', "Getting video size with `$cmd`, returns $output -- $size[0], $size[1]");
|
||||||
|
return $size;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
6
ext/graphics/theme.php
Normal file
6
ext/graphics/theme.php
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
class GraphicsTheme extends Themelet
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
@ -12,7 +12,7 @@ class FlashFileHandler extends DataHandlerExtension
|
|||||||
{
|
{
|
||||||
global $config;
|
global $config;
|
||||||
|
|
||||||
if (!create_thumbnail_ffmpeg($hash)) {
|
if (!Graphics::create_thumbnail_ffmpeg($hash)) {
|
||||||
copy("ext/handle_flash/thumb.jpg", warehouse_path(Image::THUMBNAIL_DIR, $hash));
|
copy("ext/handle_flash/thumb.jpg", warehouse_path(Image::THUMBNAIL_DIR, $hash));
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
@ -56,6 +56,12 @@ class IcoFileHandler extends DataHandlerExtension
|
|||||||
|
|
||||||
protected function create_thumb(string $hash, string $type): bool
|
protected function create_thumb(string $hash, string $type): bool
|
||||||
{
|
{
|
||||||
return create_thumbnail_convert($hash, $type);
|
try {
|
||||||
|
create_image_thumb($hash, $type, Graphics::IMAGICK_ENGINE);
|
||||||
|
return true;
|
||||||
|
} catch (GraphicsException $e) {
|
||||||
|
log_warning("handle_ico", "Could not generate thumbnail. " . $e->getMessage());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -56,24 +56,22 @@ class PixelFileHandler extends DataHandlerExtension
|
|||||||
|
|
||||||
protected function create_thumb(string $hash, string $type): bool
|
protected function create_thumb(string $hash, string $type): bool
|
||||||
{
|
{
|
||||||
global $config;
|
try {
|
||||||
|
create_image_thumb($hash, $type);
|
||||||
$inname = warehouse_path(Image::IMAGE_DIR, $hash);
|
return true;
|
||||||
$outname = warehouse_path(Image::THUMBNAIL_DIR, $hash);
|
} catch (InsufficientMemoryException $e) {
|
||||||
|
$tsize = get_thumbnail_max_size_scaled();
|
||||||
$ok = false;
|
$thumb = imagecreatetruecolor($tsize[0], min($tsize[1], 64));
|
||||||
|
$white = imagecolorallocate($thumb, 255, 255, 255);
|
||||||
switch ($config->get_string("thumb_engine")) {
|
$black = imagecolorallocate($thumb, 0, 0, 0);
|
||||||
default:
|
imagefill($thumb, 0, 0, $white);
|
||||||
case 'gd':
|
log_warning("handle_pixel", "Insufficient memory while creating thumbnail: ".$e->getMessage());
|
||||||
$ok = $this->make_thumb_gd($inname, $outname);
|
imagestring($thumb, 5, 10, 24, "Image Too Large :(", $black);
|
||||||
break;
|
return true;
|
||||||
case 'convert':
|
} catch (Exception $e) {
|
||||||
$ok = create_thumbnail_convert($hash);
|
log_error("handle_pixel", "Error while creating thumbnail: ".$e->getMessage());
|
||||||
break;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $ok;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function onImageAdminBlockBuilding(ImageAdminBlockBuildingEvent $event)
|
public function onImageAdminBlockBuilding(ImageAdminBlockBuildingEvent $event)
|
||||||
@ -90,38 +88,4 @@ class PixelFileHandler extends DataHandlerExtension
|
|||||||
", 20);
|
", 20);
|
||||||
}
|
}
|
||||||
|
|
||||||
// GD thumber {{{
|
|
||||||
private function make_thumb_gd(string $inname, string $outname): bool
|
|
||||||
{
|
|
||||||
global $config;
|
|
||||||
|
|
||||||
try {
|
|
||||||
$info = getimagesize($inname);
|
|
||||||
$tsize = get_thumbnail_size($info[0], $info[1], true);
|
|
||||||
$image = image_resize_gd(
|
|
||||||
$inname,
|
|
||||||
$info,
|
|
||||||
$tsize[0],
|
|
||||||
$tsize[1],
|
|
||||||
$outname,
|
|
||||||
$config->get_string('thumb_type'),
|
|
||||||
$config->get_int('thumb_quality')
|
|
||||||
);
|
|
||||||
} catch (InsufficientMemoryException $e) {
|
|
||||||
$tsize = get_thumbnail_max_size_scaled();
|
|
||||||
$thumb = imagecreatetruecolor($tsize[0], min($tsize[1], 64));
|
|
||||||
$white = imagecolorallocate($thumb, 255, 255, 255);
|
|
||||||
$black = imagecolorallocate($thumb, 0, 0, 0);
|
|
||||||
imagefill($thumb, 0, 0, $white);
|
|
||||||
log_warning("handle_pixel", "Insufficient memory while creating thumbnail: ".$e->getMessage());
|
|
||||||
imagestring($thumb, 5, 10, 24, "Image Too Large :(", $black);
|
|
||||||
return true;
|
|
||||||
} catch (Exception $e) {
|
|
||||||
log_error("handle_pixel", "Error while creating thumbnail: ".$e->getMessage());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// }}}
|
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ class PixelFileHandlerTheme extends Themelet
|
|||||||
global $config;
|
global $config;
|
||||||
|
|
||||||
$u_ilink = $image->get_image_link();
|
$u_ilink = $image->get_image_link();
|
||||||
if ($config->get_bool("image_show_meta") && function_exists("exif_read_data")) {
|
if ($config->get_bool(ImageConfig::SHOW_META) && function_exists(ImageIO::EXIF_READ_FUNCTION)) {
|
||||||
# FIXME: only read from jpegs?
|
# FIXME: only read from jpegs?
|
||||||
$exif = @exif_read_data($image->get_image_filename(), 0, true);
|
$exif = @exif_read_data($image->get_image_filename(), 0, true);
|
||||||
if ($exif) {
|
if ($exif) {
|
||||||
|
@ -35,10 +35,14 @@ class SVGFileHandler extends DataHandlerExtension
|
|||||||
|
|
||||||
protected function create_thumb(string $hash, string $type): bool
|
protected function create_thumb(string $hash, string $type): bool
|
||||||
{
|
{
|
||||||
if (!create_thumbnail_convert($hash)) {
|
try {
|
||||||
copy("ext/handle_svg/thumb.jpg", warehouse_path(Image::THUMBNAIL_DIR, $hash));
|
create_image_thumb($hash, $type, Graphics::IMAGICK_ENGINE);
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
|
} catch (GraphicsException $e) {
|
||||||
|
log_warning("handle_svg", "Could not generate thumbnail. " . $e->getMessage());
|
||||||
|
copy("ext/handle_svg/thumb.jpg", warehouse_path(Image::THUMBNAIL_DIR, $hash));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function onDisplayingImage(DisplayingImageEvent $event)
|
public function onDisplayingImage(DisplayingImageEvent $event)
|
||||||
|
@ -16,20 +16,21 @@
|
|||||||
|
|
||||||
class VideoFileHandler extends DataHandlerExtension
|
class VideoFileHandler extends DataHandlerExtension
|
||||||
{
|
{
|
||||||
|
const SUPPORTED_MIME = [
|
||||||
|
'video/webm',
|
||||||
|
'video/mp4',
|
||||||
|
'video/ogg',
|
||||||
|
'video/flv',
|
||||||
|
'video/x-flv'
|
||||||
|
];
|
||||||
|
const SUPPORTED_EXT = ["flv", "mp4", "m4v", "ogv", "webm"];
|
||||||
|
|
||||||
public function onInitExt(InitExtEvent $event)
|
public function onInitExt(InitExtEvent $event)
|
||||||
{
|
{
|
||||||
global $config;
|
global $config;
|
||||||
|
|
||||||
if ($config->get_int("ext_handle_video_version") < 1) {
|
if ($config->get_int("ext_handle_video_version") < 1) {
|
||||||
if ($ffmpeg = shell_exec((PHP_OS == 'WINNT' ? 'where' : 'which') . ' ffmpeg')) {
|
// This used to set the ffmpeg path. It does not do this anymore, that is now in the base graphic extension.
|
||||||
//ffmpeg exists in PATH, check if it's executable, and if so, default to it instead of static
|
|
||||||
if (is_executable(strtok($ffmpeg, PHP_EOL))) {
|
|
||||||
$config->set_default_string('thumb_ffmpeg_path', 'ffmpeg');
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$config->set_default_string('thumb_ffmpeg_path', '');
|
|
||||||
}
|
|
||||||
|
|
||||||
$config->set_int("ext_handle_video_version", 1);
|
$config->set_int("ext_handle_video_version", 1);
|
||||||
log_info("handle_video", "extension installed");
|
log_info("handle_video", "extension installed");
|
||||||
}
|
}
|
||||||
@ -41,9 +42,6 @@ class VideoFileHandler extends DataHandlerExtension
|
|||||||
public function onSetupBuilding(SetupBuildingEvent $event)
|
public function onSetupBuilding(SetupBuildingEvent $event)
|
||||||
{
|
{
|
||||||
$sb = new SetupBlock("Video Options");
|
$sb = new SetupBlock("Video Options");
|
||||||
$sb->add_label("<br>Path to ffmpeg: ");
|
|
||||||
$sb->add_text_option("thumb_ffmpeg_path");
|
|
||||||
$sb->add_label("<br>");
|
|
||||||
$sb->add_bool_option("video_playback_autoplay", "Autoplay: ");
|
$sb->add_bool_option("video_playback_autoplay", "Autoplay: ");
|
||||||
$sb->add_label("<br>");
|
$sb->add_label("<br>");
|
||||||
$sb->add_bool_option("video_playback_loop", "Loop: ");
|
$sb->add_bool_option("video_playback_loop", "Loop: ");
|
||||||
@ -55,20 +53,19 @@ class VideoFileHandler extends DataHandlerExtension
|
|||||||
*/
|
*/
|
||||||
protected function create_thumb(string $hash, string $type): bool
|
protected function create_thumb(string $hash, string $type): bool
|
||||||
{
|
{
|
||||||
return create_thumbnail_ffmpeg($hash);
|
return Graphics::create_thumbnail_ffmpeg($hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function supported_ext(string $ext): bool
|
protected function supported_ext(string $ext): bool
|
||||||
{
|
{
|
||||||
$exts = ["flv", "mp4", "m4v", "ogv", "webm"];
|
return in_array(strtolower($ext), self::SUPPORTED_EXT);
|
||||||
return in_array(strtolower($ext), $exts);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function create_image_from_data(string $filename, array $metadata): Image
|
protected function create_image_from_data(string $filename, array $metadata): Image
|
||||||
{
|
{
|
||||||
$image = new Image();
|
$image = new Image();
|
||||||
|
|
||||||
$size = video_size($filename);
|
$size = Graphics::video_size($filename);
|
||||||
$image->width = $size[0];
|
$image->width = $size[0];
|
||||||
$image->height = $size[1];
|
$image->height = $size[1];
|
||||||
|
|
||||||
@ -103,13 +100,7 @@ class VideoFileHandler extends DataHandlerExtension
|
|||||||
{
|
{
|
||||||
return (
|
return (
|
||||||
file_exists($tmpname) &&
|
file_exists($tmpname) &&
|
||||||
in_array(getMimeType($tmpname), [
|
in_array(getMimeType($tmpname), self::SUPPORTED_MIME)
|
||||||
'video/webm',
|
|
||||||
'video/mp4',
|
|
||||||
'video/ogg',
|
|
||||||
'video/flv',
|
|
||||||
'video/x-flv'
|
|
||||||
])
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,30 +9,64 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
abstract class ImageConfig {
|
||||||
|
const THUMB_ENGINE = 'thumb_engine';
|
||||||
|
const THUMB_WIDTH = 'thumb_width';
|
||||||
|
const THUMB_HEIGHT = 'thumb_height';
|
||||||
|
const THUMB_SCALING = 'thumb_scaling';
|
||||||
|
const THUMB_QUALITY = 'thumb_quality';
|
||||||
|
const THUMB_TYPE = 'thumb_type';
|
||||||
|
|
||||||
|
const SHOW_META = 'image_show_meta';
|
||||||
|
const ILINK = 'image_ilink';
|
||||||
|
const TLINK = 'image_tlink';
|
||||||
|
const TIP = 'image_tip';
|
||||||
|
const EXPIRES = 'image_expires';
|
||||||
|
const UPLOAD_COLLISION_HANDLER = 'upload_collision_handler';
|
||||||
|
|
||||||
|
const COLLISION_MERGE = 'merge';
|
||||||
|
const COLLISION_ERROR = 'error';
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A class to handle adding / getting / removing image files from the disk.
|
* A class to handle adding / getting / removing image files from the disk.
|
||||||
*/
|
*/
|
||||||
class ImageIO extends Extension
|
class ImageIO extends Extension
|
||||||
{
|
{
|
||||||
|
|
||||||
|
const COLLISION_OPTIONS = ['Error'=>ImageConfig::COLLISION_ERROR, 'Merge'=>ImageConfig::COLLISION_MERGE];
|
||||||
|
|
||||||
|
const EXIF_READ_FUNCTION = "exif_read_data";
|
||||||
|
|
||||||
|
|
||||||
|
const THUMBNAIL_ENGINES = [
|
||||||
|
'Built-in GD' => Graphics::GD_ENGINE,
|
||||||
|
'ImageMagick' => Graphics::IMAGICK_ENGINE
|
||||||
|
];
|
||||||
|
|
||||||
|
const THUMBNAIL_TYPES = [
|
||||||
|
'JPEG' => "jpg",
|
||||||
|
'WEBP (Not IE/Safari compatible)' => "webp"
|
||||||
|
];
|
||||||
|
|
||||||
public function onInitExt(InitExtEvent $event)
|
public function onInitExt(InitExtEvent $event)
|
||||||
{
|
{
|
||||||
global $config;
|
global $config;
|
||||||
$config->set_default_int('thumb_width', 192);
|
$config->set_default_int(ImageConfig::THUMB_WIDTH, 192);
|
||||||
$config->set_default_int('thumb_height', 192);
|
$config->set_default_int(ImageConfig::THUMB_HEIGHT, 192);
|
||||||
$config->set_default_int('thumb_scaling', 100);
|
$config->set_default_int(ImageConfig::THUMB_SCALING, 100);
|
||||||
$config->set_default_int('thumb_quality', 75);
|
$config->set_default_int(ImageConfig::THUMB_QUALITY, 75);
|
||||||
$config->set_default_string('thumb_type', 'jpg');
|
$config->set_default_string(ImageConfig::THUMB_TYPE, 'jpg');
|
||||||
$config->set_default_int('thumb_mem_limit', parse_shorthand_int('8MB'));
|
|
||||||
$config->set_default_string('thumb_convert_path', 'convert');
|
|
||||||
|
|
||||||
if (function_exists("exif_read_data")) {
|
if (function_exists(self::EXIF_READ_FUNCTION)) {
|
||||||
$config->set_default_bool('image_show_meta', false);
|
$config->set_default_bool(ImageConfig::SHOW_META, false);
|
||||||
}
|
}
|
||||||
$config->set_default_string('image_ilink', '');
|
$config->set_default_string(ImageConfig::ILINK, '');
|
||||||
$config->set_default_string('image_tlink', '');
|
$config->set_default_string(ImageConfig::TLINK, '');
|
||||||
$config->set_default_string('image_tip', '$tags // $size // $filesize');
|
$config->set_default_string(ImageConfig::TIP, '$tags // $size // $filesize');
|
||||||
$config->set_default_string('upload_collision_handler', 'error');
|
$config->set_default_string(ImageConfig::UPLOAD_COLLISION_HANDLER, ImageConfig::COLLISION_ERROR);
|
||||||
$config->set_default_int('image_expires', (60*60*24*31)); // defaults to one month
|
$config->set_default_int(ImageConfig::EXPIRES, (60*60*24*31)); // defaults to one month
|
||||||
}
|
}
|
||||||
|
|
||||||
public function onPageRequest(PageRequestEvent $event)
|
public function onPageRequest(PageRequestEvent $event)
|
||||||
@ -125,50 +159,36 @@ class ImageIO extends Extension
|
|||||||
$sb = new SetupBlock("Image Options");
|
$sb = new SetupBlock("Image Options");
|
||||||
$sb->position = 30;
|
$sb->position = 30;
|
||||||
// advanced only
|
// advanced only
|
||||||
//$sb->add_text_option("image_ilink", "Image link: ");
|
//$sb->add_text_option(ImageConfig::ILINK, "Image link: ");
|
||||||
//$sb->add_text_option("image_tlink", "<br>Thumbnail link: ");
|
//$sb->add_text_option(ImageConfig::TLINK, "<br>Thumbnail link: ");
|
||||||
$sb->add_text_option("image_tip", "Image tooltip: ");
|
$sb->add_text_option(ImageConfig::TIP, "Image tooltip: ");
|
||||||
$sb->add_choice_option("upload_collision_handler", ['Error'=>'error', 'Merge'=>'merge'], "<br>Upload collision handler: ");
|
$sb->add_choice_option(ImageConfig::UPLOAD_COLLISION_HANDLER, self::COLLISION_OPTIONS, "<br>Upload collision handler: ");
|
||||||
if (function_exists("exif_read_data")) {
|
if (function_exists(self::EXIF_READ_FUNCTION)) {
|
||||||
$sb->add_bool_option("image_show_meta", "<br>Show metadata: ");
|
$sb->add_bool_option(ImageConfig::SHOW_META, "<br>Show metadata: ");
|
||||||
}
|
}
|
||||||
|
|
||||||
$event->panel->add_block($sb);
|
$event->panel->add_block($sb);
|
||||||
|
|
||||||
$thumbers = [];
|
|
||||||
$thumbers['Built-in GD'] = "gd";
|
|
||||||
$thumbers['ImageMagick'] = "convert";
|
|
||||||
|
|
||||||
$thumb_types = [];
|
|
||||||
$thumb_types['JPEG'] = "jpg";
|
|
||||||
$thumb_types['WEBP (Not IE/Safari compatible)'] = "webp";
|
|
||||||
|
|
||||||
|
|
||||||
$sb = new SetupBlock("Thumbnailing");
|
$sb = new SetupBlock("Thumbnailing");
|
||||||
$sb->add_choice_option("thumb_engine", $thumbers, "Engine: ");
|
$sb->add_choice_option(ImageConfig::THUMB_ENGINE, self::THUMBNAIL_ENGINES, "Engine: ");
|
||||||
$sb->add_label("<br>");
|
$sb->add_label("<br>");
|
||||||
$sb->add_choice_option("thumb_type", $thumb_types, "Filetype: ");
|
$sb->add_choice_option(ImageConfig::THUMB_TYPE, self::THUMBNAIL_TYPES, "Filetype: ");
|
||||||
|
|
||||||
$sb->add_label("<br>Size ");
|
$sb->add_label("<br>Size ");
|
||||||
$sb->add_int_option("thumb_width");
|
$sb->add_int_option(ImageConfig::THUMB_WIDTH);
|
||||||
$sb->add_label(" x ");
|
$sb->add_label(" x ");
|
||||||
$sb->add_int_option("thumb_height");
|
$sb->add_int_option(ImageConfig::THUMB_HEIGHT);
|
||||||
$sb->add_label(" px at ");
|
$sb->add_label(" px at ");
|
||||||
$sb->add_int_option("thumb_quality");
|
$sb->add_int_option(ImageConfig::THUMB_QUALITY);
|
||||||
$sb->add_label(" % quality ");
|
$sb->add_label(" % quality ");
|
||||||
|
|
||||||
$sb->add_label("<br>High-DPI scaling ");
|
$sb->add_label("<br>High-DPI scaling ");
|
||||||
$sb->add_int_option("thumb_scaling");
|
$sb->add_int_option(ImageConfig::THUMB_SCALING);
|
||||||
$sb->add_label("%");
|
$sb->add_label("%");
|
||||||
|
|
||||||
if ($config->get_string("thumb_engine") == "convert") {
|
|
||||||
$sb->add_label("<br>ImageMagick Binary: ");
|
|
||||||
$sb->add_text_option("thumb_convert_path");
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($config->get_string("thumb_engine") == "gd") {
|
|
||||||
$sb->add_shorthand_int_option("thumb_mem_limit", "<br>Max memory use: ");
|
|
||||||
}
|
|
||||||
|
|
||||||
$event->panel->add_block($sb);
|
$event->panel->add_block($sb);
|
||||||
}
|
}
|
||||||
@ -193,8 +213,8 @@ class ImageIO extends Extension
|
|||||||
*/
|
*/
|
||||||
$existing = Image::by_hash($image->hash);
|
$existing = Image::by_hash($image->hash);
|
||||||
if (!is_null($existing)) {
|
if (!is_null($existing)) {
|
||||||
$handler = $config->get_string("upload_collision_handler");
|
$handler = $config->get_string(ImageConfig::UPLOAD_COLLISION_HANDLER);
|
||||||
if ($handler == "merge" || isset($_GET['update'])) {
|
if ($handler == ImageConfig::COLLISION_MERGE || isset($_GET['update'])) {
|
||||||
$merged = array_merge($image->get_tag_array(), $existing->get_tag_array());
|
$merged = array_merge($image->get_tag_array(), $existing->get_tag_array());
|
||||||
send_event(new TagSetEvent($existing, $merged));
|
send_event(new TagSetEvent($existing, $merged));
|
||||||
if (isset($_GET['rating']) && isset($_GET['update']) && ext_is_live("Ratings")) {
|
if (isset($_GET['rating']) && isset($_GET['update']) && ext_is_live("Ratings")) {
|
||||||
@ -256,7 +276,7 @@ class ImageIO extends Extension
|
|||||||
global $page;
|
global $page;
|
||||||
if (!is_null($image)) {
|
if (!is_null($image)) {
|
||||||
if ($type == "thumb") {
|
if ($type == "thumb") {
|
||||||
$ext = $config->get_string("thumb_type");
|
$ext = $config->get_string(ImageConfig::THUMB_TYPE);
|
||||||
if (array_key_exists($ext, MIME_TYPE_MAP)) {
|
if (array_key_exists($ext, MIME_TYPE_MAP)) {
|
||||||
$page->set_type(MIME_TYPE_MAP[$ext]);
|
$page->set_type(MIME_TYPE_MAP[$ext]);
|
||||||
} else {
|
} else {
|
||||||
@ -289,8 +309,8 @@ class ImageIO extends Extension
|
|||||||
|
|
||||||
$page->set_file($file);
|
$page->set_file($file);
|
||||||
|
|
||||||
if ($config->get_int("image_expires")) {
|
if ($config->get_int(ImageConfig::EXPIRES)) {
|
||||||
$expires = date(DATE_RFC1123, time() + $config->get_int("image_expires"));
|
$expires = date(DATE_RFC1123, time() + $config->get_int(ImageConfig::EXPIRES));
|
||||||
} else {
|
} else {
|
||||||
$expires = 'Fri, 2 Sep 2101 12:42:42 GMT'; // War was beginning
|
$expires = 'Fri, 2 Sep 2101 12:42:42 GMT'; // War was beginning
|
||||||
}
|
}
|
||||||
|
@ -231,8 +231,8 @@ class _SafeOuroborosImage
|
|||||||
$this->has_notes = false;
|
$this->has_notes = false;
|
||||||
|
|
||||||
// thumb
|
// thumb
|
||||||
$this->preview_height = $config->get_int('thumb_height');
|
$this->preview_height = $config->get_int(ImageConfig::THUMB_HEIGHT);
|
||||||
$this->preview_width = $config->get_int('thumb_width');
|
$this->preview_width = $config->get_int(ImageConfig::THUMB_WIDTH);
|
||||||
$this->preview_url = make_http($img->get_thumb_link());
|
$this->preview_url = make_http($img->get_thumb_link());
|
||||||
|
|
||||||
// sample (use the full image here)
|
// sample (use the full image here)
|
||||||
@ -481,8 +481,8 @@ class OuroborosAPI extends Extension
|
|||||||
protected function postCreate(OuroborosPost $post, string $md5 = '')
|
protected function postCreate(OuroborosPost $post, string $md5 = '')
|
||||||
{
|
{
|
||||||
global $config;
|
global $config;
|
||||||
$handler = $config->get_string("upload_collision_handler");
|
$handler = $config->get_string(ImageConfig::UPLOAD_COLLISION_HANDLER);
|
||||||
if (!empty($md5) && !($handler == 'merge')) {
|
if (!empty($md5) && !($handler == ImageConfig::COLLISION_MERGE)) {
|
||||||
$img = Image::by_hash($md5);
|
$img = Image::by_hash($md5);
|
||||||
if (!is_null($img)) {
|
if (!is_null($img)) {
|
||||||
$this->sendResponse(420, self::ERROR_POST_CREATE_DUPE);
|
$this->sendResponse(420, self::ERROR_POST_CREATE_DUPE);
|
||||||
@ -524,8 +524,8 @@ class OuroborosAPI extends Extension
|
|||||||
if (!empty($meta['hash'])) {
|
if (!empty($meta['hash'])) {
|
||||||
$img = Image::by_hash($meta['hash']);
|
$img = Image::by_hash($meta['hash']);
|
||||||
if (!is_null($img)) {
|
if (!is_null($img)) {
|
||||||
$handler = $config->get_string("upload_collision_handler");
|
$handler = $config->get_string(ImageConfig::UPLOAD_COLLISION_HANDLER);
|
||||||
if ($handler == "merge") {
|
if ($handler == ImageConfig::COLLISION_MERGE) {
|
||||||
$postTags = is_array($post->tags) ? $post->tags : Tag::explode($post->tags);
|
$postTags = is_array($post->tags) ? $post->tags : Tag::explode($post->tags);
|
||||||
$merged = array_merge($postTags, $img->get_tag_array());
|
$merged = array_merge($postTags, $img->get_tag_array());
|
||||||
send_event(new TagSetEvent($img, $merged));
|
send_event(new TagSetEvent($img, $merged));
|
||||||
|
@ -46,7 +46,7 @@ class ReportImageTheme extends Themelet
|
|||||||
";
|
";
|
||||||
}
|
}
|
||||||
|
|
||||||
$thumb_width = $config->get_int("thumb_width");
|
$thumb_width = $config->get_int(ImageConfig::THUMB_WIDTH);
|
||||||
$html = "
|
$html = "
|
||||||
<table id='reportedimage' class='zebra'>
|
<table id='reportedimage' class='zebra'>
|
||||||
<thead><td width='$thumb_width'>Image</td><td>Reason</td><td width='128'>Action</td></thead>
|
<thead><td width='$thumb_width'>Image</td><td>Reason</td><td width='128'>Action</td></thead>
|
||||||
|
@ -26,8 +26,6 @@ abstract class ResizeConfig
|
|||||||
*/
|
*/
|
||||||
class ResizeImage extends Extension
|
class ResizeImage extends Extension
|
||||||
{
|
{
|
||||||
const SUPPORTED_EXT = ["jpg","jpeg","png","gif","webp"];
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Needs to be after the data processing extensions
|
* Needs to be after the data processing extensions
|
||||||
*/
|
*/
|
||||||
@ -40,17 +38,18 @@ class ResizeImage extends Extension
|
|||||||
public function onInitExt(InitExtEvent $event)
|
public function onInitExt(InitExtEvent $event)
|
||||||
{
|
{
|
||||||
global $config;
|
global $config;
|
||||||
$config->set_default_bool('resize_enabled', true);
|
$config->set_default_bool(ResizeConfig::ENABLED, true);
|
||||||
$config->set_default_bool('resize_upload', false);
|
$config->set_default_bool(ResizeConfig::UPLOAD, false);
|
||||||
$config->set_default_int('resize_default_width', 0);
|
$config->set_default_string(ResizeConfig::ENGINE, Graphics::GD_ENGINE);
|
||||||
$config->set_default_int('resize_default_height', 0);
|
$config->set_default_int(ResizeConfig::DEFAULT_WIDTH, 0);
|
||||||
|
$config->set_default_int(ResizeConfig::DEFAULT_HEIGHT, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function onImageAdminBlockBuilding(ImageAdminBlockBuildingEvent $event)
|
public function onImageAdminBlockBuilding(ImageAdminBlockBuildingEvent $event)
|
||||||
{
|
{
|
||||||
global $user, $config;
|
global $user, $config;
|
||||||
if ($user->is_admin() && $config->get_bool("resize_enabled")
|
if ($user->is_admin() && $config->get_bool(ResizeConfig::ENABLED)
|
||||||
&& in_array($event->image->ext, self::SUPPORTED_EXT)) {
|
&& $this->can_resize_format($event->image->ext)) {
|
||||||
/* Add a link to resize the image */
|
/* Add a link to resize the image */
|
||||||
$event->add_part($this->theme->get_resize_html($event->image));
|
$event->add_part($this->theme->get_resize_html($event->image));
|
||||||
}
|
}
|
||||||
@ -60,6 +59,7 @@ class ResizeImage extends Extension
|
|||||||
{
|
{
|
||||||
$sb = new SetupBlock("Image Resize");
|
$sb = new SetupBlock("Image Resize");
|
||||||
$sb->start_table();
|
$sb->start_table();
|
||||||
|
$sb->add_choice_option(ResizeConfig::ENGINE, Media::IMAGE_MEDIA_ENGINES, "Engine: ", true);
|
||||||
$sb->add_bool_option(ResizeConfig::ENABLED, "Allow resizing images: ", true);
|
$sb->add_bool_option(ResizeConfig::ENABLED, "Allow resizing images: ", true);
|
||||||
$sb->add_bool_option(ResizeConfig::UPLOAD, "Resize on upload: ", true);
|
$sb->add_bool_option(ResizeConfig::UPLOAD, "Resize on upload: ", true);
|
||||||
$sb->end_table();
|
$sb->end_table();
|
||||||
@ -82,15 +82,15 @@ class ResizeImage extends Extension
|
|||||||
|
|
||||||
$image_obj = Image::by_id($event->image_id);
|
$image_obj = Image::by_id($event->image_id);
|
||||||
|
|
||||||
if ($config->get_bool("resize_upload") == true
|
if ($config->get_bool(ResizeConfig::UPLOAD) == true
|
||||||
&& in_array($event->type, self::SUPPORTED_EXT)) {
|
&& $this->can_resize_format($event->type)) {
|
||||||
$width = $height = 0;
|
$width = $height = 0;
|
||||||
|
|
||||||
if ($config->get_int("resize_default_width") !== 0) {
|
if ($config->get_int(ResizeConfig::DEFAULT_WIDTH) !== 0) {
|
||||||
$height = $config->get_int("resize_default_width");
|
$height = $config->get_int(ResizeConfig::DEFAULT_WIDTH);
|
||||||
}
|
}
|
||||||
if ($config->get_int("resize_default_height") !== 0) {
|
if ($config->get_int(ResizeConfig::DEFAULT_HEIGHT) !== 0) {
|
||||||
$height = $config->get_int("resize_default_height");
|
$height = $config->get_int(ResizeConfig::DEFAULT_HEIGHT);
|
||||||
}
|
}
|
||||||
$isanigif = 0;
|
$isanigif = 0;
|
||||||
if ($image_obj->ext == "gif") {
|
if ($image_obj->ext == "gif") {
|
||||||
@ -170,6 +170,14 @@ class ResizeImage extends Extension
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function can_resize_format($format): bool
|
||||||
|
{
|
||||||
|
global $config;
|
||||||
|
$engine = $config->get_string(ResizeConfig::ENGINE);
|
||||||
|
return Graphics::is_input_supported($engine, $format)
|
||||||
|
&& Graphics::is_output_supported($engine, $format);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Private functions
|
// Private functions
|
||||||
/* ----------------------------- */
|
/* ----------------------------- */
|
||||||
@ -197,7 +205,15 @@ class ResizeImage extends Extension
|
|||||||
throw new ImageResizeException("Unable to save temporary image file.");
|
throw new ImageResizeException("Unable to save temporary image file.");
|
||||||
}
|
}
|
||||||
|
|
||||||
image_resize_gd($image_filename, $info, $new_width, $new_height, $tmp_filename);
|
send_event(new GraphicResizeEvent(
|
||||||
|
Graphics::GD_ENGINE,
|
||||||
|
$image_filename,
|
||||||
|
$image_obj->ext,
|
||||||
|
$tmp_filename,
|
||||||
|
$new_width,
|
||||||
|
$new_height,
|
||||||
|
true
|
||||||
|
));
|
||||||
|
|
||||||
$new_image = new Image();
|
$new_image = new Image();
|
||||||
$new_image->hash = md5_file($tmp_filename);
|
$new_image->hash = md5_file($tmp_filename);
|
||||||
|
@ -9,8 +9,8 @@ class ResizeImageTheme extends Themelet
|
|||||||
{
|
{
|
||||||
global $config;
|
global $config;
|
||||||
|
|
||||||
$default_width = $config->get_int('resize_default_width');
|
$default_width = $config->get_int(ResizeConfig::DEFAULT_WIDTH);
|
||||||
$default_height = $config->get_int('resize_default_height');
|
$default_height = $config->get_int(ResizeConfig::DEFAULT_HEIGHT);
|
||||||
|
|
||||||
if (!$default_width) {
|
if (!$default_width) {
|
||||||
$default_width = $image->width;
|
$default_width = $image->width;
|
||||||
|
@ -130,7 +130,7 @@ class RotateImage extends Extension
|
|||||||
|
|
||||||
$info = getimagesize($image_filename);
|
$info = getimagesize($image_filename);
|
||||||
|
|
||||||
$memory_use =calc_memory_use($info);
|
$memory_use = Graphics::calc_memory_use($info);
|
||||||
$memory_limit = get_memory_limit();
|
$memory_limit = get_memory_limit();
|
||||||
|
|
||||||
if ($memory_use > $memory_limit) {
|
if ($memory_use > $memory_limit) {
|
||||||
|
@ -30,7 +30,7 @@ class Rule34 extends Extension
|
|||||||
public function onImageInfoBoxBuilding(ImageInfoBoxBuildingEvent $event)
|
public function onImageInfoBoxBuilding(ImageInfoBoxBuildingEvent $event)
|
||||||
{
|
{
|
||||||
global $config;
|
global $config;
|
||||||
$image_link = $config->get_string('image_ilink');
|
$image_link = $config->get_string(ImageConfig::ILINK);
|
||||||
$url0 = $event->image->parse_link_template($image_link, "url_escape", 0);
|
$url0 = $event->image->parse_link_template($image_link, "url_escape", 0);
|
||||||
$url1 = $event->image->parse_link_template($image_link, "url_escape", 1);
|
$url1 = $event->image->parse_link_template($image_link, "url_escape", 1);
|
||||||
$html = "<tr><th>Links</th><td><a href='$url0'>Image Only</a> (<a href='$url1'>Backup Server</a>)</td></tr>";
|
$html = "<tr><th>Links</th><td><a href='$url0'>Image Only</a> (<a href='$url1'>Backup Server</a>)</td></tr>";
|
||||||
|
@ -39,6 +39,6 @@ class SetupTest extends ShimmiePHPUnitTestCase
|
|||||||
$this->log_in_as_admin();
|
$this->log_in_as_admin();
|
||||||
$this->get_page('setup/advanced');
|
$this->get_page('setup/advanced');
|
||||||
$this->assert_title("Shimmie Setup");
|
$this->assert_title("Shimmie Setup");
|
||||||
$this->assert_text("thumb_quality");
|
$this->assert_text(ImageConfig::THUMB_QUALITY);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,50 +33,6 @@ class TranscodeImage extends Extension
|
|||||||
{
|
{
|
||||||
const ACTION_BULK_TRANSCODE = "bulk_transcode";
|
const ACTION_BULK_TRANSCODE = "bulk_transcode";
|
||||||
|
|
||||||
const CONVERSION_ENGINES = [
|
|
||||||
"GD" => "gd",
|
|
||||||
"ImageMagick" => "convert",
|
|
||||||
];
|
|
||||||
|
|
||||||
const ENGINE_INPUT_SUPPORT = [
|
|
||||||
"gd" => [
|
|
||||||
"bmp",
|
|
||||||
"gif",
|
|
||||||
"jpg",
|
|
||||||
"png",
|
|
||||||
"webp",
|
|
||||||
],
|
|
||||||
"convert" => [
|
|
||||||
"bmp",
|
|
||||||
"gif",
|
|
||||||
"jpg",
|
|
||||||
"png",
|
|
||||||
"psd",
|
|
||||||
"tiff",
|
|
||||||
"webp",
|
|
||||||
"ico",
|
|
||||||
]
|
|
||||||
];
|
|
||||||
|
|
||||||
const ENGINE_OUTPUT_SUPPORT = [
|
|
||||||
"gd" => [
|
|
||||||
"jpg",
|
|
||||||
"png",
|
|
||||||
"webp-lossy",
|
|
||||||
],
|
|
||||||
"convert" => [
|
|
||||||
"jpg",
|
|
||||||
"png",
|
|
||||||
"webp-lossy",
|
|
||||||
"webp-lossless",
|
|
||||||
]
|
|
||||||
];
|
|
||||||
|
|
||||||
const LOSSLESS_FORMATS = [
|
|
||||||
"webp-lossless",
|
|
||||||
"png",
|
|
||||||
];
|
|
||||||
|
|
||||||
const INPUT_FORMATS = [
|
const INPUT_FORMATS = [
|
||||||
"BMP" => "bmp",
|
"BMP" => "bmp",
|
||||||
"GIF" => "gif",
|
"GIF" => "gif",
|
||||||
@ -88,17 +44,12 @@ class TranscodeImage extends Extension
|
|||||||
"WEBP" => "webp",
|
"WEBP" => "webp",
|
||||||
];
|
];
|
||||||
|
|
||||||
const FORMAT_ALIASES = [
|
|
||||||
"tif" => "tiff",
|
|
||||||
"jpeg" => "jpg",
|
|
||||||
];
|
|
||||||
|
|
||||||
const OUTPUT_FORMATS = [
|
const OUTPUT_FORMATS = [
|
||||||
"" => "",
|
"" => "",
|
||||||
"JPEG (lossy)" => "jpg",
|
"JPEG (lossy)" => "jpg",
|
||||||
"PNG (lossless)" => "png",
|
"PNG (lossless)" => "png",
|
||||||
"WEBP (lossy)" => "webp-lossy",
|
"WEBP (lossy)" => Graphics::WEBP_LOSSY,
|
||||||
"WEBP (lossless)" => "webp-lossless",
|
"WEBP (lossless)" => Graphics::WEBP_LOSSLESS,
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -113,13 +64,13 @@ class TranscodeImage extends Extension
|
|||||||
public function onInitExt(InitExtEvent $event)
|
public function onInitExt(InitExtEvent $event)
|
||||||
{
|
{
|
||||||
global $config;
|
global $config;
|
||||||
$config->set_default_bool('transcode_enabled', true);
|
$config->set_default_bool(TranscodeConfig::ENABLED, true);
|
||||||
$config->set_default_bool('transcode_upload', false);
|
$config->set_default_bool(TranscodeConfig::UPLOAD, false);
|
||||||
$config->set_default_string('transcode_engine', "gd");
|
$config->set_default_string(TranscodeConfig::ENGINE, Graphics::GD_ENGINE);
|
||||||
$config->set_default_int('transcode_quality', 80);
|
$config->set_default_int(TranscodeConfig::QUALITY, 80);
|
||||||
|
|
||||||
foreach (array_values(self::INPUT_FORMATS) as $format) {
|
foreach (array_values(self::INPUT_FORMATS) as $format) {
|
||||||
$config->set_default_string('transcode_upload_'.$format, "");
|
$config->set_default_string(TranscodeConfig::UPLOAD_PREFIX.$format, "");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -127,8 +78,8 @@ class TranscodeImage extends Extension
|
|||||||
{
|
{
|
||||||
global $user, $config;
|
global $user, $config;
|
||||||
|
|
||||||
if ($user->is_admin() && $config->get_bool("resize_enabled")) {
|
if ($user->is_admin()) {
|
||||||
$engine = $config->get_string("transcode_engine");
|
$engine = $config->get_string(TranscodeConfig::ENGINE);
|
||||||
if ($this->can_convert_format($engine, $event->image->ext)) {
|
if ($this->can_convert_format($engine, $event->image->ext)) {
|
||||||
$options = $this->get_supported_output_formats($engine, $event->image->ext);
|
$options = $this->get_supported_output_formats($engine, $event->image->ext);
|
||||||
$event->add_part($this->theme->get_transcode_html($event->image, $options));
|
$event->add_part($this->theme->get_transcode_html($event->image, $options));
|
||||||
@ -140,16 +91,16 @@ class TranscodeImage extends Extension
|
|||||||
{
|
{
|
||||||
global $config;
|
global $config;
|
||||||
|
|
||||||
$engine = $config->get_string("transcode_engine");
|
$engine = $config->get_string(TranscodeConfig::ENGINE);
|
||||||
|
|
||||||
|
|
||||||
$sb = new SetupBlock("Image Transcode");
|
$sb = new SetupBlock("Image Transcode");
|
||||||
$sb->start_table();
|
$sb->start_table();
|
||||||
$sb->add_bool_option(TranscodeConfig::ENABLED, "Allow transcoding images: ", true);
|
$sb->add_bool_option(TranscodeConfig::ENABLED, "Allow transcoding images: ", true);
|
||||||
$sb->add_bool_option(TranscodeConfig::UPLOAD, "Transcode on upload: ", true);
|
$sb->add_bool_option(TranscodeConfig::UPLOAD, "Transcode on upload: ", true);
|
||||||
$sb->add_choice_option(TranscodeConfig::ENGINE, self::CONVERSION_ENGINES, "Engine", true);
|
$sb->add_choice_option(TranscodeConfig::ENGINE, Graphics::IMAGE_GRAPHICS_ENGINES, "Engine", true);
|
||||||
foreach (self::INPUT_FORMATS as $display=>$format) {
|
foreach (self::INPUT_FORMATS as $display=>$format) {
|
||||||
if (in_array($format, self::ENGINE_INPUT_SUPPORT[$engine])) {
|
if (in_array($format, Graphics::ENGINE_INPUT_SUPPORT[$engine])) {
|
||||||
$outputs = $this->get_supported_output_formats($engine, $format);
|
$outputs = $this->get_supported_output_formats($engine, $format);
|
||||||
$sb->add_choice_option(TranscodeConfig::UPLOAD_PREFIX.$format, $outputs, "$display", true);
|
$sb->add_choice_option(TranscodeConfig::UPLOAD_PREFIX.$format, $outputs, "$display", true);
|
||||||
}
|
}
|
||||||
@ -163,23 +114,23 @@ class TranscodeImage extends Extension
|
|||||||
{
|
{
|
||||||
global $config, $page;
|
global $config, $page;
|
||||||
|
|
||||||
if ($config->get_bool("transcode_upload") == true) {
|
if ($config->get_bool(TranscodeConfig::UPLOAD) == true) {
|
||||||
$ext = strtolower($event->type);
|
$ext = strtolower($event->type);
|
||||||
|
|
||||||
$ext = $this->clean_format($ext);
|
$ext = Graphics::normalize_format($ext);
|
||||||
|
|
||||||
if ($event->type=="gif"&&is_animated_gif($event->tmpname)) {
|
if ($event->type=="gif"&&Graphics::is_animated_gif($event->tmpname)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (in_array($ext, array_values(self::INPUT_FORMATS))) {
|
if (in_array($ext, array_values(self::INPUT_FORMATS))) {
|
||||||
$target_format = $config->get_string("transcode_upload_".$ext);
|
$target_format = $config->get_string(TranscodeConfig::UPLOAD_PREFIX.$ext);
|
||||||
if (empty($target_format)) {
|
if (empty($target_format)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
$new_image = $this->transcode_image($event->tmpname, $ext, $target_format);
|
$new_image = $this->transcode_image($event->tmpname, $ext, $target_format);
|
||||||
$event->set_type($this->determine_ext($target_format));
|
$event->set_type(Graphics::determine_ext($target_format));
|
||||||
$event->set_tmpname($new_image);
|
$event->set_tmpname($new_image);
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
log_error("transcode", "Error while performing upload transcode: ".$e->getMessage());
|
log_error("transcode", "Error while performing upload transcode: ".$e->getMessage());
|
||||||
@ -227,7 +178,7 @@ class TranscodeImage extends Extension
|
|||||||
{
|
{
|
||||||
global $user, $config;
|
global $user, $config;
|
||||||
|
|
||||||
$engine = $config->get_string("transcode_engine");
|
$engine = $config->get_string(TranscodeConfig::ENGINE);
|
||||||
|
|
||||||
if ($user->is_admin()) {
|
if ($user->is_admin()) {
|
||||||
$event->add_action(self::ACTION_BULK_TRANSCODE, "Transcode", null,"", $this->theme->get_transcode_picker_html($this->get_supported_output_formats($engine)));
|
$event->add_action(self::ACTION_BULK_TRANSCODE, "Transcode", null,"", $this->theme->get_transcode_picker_html($this->get_supported_output_formats($engine)));
|
||||||
@ -239,7 +190,7 @@ class TranscodeImage extends Extension
|
|||||||
global $user, $database;
|
global $user, $database;
|
||||||
|
|
||||||
switch ($event->action) {
|
switch ($event->action) {
|
||||||
case "bulk_transcode":
|
case self::ACTION_BULK_TRANSCODE:
|
||||||
if (!isset($_POST['transcode_format'])) {
|
if (!isset($_POST['transcode_format'])) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -251,8 +202,9 @@ class TranscodeImage extends Extension
|
|||||||
$database->beginTransaction();
|
$database->beginTransaction();
|
||||||
|
|
||||||
$this->transcode_and_replace_image($image, $format);
|
$this->transcode_and_replace_image($image, $format);
|
||||||
// If a subsequent transcode fails, the database need to have everything about the previous transcodes recorded already,
|
// If a subsequent transcode fails, the database needs to have everything about the previous
|
||||||
// otherwise the image entries will be stuck pointing to missing image files
|
// transcodes recorded already, otherwise the image entries will be stuck pointing to
|
||||||
|
// missing image files
|
||||||
$database->commit();
|
$database->commit();
|
||||||
$total++;
|
$total++;
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
@ -269,54 +221,34 @@ class TranscodeImage extends Extension
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function clean_format($format): ?string
|
|
||||||
{
|
|
||||||
if (array_key_exists($format, self::FORMAT_ALIASES)) {
|
|
||||||
return self::FORMAT_ALIASES[$format];
|
|
||||||
}
|
|
||||||
return $format;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function can_convert_format($engine, $format): bool
|
private function can_convert_format($engine, $format): bool
|
||||||
{
|
{
|
||||||
$format = $this->clean_format($format);
|
return Graphics::is_input_supported($engine, $format);
|
||||||
if (!in_array($format, self::ENGINE_INPUT_SUPPORT[$engine])) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private function get_supported_output_formats($engine, ?String $omit_format = null): array
|
private function get_supported_output_formats($engine, ?String $omit_format = null): array
|
||||||
{
|
{
|
||||||
$omit_format = $this->clean_format($omit_format);
|
$omit_format = Graphics::normalize_format($omit_format);
|
||||||
$output = [];
|
$output = [];
|
||||||
foreach (self::OUTPUT_FORMATS as $key=>$value) {
|
foreach (self::OUTPUT_FORMATS as $key=>$value) {
|
||||||
if ($value=="") {
|
if ($value=="") {
|
||||||
$output[$key] = $value;
|
$output[$key] = $value;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (in_array($value, self::ENGINE_OUTPUT_SUPPORT[$engine])
|
if(Graphics::is_output_supported($engine, $value)
|
||||||
&&(empty($omit_format)||$omit_format!=$this->determine_ext($value))) {
|
&&(empty($omit_format)||$omit_format!=Graphics::determine_ext($value))) {
|
||||||
$output[$key] = $value;
|
$output[$key] = $value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return $output;
|
return $output;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function determine_ext(String $format): String
|
|
||||||
{
|
|
||||||
switch ($format) {
|
|
||||||
case "webp-lossless":
|
|
||||||
case "webp-lossy":
|
|
||||||
return "webp";
|
|
||||||
default:
|
|
||||||
return $format;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function transcode_and_replace_image(Image $image_obj, String $target_format)
|
private function transcode_and_replace_image(Image $image_obj, String $target_format)
|
||||||
{
|
{
|
||||||
$target_format = $this->clean_format($target_format);
|
|
||||||
$original_file = warehouse_path(Image::IMAGE_DIR, $image_obj->hash);
|
$original_file = warehouse_path(Image::IMAGE_DIR, $image_obj->hash);
|
||||||
|
|
||||||
$tmp_filename = $this->transcode_image($original_file, $image_obj->ext, $target_format);
|
$tmp_filename = $this->transcode_image($original_file, $image_obj->ext, $target_format);
|
||||||
@ -327,7 +259,7 @@ class TranscodeImage extends Extension
|
|||||||
$new_image->filename = $image_obj->filename;
|
$new_image->filename = $image_obj->filename;
|
||||||
$new_image->width = $image_obj->width;
|
$new_image->width = $image_obj->width;
|
||||||
$new_image->height = $image_obj->height;
|
$new_image->height = $image_obj->height;
|
||||||
$new_image->ext = $this->determine_ext($target_format);
|
$new_image->ext = Graphics::determine_ext($target_format);
|
||||||
|
|
||||||
/* Move the new image into the main storage location */
|
/* Move the new image into the main storage location */
|
||||||
$target = warehouse_path(Image::IMAGE_DIR, $new_image->hash);
|
$target = warehouse_path(Image::IMAGE_DIR, $new_image->hash);
|
||||||
@ -346,7 +278,7 @@ class TranscodeImage extends Extension
|
|||||||
{
|
{
|
||||||
global $config;
|
global $config;
|
||||||
|
|
||||||
if ($source_format==$this->determine_ext($target_format)) {
|
if ($source_format==Graphics::determine_ext($target_format)) {
|
||||||
throw new ImageTranscodeException("Source and target formats are the same: ".$source_format);
|
throw new ImageTranscodeException("Source and target formats are the same: ".$source_format);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -357,7 +289,7 @@ class TranscodeImage extends Extension
|
|||||||
if (!$this->can_convert_format($engine, $source_format)) {
|
if (!$this->can_convert_format($engine, $source_format)) {
|
||||||
throw new ImageTranscodeException("Engine $engine does not support input format $source_format");
|
throw new ImageTranscodeException("Engine $engine does not support input format $source_format");
|
||||||
}
|
}
|
||||||
if (!in_array($target_format, self::ENGINE_OUTPUT_SUPPORT[$engine])) {
|
if (!in_array($target_format, Graphics::ENGINE_OUTPUT_SUPPORT[$engine])) {
|
||||||
throw new ImageTranscodeException("Engine $engine does not support output format $target_format");
|
throw new ImageTranscodeException("Engine $engine does not support output format $target_format");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -426,20 +358,20 @@ class TranscodeImage extends Extension
|
|||||||
global $config;
|
global $config;
|
||||||
|
|
||||||
$q = $config->get_int("transcode_quality");
|
$q = $config->get_int("transcode_quality");
|
||||||
$convert = $config->get_string("thumb_convert_path");
|
$convert = $config->get_string(GraphicsConfig::CONVERT_PATH);
|
||||||
|
|
||||||
if ($convert==null||$convert=="") {
|
if ($convert==null||$convert=="") {
|
||||||
throw new ImageTranscodeException("ImageMagick path not configured");
|
throw new ImageTranscodeException("ImageMagick path not configured");
|
||||||
}
|
}
|
||||||
$ext = $this->determine_ext($target_format);
|
$ext = Graphics::determine_ext($target_format);
|
||||||
|
|
||||||
$args = " -flatten ";
|
$args = " -flatten ";
|
||||||
$bg = "none";
|
$bg = "none";
|
||||||
switch ($target_format) {
|
switch ($target_format) {
|
||||||
case "webp-lossless":
|
case Graphics::WEBP_LOSSLESS:
|
||||||
$args .= '-define webp:lossless=true';
|
$args .= '-define webp:lossless=true';
|
||||||
break;
|
break;
|
||||||
case "webp-lossy":
|
case Graphics::WEBP_LOSSY:
|
||||||
$args .= '';
|
$args .= '';
|
||||||
break;
|
break;
|
||||||
case "png":
|
case "png":
|
||||||
|
Loading…
x
Reference in New Issue
Block a user