From 70d6d398be9e9fb9fa2645ecc6e9141391aec046 Mon Sep 17 00:00:00 2001 From: the-djmaze <> Date: Wed, 23 Mar 2022 17:09:25 +0100 Subject: [PATCH] Speedup raw view of images by changing detect_image_exif_orientation default to Off and loadImage() uses stream instead of string. --- .../app/libraries/RainLoop/Actions/Raw.php | 141 ++++++++---------- .../libraries/RainLoop/Config/Application.php | 2 +- .../0.0.0/app/libraries/snappymail/image.php | 2 + .../app/libraries/snappymail/image/gd2.php | 6 + .../libraries/snappymail/image/gmagick.php | 14 ++ .../libraries/snappymail/image/imagick.php | 10 ++ 6 files changed, 94 insertions(+), 81 deletions(-) diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Raw.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Raw.php index e2da87d72..69d5aa62c 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Raw.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Raw.php @@ -139,7 +139,7 @@ trait Raw $sFileNameIn = isset($aValues['FileName']) ? (string) $aValues['FileName'] : ''; $sFileHashIn = isset($aValues['FileHash']) ? (string) $aValues['FileHash'] : ''; - $bDetectImageOrientation = !!$this->Config()->Get('labs', 'detect_image_exif_orientation', true); + $bDetectImageOrientation = !!$this->Config()->Get('labs', 'detect_image_exif_orientation', false); if (!empty($sFileHashIn)) { @@ -217,17 +217,33 @@ trait Raw $self->cacheByKey($sRawKey); - $sLoadedData = null; if (!$bDownload) { if ($bThumbnail) { try { - $oImage = static::loadImage(\stream_get_contents($rResource), $bDetectImageOrientation, 60); + $oImage = static::loadImage($rResource, $bDetectImageOrientation, 60); \header('Content-Disposition: inline; '. \trim(\MailSo\Base\Utils::EncodeHeaderUtf8AttributeValue('filename', $sFileNameOut.'_thumb60x60.png'))); $oImage->show('png'); +// $oImage->show('webp'); // Little Britain: "Safari says NO" + } + catch (\Throwable $oException) + { + $self->Logger()->WriteExceptionShort($oException); + } + exit; + } + else if ($bDetectImageOrientation && + \in_array($sContentTypeOut, array('image/png', 'image/jpeg', 'image/jpg', 'image/webp'))) + { + try + { + $oImage = static::loadImage($rResource, $bDetectImageOrientation); + \header('Content-Disposition: inline; '. + \trim(\MailSo\Base\Utils::EncodeHeaderUtf8AttributeValue('filename', $sFileNameOut))); + $oImage->show(); // $oImage->show('webp'); // Little Britain: "Safari says NO" exit; } @@ -235,108 +251,73 @@ trait Raw { $self->Logger()->WriteExceptionShort($oException); } - } - else if ($bDetectImageOrientation && - \in_array($sContentTypeOut, array('image/png', 'image/jpeg', 'image/jpg', 'image/webp'))) - { - try - { - $sLoadedData = \stream_get_contents($rResource); - $oImage = static::loadImage($sLoadedData, $bDetectImageOrientation); - \header('Content-Disposition: inline; '. - \trim(\MailSo\Base\Utils::EncodeHeaderUtf8AttributeValue('filename', $sFileNameOut))); - $oImage->show(); - } - catch (\Throwable $oException) - { - $self->Logger()->WriteExceptionShort($oException); - } - } - else - { - $sLoadedData = \stream_get_contents($rResource); + exit; } } - if ($bDownload || $sLoadedData) - { - if (!headers_sent()) { - \header('Content-Type: '.$sContentTypeOut); - \header('Content-Disposition: '.($bDownload ? 'attachment' : 'inline').'; '. - \trim(\MailSo\Base\Utils::EncodeHeaderUtf8AttributeValue('filename', $sFileNameOut))); + if (!\headers_sent()) { + \header('Content-Type: '.$sContentTypeOut); + \header('Content-Disposition: '.($bDownload ? 'attachment' : 'inline').'; '. + \trim(\MailSo\Base\Utils::EncodeHeaderUtf8AttributeValue('filename', $sFileNameOut))); - \header('Accept-Ranges: bytes'); - \header('Content-Transfer-Encoding: binary'); - } + \header('Accept-Ranges: bytes'); + \header('Content-Transfer-Encoding: binary'); + } - if ($bIsRangeRequest && !$sLoadedData) - { - $sLoadedData = \stream_get_contents($rResource); - } + $sLoadedData = null; + if ($bIsRangeRequest || !$bDownload) { + $sLoadedData = \stream_get_contents($rResource); + } - \MailSo\Base\Utils::ResetTimeLimit(); + \MailSo\Base\Utils::ResetTimeLimit(); - if ($sLoadedData) - { - if ($bIsRangeRequest && (\strlen($sRangeStart) || \strlen($sRangeEnd))) - { - $iFullContentLength = \strlen($sLoadedData); + if ($sLoadedData) { + if ($bIsRangeRequest && (\strlen($sRangeStart) || \strlen($sRangeEnd))) { + $iFullContentLength = \strlen($sLoadedData); - \MailSo\Base\Http::StatusHeader(206); + \MailSo\Base\Http::StatusHeader(206); - $iRangeStart = (int) $sRangeStart; - $iRangeEnd = (int) $sRangeEnd; + $iRangeStart = (int) $sRangeStart; + $iRangeEnd = (int) $sRangeEnd; - if ('' === $sRangeEnd) - { + if ('' === $sRangeEnd) { + $sLoadedData = 0 < $iRangeStart ? \substr($sLoadedData, $iRangeStart) : $sLoadedData; + } else { + if ($iRangeStart < $iRangeEnd) { + $sLoadedData = \substr($sLoadedData, $iRangeStart, $iRangeEnd - $iRangeStart); + } else { $sLoadedData = 0 < $iRangeStart ? \substr($sLoadedData, $iRangeStart) : $sLoadedData; } - else - { - if ($iRangeStart < $iRangeEnd) - { - $sLoadedData = \substr($sLoadedData, $iRangeStart, $iRangeEnd - $iRangeStart); - } - else - { - $sLoadedData = 0 < $iRangeStart ? \substr($sLoadedData, $iRangeStart) : $sLoadedData; - } - } - - $iContentLength = \strlen($sLoadedData); - - if (0 < $iContentLength) - { - \header('Content-Length: '.$iContentLength); - \header('Content-Range: bytes '.$sRangeStart.'-'.(0 < $iRangeEnd ? $iRangeEnd : $iFullContentLength - 1).'/'.$iFullContentLength); - } - - echo $sLoadedData; - } - else - { - echo $sLoadedData; } - unset($sLoadedData); - } - else - { - \MailSo\Base\Utils::FpassthruWithTimeLimitReset($rResource); + $iContentLength = \strlen($sLoadedData); + + if (0 < $iContentLength) { + \header('Content-Length: '.$iContentLength); + \header('Content-Range: bytes '.$sRangeStart.'-'.(0 < $iRangeEnd ? $iRangeEnd : $iFullContentLength - 1).'/'.$iFullContentLength); + } + } else { + \header('Content-Length: '.\strlen($sLoadedData)); } + + echo $sLoadedData; + + unset($sLoadedData); + } else { + \MailSo\Base\Utils::FpassthruWithTimeLimitReset($rResource); } } }, $sFolder, $iUid, $sMimeIndex); } - private static function loadImage(string $data, bool $bDetectImageOrientation = true, int $iThumbnailBoxSize = 0) : \SnappyMail\Image + private static function loadImage($resource, bool $bDetectImageOrientation = true, int $iThumbnailBoxSize = 0) : \SnappyMail\Image { if (\extension_loaded('gmagick')) { $handler = 'gmagick'; } else if (\extension_loaded('imagick')) { $handler = 'imagick'; } else if (\extension_loaded('gd')) { $handler = 'gd2'; } else { return null; } - $handler = 'SnappyMail\\Image\\'.$handler.'::createFromString'; - $oImage = $handler($data); + $handler = 'SnappyMail\\Image\\'.$handler.'::createFromStream'; + $oImage = $handler($resource); // rotateImageByOrientation if ($bDetectImageOrientation) { diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Config/Application.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Config/Application.php index e9557d037..ddd7e9fb0 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Config/Application.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Config/Application.php @@ -381,7 +381,7 @@ Enables caching in the system'), 'fast_cache_redis_host' => array('127.0.0.1'), 'fast_cache_redis_port' => array(6379), 'use_local_proxy_for_external_images' => array(true), - 'detect_image_exif_orientation' => array(true), + 'detect_image_exif_orientation' => array(false), 'cookie_default_path' => array(''), 'cookie_default_secure' => array(false), 'check_new_messages' => array(true), diff --git a/snappymail/v/0.0.0/app/libraries/snappymail/image.php b/snappymail/v/0.0.0/app/libraries/snappymail/image.php index 2469de1df..b9991964d 100644 --- a/snappymail/v/0.0.0/app/libraries/snappymail/image.php +++ b/snappymail/v/0.0.0/app/libraries/snappymail/image.php @@ -6,6 +6,8 @@ interface Image { public static function createFromString(string &$data); + public static function createFromStream(/*resource*/$fp); + public function getOrientation() : int; public function rotate(float $degrees) : bool; diff --git a/snappymail/v/0.0.0/app/libraries/snappymail/image/gd2.php b/snappymail/v/0.0.0/app/libraries/snappymail/image/gd2.php index cbbb43a25..59a955d34 100644 --- a/snappymail/v/0.0.0/app/libraries/snappymail/image/gd2.php +++ b/snappymail/v/0.0.0/app/libraries/snappymail/image/gd2.php @@ -55,6 +55,12 @@ class GD2 implements \SnappyMail\Image return $gd2; } + public static function createFromStream($fp) + { + $data = \stream_get_contents($fp); + return static::createFromString($data); + } + public function getOrientation() : int { return $this->orientation; diff --git a/snappymail/v/0.0.0/app/libraries/snappymail/image/gmagick.php b/snappymail/v/0.0.0/app/libraries/snappymail/image/gmagick.php index e10cc73d9..74db6357b 100644 --- a/snappymail/v/0.0.0/app/libraries/snappymail/image/gmagick.php +++ b/snappymail/v/0.0.0/app/libraries/snappymail/image/gmagick.php @@ -28,6 +28,20 @@ class GMagick extends \Gmagick implements \SnappyMail\Image return $gmagick; } + public static function createFromStream($fp) + { + if (!\method_exists($gmagick, 'getImageOrientation')) { + $data = \stream_get_contents($fp); + return static::createFromString($data); + } + $gmagick = new static(); + if (!$gmagick->readimagefile($fp)) { + throw new \InvalidArgumentException('Failed to load image'); + } + $gmagick->orientation = $gmagick->getImageOrientation(); + return $gmagick; + } + public function getOrientation() : int { return $this->orientation; diff --git a/snappymail/v/0.0.0/app/libraries/snappymail/image/imagick.php b/snappymail/v/0.0.0/app/libraries/snappymail/image/imagick.php index 8c67e6483..743e215cd 100644 --- a/snappymail/v/0.0.0/app/libraries/snappymail/image/imagick.php +++ b/snappymail/v/0.0.0/app/libraries/snappymail/image/imagick.php @@ -21,6 +21,16 @@ class IMagick extends \Imagick implements \SnappyMail\Image return $imagick; } + public static function createFromStream($fp) + { + $imagick = new static(); + if (!$imagick->readImageFile($fp)) { + throw new \InvalidArgumentException('Failed to load image'); + } + $imagick->setImageMatte(true); + return $imagick; + } + public function getOrientation() : int { return $this->getImageOrientation();