Compare commits

...

6 commits

6 changed files with 86 additions and 19 deletions

View file

@ -21,4 +21,40 @@ class Exif {
public static function exifOrientationOnSide(int $exif_orientation): bool {
return $exif_orientation >= 5 && $exif_orientation <= 8;
}
/**
* Gives the rotation in degrees of the given exif orientation.
*
* @param int $exif_orientation One of the EXIF_ORIENTATION_* constants.
* @return int Rotation in degrees.
*/
public static function exifOrientationDegrees(int $exif_orientation): int {
switch ($exif_orientation) {
case self::EXIF_ORIENTATION_UNDEFINED:
case self::EXIF_ORIENTATION_0_UPRIGHT:
case self::EXIF_ORIENTATION_0_FLIPPED:
return 0;
case self::EXIF_ORIENTATION_180_UPRIGHT:
case self::EXIF_ORIENTATION_180_FLIPPED:
return 180;
case self::EXIF_ORIENTATION_90_UPRIGHT:
case self::EXIF_ORIENTATION_90_FLIPPED:
return 90;
case self::EXIF_ORIENTATION_270_UPRIGHT:
case self::EXIF_ORIENTATION_270_FLIPPED:
return 270;
default:
throw new \RuntimeException("Invalid exif orientation '$exif_orientation'");
}
}
/**
* If the given exif orientation flips the image.
*
* @param int $exif_orientation One of the EXIF_ORIENTATION_* constants.
* @return bool If the image is flipped.
*/
public static function exifOrientationIsFlipped(int $exif_orientation): bool {
return $exif_orientation % 2 == 0;
}
}

View file

