forked from leftypol/leftypol
LibMagickMediaHandler.php: add support for animated webp thumbnails
This commit is contained in:
parent
37fbb35f8b
commit
096a6f04f1
1 changed files with 126 additions and 62 deletions
|
@ -8,7 +8,7 @@ use Vichan\Functions\{Fs, Metadata};
|
|||
class LibMagickMediaHandler implements MediaHandler {
|
||||
use MediaHandlerTrait;
|
||||
|
||||
// getImageAlphaChannel requires Imagick >= 2.3.0 and ImageMagick >= 6.4.0
|
||||
// getImageAlphaChannel and writeImages requires Imagick >= 2.3.0 and ImageMagick >= 6.4.0
|
||||
private const MIN_IMAGICK_VERSION = '2.3.0';
|
||||
private const MIN_IMAGEMAGICK_VERSION = '6.4.0';
|
||||
|
||||
|
@ -17,10 +17,11 @@ class LibMagickMediaHandler implements MediaHandler {
|
|||
|
||||
|
||||
private bool $strip_metadata;
|
||||
private bool $frames_for_gif_thumbs;
|
||||
private bool $frames_for_animated_thumbs;
|
||||
private int $image_max_width;
|
||||
private int $image_max_height;
|
||||
private string $static_thumb_mime;
|
||||
private ?string $animated_thumb_mime;
|
||||
|
||||
|
||||
private static function degreesFromOrientation(int $orientation): int {
|
||||
|
@ -65,6 +66,13 @@ class LibMagickMediaHandler implements MediaHandler {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If the mime type supports animations.
|
||||
*/
|
||||
private static function mimeSupportAnimation(string $mime) {
|
||||
return $mime === 'image/gif' || $mime === 'image/webp';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool Returns if width and height were swapped.
|
||||
*/
|
||||
|
@ -83,6 +91,40 @@ class LibMagickMediaHandler implements MediaHandler {
|
|||
}
|
||||
}
|
||||
|
||||
private function generateThumbImplSingleFrame(
|
||||
\Imagick $imagick,
|
||||
string $preferred_out_file_basepath,
|
||||
int $width,
|
||||
int $height,
|
||||
int $max_width,
|
||||
int $max_height
|
||||
) {
|
||||
if ($width > $max_width || $height > $max_height) {
|
||||
$thumb_width = $max_width;
|
||||
$thumb_height = $max_height;
|
||||
|
||||
// Unreliable behavior on some versions if the target width/height are under the limit?
|
||||
$imagick->thumbnailImage($max_width, $max_height, true);
|
||||
} else {
|
||||
$thumb_width = $width;
|
||||
$thumb_height = $height;
|
||||
}
|
||||
|
||||
$out_ext = Metadata\mime_to_ext($this->static_thumb_mime);
|
||||
$out_path = "$preferred_out_file_basepath.$out_ext";
|
||||
|
||||
$imagick->stripImage();
|
||||
$imagick->setImageCompressionQuality(70);
|
||||
$imagick->writeImage("$out_ext:$out_path");
|
||||
|
||||
return new ThumbGenerationResult(
|
||||
$out_path,
|
||||
$this->static_thumb_mime,
|
||||
$thumb_width,
|
||||
$thumb_height
|
||||
);
|
||||
}
|
||||
|
||||
private function generateThumbImpl(
|
||||
\Imagick $imagick,
|
||||
string $source_file_mime,
|
||||
|
@ -92,13 +134,12 @@ class LibMagickMediaHandler implements MediaHandler {
|
|||
int $max_width,
|
||||
int $max_height
|
||||
) {
|
||||
// Special handling for gifs with multiple frames.
|
||||
if (
|
||||
$source_file_mime === 'image/gif'
|
||||
&& $this->frames_for_gif_thumbs !== self::THUMB_KEEP_FRAMES_NO
|
||||
&& $imagick->getNumberImages() > 1
|
||||
) {
|
||||
$out_path = $preferred_out_file_basepath . '.gif';
|
||||
$source_is_animated = self::mimeSupportAnimation($source_file_mime) && $imagick->getNumberImages() > 1;
|
||||
|
||||
// Special handling for animated images with multiple frames.
|
||||
if ($this->frames_for_animated_thumbs !== self::THUMB_KEEP_FRAMES_NO && $source_is_animated) {
|
||||
$animated_thumb_ext = Metadata\mime_to_ext($this->animated_thumb_mime);
|
||||
$out_path = "$preferred_out_file_basepath.$animated_thumb_ext";
|
||||
|
||||
if ($width > $max_width || $height > $max_height) {
|
||||
$thumb_width = $max_width;
|
||||
|
@ -109,14 +150,15 @@ class LibMagickMediaHandler implements MediaHandler {
|
|||
}
|
||||
|
||||
// By now $this->frames_for_gif_thumbs !== 0.
|
||||
$step = \floor($imagick->getNumberImages() / $this->frames_for_gif_thumbs);
|
||||
$step = \floor($imagick->getNumberImages() / $this->frames_for_animated_thumbs);
|
||||
|
||||
if ($this->frames_for_gif_thumbs !== self::THUMB_KEEP_FRAMES_ALL && $step > 1) {
|
||||
if ($this->frames_for_animated_thumbs !== self::THUMB_KEEP_FRAMES_ALL) {
|
||||
if ($step > 1) {
|
||||
// Reduce the number of frames.
|
||||
|
||||
$other = new \Imagick();
|
||||
try {
|
||||
$other->setFormat('gif');
|
||||
$other->setFormat($animated_thumb_ext);
|
||||
|
||||
for ($i = 0, $j = 0; $i < $imagick->getNumberImages(); $i += $step, $j++) {
|
||||
$imagick->setIteratorIndex($i);
|
||||
|
@ -132,49 +174,47 @@ class LibMagickMediaHandler implements MediaHandler {
|
|||
$other->optimizeImageLayers();
|
||||
|
||||
$other->setImageCompressionQuality(70);
|
||||
$other->writeImage("gif:$out_path");
|
||||
$other->writeImages("$animated_thumb_ext:$out_path", true);
|
||||
} finally {
|
||||
$other->clear();
|
||||
}
|
||||
} else {
|
||||
// Only a single frame would be left, save it as a single image.
|
||||
$imagick->setIteratorIndex(0);
|
||||
|
||||
return $this->generateThumbImplSingleFrame(
|
||||
$imagick,
|
||||
$preferred_out_file_basepath,
|
||||
$width,
|
||||
$height,
|
||||
$max_width,
|
||||
$max_height
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// Just try to optimize it a little.
|
||||
$imagick->stripImage();
|
||||
$imagick->optimizeImageLayers();
|
||||
|
||||
$imagick->setFormat($animated_thumb_ext);
|
||||
$imagick->setImageCompressionQuality(70);
|
||||
$imagick->writeImage("gif:$out_path");
|
||||
$imagick->writeImages("$animated_thumb_ext:$out_path", true);
|
||||
}
|
||||
|
||||
return new ThumbGenerationResult(
|
||||
$out_path,
|
||||
'image/gif',
|
||||
$this->animated_thumb_mime,
|
||||
$thumb_width,
|
||||
$thumb_height
|
||||
);
|
||||
} else {
|
||||
if ($width > $max_width || $height > $max_height) {
|
||||
$thumb_width = $max_width;
|
||||
$thumb_height = $max_height;
|
||||
|
||||
// Unreliable behavior on some versions if the target width/height are under the limit?
|
||||
$imagick->thumbnailImage($max_width, $max_height, true);
|
||||
} else {
|
||||
$thumb_width = $width;
|
||||
$thumb_height = $height;
|
||||
}
|
||||
|
||||
$out_ext = Metadata\mime_to_ext($this->static_thumb_mime);
|
||||
$out_path = $preferred_out_file_basepath . '.' . $out_ext;
|
||||
|
||||
$imagick->stripImage();
|
||||
$imagick->setImageCompressionQuality(70);
|
||||
$imagick->writeImage("$out_ext:$out_path");
|
||||
|
||||
return new ThumbGenerationResult(
|
||||
$out_path,
|
||||
$this->static_thumb_mime,
|
||||
$thumb_width,
|
||||
$thumb_height
|
||||
return $this->generateThumbImplSingleFrame(
|
||||
$imagick,
|
||||
$preferred_out_file_basepath,
|
||||
$width,
|
||||
$height,
|
||||
$max_width,
|
||||
$max_height
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -184,22 +224,46 @@ class LibMagickMediaHandler implements MediaHandler {
|
|||
* @return bool
|
||||
*/
|
||||
public static function checkImagickVersion(): bool {
|
||||
static $version_ok = null;
|
||||
|
||||
if ($version_ok === null) {
|
||||
$version_ok = false;
|
||||
$imagick_ver = \phpversion('imagick');
|
||||
if ($imagick_ver !== false && \version_compare($imagick_ver, self::MIN_IMAGICK_VERSION, '>=')) {
|
||||
$str = \Imagick::getVersion()['versionString'];
|
||||
if (\preg_match('/ImageMagick ([0-9]+\.[0-9]+\.[0-9]+)/', $str, $matches)) {
|
||||
return \version_compare($matches[1], self::MIN_IMAGEMAGICK_VERSION, '>=');
|
||||
$version_ok = \version_compare($matches[1], self::MIN_IMAGEMAGICK_VERSION, '>=');
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public function __construct(bool $strip_metadata, int $frames_for_gif_thumbs, int $max_width, int $max_height, string $static_thumb_mime) {
|
||||
return $version_ok;
|
||||
}
|
||||
|
||||
public function __construct(
|
||||
bool $strip_metadata,
|
||||
int $frames_for_animated_thumbs,
|
||||
int $max_width,
|
||||
int $max_height,
|
||||
string $static_thumb_mime,
|
||||
?string $animated_thumb_mime
|
||||
) {
|
||||
if (!self::checkImagickVersion()) {
|
||||
throw new MediaException('Imagick extension or ImageMagick are not available or too old', MediaException::ERR_BAD_HANDLER);
|
||||
}
|
||||
if ($frames_for_animated_thumbs !== self::THUMB_KEEP_FRAMES_NO && $animated_thumb_mime === null) {
|
||||
throw new MediaException('Missing mime type for animated thumbnails', MediaException::ERR_BAD_MEDIA_TYPE);
|
||||
}
|
||||
if ($animated_thumb_mime !== null && !self::mimeSupportAnimation($animated_thumb_mime)) {
|
||||
throw new MediaException("$animated_thumb_mime does not support animations", MediaException::ERR_BAD_MEDIA_TYPE);
|
||||
}
|
||||
|
||||
$this->strip_metadata = $strip_metadata;
|
||||
$this->frames_for_gif_thumbs = $frames_for_gif_thumbs;
|
||||
$this->frames_for_animated_thumbs = $frames_for_animated_thumbs;
|
||||
$this->image_max_width = $max_width;
|
||||
$this->image_max_height = $max_height;
|
||||
$this->static_thumb_mime = $static_thumb_mime;
|
||||
$this->animated_thumb_mime = $animated_thumb_mime;
|
||||
}
|
||||
|
||||
public function supportsMime(string $mime): bool {
|
||||
|
@ -324,7 +388,7 @@ class LibMagickMediaHandler implements MediaHandler {
|
|||
}
|
||||
|
||||
$out_ext = Metadata\mime_to_ext($media_file_mime);
|
||||
$out_path = $media_preferred_out_file_basepath . '.' . $out_ext;
|
||||
$out_path = "$media_preferred_out_file_basepath.$out_ext";
|
||||
$imagick->writeImage("$out_ext:$out_path");
|
||||
|
||||
$thumb = self::generateThumbImpl(
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue