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:
		
							parent
							
								
									8e976fb812
								
							
						
					
					
						commit
						b937ad6255
					
				| @ -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 | ||||
|     )); | ||||
| } | ||||
|  | ||||
| @ -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'; | ||||
|  | ||||
| @ -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); | ||||
|     } | ||||
|  | ||||
| @ -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; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -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 && | ||||
|  | ||||
| @ -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 | ||||
|         ] | ||||
|     ]; | ||||
| } | ||||
|  | ||||
| @ -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); | ||||
|     } | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user