@ -2,8 +2,10 @@
namespace Vichan\Data;
class ImageMetadataResult {
class ImageMetadata {
// NOT affected by the exit orientation.
public int $width;
// NOT affected by the exif orientation.
public int $height;
public string $mime;
public ?int $exif_orientation;

View file

@ -1,7 +1,7 @@
<?php
namespace Vichan\Service\Media;
use Vichan\Data\{Exif, ImageMetadataResult};
use Vichan\Data\{Exif, ImageMetadata};
use Vichan\Functions\Mime;
@ -47,7 +47,7 @@ class CmdMagickImageMetadataReader implements ImageMetadataReader {
$this->prefix = $prefix;
}
public function getMetadata(string $file_path): ImageMetadataResult {
public function getMetadata(string $file_path): ImageMetadata {
$arg = \escapeshellarg("$file_path[0]");
$ret_exec = shell_exec_error("{$this->prefix} identify -format \"%w %h %m\" $arg");
@ -65,6 +65,6 @@ class CmdMagickImageMetadataReader implements ImageMetadataReader {
$mime = Mime\ext_to_mime($ext) ?? 'application/octet-stream';
$exif_orientation = self::parseOrientation($orientation_str);
return new ImageMetadataResult($width, $height, $mime, $exif_orientation);
return new ImageMetadata($width, $height, $mime, $exif_orientation);
}
}

View file

@ -2,7 +2,7 @@
namespace Vichan\Service\Media;
use Vichan\Data\Driver\Metadata\ExifReaderFactory;
use Vichan\Data\{Exif, ImageMetadataResult};
use Vichan\Data\ImageMetadata;
/**
@ -21,7 +21,7 @@ class DefaultImageMetadataReader implements ImageMetadataReader {
$this->exif_reader_factory = $exif_reader_factory;
}
public function getMetadata(string $file_path): ImageMetadataResult {
public function getMetadata(string $file_path): ImageMetadata {
$ret = \getimagesize($file_path, $info);
if ($ret === false) {
throw new \RuntimeException("Could not read image sizes of '$file_path'");
@ -38,13 +38,8 @@ class DefaultImageMetadataReader implements ImageMetadataReader {
$exif_reader = $this->exif_reader_factory->getReader($mime);
if ($exif_reader !== null) {
$orientation = $exif_reader->getOrientation($file_path);
if ($orientation !== null && Exif::exifOrientationOnSide($orientation)) {
$tmp = $width;
$width = $height;
$height = $tmp;
}
}
return new ImageMetadataResult($width, $height, $mime, $orientation);
return new ImageMetadata($width, $height, $mime, $orientation);
}
}

View file

@ -1,7 +1,7 @@
<?php
namespace Vichan\Service\Media;
use Vichan\Data\ThumbGenerationResult;
use Vichan\Data\{Exif, ThumbGenerationResult};
use Vichan\Functions\{Fs, Mime};
@ -13,6 +13,7 @@ class GdMediaHandler implements MediaHandler {
private bool $strip_redraw;
private ImageMetadataReader $image_metadate_reader;
private static function imageCreateFrom(string $file, string $mime): mixed {
@ -33,7 +34,7 @@ class GdMediaHandler implements MediaHandler {
return false;
}
public static function imageSaveTo(mixed $gd, string $file, string $mime) {
private static function imageSaveTo(mixed $gd, string $file, string $mime) {
// Somebody should tune the quality and speed values...
// Won't be me.
switch ($mime) {
@ -53,8 +54,7 @@ class GdMediaHandler implements MediaHandler {
return false;
}
private static function createCanvas(string $mime, int $width, int $height) {
$gd = \imagecreatetruecolor($width, $height);
private static function enableTransparency(mixed $gd, string $mime) {
switch ($mime) {
case 'image/png':
\imagecolortransparent($gd, \imagecolorallocatealpha($gd, 0, 0, 0, 0));
@ -66,6 +66,11 @@ class GdMediaHandler implements MediaHandler {
\imagesavealpha($gd, true);
break;
}
}
private static function createCanvas(string $mime, int $width, int $height) {
$gd = \imagecreatetruecolor($width, $height);
self::enableTransparency($gd, $mime);
return $gd;
}
@ -130,8 +135,9 @@ class GdMediaHandler implements MediaHandler {
* @param bool $strip_redraw If the EXIF metadata should be stripped by redrawing it.
* May cause the loss of color profiles. Orientation is still handled.
*/
public function __construct(bool $strip_redraw) {
public function __construct(bool $strip_redraw, ImageMetadataReader $image_metadate_reader) {
$this->strip_redraw = $strip_redraw;
$this->image_metadate_reader = $image_metadate_reader;
}
public function supportsMime(string $mime): bool {
@ -145,10 +151,38 @@ class GdMediaHandler implements MediaHandler {
}
public function openHandle(string $file_path, string $file_mime, int $file_kind): mixed {
$metadata = $this->image_metadate_reader->getMetadata($file_path);
if ($file_mime !== $file_mime) {
throw new \RuntimeException("Mime mismatch on '$file_path'");
}
$gd = self::imageCreateFrom($file_path, $file_mime);
if ($gd === false) {
throw new \RuntimeException("Could not open '$file_path'");
}
// Fix the orientation once and for all.
if ($metadata->exif_orientation !== null) {
$degrees = Exif::exifOrientationDegrees($metadata->exif_orientation);
$flipped = Exif::exifOrientationIsFlipped($metadata->exif_orientation);
if ($degrees !== 0) {
self::enableTransparency($gd, $file_mime);
$gd_other = \imagerotate($gd, $degrees, \imagecolorallocatealpha($gd, 0, 0, 0, 127));
\imagedestroy($gd);
if ($gd_other === false) {
throw new \RuntimeException("Error while correcting rotation of '$file_path'");
}
$gd = $gd_other;
}
if ($flipped) {
if (!\imageflip($gd, \IMG_FLIP_HORIZONTAL)) {
throw new \RuntimeException("Error while correcting flipping of '$file_path'");
}
}
}
return [ $gd, $file_path, $file_mime, $file_kind ];
}

View file

@ -1,12 +1,12 @@
<?php
namespace Vichan\Service\Media;
use Vichan\Data\ImageMetadataResult;
use Vichan\Data\ImageMetadata;
interface ImageMetadataReader {
/**
* @param string $file_path Image file path.
*/
public function getMetadata(string $file_path): ImageMetadataResult;
public function getMetadata(string $file_path): ImageMetadata;
}