diff --git a/lib/private/Preview/Generator.php b/lib/private/Preview/Generator.php index 073001d6788..eedf80d8522 100644 --- a/lib/private/Preview/Generator.php +++ b/lib/private/Preview/Generator.php @@ -138,6 +138,7 @@ class Generator { // Get the max preview and infer the max preview sizes from that $maxPreview = $this->getMaxPreview($previewFolder, $file, $mimeType, $previewVersion); + $maxPreviewImage = null; // only load the image when we need it if ($maxPreview->getSize() === 0) { $maxPreview->delete(); throw new NotFoundException('Max preview size 0, invalid!'); @@ -174,7 +175,11 @@ class Generator { try { $preview = $this->getCachedPreview($previewFolder, $width, $height, $crop, $maxPreview->getMimeType(), $previewVersion); } catch (NotFoundException $e) { - $preview = $this->generatePreview($previewFolder, $maxPreview, $width, $height, $crop, $maxWidth, $maxHeight, $previewVersion); + if ($maxPreviewImage === null) { + $maxPreviewImage = $this->helper->getImage($maxPreview); + } + + $preview = $this->generatePreview($previewFolder, $maxPreviewImage, $width, $height, $crop, $maxWidth, $maxHeight, $previewVersion); } } catch (\InvalidArgumentException $e) { throw new NotFoundException(); @@ -386,9 +391,8 @@ class Generator { * @throws NotFoundException * @throws \InvalidArgumentException if the preview would be invalid (in case the original image is invalid) */ - private function generatePreview(ISimpleFolder $previewFolder, ISimpleFile $maxPreview, $width, $height, $crop, $maxWidth, $maxHeight, $prefix) { - $preview = $this->helper->getImage($maxPreview); - + private function generatePreview(ISimpleFolder $previewFolder, IImage $maxPreview, $width, $height, $crop, $maxWidth, $maxHeight, $prefix) { + $preview = $maxPreview; if (!$preview->valid()) { throw new \InvalidArgumentException('Failed to generate preview, failed to load image'); } @@ -406,13 +410,13 @@ class Generator { $scaleH = $maxHeight / $widthR; $scaleW = $width; } - $preview->preciseResize((int)round($scaleW), (int)round($scaleH)); + $preview = $preview->preciseResizeCopy((int)round($scaleW), (int)round($scaleH)); } $cropX = (int)floor(abs($width - $preview->width()) * 0.5); $cropY = (int)floor(abs($height - $preview->height()) * 0.5); - $preview->crop($cropX, $cropY, $width, $height); + $preview = $preview->cropCopy($cropX, $cropY, $width, $height); } else { - $preview->resize(max($width, $height)); + $preview = $maxPreview->resizeCopy(max($width, $height)); } diff --git a/lib/private/legacy/OC_Image.php b/lib/private/legacy/OC_Image.php index 7d23ece7ffa..73d380eb99a 100644 --- a/lib/private/legacy/OC_Image.php +++ b/lib/private/legacy/OC_Image.php @@ -39,6 +39,8 @@ * */ +use OCP\IImage; + /** * Class for basic image manipulation */ @@ -845,6 +847,17 @@ class OC_Image implements \OCP\IImage { * @return bool */ public function resize($maxSize) { + $result = $this->resizeNew($maxSize); + imagedestroy($this->resource); + $this->resource = $result; + return is_resource($result); + } + + /** + * @param $maxSize + * @return resource | bool + */ + private function resizeNew($maxSize) { if (!$this->valid()) { $this->logger->error(__METHOD__ . '(): No image loaded', ['app' => 'core']); return false; @@ -861,8 +874,7 @@ class OC_Image implements \OCP\IImage { $newHeight = $maxSize; } - $this->preciseResize((int)round($newWidth), (int)round($newHeight)); - return true; + return $this->preciseResizeNew((int)round($newWidth), (int)round($newHeight)); } /** @@ -871,6 +883,19 @@ class OC_Image implements \OCP\IImage { * @return bool */ public function preciseResize(int $width, int $height): bool { + $result = $this->preciseResizeNew($width, $height); + imagedestroy($this->resource); + $this->resource = $result; + return is_resource($result); + } + + + /** + * @param int $width + * @param int $height + * @return resource | bool + */ + public function preciseResizeNew(int $width, int $height) { if (!$this->valid()) { $this->logger->error(__METHOD__ . '(): No image loaded', ['app' => 'core']); return false; @@ -896,9 +921,7 @@ class OC_Image implements \OCP\IImage { imagedestroy($process); return false; } - imagedestroy($this->resource); - $this->resource = $process; - return true; + return $process; } /** @@ -969,6 +992,22 @@ class OC_Image implements \OCP\IImage { * @return bool for success or failure */ public function crop(int $x, int $y, int $w, int $h): bool { + $result = $this->cropNew($x, $y, $w, $h); + imagedestroy($this->resource); + $this->resource = $result; + return is_resource($result); + } + + /** + * Crops the image from point $x$y with dimension $wx$h. + * + * @param int $x Horizontal position + * @param int $y Vertical position + * @param int $w Width + * @param int $h Height + * @return resource | bool + */ + public function cropNew(int $x, int $y, int $w, int $h) { if (!$this->valid()) { $this->logger->error(__METHOD__ . '(): No image loaded', ['app' => 'core']); return false; @@ -993,9 +1032,7 @@ class OC_Image implements \OCP\IImage { imagedestroy($process); return false; } - imagedestroy($this->resource); - $this->resource = $process; - return true; + return $process; } /** @@ -1045,6 +1082,55 @@ class OC_Image implements \OCP\IImage { return false; } + public function copy(): IImage { + $image = new OC_Image(null, $this->logger, $this->config); + $image->resource = imagecreatetruecolor($this->width(), $this->height()); + imagecopy( + $image->resource(), + $this->resource(), + 0, + 0, + 0, + 0, + $this->width(), + $this->height() + ); + + return $image; + } + + public function cropCopy(int $x, int $y, int $w, int $h): IImage { + $image = new OC_Image(null, $this->logger, $this->config); + $image->resource = $this->cropNew($x, $y, $w, $h); + + return $image; + } + + public function preciseResizeCopy(int $width, int $height): IImage { + $image = new OC_Image(null, $this->logger, $this->config); + $image->resource = $this->preciseResizeNew($width, $height); + + return $image; + } + + public function resizeCopy(int $maxSize): IImage { + $image = new OC_Image(null, $this->logger, $this->config); + $image->resource = $this->resizeNew($maxSize); + + return $image; + } + + + /** + * Resizes the image preserving ratio, returning a new copy + * + * @param integer $maxSize The maximum size of either the width or height. + * @return bool + */ + public function copyResize($maxSize): IImage { + + } + /** * Destroys the current image and resets the object */ diff --git a/lib/public/IImage.php b/lib/public/IImage.php index 67db6b097ef..6e6c28609d8 100644 --- a/lib/public/IImage.php +++ b/lib/public/IImage.php @@ -190,4 +190,43 @@ interface IImage { * @since 8.1.0 */ public function scaleDownToFit($maxWidth, $maxHeight); + + /** + * create a copy of this image + * + * @return IImage + * @since 19.0.0 + */ + public function copy(): IImage; + + /** + * create a new cropped copy of this image + * + * @param int $x Horizontal position + * @param int $y Vertical position + * @param int $w Width + * @param int $h Height + * @return IImage + * @since 19.0.0 + */ + public function cropCopy(int $x, int $y, int $w, int $h): IImage; + + /** + * create a new resized copy of this image + * + * @param int $width + * @param int $height + * @return IImage + * @since 19.0.0 + */ + public function preciseResizeCopy(int $width, int $height): IImage; + + /** + * create a new resized copy of this image + * + * @param integer $maxSize The maximum size of either the width or height. + * @return IImage + * @since 19.0.0 + */ + public function resizeCopy(int $maxSize): IImage; }