GdMediaHandler.php: properly handle AVIF, rotated image sizes

This commit is contained in:
Zankaria 2025-03-28 12:14:10 +01:00
parent 020ab7d4fc
commit c4b766f133

View file

@ -11,7 +11,8 @@ use Vichan\Functions\{Fs, Metadata};
class GdMediaHandler implements MediaHandler {
use MediaHandlerTrait;
private const PHP81 = \PHP_MAJOR_VERSION >= 8 && \PHP_MINOR_VERSION >= 1;
// GD requires PHP 8.1 to support avif, getimagesize in sniff_image requires PHP 8.2
private const PHP_AVIF_SUPPORT = \PHP_MAJOR_VERSION >= 8 && \PHP_MINOR_VERSION >= 2;
private bool $strip_redraw;
@ -33,7 +34,7 @@ class GdMediaHandler implements MediaHandler {
case 'image/bmp':
return \imagecreatefrombmp($file);
case 'image/avif':
return self::PHP81 ? \imagecreatefromavif($file) : false;
return self::PHP_AVIF_SUPPORT ? \imagecreatefromavif($file) : false;
}
return false;
}
@ -53,7 +54,7 @@ class GdMediaHandler implements MediaHandler {
case 'image/bmp':
return \imagebmp($gd, $file, true);
case 'image/avif':
return self::PHP81 ? \imageavif($gd, $file, 30, 6) : false;
return self::PHP_AVIF_SUPPORT ? \imageavif($gd, $file, 30, 6) : false;
}
return false;
}
@ -151,29 +152,50 @@ class GdMediaHandler implements MediaHandler {
|| ($mime === 'image/gif' && $info['GIF Read Support'] && $info['GIF Create Support'])
|| ($mime === 'image/webp' && $info['WebP Support'])
|| $mime === 'image/bmp'
|| ($mime === 'image/avif' && self::PHP81 && $info['AVIF Support']);
|| ($mime === 'image/avif' && self::PHP_AVIF_SUPPORT && $info['AVIF Support']);
}
public function openHandle(string $file_path, string $file_mime, int $file_kind): mixed {
$exif_orientation = null;
$exif_orientation_read = false;
try {
list($width, $height, $mime) = Metadata\sniff_image($file_path);
if ($width > $this->image_max_width || $height > $this->image_max_height) {
throw new MediaException("Image too big", MediaException::ERR_IMAGE_TOO_LARGE);
}
if ($mime !== $file_mime) {
throw new MediaException("Mime type mismatch, expected $file_mime, got $mime", MediaException::ERR_COMPUTE_ERR);
}
} catch (\RuntimeException $e) {
throw new MediaException("Could not sniff '$file_path'", MediaException::ERR_NO_OPEN, $e);
}
if ($mime !== $file_mime) {
throw new MediaException("Mime type mismatch, expected $file_mime, got $mime", MediaException::ERR_COMPUTE_ERR);
}
if ($width > $this->image_max_width && $height > $this->image_max_height) {
// Too big in all directions.
throw new MediaException("Image too big", MediaException::ERR_IMAGE_TOO_LARGE);
} elseif ($width > $this->image_max_width || $height > $this->image_max_height) {
// Too big in just one direction.
// Suppose the image is rotated by the exif orientation, would it fit?
if ($width <= $this->image_max_height && $height <= $this->image_max_width) {
$exif_orientation_read = true;
$exif_orientation = $this->getOrientation($file_path, $file_mime);
// If width and height aren't inverted, then it wouldn't fit.
if ($exif_orientation === null || !Exif::exifOrientationOnSide($exif_orientation)) {
throw new MediaException("Image too big", MediaException::ERR_IMAGE_TOO_LARGE);
}
} else {
throw new MediaException("Image too big", MediaException::ERR_IMAGE_TOO_LARGE);
}
}
$gd = self::imageCreateFrom($file_path, $file_mime);
if ($gd === false) {
throw new MediaException("Could not open '$file_path'", MediaException::ERR_NO_OPEN);
}
// Fix the orientation once and for all.
$exif_orientation = $this->getOrientation($file_path, $file_mime);
if (!$exif_orientation_read) {
$exif_orientation = $this->getOrientation($file_path, $file_mime);
}
if ($exif_orientation !== null) {
$degrees = Exif::exifOrientationDegrees($exif_orientation);
$flipped = Exif::exifOrientationIsFlipped($exif_orientation);