Compare commits
2 commits
master
...
preview-ge
Author | SHA1 | Date | |
---|---|---|---|
|
636b4b752b | ||
|
f00a59b4e2 |
5 changed files with 249 additions and 63 deletions
|
@ -93,22 +93,37 @@ class Generator {
|
|||
* @throws \InvalidArgumentException if the preview would be invalid (in case the original image is invalid)
|
||||
*/
|
||||
public function getPreview(File $file, $width = -1, $height = -1, $crop = false, $mode = IPreview::MODE_FILL, $mimeType = null) {
|
||||
$specification = [
|
||||
'width' => $width,
|
||||
'height' => $height,
|
||||
'crop' => $crop,
|
||||
'mode' => $mode,
|
||||
];
|
||||
$this->eventDispatcher->dispatch(
|
||||
IPreview::EVENT,
|
||||
new GenericEvent($file, $specification)
|
||||
);
|
||||
|
||||
// since we only ask for one preview, and the generate method return the last one it created, it returns the one we want
|
||||
return $this->generatePreviews($file, [$specification], $mimeType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates previews of a file
|
||||
*
|
||||
* @param File $file
|
||||
* @param array $specifications
|
||||
* @param string $mimeType
|
||||
* @return ISimpleFile the last preview that was generated
|
||||
* @throws NotFoundException
|
||||
* @throws \InvalidArgumentException if the preview would be invalid (in case the original image is invalid)
|
||||
*/
|
||||
public function generatePreviews(File $file, array $specifications, $mimeType = null) {
|
||||
//Make sure that we can read the file
|
||||
if (!$file->isReadable()) {
|
||||
throw new NotFoundException('Cannot read file');
|
||||
}
|
||||
|
||||
|
||||
$this->eventDispatcher->dispatch(
|
||||
IPreview::EVENT,
|
||||
new GenericEvent($file, [
|
||||
'width' => $width,
|
||||
'height' => $height,
|
||||
'crop' => $crop,
|
||||
'mode' => $mode
|
||||
])
|
||||
);
|
||||
|
||||
if ($mimeType === null) {
|
||||
$mimeType = $file->getMimeType();
|
||||
}
|
||||
|
@ -125,41 +140,57 @@ 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!');
|
||||
}
|
||||
|
||||
list($maxWidth, $maxHeight) = $this->getPreviewSize($maxPreview, $previewVersion);
|
||||
[$maxWidth, $maxHeight] = $this->getPreviewSize($maxPreview, $previewVersion);
|
||||
|
||||
// If both width and heigth are -1 we just want the max preview
|
||||
if ($width === -1 && $height === -1) {
|
||||
$width = $maxWidth;
|
||||
$height = $maxHeight;
|
||||
}
|
||||
$preview = null;
|
||||
|
||||
// Calculate the preview size
|
||||
list($width, $height) = $this->calculateSize($width, $height, $crop, $mode, $maxWidth, $maxHeight);
|
||||
foreach ($specifications as $specification) {
|
||||
$width = $specification['width'] ?? -1;
|
||||
$height = $specification['height'] ?? -1;
|
||||
$crop = $specification['crop'] ?? false;
|
||||
$mode = $specification['mode'] ?? IPreview::MODE_FILL;
|
||||
|
||||
// No need to generate a preview that is just the max preview
|
||||
if ($width === $maxWidth && $height === $maxHeight) {
|
||||
return $maxPreview;
|
||||
}
|
||||
|
||||
// Try to get a cached preview. Else generate (and store) one
|
||||
try {
|
||||
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 both width and heigth are -1 we just want the max preview
|
||||
if ($width === -1 && $height === -1) {
|
||||
$width = $maxWidth;
|
||||
$height = $maxHeight;
|
||||
}
|
||||
} catch (\InvalidArgumentException $e) {
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
if ($preview->getSize() === 0) {
|
||||
$preview->delete();
|
||||
throw new NotFoundException('Cached preview size 0, invalid!');
|
||||
// Calculate the preview size
|
||||
[$width, $height] = $this->calculateSize($width, $height, $crop, $mode, $maxWidth, $maxHeight);
|
||||
|
||||
// No need to generate a preview that is just the max preview
|
||||
if ($width === $maxWidth && $height === $maxHeight) {
|
||||
// ensure correct return value if this was the last one
|
||||
$preview = $maxPreview;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Try to get a cached preview. Else generate (and store) one
|
||||
try {
|
||||
try {
|
||||
$preview = $this->getCachedPreview($previewFolder, $width, $height, $crop, $maxPreview->getMimeType(), $previewVersion);
|
||||
} catch (NotFoundException $e) {
|
||||
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();
|
||||
}
|
||||
|
||||
if ($preview->getSize() === 0) {
|
||||
$preview->delete();
|
||||
throw new NotFoundException('Cached preview size 0, invalid!');
|
||||
}
|
||||
}
|
||||
|
||||
return $preview;
|
||||
|
@ -362,9 +393,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');
|
||||
}
|
||||
|
@ -382,13 +412,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));
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -151,6 +151,22 @@ class PreviewManager implements IPreview {
|
|||
return !empty($this->providers);
|
||||
}
|
||||
|
||||
private function getGenerator(): Generator {
|
||||
if ($this->generator === null) {
|
||||
$this->generator = new Generator(
|
||||
$this->config,
|
||||
$this,
|
||||
$this->appData,
|
||||
new GeneratorHelper(
|
||||
$this->rootFolder,
|
||||
$this->config
|
||||
),
|
||||
$this->eventDispatcher
|
||||
);
|
||||
}
|
||||
return $this->generator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a preview of a file
|
||||
*
|
||||
|
@ -169,20 +185,22 @@ class PreviewManager implements IPreview {
|
|||
* @since 11.0.0 - \InvalidArgumentException was added in 12.0.0
|
||||
*/
|
||||
public function getPreview(File $file, $width = -1, $height = -1, $crop = false, $mode = IPreview::MODE_FILL, $mimeType = null) {
|
||||
if ($this->generator === null) {
|
||||
$this->generator = new Generator(
|
||||
$this->config,
|
||||
$this,
|
||||
$this->appData,
|
||||
new GeneratorHelper(
|
||||
$this->rootFolder,
|
||||
$this->config
|
||||
),
|
||||
$this->eventDispatcher
|
||||
);
|
||||
}
|
||||
return $this->getGenerator()->getPreview($file, $width, $height, $crop, $mode, $mimeType);
|
||||
}
|
||||
|
||||
return $this->generator->getPreview($file, $width, $height, $crop, $mode, $mimeType);
|
||||
/**
|
||||
* Generates previews of a file
|
||||
*
|
||||
* @param File $file
|
||||
* @param array $specifications
|
||||
* @param string $mimeType
|
||||
* @return ISimpleFile the last preview that was generated
|
||||
* @throws NotFoundException
|
||||
* @throws \InvalidArgumentException if the preview would be invalid (in case the original image is invalid)
|
||||
* @since 19.0.0
|
||||
*/
|
||||
public function generatePreviews(File $file, array $specifications, $mimeType = null) {
|
||||
return $this->getGenerator()->generatePreviews($file, $specifications, $mimeType);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -38,6 +38,8 @@
|
|||
*
|
||||
*/
|
||||
|
||||
use OCP\IImage;
|
||||
|
||||
/**
|
||||
* Class for basic image manipulation
|
||||
*/
|
||||
|
@ -844,6 +846,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', array('app' => 'core'));
|
||||
return false;
|
||||
|
@ -860,8 +873,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));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -870,6 +882,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', array('app' => 'core'));
|
||||
return false;
|
||||
|
@ -895,9 +920,7 @@ class OC_Image implements \OCP\IImage {
|
|||
imagedestroy($process);
|
||||
return false;
|
||||
}
|
||||
imagedestroy($this->resource);
|
||||
$this->resource = $process;
|
||||
return true;
|
||||
return $process;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -968,6 +991,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', array('app' => 'core'));
|
||||
return false;
|
||||
|
@ -992,9 +1031,7 @@ class OC_Image implements \OCP\IImage {
|
|||
imagedestroy($process);
|
||||
return false;
|
||||
}
|
||||
imagedestroy($this->resource);
|
||||
$this->resource = $process;
|
||||
return true;
|
||||
return $process;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1044,6 +1081,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
|
||||
*/
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -115,4 +115,17 @@ interface IPreview {
|
|||
* @since 8.0.0
|
||||
*/
|
||||
public function isAvailable(\OCP\Files\FileInfo $file);
|
||||
|
||||
/**
|
||||
* Generates previews of a file
|
||||
*
|
||||
* @param File $file
|
||||
* @param array $specifications
|
||||
* @param string $mimeType
|
||||
* @return ISimpleFile the last preview that was generated
|
||||
* @throws NotFoundException
|
||||
* @throws \InvalidArgumentException if the preview would be invalid (in case the original image is invalid)
|
||||
* @since 19.0.0
|
||||
*/
|
||||
public function generatePreviews(File $file, array $specifications, $mimeType = null);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue