diff --git a/core/imageboard/misc.php b/core/imageboard/misc.php index dc9b8231..888aae0c 100644 --- a/core/imageboard/misc.php +++ b/core/imageboard/misc.php @@ -176,6 +176,7 @@ function create_scaled_image(string $inname, string $outname, array $tsize, stri $tsize[1], $resize_type, $output_mime, + $config->get_string(ImageConfig::THUMB_ALPHA_COLOR), $config->get_int(ImageConfig::THUMB_QUALITY), true, true diff --git a/ext/image/config.php b/ext/image/config.php index 2f967588..1485c7d0 100644 --- a/ext/image/config.php +++ b/ext/image/config.php @@ -11,6 +11,7 @@ abstract class ImageConfig const THUMB_QUALITY = 'thumb_quality'; const THUMB_MIME = 'thumb_mime'; const THUMB_FIT = 'thumb_fit'; + const THUMB_ALPHA_COLOR ='thumb_alpha_color'; const SHOW_META = 'image_show_meta'; const ILINK = 'image_ilink'; diff --git a/ext/image/main.php b/ext/image/main.php index e8a8937f..d30752cb 100644 --- a/ext/image/main.php +++ b/ext/image/main.php @@ -37,6 +37,7 @@ class ImageIO extends Extension $config->set_default_int(ImageConfig::THUMB_QUALITY, 75); $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_ALPHA_COLOR, Media::DEFAULT_ALPHA_CONVERSION_COLOR); if (function_exists(self::EXIF_READ_FUNCTION)) { $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_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(); diff --git a/ext/media/events.php b/ext/media/events.php index b76c9245..57e790ea 100644 --- a/ext/media/events.php +++ b/ext/media/events.php @@ -10,6 +10,7 @@ class MediaResizeEvent extends Event public $target_width; public $target_height; public $target_quality; + public $alpha_color; public $minimize; public $allow_upscale; public $resize_type; @@ -23,6 +24,7 @@ class MediaResizeEvent extends Event int $target_height, string $resize_type = Media::RESIZE_TYPE_FIT, string $target_mime = null, + string $alpha_color = Media::DEFAULT_ALPHA_CONVERSION_COLOR, int $target_quality = 80, bool $minimize = false, bool $allow_upscale = true @@ -36,6 +38,10 @@ class MediaResizeEvent extends Event $this->target_height = $target_height; $this->target_width = $target_width; $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->minimize = $minimize; $this->allow_upscale = $allow_upscale; diff --git a/ext/media/main.php b/ext/media/main.php index 916ec261..769e1b56 100644 --- a/ext/media/main.php +++ b/ext/media/main.php @@ -16,12 +16,7 @@ class Media extends Extension /** @var MediaTheme */ protected $theme; - const IMAGE_MEDIA_ENGINES = [ - "GD" => MediaEngine::GD, - "ImageMagick" => MediaEngine::IMAGICK, - ]; - - const LOSSLESS_FORMATS = [ + private const LOSSLESS_FORMATS = [ MimeType::WEBP_LOSSLESS, MimeType::PNG, MimeType::PSD, @@ -31,16 +26,17 @@ class Media extends Extension MimeType::GIF ]; - const ALPHA_FORMATS = [ + private const ALPHA_FORMATS = [ MimeType::WEBP_LOSSLESS, MimeType::WEBP, MimeType::PNG, ]; - const RESIZE_TYPE_FIT = "Fit"; - const RESIZE_TYPE_FIT_BLUR = "Fit Blur"; - const RESIZE_TYPE_FILL = "Fill"; - const RESIZE_TYPE_STRETCH = "Stretch"; + public const RESIZE_TYPE_FIT = "Fit"; + public const RESIZE_TYPE_FIT_BLUR = "Fit Blur"; + public const RESIZE_TYPE_FILL = "Fill"; + public const RESIZE_TYPE_STRETCH = "Stretch"; + public const DEFAULT_ALPHA_CONVERSION_COLOR = "#00000000"; public static function imagick_available(): bool { @@ -194,6 +190,7 @@ class Media extends Extension $event->target_height, $event->output_path, $event->target_mime, + $event->alpha_color, $event->resize_type, $event->target_quality, $event->allow_upscale @@ -210,6 +207,7 @@ class Media extends Extension $event->target_height, $event->output_path, $event->target_mime, + $event->alpha_color, $event->resize_type, $event->target_quality, $event->minimize, @@ -542,6 +540,7 @@ class Media extends Extension int $new_height, string $output_filename, string $output_mime = null, + string $alpha_color = Media::DEFAULT_ALPHA_CONVERSION_COLOR, string $resize_type = self::RESIZE_TYPE_FIT, int $output_quality = 80, bool $minimize = false, @@ -563,7 +562,7 @@ class Media extends Extension $output_mime = MimeType::WEBP_LOSSLESS; } - $bg = "black"; + $bg = "\"$alpha_color\""; if (self::supports_alpha($output_mime)) { $bg = "none"; } @@ -590,15 +589,15 @@ class Media extends Extension 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}"; + $args .= "${file_arg} ${resize_arg} ${new_width}x${new_height}${resize_suffix} -background ${bg} -flatten "; break; 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; 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}\^ -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} \) ". "-delete 0 -gravity center -compose over -composite"; 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); @@ -651,6 +650,7 @@ class Media extends Extension int $new_height, string $output_filename, string $output_mime = null, + string $alpha_color = Media::DEFAULT_ALPHA_CONVERSION_COLOR, string $resize_type = self::RESIZE_TYPE_FIT, int $output_quality = 80, bool $allow_upscale = true @@ -769,6 +769,34 @@ class Media extends Extension 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) { case MimeType::BMP: $result = imagebmp($image_resized, $output_filename, true); @@ -904,4 +932,13 @@ class Media extends Extension $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); + } } diff --git a/ext/media/media_engine.php b/ext/media/media_engine.php index 94636bb8..711fdc97 100644 --- a/ext/media/media_engine.php +++ b/ext/media/media_engine.php @@ -7,6 +7,11 @@ abstract class MediaEngine public const FFMPEG = "ffmpeg"; public const STATIC = "static"; + const IMAGE_ENGINES = [ + "GD" => MediaEngine::GD, + "ImageMagick" => MediaEngine::IMAGICK, + ]; + public const ALL = [ MediaEngine::GD, MediaEngine::FFMPEG, diff --git a/ext/resize/main.php b/ext/resize/main.php index 3def0f86..ab33272c 100644 --- a/ext/resize/main.php +++ b/ext/resize/main.php @@ -50,9 +50,9 @@ class ResizeImage extends Extension { $sb = new SetupBlock("Image Resize"); $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::UPLOAD, "Resize on upload: ", 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::UPLOAD, "Resize on upload", true); $sb->end_table(); $sb->start_table(); $sb->add_table_header("Preset/Default Dimensions"); diff --git a/ext/setup/main.php b/ext/setup/main.php index fb85f597..dfa69e9d 100644 --- a/ext/setup/main.php +++ b/ext/setup/main.php @@ -278,6 +278,17 @@ class SetupBlock extends Block $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 = "\n"; + $html .= "\n"; + + $this->format_option($name, $html, $label, $table_row); + } } class Setup extends Extension diff --git a/ext/transcode/config.php b/ext/transcode/config.php index 7d6a454d..07572141 100644 --- a/ext/transcode/config.php +++ b/ext/transcode/config.php @@ -9,4 +9,5 @@ class TranscodeConfig const UPLOAD = "transcode_upload"; const UPLOAD_PREFIX = "transcode_upload_"; const QUALITY = "transcode_quality"; + const ALPHA_COLOR = "transcode_alpha_color"; } diff --git a/ext/transcode/main.php b/ext/transcode/main.php index 8c94678f..7289f264 100644 --- a/ext/transcode/main.php +++ b/ext/transcode/main.php @@ -51,6 +51,7 @@ class TranscodeImage extends Extension $config->set_default_bool(TranscodeConfig::UPLOAD, false); $config->set_default_string(TranscodeConfig::ENGINE, MediaEngine::GD); $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) { $config->set_default_string(self::get_mapping_name($mime), ""); @@ -142,9 +143,9 @@ class TranscodeImage extends Extension $sb = new SetupBlock("Image Transcode"); $sb->start_table(); - $sb->add_bool_option(TranscodeConfig::ENABLED, "Allow transcoding images: ", 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_bool_option(TranscodeConfig::ENABLED, "Allow transcoding images", true); + $sb->add_bool_option(TranscodeConfig::UPLOAD, "Transcode on upload", true); + $sb->add_choice_option(TranscodeConfig::ENGINE, MediaEngine::IMAGE_ENGINES, "Engine", true); foreach (self::INPUT_MIMES as $display=> $mime) { if (MediaEngine::is_input_supported($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_color_option(TranscodeConfig::ALPHA_COLOR, "Alpha Conversion Color", true); $sb->end_table(); $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); } - $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"); } try { - $black = imagecolorallocate($new_image, 0, 0, 0); - if ($black===false) { + $background_color = Media::hex_color_allocate($new_image, $config->get_string(TranscodeConfig::ALPHA_COLOR)); + if ($background_color===false) { 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"); } 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); - $args = " -flatten -background "; + $args = " -background "; if (Media::supports_alpha($target_mime)) { $args .= "none "; } else { - $args .= "black "; + $args .= "\"".$config->get_string(TranscodeConfig::ALPHA_COLOR)."\" "; } + $args .= " -flatten "; switch ($target_mime) { case MimeType::PNG: