Skip to content
Closed
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion lib/private/Preview/Generator.php
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,6 @@ public function getPreview(File $file, $width = -1, $height = -1, $crop = false,
throw new NotFoundException('Cannot read file');
}


$this->eventDispatcher->dispatch(
IPreview::EVENT,
new GenericEvent($file,[
Expand Down
21 changes: 19 additions & 2 deletions lib/private/Preview/GeneratorHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@
use OCP\Preview\IProvider;
use OCP\Preview\IProviderV2;

// TODO use autoloader
require_once 'JPEG.php';
use OC\Preview\OC_Image_JPEG as OCPImage_JPEG;


/**
* Very small wrapper class to make the generator fully unit testable
*/
Expand Down Expand Up @@ -63,8 +68,20 @@ public function getThumbnail(IProviderV2 $provider, File $file, $maxWidth, $maxH
* @param ISimpleFile $maxPreview
* @return IImage
*/
public function getImage(ISimpleFile $maxPreview) {
$image = new OCPImage();
public function getImage($maxPreview) {
$mimeType = $maxPreview->getMimeType();
$imagick_mode = (bool)$this->config->getSystemValue('preview_use_imagick', false);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
$imagick_mode = (bool)$this->config->getSystemValue('preview_use_imagick', false);
$imagick_mode = $this->config->getSystemValueBool('preview_use_imagick', false) && extension_loaded('imagick');

if ($imagick_mode && $mimeType !== null && extension_loaded('imagick')) {
switch ($mimeType) {
case 'image/jpeg':
$image = new OCPImage_JPEG();
break;
default:
$image = new OCPImage();
}
} else {
$image = new OCPImage();
}
$image->loadFromData($maxPreview->getContent());
return $image;
}
Expand Down
1 change: 0 additions & 1 deletion lib/private/Preview/Image.php
Original file line number Diff line number Diff line change
Expand Up @@ -59,5 +59,4 @@ public function getThumbnail(File $file, int $maxX, int $maxY): ?IImage {
}
return null;
}

}
251 changes: 249 additions & 2 deletions lib/private/Preview/JPEG.php
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
<?php
/**
* @copyright Copyright (c) 2016, ownCloud, Inc.
* @copyright Copyright (c) 2019, ownCloud, Inc.
*
* @author Olivier Paroz <github@oparoz.com>
* @author Ignacio Nunez <nachoparker@ownyourbits.com>
*
* @license AGPL-3.0
*
Expand All @@ -22,11 +22,258 @@

namespace OC\Preview;

use Imagick;
use OCP\ILogger;

class JPEG extends Image {
/**
* {@inheritDoc}
*/
public function getMimeType(): string {
return '/image\/jpeg/';
}

/**
* {@inheritDoc}
*/
public function getThumbnail($path, $maxX, $maxY, $scalingup, $fileview) {
$imagick_mode = (bool)\OC::$server->getConfig()->getSystemValue('preview_use_imagick', false);
if (!$imagick_mode || !extension_loaded('imagick')) {
return parent::getThumbnail($path, $maxX, $maxY, $scalingup, $fileview);
}

$tmpPath = $fileview->toTmpFile($path);
if (!$tmpPath) {
return false;
}

// Creates \Imagick object from the JPG file
try {
$bp = $this->getResizedPreview($tmpPath, $maxX, $maxY);
} catch (\Exception $e) {
\OC::$server->getLogger()->logException($e, [
'message' => 'File: ' . $fileview->getAbsolutePath($path) . ' Imagick says:',
'level' => ILogger::ERROR,
'app' => 'core',
]);
return false;
}

unlink($tmpPath);

//new bitmap image object
$image = new OC_Image_JPEG();
$image->loadFromData($bp->getImageBlob());
//check if image object is valid
return $image->valid() ? $image : false;
}

/**
* Returns a preview of maxX times maxY dimensions in JPG format
*
* @param string $tmpPath the location of the file to convert
* @param int $maxX
* @param int $maxY
*
* @return \Imagick
*/
private function getResizedPreview($tmpPath, $maxX, $maxY) {
$config = \OC::$server->getConfig();
$bp = new Imagick();

$bp->readImage($tmpPath);

$threads = (int)$config->getSystemValue('preview_thread_limit', 1);
if ($threads != -1) {
$bp->setResourceLimit(imagick::RESOURCETYPE_THREAD, $threads);
}
$quality = (int)$config->getSystemValue('jpeg_quality', 90);
if ($quality !== null) {
$quality = min(100, max(10, (int) $quality));
}
$bp->setImageCompressionQuality($quality);
$bp->setImageFormat('jpg');
$bp = $this->resize($bp, $maxX, $maxY);

return $bp;
}

/**
* Returns a resized \Imagick object
*
* If you want to know more on the various methods available to resize an
* image, check out this link : @link https://stackoverflow.com/questions/8517304/what-the-difference-of-sample-resample-scale-resize-adaptive-resize-thumbnail-im
*
* @param \Imagick $bp
* @param int $maxX
* @param int $maxY
*
* @return \Imagick
*/
private function resize($bp, $maxX, $maxY) {
list($previewWidth, $previewHeight) = array_values($bp->getImageGeometry());

// We only need to resize a preview which doesn't fit in the maximum dimensions
if ($previewWidth > $maxX || $previewHeight > $maxY) {
$interpolate = (bool)\OC::$server->getConfig()->getSystemValue('preview_interpolate', false);
if ($interpolate) {
$bp->resizeImage($maxX, $maxY, imagick::FILTER_CATROM, 1, true);
} else {
$bp->scaleImage($maxX, $maxY, true);
}
}

return $bp;
}
}

// TODO move to a new file
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

class OC_Image_JPEG extends \OC_Image {

/** @var string */
protected $mimeType = 'image/jpeg';

/**
* Loads an image from a string of data.
*
* @param string $str A string of image data as read from a file.
* @return bool|resource An image resource or false on error
*/
public function loadFromData($str) {
$bp = new Imagick();
try {
$bp->readImageBlob($str);
} catch (\Exception $e) {
$this->logger->error('OC_Image_JPEG->loadFromData. Error loading image.', array('app' => 'core'));
return false;
}
$threads = (int)$this->config->getSystemValue('preview_thread_limit', 1);
if ($threads != 0) {
$bp->setResourceLimit(imagick::RESOURCETYPE_THREAD, $threads);
}
$bp->setImageFormat('jpg');
$this->resource = $bp;
return $this->resource;
}

/**
* @return null|string Returns the raw image data.
*/
public function data() {
if (!$this->valid()) {
return null;
}
try {
$quality = $this->getJpegQuality();
$this->resource->setImageCompressionQuality($quality);
$data = $this->resource->getImageBlob();
} catch (\Exception $e) {
$this->logger->error('OC_Image_JPEG->data. Error getting image data.', array('app' => 'core'));
}
return $data;
}
/**
* Determine whether the object contains an image resource.
*
* @return bool
*/
public function valid() { // apparently you can't name a method 'empty'...
return $this->resource->valid();
}

/**
* Destroys the current image and resets the object
*/
public function destroy() {
if ($this->valid()) {
$this->resource->clear();
}
$this->resource = null;
}

public function __destruct() {
$this->destroy();
}

/**
* Returns the width of the image or -1 if no image is loaded.
*
* @return int
*/
public function width() {
return $this->valid() ? $this->resource->getImageWidth() : -1;
}

/**
* Returns the height of the image or -1 if no image is loaded.
*
* @return int
*/
public function height() {
return $this->valid() ? $this->resource->getImageHeight() : -1;
}

/**
* Resizes the image preserving ratio.
*
* @param integer $maxSize The maximum size of either the width or height.
* @return bool
*/
public function resize($maxSize) {
if (!$this->valid()) {
$this->logger->error(__METHOD__ . '(): No image loaded', array('app' => 'core'));
return false;
}
$widthOrig = $this->resource->getImageWidth();
$heightOrig = $this->resource->getImageHeight();
$ratioOrig = $widthOrig / $heightOrig;

if ($ratioOrig > 1) {
$newHeight = round($maxSize / $ratioOrig);
$newWidth = $maxSize;
} else {
$newWidth = round($maxSize * $ratioOrig);
$newHeight = $maxSize;
}

$this->preciseResize((int)round($newWidth), (int)round($newHeight));
return true;
}

/**
* @param int $width
* @param int $height
* @return bool
*/
public function preciseResize(int $width, int $height): bool {
if (!$this->valid()) {
$this->logger->error(__METHOD__ . '(): No image loaded', array('app' => 'core'));
return false;
}
$interpolate = (bool)$this->config->getSystemValue('preview_interpolate', false);
if ($interpolate) {
$this->resource->resizeImage($width, $height, imagick::FILTER_CATROM, 1, true);
} else {
$this->resource->scaleImage($width, $height, true);
}
return true;
}

/**
* 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 bool for success or failure
*/
public function crop(int $x, int $y, int $w, int $h): bool {
if (!$this->valid()) {
$this->logger->error(__METHOD__ . '(): No image loaded', array('app' => 'core'));
return false;
}
$this->resource->cropImage($w, $h, $x, $y);
return true;
}
}
18 changes: 14 additions & 4 deletions lib/private/legacy/image.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,9 @@ class OC_Image implements \OCP\IImage {
/** @var finfo */
private $fileInfo;
/** @var \OCP\ILogger */
private $logger;
protected $logger;
/** @var \OCP\IConfig */
private $config;
protected $config;
/** @var array */
private $exif;

Expand Down Expand Up @@ -892,7 +892,12 @@ public function preciseResize(int $width, int $height): bool {
imagesavealpha($process, true);
}

imagecopyresampled($process, $this->resource, 0, 0, 0, 0, $width, $height, $widthOrig, $heightOrig);
$interpolate = (bool)$this->config->getSystemValue('preview_interpolate', false);
if ($interpolate) {
imagecopyresampled($process, $this->resource, 0, 0, 0, 0, $width, $height, $widthOrig, $heightOrig);
} else {
imagecopyresized($process, $this->resource, 0, 0, 0, 0, $width, $height, $widthOrig, $heightOrig);
}
if ($process == false) {
$this->logger->error(__METHOD__ . '(): Error re-sampling process image', array('app' => 'core'));
imagedestroy($process);
Expand Down Expand Up @@ -950,7 +955,12 @@ public function centerCrop($size = 0) {
imagesavealpha($process, true);
}

imagecopyresampled($process, $this->resource, 0, 0, $x, $y, $targetWidth, $targetHeight, $width, $height);
$interpolate = (bool)$this->config->getSystemValue('preview_interpolate', false);
if ($interpolate) {
imagecopyresampled($process, $this->resource, 0, 0, $x, $y, $targetWidth, $targetHeight, $width, $height);
} else {
imagecopyresized($process, $this->resource, 0, 0, $x, $y, $targetWidth, $targetHeight, $width, $height);
}
if ($process == false) {
$this->logger->error('OC_Image->centerCrop, Error re-sampling process image ' . $width . 'x' . $height, array('app' => 'core'));
imagedestroy($process);
Expand Down