Added option to transcode extension and thumbnailing to choose the color to use to fill in the background when converting an image with an alpha channel to a format without

This commit is contained in:
Matthew Barbour 2020-06-14 11:36:52 -05:00 committed by Shish
parent 984c9702ec
commit 7f68ef1cfd
10 changed files with 97 additions and 28 deletions

View file

@ -176,6 +176,7 @@ function create_scaled_image(string $inname, string $outname, array $tsize, stri
$tsize[1], $tsize[1],
$resize_type, $resize_type,
$output_mime, $output_mime,
$config->get_string(ImageConfig::THUMB_ALPHA_COLOR),
$config->get_int(ImageConfig::THUMB_QUALITY), $config->get_int(ImageConfig::THUMB_QUALITY),
true, true,
true true

View file

@ -11,6 +11,7 @@ abstract class ImageConfig
const THUMB_QUALITY = 'thumb_quality'; const THUMB_QUALITY = 'thumb_quality';
const THUMB_MIME = 'thumb_mime'; const THUMB_MIME = 'thumb_mime';
const THUMB_FIT = 'thumb_fit'; const THUMB_FIT = 'thumb_fit';
const THUMB_ALPHA_COLOR ='thumb_alpha_color';
const SHOW_META = 'image_show_meta'; const SHOW_META = 'image_show_meta';
const ILINK = 'image_ilink'; const ILINK = 'image_ilink';

View file

@ -37,6 +37,7 @@ class ImageIO extends Extension
$config->set_default_int(ImageConfig::THUMB_QUALITY, 75); $config->set_default_int(ImageConfig::THUMB_QUALITY, 75);
$config->set_default_string(ImageConfig::THUMB_MIME, MimeType::JPEG); $config->set_default_string(ImageConfig::THUMB_MIME, MimeType::JPEG);
$config->set_default_string(ImageConfig::THUMB_FIT, Media::RESIZE_TYPE_FIT); $config->set_default_string(ImageConfig::THUMB_FIT, Media::RESIZE_TYPE_FIT);
$config->set_default_string(ImageConfig::THUMB_ALPHA_COLOR, Media::DEFAULT_ALPHA_CONVERSION_COLOR);
if (function_exists(self::EXIF_READ_FUNCTION)) { if (function_exists(self::EXIF_READ_FUNCTION)) {
$config->set_default_bool(ImageConfig::SHOW_META, false); $config->set_default_bool(ImageConfig::SHOW_META, false);
@ -275,6 +276,9 @@ class ImageIO extends Extension
$sb->add_int_option(ImageConfig::THUMB_QUALITY, "Quality", true); $sb->add_int_option(ImageConfig::THUMB_QUALITY, "Quality", true);
$sb->add_int_option(ImageConfig::THUMB_SCALING, "High-DPI Scale %", true); $sb->add_int_option(ImageConfig::THUMB_SCALING, "High-DPI Scale %", true);
if ($config->get_string(ImageConfig::THUMB_MIME)===MimeType::JPEG) {
$sb->add_color_option(ImageConfig::THUMB_ALPHA_COLOR, "Alpha Conversion Color", true);
}
$sb->end_table(); $sb->end_table();

View file

@ -10,6 +10,7 @@ class MediaResizeEvent extends Event
public $target_width; public $target_width;
public $target_height; public $target_height;
public $target_quality; public $target_quality;
public $alpha_color;
public $minimize; public $minimize;
public $allow_upscale; public $allow_upscale;
public $resize_type; public $resize_type;
@ -23,6 +24,7 @@ class MediaResizeEvent extends Event
int $target_height, int $target_height,
string $resize_type = Media::RESIZE_TYPE_FIT, string $resize_type = Media::RESIZE_TYPE_FIT,
string $target_mime = null, string $target_mime = null,
string $alpha_color = Media::DEFAULT_ALPHA_CONVERSION_COLOR,
int $target_quality = 80, int $target_quality = 80,
bool $minimize = false, bool $minimize = false,
bool $allow_upscale = true bool $allow_upscale = true
@ -36,6 +38,10 @@ class MediaResizeEvent extends Event
$this->target_height = $target_height; $this->target_height = $target_height;
$this->target_width = $target_width; $this->target_width = $target_width;
$this->target_mime = $target_mime; $this->target_mime = $target_mime;
if (empty($alpha_color)) {
$alpha_color = Media::DEFAULT_ALPHA_CONVERSION_COLOR;
}
$this->alpha_color = $alpha_color;
$this->target_quality = $target_quality; $this->target_quality = $target_quality;
$this->minimize = $minimize; $this->minimize = $minimize;
$this->allow_upscale = $allow_upscale; $this->allow_upscale = $allow_upscale;

View file

@ -16,12 +16,7 @@ class Media extends Extension
/** @var MediaTheme */ /** @var MediaTheme */
protected $theme; protected $theme;
const IMAGE_MEDIA_ENGINES = [ private const LOSSLESS_FORMATS = [
"GD" => MediaEngine::GD,
"ImageMagick" => MediaEngine::IMAGICK,
];
const LOSSLESS_FORMATS = [
MimeType::WEBP_LOSSLESS, MimeType::WEBP_LOSSLESS,
MimeType::PNG, MimeType::PNG,
MimeType::PSD, MimeType::PSD,
@ -31,16 +26,17 @@ class Media extends Extension
MimeType::GIF MimeType::GIF
]; ];
const ALPHA_FORMATS = [ private const ALPHA_FORMATS = [
MimeType::WEBP_LOSSLESS, MimeType::WEBP_LOSSLESS,
MimeType::WEBP, MimeType::WEBP,
MimeType::PNG, MimeType::PNG,
]; ];
const RESIZE_TYPE_FIT = "Fit"; public const RESIZE_TYPE_FIT = "Fit";
const RESIZE_TYPE_FIT_BLUR = "Fit Blur"; public const RESIZE_TYPE_FIT_BLUR = "Fit Blur";
const RESIZE_TYPE_FILL = "Fill"; public const RESIZE_TYPE_FILL = "Fill";
const RESIZE_TYPE_STRETCH = "Stretch"; public const RESIZE_TYPE_STRETCH = "Stretch";
public const DEFAULT_ALPHA_CONVERSION_COLOR = "#00000000";
public static function imagick_available(): bool public static function imagick_available(): bool
{ {
@ -194,6 +190,7 @@ class Media extends Extension
$event->target_height, $event->target_height,
$event->output_path, $event->output_path,
$event->target_mime, $event->target_mime,
$event->alpha_color,
$event->resize_type, $event->resize_type,
$event->target_quality, $event->target_quality,
$event->allow_upscale $event->allow_upscale
@ -210,6 +207,7 @@ class Media extends Extension
$event->target_height, $event->target_height,
$event->output_path, $event->output_path,
$event->target_mime, $event->target_mime,
$event->alpha_color,
$event->resize_type, $event->resize_type,
$event->target_quality, $event->target_quality,
$event->minimize, $event->minimize,
@ -542,6 +540,7 @@ class Media extends Extension
int $new_height, int $new_height,
string $output_filename, string $output_filename,
string $output_mime = null, string $output_mime = null,
string $alpha_color = Media::DEFAULT_ALPHA_CONVERSION_COLOR,
string $resize_type = self::RESIZE_TYPE_FIT, string $resize_type = self::RESIZE_TYPE_FIT,
int $output_quality = 80, int $output_quality = 80,
bool $minimize = false, bool $minimize = false,
@ -563,7 +562,7 @@ class Media extends Extension
$output_mime = MimeType::WEBP_LOSSLESS; $output_mime = MimeType::WEBP_LOSSLESS;
} }
$bg = "black"; $bg = "\"$alpha_color\"";
if (self::supports_alpha($output_mime)) { if (self::supports_alpha($output_mime)) {
$bg = "none"; $bg = "none";
} }
@ -590,15 +589,15 @@ class Media extends Extension
switch ($resize_type) { switch ($resize_type) {
case Media::RESIZE_TYPE_FIT: case Media::RESIZE_TYPE_FIT:
case Media::RESIZE_TYPE_STRETCH: case Media::RESIZE_TYPE_STRETCH:
$args .= "${resize_arg} ${new_width}x${new_height}${resize_suffix} ${file_arg}"; $args .= "${file_arg} ${resize_arg} ${new_width}x${new_height}${resize_suffix} -background ${bg} -flatten ";
break; break;
case Media::RESIZE_TYPE_FILL: case Media::RESIZE_TYPE_FILL:
$args .= "${resize_arg} ${new_width}x${new_height}\^ -background none -gravity center -extent ${new_width}x${new_height} ${file_arg}"; $args .= "${file_arg} ${resize_arg} ${new_width}x${new_height}\^ -background ${bg} -flatten -gravity center -extent ${new_width}x${new_height} ";
break; break;
case Media::RESIZE_TYPE_FIT_BLUR: case Media::RESIZE_TYPE_FIT_BLUR:
$blur_size = max(ceil(max($new_width, $new_height) / 25), 5); $blur_size = max(ceil(max($new_width, $new_height) / 25), 5);
$args .= "${file_arg} ". $args .= "${file_arg} ".
"\( -clone 0 -resize ${new_width}x${new_height}\^ -background none -gravity center -fill black -colorize 50% -extent ${new_width}x${new_height} -blur 0x${blur_size} \) ". "\( -clone 0 -resize ${new_width}x${new_height}\^ -background ${bg} -flatten -gravity center -fill black -colorize 50% -extent ${new_width}x${new_height} -blur 0x${blur_size} \) ".
"\( -clone 0 -resize ${new_width}x${new_height} \) ". "\( -clone 0 -resize ${new_width}x${new_height} \) ".
"-delete 0 -gravity center -compose over -composite"; "-delete 0 -gravity center -compose over -composite";
break; break;
@ -615,7 +614,7 @@ class Media extends Extension
} }
$args .= " -quality ${output_quality} -background ${bg}"; $args .= " -quality ${output_quality} ";
$output_ext = self::determine_ext($output_mime); $output_ext = self::determine_ext($output_mime);
@ -651,6 +650,7 @@ class Media extends Extension
int $new_height, int $new_height,
string $output_filename, string $output_filename,
string $output_mime = null, string $output_mime = null,
string $alpha_color = Media::DEFAULT_ALPHA_CONVERSION_COLOR,
string $resize_type = self::RESIZE_TYPE_FIT, string $resize_type = self::RESIZE_TYPE_FIT,
int $output_quality = 80, int $output_quality = 80,
bool $allow_upscale = true bool $allow_upscale = true
@ -769,6 +769,34 @@ class Media extends Extension
throw new MediaException("Unable to copy resized image data to new image"); throw new MediaException("Unable to copy resized image data to new image");
} }
switch ($output_mime) {
case MimeType::BMP:
case MimeType::JPEG:
// In case of alpha channels
$width = imagesx($image_resized);
$height = imagesy($image_resized);
$new_image = imagecreatetruecolor($width, $height);
if ($new_image===false) {
throw new ImageTranscodeException("Could not create image with dimensions $width x $height");
}
$background_color = Media::hex_color_allocate($new_image, $alpha_color);
if ($background_color===false) {
throw new ImageTranscodeException("Could not allocate background color");
}
if (imagefilledrectangle($new_image, 0, 0, $width, $height, $background_color)===false) {
throw new ImageTranscodeException("Could not fill background color");
}
if (imagecopy($new_image, $image_resized, 0, 0, 0, 0, $width, $height)===false) {
throw new ImageTranscodeException("Could not copy source image to new image");
}
imagedestroy($image_resized);
$image_resized = $new_image;
break;
}
switch ($output_mime) { switch ($output_mime) {
case MimeType::BMP: case MimeType::BMP:
$result = imagebmp($image_resized, $output_filename, true); $result = imagebmp($image_resized, $output_filename, true);
@ -904,4 +932,13 @@ class Media extends Extension
$database->begin_transaction(); $database->begin_transaction();
} }
} }
public static function hex_color_allocate($im, $hex)
{
$hex = ltrim($hex, '#');
$a = hexdec(substr($hex, 0, 2));
$b = hexdec(substr($hex, 2, 2));
$c = hexdec(substr($hex, 4, 2));
return imagecolorallocate($im, $a, $b, $c);
}
} }

View file

@ -7,6 +7,11 @@ abstract class MediaEngine
public const FFMPEG = "ffmpeg"; public const FFMPEG = "ffmpeg";
public const STATIC = "static"; public const STATIC = "static";
const IMAGE_ENGINES = [
"GD" => MediaEngine::GD,
"ImageMagick" => MediaEngine::IMAGICK,
];
public const ALL = [ public const ALL = [
MediaEngine::GD, MediaEngine::GD,
MediaEngine::FFMPEG, MediaEngine::FFMPEG,

View file

@ -50,9 +50,9 @@ 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_choice_option(ResizeConfig::ENGINE, MediaEngine::IMAGE_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();
$sb->start_table(); $sb->start_table();
$sb->add_table_header("Preset/Default Dimensions"); $sb->add_table_header("Preset/Default Dimensions");

View file

@ -278,6 +278,17 @@ class SetupBlock extends Block
$this->format_option($name, $html, $label, $table_row); $this->format_option($name, $html, $label, $table_row);
} }
public function add_color_option(string $name, string $label=null, bool $table_row = false)
{
global $config;
$val = html_escape($config->get_string($name));
$html = "<input type='color' id='{$name}' name='_config_{$name}' value='{$val}'>\n";
$html .= "<input type='hidden' name='_type_{$name}' value='string'>\n";
$this->format_option($name, $html, $label, $table_row);
}
} }
class Setup extends Extension class Setup extends Extension

View file

@ -9,4 +9,5 @@ class TranscodeConfig
const UPLOAD = "transcode_upload"; const UPLOAD = "transcode_upload";
const UPLOAD_PREFIX = "transcode_upload_"; const UPLOAD_PREFIX = "transcode_upload_";
const QUALITY = "transcode_quality"; const QUALITY = "transcode_quality";
const ALPHA_COLOR = "transcode_alpha_color";
} }

View file

@ -51,6 +51,7 @@ class TranscodeImage extends Extension
$config->set_default_bool(TranscodeConfig::UPLOAD, false); $config->set_default_bool(TranscodeConfig::UPLOAD, false);
$config->set_default_string(TranscodeConfig::ENGINE, MediaEngine::GD); $config->set_default_string(TranscodeConfig::ENGINE, MediaEngine::GD);
$config->set_default_int(TranscodeConfig::QUALITY, 80); $config->set_default_int(TranscodeConfig::QUALITY, 80);
$config->set_default_string(TranscodeConfig::ALPHA_COLOR, Media::DEFAULT_ALPHA_CONVERSION_COLOR);
foreach (array_values(self::INPUT_MIMES) as $mime) { foreach (array_values(self::INPUT_MIMES) as $mime) {
$config->set_default_string(self::get_mapping_name($mime), ""); $config->set_default_string(self::get_mapping_name($mime), "");
@ -142,9 +143,9 @@ class TranscodeImage extends Extension
$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, Media::IMAGE_MEDIA_ENGINES, "Engine", true); $sb->add_choice_option(TranscodeConfig::ENGINE, MediaEngine::IMAGE_ENGINES, "Engine", true);
foreach (self::INPUT_MIMES as $display=> $mime) { foreach (self::INPUT_MIMES as $display=> $mime) {
if (MediaEngine::is_input_supported($engine, $mime)) { if (MediaEngine::is_input_supported($engine, $mime)) {
$outputs = $this->get_supported_output_mimes($engine, $mime); $outputs = $this->get_supported_output_mimes($engine, $mime);
@ -152,6 +153,7 @@ class TranscodeImage extends Extension
} }
} }
$sb->add_int_option(TranscodeConfig::QUALITY, "Lossy Format Quality", true); $sb->add_int_option(TranscodeConfig::QUALITY, "Lossy Format Quality", true);
$sb->add_color_option(TranscodeConfig::ALPHA_COLOR, "Alpha Conversion Color", true);
$sb->end_table(); $sb->end_table();
$event->panel->add_block($sb); $event->panel->add_block($sb);
} }
@ -337,7 +339,7 @@ class TranscodeImage extends Extension
throw new ImageTranscodeException("Source and target MIMEs are the same: ".$source_mime); throw new ImageTranscodeException("Source and target MIMEs are the same: ".$source_mime);
} }
$engine = $config->get_string("transcode_engine"); $engine = $config->get_string(TranscodeConfig::ENGINE);
@ -385,11 +387,11 @@ class TranscodeImage extends Extension
throw new ImageTranscodeException("Could not create image with dimensions $width x $height"); throw new ImageTranscodeException("Could not create image with dimensions $width x $height");
} }
try { try {
$black = imagecolorallocate($new_image, 0, 0, 0); $background_color = Media::hex_color_allocate($new_image, $config->get_string(TranscodeConfig::ALPHA_COLOR));
if ($black===false) { if ($background_color===false) {
throw new ImageTranscodeException("Could not allocate background color"); throw new ImageTranscodeException("Could not allocate background color");
} }
if (imagefilledrectangle($new_image, 0, 0, $width, $height, $black)===false) { if (imagefilledrectangle($new_image, 0, 0, $width, $height, $background_color)===false) {
throw new ImageTranscodeException("Could not fill background color"); throw new ImageTranscodeException("Could not fill background color");
} }
if (imagecopy($new_image, $image, 0, 0, 0, 0, $width, $height)===false) { if (imagecopy($new_image, $image, 0, 0, 0, 0, $width, $height)===false) {
@ -422,13 +424,14 @@ class TranscodeImage extends Extension
} }
$ext = Media::determine_ext($target_mime); $ext = Media::determine_ext($target_mime);
$args = " -flatten -background "; $args = " -background ";
if (Media::supports_alpha($target_mime)) { if (Media::supports_alpha($target_mime)) {
$args .= "none "; $args .= "none ";
} else { } else {
$args .= "black "; $args .= "\"".$config->get_string(TranscodeConfig::ALPHA_COLOR)."\" ";
} }
$args .= " -flatten ";
switch ($target_mime) { switch ($target_mime) {
case MimeType::PNG: case MimeType::PNG: