= 2.3.0 and ImageMagick >= 6.4.0 private const MIN_IMAGICK_VERSION = '2.3.0'; private const MIN_IMAGEMAGICK_VERSION = '6.4.0'; public const THUMB_KEEP_FRAMES_NO = 0; public const THUMB_KEEP_FRAMES_ALL = -1; private bool $strip_metadata; private bool $frames_for_gif_thumbs; private static function degreesFromOrientation(int $orientation): int { switch ($orientation) { case \Imagick::ORIENTATION_UNDEFINED: case \Imagick::ORIENTATION_TOPLEFT: case \Imagick::ORIENTATION_TOPRIGHT: default: return 0; case \Imagick::ORIENTATION_BOTTOMRIGHT: case \Imagick::ORIENTATION_BOTTOMLEFT: return 180; case \Imagick::ORIENTATION_LEFTTOP: case \Imagick::ORIENTATION_RIGHTTOP: return 90; case \Imagick::ORIENTATION_RIGHTBOTTOM: case \Imagick::ORIENTATION_LEFTBOTTOM: return 270; } } public static function isFlippedFromOrientation(int $orientation): bool { switch ($orientation) { case \Imagick::ORIENTATION_UNDEFINED: case \Imagick::ORIENTATION_TOPLEFT: default: return false; case \Imagick::ORIENTATION_TOPRIGHT: return true; case \Imagick::ORIENTATION_BOTTOMRIGHT: return false; case \Imagick::ORIENTATION_BOTTOMLEFT: return true; case \Imagick::ORIENTATION_LEFTTOP: return false; case \Imagick::ORIENTATION_RIGHTTOP: return true; case \Imagick::ORIENTATION_RIGHTBOTTOM: return false; case \Imagick::ORIENTATION_LEFTBOTTOM: return true; } } /** * @return bool Returns if width and height were swapped. */ private static function adjustOrientation(\Imagick $imagick) { $orientation = $imagick->getImageOrientation(); $degrees = self::degreesFromOrientation($orientation); $flipped = self::isFlippedFromOrientation($orientation); if ($degrees !== 0) { // Used to return an int, make it always a bool. $has_alpha = !!$imagick->getImageAlphaChannel(); $background = $has_alpha ? '#0000' : '#000'; $imagick->rotateImage($background, $degrees); } if ($flipped) { $imagick->flopImage(); } return $degrees == 0 || $degrees == 180; } private function generateThumbImpl( \Imagick $imagick, string $source_file_mime, string $preferred_out_file_dir, string $preferred_out_file_name, string $preferred_out_mime, int $width, int $height, int $max_width, int $max_height ) { if ( $source_file_mime === 'image/gif' && $this->frames_for_gif_thumbs !== self::THUMB_KEEP_FRAMES_NO && $imagick->getNumberImages() > 1 ) { $out_path = $preferred_out_file_dir . \DIRECTORY_SEPARATOR . $preferred_out_file_name . '.gif'; if ($width > $max_width || $height > $max_height) { $thumb_width = $max_width; $thumb_height = $max_height; } else { $thumb_width = $width; $thumb_height = $height; } if ($this->frames_for_gif_thumbs !== self::THUMB_KEEP_FRAMES_ALL) { $other = new \Imagick(); try { $other->setFormat('gif'); $step = \floor($imagick->getNumberImages() / $this->frames_for_gif_thumbs); for ($i = 0, $j = 0; $i < $imagick->getNumberImages(); $i += $step, $j++) { $imagick->setIteratorIndex($i); $delay = $imagick->getImageDelay(); $imagick->sampleImage($thumb_width, $thumb_height); $imagick->setImagePage($thumb_width, $thumb_height, 0, 0); $imagick->setImageDelay($delay); $other->addImage($imagick->getImage()); } $other->optimizeImageLayers(); $other->setImageCompressionQuality(70); $other->writeImage("gif:$out_path"); } finally { $other->clear(); } } else { $imagick->stripImage(); $imagick->optimizeImageLayers(); $imagick->setImageCompressionQuality(70); $imagick->writeImage("gif:$out_path"); } return new ThumbGenerationResult( $out_path, 'image/gif', false, $thumb_width, $thumb_height ); } else { if ($width > $max_width || $height > $max_height) { $thumb_width = $max_width; $thumb_height = $max_height; $imagick->thumbnailImage($max_width, $max_height, true); } else { $thumb_width = $width; $thumb_height = $height; } $out_ext = Mime\mime_to_ext($preferred_out_mime); $out_path = $preferred_out_file_dir . \DIRECTORY_SEPARATOR . $preferred_out_file_name . '.' . $out_ext; $imagick->stripImage(); $imagick->setImageCompressionQuality(70); $imagick->writeImage("$out_ext:$out_path"); return new ThumbGenerationResult( $out_path, $preferred_out_mime, false, $thumb_width, $thumb_height ); } } public static function checkImagickVersion(): bool { $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, '>='); } } return false; } public function __construct(bool $strip_metadata, int $frames_for_gif_thumbs) { $this->strip_metadata = $strip_metadata; $this->frames_for_gif_thumbs = $frames_for_gif_thumbs; } public function supportsMime(string $mime): bool { $ext = Mime\mime_to_ext($mime); if ($ext === null) { return false; } $ext = \strtoupper($ext); return !empty(\Imagick::queryFormats("$ext*")); } public function openHandle(string $file_path, string $file_mime, int $file_kind): mixed { $ext = Mime\mime_to_ext($file_mime); $path = \realpath($file_path); // Open it as the supplied mime type. $imagick = new \Imagick("$ext:$path"); return [ $imagick, $file_path, $file_mime, $file_kind ]; } public function closeHandle(mixed $handle) { $handle[0]->clear(); } public function generateThumb( mixed $handle, string $preferred_out_file_dir, string $preferred_out_file_name, string $preferred_out_mime, int $max_width, int $max_height ): ThumbGenerationResult { list($imagick, $source_file_path, $source_file_mime, $source_file_kind) = $handle; $width = $imagick->getImageWidth(); $height = $imagick->getImageHeight(); if (!$this->strip_metadata && $width <= $max_width && $height <= $max_height) { $out_path = $preferred_out_file_dir . \DIRECTORY_SEPARATOR . $preferred_out_file_name . '.' . Mime\mime_to_ext($source_file_mime); if ($source_file_kind === self::FILE_KIND_UPLOADED) { if (!Fs\move_or_copy_uploaded($source_file_path, $out_path)) { throw new \RuntimeException("Could not move or copy uploaded file '$source_file_path' to '$out_path'"); } } else { if (!Fs\link_or_copy($source_file_path, $out_path)) { throw new \RuntimeException("Could not link or copy '$source_file_path' to '$out_path'"); } } return new ThumbGenerationResult( $out_path, $source_file_mime, false, $max_width, $max_height ); } else { $swap = self::adjustOrientation($imagick); if ($swap) { $tmp = $width; $width = $height; $height = $tmp; } return self::generateThumbImpl( $imagick, $source_file_mime, $preferred_out_file_dir, $preferred_out_file_name, $preferred_out_mime, $width, $height, $max_width, $max_height ); } } public function installMediaAndGenerateThumb( mixed $handle, string $media_preferred_out_file_dir, string $media_preferred_out_file_name, string $thumb_preferred_out_file_dir, string $thumb_preferred_out_file_name, string $thumb_preferred_out_mime, int $thumb_max_width, int $thumb_max_height ): ThumbGenerationResult { list($imagick, $file_path, $file_mime, $file_kind) = $handle; } }