Initial commit
This commit is contained in:
commit
08a8a71c55
631 changed files with 139902 additions and 0 deletions
143
public/kirby/src/Image/Darkroom/GdLib.php
Normal file
143
public/kirby/src/Image/Darkroom/GdLib.php
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
<?php
|
||||
|
||||
namespace Kirby\Image\Darkroom;
|
||||
|
||||
use claviska\SimpleImage;
|
||||
use Kirby\Filesystem\Mime;
|
||||
use Kirby\Image\Darkroom;
|
||||
use Kirby\Image\Focus;
|
||||
|
||||
/**
|
||||
* GdLib
|
||||
*
|
||||
* @package Kirby Image
|
||||
* @author Bastian Allgeier <bastian@getkirby.com>
|
||||
* @link https://getkirby.com
|
||||
* @copyright Bastian Allgeier
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
*/
|
||||
class GdLib extends Darkroom
|
||||
{
|
||||
/**
|
||||
* Processes the image with the SimpleImage library
|
||||
*/
|
||||
public function process(string $file, array $options = []): array
|
||||
{
|
||||
$options = $this->preprocess($file, $options);
|
||||
$mime = $this->mime($options);
|
||||
|
||||
$image = new SimpleImage();
|
||||
$image->fromFile($file);
|
||||
|
||||
$image = $this->resize($image, $options);
|
||||
$image = $this->autoOrient($image, $options);
|
||||
$image = $this->blur($image, $options);
|
||||
$image = $this->grayscale($image, $options);
|
||||
$image = $this->sharpen($image, $options);
|
||||
|
||||
$image->toFile($file, $mime, $options);
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Activates the autoOrient option in SimpleImage
|
||||
* unless this is deactivated
|
||||
*/
|
||||
protected function autoOrient(SimpleImage $image, array $options): SimpleImage
|
||||
{
|
||||
if ($options['autoOrient'] === false) {
|
||||
return $image;
|
||||
}
|
||||
|
||||
return $image->autoOrient();
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper around SimpleImage's resize and crop methods
|
||||
*/
|
||||
protected function resize(SimpleImage $image, array $options): SimpleImage
|
||||
{
|
||||
// just resize, no crop
|
||||
if ($options['crop'] === false) {
|
||||
return $image->resize($options['width'], $options['height']);
|
||||
}
|
||||
|
||||
// crop based on focus point
|
||||
if (Focus::isFocalPoint($options['crop']) === true) {
|
||||
// get crop coords for focal point:
|
||||
// if image needs to be cropped, crop before resizing
|
||||
if ($focus = Focus::coords(
|
||||
$options['crop'],
|
||||
$options['sourceWidth'],
|
||||
$options['sourceHeight'],
|
||||
$options['width'],
|
||||
$options['height']
|
||||
)) {
|
||||
$image->crop(
|
||||
$focus['x1'],
|
||||
$focus['y1'],
|
||||
$focus['x2'],
|
||||
$focus['y2']
|
||||
);
|
||||
}
|
||||
|
||||
return $image->thumbnail($options['width'], $options['height']);
|
||||
}
|
||||
|
||||
// normal crop with crop anchor
|
||||
return $image->thumbnail(
|
||||
$options['width'],
|
||||
$options['height'] ?? $options['width'],
|
||||
$options['crop']
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the correct blur settings for SimpleImage
|
||||
*/
|
||||
protected function blur(SimpleImage $image, array $options): SimpleImage
|
||||
{
|
||||
if ($options['blur'] === false) {
|
||||
return $image;
|
||||
}
|
||||
|
||||
return $image->blur('gaussian', (int)$options['blur']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies grayscale conversion if activated in the options.
|
||||
*/
|
||||
protected function grayscale(SimpleImage $image, array $options): SimpleImage
|
||||
{
|
||||
if ($options['grayscale'] === false) {
|
||||
return $image;
|
||||
}
|
||||
|
||||
return $image->desaturate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies sharpening if activated in the options.
|
||||
*/
|
||||
protected function sharpen(SimpleImage $image, array $options): SimpleImage
|
||||
{
|
||||
if (is_int($options['sharpen']) === false) {
|
||||
return $image;
|
||||
}
|
||||
|
||||
return $image->sharpen($options['sharpen']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns mime type based on `format` option
|
||||
*/
|
||||
protected function mime(array $options): string|null
|
||||
{
|
||||
if ($options['format'] === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return Mime::fromExtension($options['format']);
|
||||
}
|
||||
}
|
||||
246
public/kirby/src/Image/Darkroom/ImageMagick.php
Normal file
246
public/kirby/src/Image/Darkroom/ImageMagick.php
Normal file
|
|
@ -0,0 +1,246 @@
|
|||
<?php
|
||||
|
||||
namespace Kirby\Image\Darkroom;
|
||||
|
||||
use Exception;
|
||||
use Kirby\Filesystem\F;
|
||||
use Kirby\Image\Darkroom;
|
||||
use Kirby\Image\Focus;
|
||||
|
||||
/**
|
||||
* ImageMagick
|
||||
*
|
||||
* @package Kirby Image
|
||||
* @author Bastian Allgeier <bastian@getkirby.com>
|
||||
* @link https://getkirby.com
|
||||
* @copyright Bastian Allgeier
|
||||
* @license https://opensource.org/licenses/MIT
|
||||
*/
|
||||
class ImageMagick extends Darkroom
|
||||
{
|
||||
/**
|
||||
* Activates imagemagick's auto-orient feature unless
|
||||
* it is deactivated via the options
|
||||
*/
|
||||
protected function autoOrient(string $file, array $options): string|null
|
||||
{
|
||||
if ($options['autoOrient'] === true) {
|
||||
return '-auto-orient';
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the blur settings
|
||||
*/
|
||||
protected function blur(string $file, array $options): string|null
|
||||
{
|
||||
if ($options['blur'] !== false) {
|
||||
return '-blur ' . escapeshellarg('0x' . $options['blur']);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Keep animated gifs
|
||||
*/
|
||||
protected function coalesce(string $file, array $options): string|null
|
||||
{
|
||||
if (F::extension($file) === 'gif') {
|
||||
return '-coalesce';
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the convert command with the right path to the binary file
|
||||
*/
|
||||
protected function convert(string $file, array $options): string
|
||||
{
|
||||
$command = escapeshellarg($options['bin']);
|
||||
|
||||
// default is limiting to single-threading to keep CPU usage sane
|
||||
$command .= ' -limit thread ' . escapeshellarg($options['threads']);
|
||||
|
||||
// append input file
|
||||
return $command . ' ' . escapeshellarg($file);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns additional default parameters for imagemagick
|
||||
*/
|
||||
protected function defaults(): array
|
||||
{
|
||||
return parent::defaults() + [
|
||||
'bin' => 'convert',
|
||||
'interlace' => false,
|
||||
'threads' => 1,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the correct settings for grayscale images
|
||||
*/
|
||||
protected function grayscale(string $file, array $options): string|null
|
||||
{
|
||||
if ($options['grayscale'] === true) {
|
||||
return '-colorspace gray';
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies sharpening if activated in the options.
|
||||
*/
|
||||
protected function sharpen(string $file, array $options): string|null
|
||||
{
|
||||
if (is_int($options['sharpen']) === false) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$amount = max(1, min(100, $options['sharpen'])) / 100;
|
||||
return '-sharpen ' . escapeshellarg('0x' . $amount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the correct settings for interlaced JPEGs if
|
||||
* activated via options
|
||||
*/
|
||||
protected function interlace(string $file, array $options): string|null
|
||||
{
|
||||
if ($options['interlace'] === true) {
|
||||
return '-interlace line';
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and runs the full imagemagick command
|
||||
* to process the image
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function process(string $file, array $options = []): array
|
||||
{
|
||||
$options = $this->preprocess($file, $options);
|
||||
$command = [];
|
||||
|
||||
$command[] = $this->convert($file, $options);
|
||||
$command[] = $this->strip($file, $options);
|
||||
$command[] = $this->interlace($file, $options);
|
||||
$command[] = $this->coalesce($file, $options);
|
||||
$command[] = $this->grayscale($file, $options);
|
||||
$command[] = $this->autoOrient($file, $options);
|
||||
$command[] = $this->resize($file, $options);
|
||||
$command[] = $this->quality($file, $options);
|
||||
$command[] = $this->blur($file, $options);
|
||||
$command[] = $this->sharpen($file, $options);
|
||||
$command[] = $this->save($file, $options);
|
||||
|
||||
// remove all null values and join the parts
|
||||
$command = implode(' ', array_filter($command));
|
||||
|
||||
// try to execute the command
|
||||
exec($command, $output, $return);
|
||||
|
||||
// log broken commands
|
||||
if ($return !== 0) {
|
||||
throw new Exception('The imagemagick convert command could not be executed: ' . $command);
|
||||
}
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the correct JPEG compression quality settings
|
||||
*/
|
||||
protected function quality(string $file, array $options): string
|
||||
{
|
||||
return '-quality ' . escapeshellarg($options['quality']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the correct options to crop or resize the image
|
||||
* and translates the crop positions for imagemagick
|
||||
*/
|
||||
protected function resize(string $file, array $options): string
|
||||
{
|
||||
// simple resize
|
||||
if ($options['crop'] === false) {
|
||||
return '-thumbnail ' . escapeshellarg(sprintf('%sx%s!', $options['width'], $options['height']));
|
||||
}
|
||||
|
||||
// crop based on focus point
|
||||
if (Focus::isFocalPoint($options['crop']) === true) {
|
||||
if ($focus = Focus::coords(
|
||||
$options['crop'],
|
||||
$options['sourceWidth'],
|
||||
$options['sourceHeight'],
|
||||
$options['width'],
|
||||
$options['height']
|
||||
)) {
|
||||
return sprintf(
|
||||
'-crop %sx%s+%s+%s -resize %sx%s^',
|
||||
$focus['width'],
|
||||
$focus['height'],
|
||||
$focus['x1'],
|
||||
$focus['y1'],
|
||||
$options['width'],
|
||||
$options['height']
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// translate the gravity option into something imagemagick understands
|
||||
$gravity = match ($options['crop'] ?? null) {
|
||||
'top left' => 'NorthWest',
|
||||
'top' => 'North',
|
||||
'top right' => 'NorthEast',
|
||||
'left' => 'West',
|
||||
'right' => 'East',
|
||||
'bottom left' => 'SouthWest',
|
||||
'bottom' => 'South',
|
||||
'bottom right' => 'SouthEast',
|
||||
default => 'Center'
|
||||
};
|
||||
|
||||
$command = '-thumbnail ' . escapeshellarg(sprintf('%sx%s^', $options['width'], $options['height']));
|
||||
$command .= ' -gravity ' . escapeshellarg($gravity);
|
||||
$command .= ' -crop ' . escapeshellarg(sprintf('%sx%s+0+0', $options['width'], $options['height']));
|
||||
|
||||
return $command;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the option for the output file
|
||||
*/
|
||||
protected function save(string $file, array $options): string
|
||||
{
|
||||
if ($options['format'] !== null) {
|
||||
$file = pathinfo($file, PATHINFO_DIRNAME) . '/' . pathinfo($file, PATHINFO_FILENAME) . '.' . $options['format'];
|
||||
}
|
||||
|
||||
return escapeshellarg($file);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all metadata from the image
|
||||
*/
|
||||
protected function strip(string $file, array $options): string
|
||||
{
|
||||
if (F::extension($file) === 'png') {
|
||||
// ImageMagick does not support keeping ICC profiles while
|
||||
// stripping other privacy- and security-related information,
|
||||
// such as GPS data; so discard all color profiles for PNG files
|
||||
// (tested with ImageMagick 7.0.11-14 Q16 x86_64 2021-05-31)
|
||||
return '-strip';
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue