Added thumbnail scaling options

Changed ffmpeg thumbnailer to instead output a full-size png which is forwarded to the image thumbnailer, to allow it to take advantage of all available scaling options
This commit is contained in:
Matthew Barbour 2020-06-11 16:58:19 -05:00 committed by Shish
parent 8e976fb812
commit b937ad6255
7 changed files with 123 additions and 46 deletions

View file

@ -73,6 +73,12 @@ function get_thumbnail_size(int $orig_width, int $orig_height, bool $use_dpi_sca
{
global $config;
$fit = $config->get_string(ImageConfig::THUMB_FIT);
if (in_array($fit, [Media::RESIZE_TYPE_FILL, Media::RESIZE_TYPE_STRETCH, Media::RESIZE_TYPE_FIT_BLUR])) {
return [$config->get_int(ImageConfig::THUMB_WIDTH), $config->get_int(ImageConfig::THUMB_HEIGHT)];
}
if ($orig_width === 0) {
$orig_width = 192;
}
@ -132,18 +138,32 @@ function get_thumbnail_max_size_scaled(): array
function create_image_thumb(string $hash, string $type, string $engine = null)
{
global $config;
$inname = warehouse_path(Image::IMAGE_DIR, $hash);
$outname = warehouse_path(Image::THUMBNAIL_DIR, $hash);
$tsize = get_thumbnail_max_size_scaled();
create_scaled_image($inname, $outname, $tsize, $type, $engine);
create_scaled_image(
$inname,
$outname,
$tsize,
$type,
$engine,
$config->get_string(ImageConfig::THUMB_FIT)
);
}
function create_scaled_image(string $inname, string $outname, array $tsize, string $type, ?string $engine)
function create_scaled_image(string $inname, string $outname, array $tsize, string $type, ?string $engine = null, ?string $resize_type = null)
{
global $config;
if (empty($engine)) {
$engine = $config->get_string(ImageConfig::THUMB_ENGINE);
}
if (empty($resize_type)) {
$resize_type = $config->get_string(ImageConfig::THUMB_FIT);
}
$output_format = $config->get_string(ImageConfig::THUMB_TYPE);
if ($output_format==EXTENSION_WEBP) {
@ -157,10 +177,10 @@ function create_scaled_image(string $inname, string $outname, array $tsize, stri
$outname,
$tsize[0],
$tsize[1],
false,
$resize_type,
$output_format,
$config->get_int(ImageConfig::THUMB_QUALITY),
true,
$config->get_bool('thumb_upscale', false)
true
));
}

View file

@ -8,6 +8,7 @@ abstract class ImageConfig
const THUMB_SCALING = 'thumb_scaling';
const THUMB_QUALITY = 'thumb_quality';
const THUMB_TYPE = 'thumb_type';
const THUMB_FIT = 'thumb_fit';
const SHOW_META = 'image_show_meta';
const ILINK = 'image_ilink';

View file

@ -33,6 +33,7 @@ class ImageIO extends Extension
$config->set_default_int(ImageConfig::THUMB_SCALING, 100);
$config->set_default_int(ImageConfig::THUMB_QUALITY, 75);
$config->set_default_string(ImageConfig::THUMB_TYPE, EXTENSION_JPG);
$config->set_default_string(ImageConfig::THUMB_FIT, Media::RESIZE_TYPE_FIT);
if (function_exists(self::EXIF_READ_FUNCTION)) {
$config->set_default_bool(ImageConfig::SHOW_META, false);
@ -215,35 +216,41 @@ class ImageIO extends Extension
public function onSetupBuilding(SetupBuildingEvent $event)
{
global $config;
$sb = new SetupBlock("Image Options");
$sb->start_table();
$sb->position = 30;
// advanced only
//$sb->add_text_option(ImageConfig::ILINK, "Image link: ");
//$sb->add_text_option(ImageConfig::TLINK, "<br>Thumbnail link: ");
$sb->add_text_option(ImageConfig::TIP, "Image tooltip: ");
$sb->add_choice_option(ImageConfig::UPLOAD_COLLISION_HANDLER, self::COLLISION_OPTIONS, "<br>Upload collision handler: ");
$sb->add_text_option(ImageConfig::TIP, "Image tooltip", true);
$sb->add_choice_option(ImageConfig::UPLOAD_COLLISION_HANDLER, self::COLLISION_OPTIONS, "Upload collision handler", true);
if (function_exists(self::EXIF_READ_FUNCTION)) {
$sb->add_bool_option(ImageConfig::SHOW_META, "<br>Show metadata: ");
$sb->add_bool_option(ImageConfig::SHOW_META, "Show metadata", true);
}
$sb->end_table();
$event->panel->add_block($sb);
$sb = new SetupBlock("Thumbnailing");
$sb->add_choice_option(ImageConfig::THUMB_ENGINE, self::THUMBNAIL_ENGINES, "Engine: ");
$sb->add_label("<br>");
$sb->add_choice_option(ImageConfig::THUMB_TYPE, self::THUMBNAIL_TYPES, "Filetype: ");
$sb->start_table();
$sb->add_choice_option(ImageConfig::THUMB_ENGINE, self::THUMBNAIL_ENGINES, "Engine", true);
$sb->add_choice_option(ImageConfig::THUMB_TYPE, self::THUMBNAIL_TYPES, "Filetype", true);
$sb->add_label("<br>Size ");
$sb->add_int_option(ImageConfig::THUMB_WIDTH);
$sb->add_label(" x ");
$sb->add_int_option(ImageConfig::THUMB_HEIGHT);
$sb->add_label(" px at ");
$sb->add_int_option(ImageConfig::THUMB_QUALITY);
$sb->add_label(" % quality ");
$sb->add_int_option(ImageConfig::THUMB_WIDTH, "Max Width", true);
$sb->add_int_option(ImageConfig::THUMB_HEIGHT, "Max Height", true);
$sb->add_label("<br>High-DPI scaling ");
$sb->add_int_option(ImageConfig::THUMB_SCALING);
$sb->add_label("%");
$options = [];
foreach (MediaEngine::RESIZE_TYPE_SUPPORT[$config->get_string(ImageConfig::THUMB_ENGINE)] as $type) {
$options[$type] = $type;
}
$sb->add_choice_option(ImageConfig::THUMB_FIT, $options, "Fit", true);
$sb->add_int_option(ImageConfig::THUMB_QUALITY, "Quality", true);
$sb->add_int_option(ImageConfig::THUMB_SCALING, "High-DPI Scale %", true);
$sb->end_table();
$event->panel->add_block($sb);
}

View file

@ -11,8 +11,8 @@ class MediaResizeEvent extends Event
public $target_height;
public $target_quality;
public $minimize;
public $ignore_aspect_ratio;
public $allow_upscale;
public $resize_type;
public function __construct(
String $engine,
@ -21,7 +21,7 @@ class MediaResizeEvent extends Event
string $output_path,
int $target_width,
int $target_height,
bool $ignore_aspect_ratio = false,
string $resize_type = Media::RESIZE_TYPE_FIT,
string $target_format = null,
int $target_quality = 80,
bool $minimize = false,
@ -38,8 +38,8 @@ class MediaResizeEvent extends Event
$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;
$this->resize_type = $resize_type;
}
}

View file

@ -48,6 +48,10 @@ class Media extends Extension
EXTENSION_JPEG => EXTENSION_JPG,
];
const RESIZE_TYPE_FIT = "Fit";
const RESIZE_TYPE_FIT_BLUR = "Fit Blur";
const RESIZE_TYPE_FILL = "Fill";
const RESIZE_TYPE_STRETCH = "Stretch";
//RIFF####WEBPVP8?..............ANIM
private const WEBP_ANIMATION_HEADER =
@ -206,6 +210,10 @@ class Media extends Extension
*/
public function onMediaResize(MediaResizeEvent $event)
{
if (!in_array($event->resize_type, MediaEngine::RESIZE_TYPE_SUPPORT[MediaEngine::IMAGICK])) {
throw new MediaException("Resize type $event->resize_type not supported by selected media engine $event->engine");
}
switch ($event->engine) {
case MediaEngine::GD:
$info = getimagesize($event->input_path);
@ -220,7 +228,7 @@ class Media extends Extension
$event->target_height,
$event->output_path,
$event->target_format,
$event->ignore_aspect_ratio,
$event->resize_type,
$event->target_quality,
$event->allow_upscale
);
@ -236,7 +244,7 @@ class Media extends Extension
$event->target_height,
$event->output_path,
$event->target_format,
$event->ignore_aspect_ratio,
$event->resize_type,
$event->target_quality,
$event->minimize,
$event->allow_upscale
@ -356,6 +364,7 @@ class Media extends Extension
}
$inname = warehouse_path(Image::IMAGE_DIR, $hash);
$tmpname = tempnam("/tmp", "shimmie_ffmpeg_thumb");
$outname = warehouse_path(Image::THUMBNAIL_DIR, $hash);
$orig_size = self::video_size($inname);
@ -376,12 +385,11 @@ class Media extends Extension
$args = [
escapeshellarg($ffmpeg),
"-y", "-i", escapeshellarg($inname),
"-vf", "thumbnail,scale={$scaled_size[0]}:{$scaled_size[1]}",
"-vf", "thumbnail",
"-f", "image2",
"-vframes", "1",
"-c:v", $codec,
"-q:v", $quality,
escapeshellarg($outname),
"-c:v", "png",
escapeshellarg($tmpname),
];
$cmd = escapeshellcmd(implode(" ", $args));
@ -390,6 +398,10 @@ class Media extends Extension
if ((int)$ret == (int)0) {
log_debug('media', "Generating thumbnail with command `$cmd`, returns $ret");
create_scaled_image($tmpname, $outname, $scaled_size, "png");
return true;
} else {
log_error('media', "Generating thumbnail with command `$cmd`, returns $ret");
@ -569,7 +581,7 @@ class Media extends Extension
int $new_height,
string $output_filename,
string $output_type = null,
bool $ignore_aspect_ratio = false,
string $resize_type = self::RESIZE_TYPE_FIT,
int $output_quality = 80,
bool $minimize = false,
bool $allow_upscale = true
@ -598,16 +610,41 @@ class Media extends Extension
$input_type = $input_type . ":";
}
$resize_args = "";
$resize_suffix = "";
if (!$allow_upscale) {
$resize_args .= "\>";
$resize_suffix .= "\>";
}
if ($ignore_aspect_ratio) {
$resize_args .= "\!";
if ($resize_type==Media::RESIZE_TYPE_STRETCH) {
$resize_suffix .= "\!";
}
$args = "";
$resize_arg = "-resize";
if ($minimize) {
$args .= "-strip ";
$resize_arg = "-thumbnail";
}
$file_arg = "${input_type}\"${input_path}[0]\"";
switch ($resize_type) {
case Media::RESIZE_TYPE_FIT:
case Media::RESIZE_TYPE_STRETCH:
$args .= "${resize_arg} ${new_width}x${new_height}${resize_suffix} ${file_arg}";
break;
case Media::RESIZE_TYPE_FILL:
$args .= "${resize_arg} ${new_width}x${new_height}\^ -gravity center -extent ${new_width}x${new_height} ${file_arg}";
break;
case Media::RESIZE_TYPE_FIT_BLUR:
$blur_size = max(ceil(max($new_width, $new_height) / 25), 5);
$args .= "${file_arg} ".
"\( -clone 0 -resize ${new_width}x${new_height}\^ -gravity center -fill black -colorize 50% -extent ${new_width}x${new_height} -blur 0x${blur_size} \) ".
"\( -clone 0 -resize ${new_width}x${new_height} \) ".
"-delete 0 -gravity center -compose over -composite";
break;
}
switch ($output_type) {
case Media::WEBP_LOSSLESS:
$args .= '-define webp:lossless=true';
@ -617,17 +654,14 @@ class Media extends Extension
break;
}
if ($minimize) {
$args .= " -strip -thumbnail";
} else {
$args .= " -resize";
}
$args .= " -quality ${output_quality} -background ${bg}";
$output_ext = self::determine_ext($output_type);
$format = '"%s" %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_ext, $output_filename);
$format = '"%s" %s %s:"%s" 2>&1';
$cmd = sprintf($format, $convert, $args, $output_ext, $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) {
@ -657,7 +691,7 @@ class Media extends Extension
int $new_height,
string $output_filename,
string $output_type = null,
bool $ignore_aspect_ratio = false,
string $resize_type = self::RESIZE_TYPE_FIT,
int $output_quality = 80,
bool $allow_upscale = true
) {
@ -693,7 +727,7 @@ class Media extends Extension
throw new InsufficientMemoryException("The image is too large to resize given the memory limits. ($memory_use > $memory_limit)");
}
if (!$ignore_aspect_ratio) {
if ($resize_type==Media::RESIZE_TYPE_FIT) {
list($new_width, $new_height) = get_scaled_by_aspect_ratio($width, $height, $new_width, $new_height);
}
if (!$allow_upscale &&

View file

@ -74,4 +74,19 @@ abstract class MediaEngine
EXTENSION_PNG,
],
];
public const RESIZE_TYPE_SUPPORT = [
MediaEngine::GD => [
Media::RESIZE_TYPE_FIT,
Media::RESIZE_TYPE_STRETCH
],
MediaEngine::IMAGICK => [
Media::RESIZE_TYPE_FIT,
Media::RESIZE_TYPE_FIT_BLUR,
Media::RESIZE_TYPE_FILL,
Media::RESIZE_TYPE_STRETCH,
],
MediaEngine::FFMPEG => [
Media::RESIZE_TYPE_FIT
]
];
}

View file

@ -88,7 +88,7 @@ class TranscodeImage extends Extension
$sb->add_choice_option(TranscodeConfig::UPLOAD_PREFIX.$format, $outputs, "$display", true);
}
}
$sb->add_int_option(TranscodeConfig::QUALITY, "Lossy format quality: ");
$sb->add_int_option(TranscodeConfig::QUALITY, "Lossy Format Quality", true);
$sb->end_table();
$event->panel->add_block($sb);
}