parent
6d4398bd87
commit
678c46b15e
|
@ -69,6 +69,7 @@ return [
|
||||||
['name' => 'Image#info', 'url' => '/api/image/info/{id}', 'verb' => 'GET'],
|
['name' => 'Image#info', 'url' => '/api/image/info/{id}', 'verb' => 'GET'],
|
||||||
['name' => 'Image#setExif', 'url' => '/api/image/set-exif/{id}', 'verb' => 'PATCH'],
|
['name' => 'Image#setExif', 'url' => '/api/image/set-exif/{id}', 'verb' => 'PATCH'],
|
||||||
['name' => 'Image#decodable', 'url' => '/api/image/decodable/{id}', 'verb' => 'GET'],
|
['name' => 'Image#decodable', 'url' => '/api/image/decodable/{id}', 'verb' => 'GET'],
|
||||||
|
['name' => 'Image#editImage', 'url' => '/api/image/edit/{id}', 'verb' => 'PUT'],
|
||||||
|
|
||||||
['name' => 'Video#transcode', 'url' => '/api/video/transcode/{client}/{fileid}/{profile}', 'verb' => 'GET'],
|
['name' => 'Video#transcode', 'url' => '/api/video/transcode/{client}/{fileid}/{profile}', 'verb' => 'GET'],
|
||||||
['name' => 'Video#livephoto', 'url' => '/api/video/livephoto/{fileid}', 'verb' => 'GET'],
|
['name' => 'Video#livephoto', 'url' => '/api/video/livephoto/{fileid}', 'verb' => 'GET'],
|
||||||
|
|
|
@ -26,6 +26,7 @@ namespace OCA\Memories\Controller;
|
||||||
use OCA\Memories\AppInfo\Application;
|
use OCA\Memories\AppInfo\Application;
|
||||||
use OCA\Memories\Exceptions;
|
use OCA\Memories\Exceptions;
|
||||||
use OCA\Memories\Exif;
|
use OCA\Memories\Exif;
|
||||||
|
use OCA\Memories\Service;
|
||||||
use OCA\Memories\Util;
|
use OCA\Memories\Util;
|
||||||
use OCP\AppFramework\Http;
|
use OCP\AppFramework\Http;
|
||||||
use OCP\AppFramework\Http\FileDisplayResponse;
|
use OCP\AppFramework\Http\FileDisplayResponse;
|
||||||
|
@ -285,6 +286,61 @@ class ImageController extends GenericApiController
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @NoAdminRequired
|
||||||
|
*/
|
||||||
|
public function editImage(
|
||||||
|
int $id,
|
||||||
|
string $name,
|
||||||
|
int $width,
|
||||||
|
int $height,
|
||||||
|
float $quality,
|
||||||
|
string $extension,
|
||||||
|
array $state
|
||||||
|
): Http\Response {
|
||||||
|
return Util::guardEx(function () use ($id, $name, $quality, $extension, $state) {
|
||||||
|
// Get the file
|
||||||
|
$file = $this->fs->getUserFile($id);
|
||||||
|
|
||||||
|
// Check if creating a copy
|
||||||
|
$copy = $name !== $file->getName();
|
||||||
|
|
||||||
|
// Check if user has permissions to do this
|
||||||
|
if (!$file->isUpdateable() || ($copy && !$file->getParent()->isCreatable())) {
|
||||||
|
throw Exceptions::ForbiddenFileUpdate($file->getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if we have imagick
|
||||||
|
if (!class_exists('Imagick')) {
|
||||||
|
throw Exceptions::Forbidden('Imagick extension is not available');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the image
|
||||||
|
$image = new \Imagick();
|
||||||
|
$image->readImageBlob($file->getContent());
|
||||||
|
|
||||||
|
// Apply the edits
|
||||||
|
(new Service\FileRobotMagick($image, $state))->apply();
|
||||||
|
|
||||||
|
// Save the image
|
||||||
|
$image->setImageFormat($extension);
|
||||||
|
$image->setImageCompressionQuality((int) round(100 * $quality));
|
||||||
|
$blob = $image->getImageBlob();
|
||||||
|
|
||||||
|
// Save the file
|
||||||
|
if ($copy) {
|
||||||
|
$file = $file->getParent()->newFile($name, $blob);
|
||||||
|
} else {
|
||||||
|
$file->putContent($blob);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new JSONResponse([
|
||||||
|
'fileid' => $file->getId(),
|
||||||
|
'etag' => $file->getEtag(),
|
||||||
|
], Http::STATUS_OK);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Given a blob of image data, return a JPEG blob.
|
* Given a blob of image data, return a JPEG blob.
|
||||||
*
|
*
|
||||||
|
|
|
@ -0,0 +1,653 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @copyright Copyright (c) 2022, Varun Patil <radialapps@gmail.com>
|
||||||
|
* @author Varun Patil <radialapps@gmail.com>
|
||||||
|
* @license AGPL-3.0-or-later
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace OCA\Memories\Service;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a FileRobotImageState object from a JSON array state.
|
||||||
|
*/
|
||||||
|
class FileRobotImageState
|
||||||
|
{
|
||||||
|
/** -1 to 1 */
|
||||||
|
public ?float $brightness = null;
|
||||||
|
|
||||||
|
/** -100 to 100 */
|
||||||
|
public ?float $contrast = null;
|
||||||
|
|
||||||
|
/** 0 to 259 */
|
||||||
|
public ?float $hue = null;
|
||||||
|
|
||||||
|
/** -2 to 10 */
|
||||||
|
public ?float $saturation = null;
|
||||||
|
|
||||||
|
/** -2 to 2 */
|
||||||
|
public ?float $value = null;
|
||||||
|
|
||||||
|
/** 0 to 100 */
|
||||||
|
public ?float $blurRadius = null;
|
||||||
|
|
||||||
|
/** 0 to 200 */
|
||||||
|
public ?float $warmth = null;
|
||||||
|
|
||||||
|
/** Order of filters */
|
||||||
|
public array $finetuneOrder = [];
|
||||||
|
|
||||||
|
/** Crop X coordinate */
|
||||||
|
public ?float $cropX = null;
|
||||||
|
|
||||||
|
/** Crop Y coordinate */
|
||||||
|
public ?float $cropY = null;
|
||||||
|
|
||||||
|
/** Crop width */
|
||||||
|
public ?float $cropWidth = null;
|
||||||
|
|
||||||
|
/** Crop height */
|
||||||
|
public ?float $cropHeight = null;
|
||||||
|
|
||||||
|
/** Rotation */
|
||||||
|
public ?int $rotation = null;
|
||||||
|
|
||||||
|
/** Flipped X */
|
||||||
|
public bool $isFlippedX = false;
|
||||||
|
|
||||||
|
/** Flipped Y */
|
||||||
|
public bool $isFlippedY = false;
|
||||||
|
|
||||||
|
/** Resize width */
|
||||||
|
public ?int $resizeWidth = null;
|
||||||
|
|
||||||
|
/** Resize height */
|
||||||
|
public ?int $resizeHeight = null;
|
||||||
|
|
||||||
|
/** Filter */
|
||||||
|
public ?string $filter = null;
|
||||||
|
|
||||||
|
public function __construct(array $json)
|
||||||
|
{
|
||||||
|
if ($order = $json['finetunes']) {
|
||||||
|
foreach ($order as $key) {
|
||||||
|
$this->finetuneOrder[] = $key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($props = $json['finetunesProps']) {
|
||||||
|
$this->_set($props, 'brightness');
|
||||||
|
$this->_set($props, 'contrast');
|
||||||
|
$this->_set($props, 'hue');
|
||||||
|
$this->_set($props, 'saturation');
|
||||||
|
$this->_set($props, 'value');
|
||||||
|
$this->_set($props, 'blurRadius');
|
||||||
|
$this->_set($props, 'warmth');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($props = $json['adjustments']) {
|
||||||
|
if ($crop = $props['crop']) {
|
||||||
|
$this->_set($crop, 'x', 'cropX');
|
||||||
|
$this->_set($crop, 'y', 'cropY');
|
||||||
|
$this->_set($crop, 'width', 'cropWidth');
|
||||||
|
$this->_set($crop, 'height', 'cropHeight');
|
||||||
|
}
|
||||||
|
$this->_set($props, 'rotation');
|
||||||
|
$this->_set($props, 'isFlippedX');
|
||||||
|
$this->_set($props, 'isFlippedY');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($filter = $json['filter']) {
|
||||||
|
// https://github.com/scaleflex/filerobot-image-editor/blob/7113bf4968d97f41381f4a2965a59defd44562c8/packages/react-filerobot-image-editor/src/components/tools/Filters/Filters.constants.js#L8
|
||||||
|
$this->filter = $filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($resize = $json['resize']) {
|
||||||
|
$this->_set($resize, 'width', 'resizeWidth');
|
||||||
|
$this->_set($resize, 'height', 'resizeHeight');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function _set(array $parent, string $key, string $ckey = null)
|
||||||
|
{
|
||||||
|
$ckey ??= $key;
|
||||||
|
if (\array_key_exists($key, $parent)) {
|
||||||
|
$this->{$ckey} = $parent[$key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies a FileRobotImageState to an Imagick object.
|
||||||
|
*/
|
||||||
|
class FileRobotMagick
|
||||||
|
{
|
||||||
|
private \Imagick $image;
|
||||||
|
private FileRobotImageState $state;
|
||||||
|
|
||||||
|
public function __construct(\Imagick $image, array $state)
|
||||||
|
{
|
||||||
|
$this->image = $image;
|
||||||
|
$this->state = new FileRobotImageState($state);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function apply()
|
||||||
|
{
|
||||||
|
$this->applyCrop();
|
||||||
|
$this->applyFlipRotation();
|
||||||
|
$this->applyResize();
|
||||||
|
|
||||||
|
foreach ($this->state->finetuneOrder as $key) {
|
||||||
|
$method = 'apply'.$key;
|
||||||
|
if (!method_exists($this, $method)) {
|
||||||
|
throw new \Exception('Unknown finetune: '.$key);
|
||||||
|
}
|
||||||
|
$this->{$method}();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->state->filter) {
|
||||||
|
$method = 'applyFilter'.$this->state->filter;
|
||||||
|
if (!method_exists($this, $method)) {
|
||||||
|
throw new \Exception('Unknown filter: '.$this->state->filter);
|
||||||
|
}
|
||||||
|
$this->{$method}();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->image;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function applyCrop()
|
||||||
|
{
|
||||||
|
if ($this->state->cropX || $this->state->cropY || $this->state->cropWidth || $this->state->cropHeight) {
|
||||||
|
$iw = $this->image->getImageWidth();
|
||||||
|
$ih = $this->image->getImageHeight();
|
||||||
|
$this->image->cropImage(
|
||||||
|
(int) (($this->state->cropWidth ?? 1) * $iw),
|
||||||
|
(int) (($this->state->cropHeight ?? 1) * $ih),
|
||||||
|
(int) (($this->state->cropX ?? 0) * $iw),
|
||||||
|
(int) (($this->state->cropY ?? 0) * $ih)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function applyFlipRotation()
|
||||||
|
{
|
||||||
|
if ($this->state->isFlippedX) {
|
||||||
|
$this->image->flopImage();
|
||||||
|
}
|
||||||
|
if ($this->state->isFlippedY) {
|
||||||
|
$this->image->flipImage();
|
||||||
|
}
|
||||||
|
if ($this->state->rotation) {
|
||||||
|
$this->image->rotateImage(new \ImagickPixel(), $this->state->rotation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function applyResize()
|
||||||
|
{
|
||||||
|
if ($this->state->resizeWidth || $this->state->resizeHeight) {
|
||||||
|
$this->image->resizeImage(
|
||||||
|
$this->state->resizeWidth ?? 0,
|
||||||
|
$this->state->resizeHeight ?? 0,
|
||||||
|
\Imagick::FILTER_LANCZOS,
|
||||||
|
1
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function applyBrighten(?float $value = null)
|
||||||
|
{
|
||||||
|
$brightness = $value ?? $this->state->brightness ?? 0;
|
||||||
|
if (0 === $brightness) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://github.com/konvajs/konva/blob/f0e18b09079175404a1026363689f8f89eae0749/src/filters/Brighten.ts#L15-L29
|
||||||
|
$this->image->evaluateImage(\Imagick::EVALUATE_ADD, $brightness * 255 * 255, \Imagick::CHANNEL_ALL);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function applyContrast(?float $value = null)
|
||||||
|
{
|
||||||
|
$contrast = $value ?? $this->state->contrast ?? 0;
|
||||||
|
if (0 === $contrast) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://github.com/konvajs/konva/blob/f0e18b09079175404a1026363689f8f89eae0749/src/filters/Contrast.ts#L15-L59
|
||||||
|
// m = ((a + 100) / 100) ** 2 // slope
|
||||||
|
// y = (x - 0.5) * m + 0.5
|
||||||
|
// y = mx + (0.5 * (1 - m)) // simplify
|
||||||
|
$m = (($contrast + 100) / 100) ** 2;
|
||||||
|
$c = 0.5 * (1 - $m);
|
||||||
|
|
||||||
|
$this->image->functionImage(\Imagick::FUNCTION_POLYNOMIAL, [$m, $c], \Imagick::CHANNEL_ALL);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function applyHSV(?float $hue = null, ?float $saturation = null, ?float $value = null)
|
||||||
|
{
|
||||||
|
$hue ??= $this->state->hue ?? 0;
|
||||||
|
$saturation ??= $this->state->saturation ?? 0;
|
||||||
|
$value ??= $this->state->value ?? 0;
|
||||||
|
|
||||||
|
if (0 === $hue && 0 === $saturation && 0 === $value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$h = abs(($hue ?? 0) + 360) % 360;
|
||||||
|
$s = 2 ** ($saturation ?? 0);
|
||||||
|
$v = 2 ** ($value ?? 0);
|
||||||
|
|
||||||
|
// https://github.com/konvajs/konva/blob/f0e18b09079175404a1026363689f8f89eae0749/src/filters/HSV.ts#L17-L63
|
||||||
|
$vsu = $v * $s * cos(($h * M_PI) / 180);
|
||||||
|
$vsw = $v * $s * sin(($h * M_PI) / 180);
|
||||||
|
|
||||||
|
$rr = 0.299 * $v + 0.701 * $vsu + 0.167 * $vsw;
|
||||||
|
$rg = 0.587 * $v - 0.587 * $vsu + 0.33 * $vsw;
|
||||||
|
$rb = 0.114 * $v - 0.114 * $vsu - 0.497 * $vsw;
|
||||||
|
$gr = 0.299 * $v - 0.299 * $vsu - 0.328 * $vsw;
|
||||||
|
$gg = 0.587 * $v + 0.413 * $vsu + 0.035 * $vsw;
|
||||||
|
$gb = 0.114 * $v - 0.114 * $vsu + 0.293 * $vsw;
|
||||||
|
$br = 0.299 * $v - 0.3 * $vsu + 1.25 * $vsw;
|
||||||
|
$bg = 0.587 * $v - 0.586 * $vsu - 1.05 * $vsw;
|
||||||
|
$bb = 0.114 * $v + 0.886 * $vsu - 0.2 * $vsw;
|
||||||
|
|
||||||
|
$colorMatrix = [
|
||||||
|
$rr, $rg, $rb, 0, 0,
|
||||||
|
$gr, $gg, $gb, 0, 0,
|
||||||
|
$br, $bg, $bb, 0, 0,
|
||||||
|
0, 0, 0, 1, 0,
|
||||||
|
0, 0, 0, 0, 1,
|
||||||
|
];
|
||||||
|
|
||||||
|
$this->image->colorMatrixImage($colorMatrix);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function applyBlur()
|
||||||
|
{
|
||||||
|
if ($this->state->blurRadius <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://github.com/konvajs/konva/blob/f0e18b09079175404a1026363689f8f89eae0749/src/filters/Blur.ts#L834
|
||||||
|
$sigma = min(round($this->state->blurRadius * 1.5), 100);
|
||||||
|
$this->image->blurImage(0, $sigma);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function applyWarmth()
|
||||||
|
{
|
||||||
|
// https://github.com/scaleflex/filerobot-image-editor/blob/7113bf4968d97f41381f4a2965a59defd44562c8/packages/react-filerobot-image-editor/src/custom/finetunes/Warmth.js#L17-L28
|
||||||
|
$warmth = ($this->state->warmth ?? 0);
|
||||||
|
if ($warmth <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add to red channel, subtract from blue channel
|
||||||
|
$this->image->evaluateImage(\Imagick::EVALUATE_ADD, $warmth * 255, \Imagick::CHANNEL_RED);
|
||||||
|
$this->image->evaluateImage(\Imagick::EVALUATE_SUBTRACT, $warmth * 255, \Imagick::CHANNEL_BLUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://github.com/scaleflex/filerobot-image-editor/blob/7113bf4968d97f41381f4a2965a59defd44562c8/packages/react-filerobot-image-editor/src/components/tools/Filters/Filters.constants.js#L8
|
||||||
|
protected function applyFilterInvert()
|
||||||
|
{
|
||||||
|
$this->image->negateImage(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function applyFilterBlackAndWhite()
|
||||||
|
{
|
||||||
|
// https://github.com/scaleflex/filerobot-image-editor/blob/7113bf4968d97f41381f4a2965a59defd44562c8/packages/react-filerobot-image-editor/src/custom/filters/BlackAndWhite.js
|
||||||
|
$this->image->thresholdImage(100 * 255);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function applyFilterSepia()
|
||||||
|
{
|
||||||
|
// https://github.com/konvajs/konva/blob/master/src/filters/Sepia.ts
|
||||||
|
$this->image->colorMatrixImage([
|
||||||
|
0.393, 0.769, 0.189, 0, 0,
|
||||||
|
0.349, 0.686, 0.168, 0, 0,
|
||||||
|
0.272, 0.534, 0.131, 0, 0,
|
||||||
|
0, 0, 0, 1, 0,
|
||||||
|
0, 0, 0, 0, 1,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function applyFilterSolarize()
|
||||||
|
{
|
||||||
|
// https://github.com/konvajs/konva/blob/master/src/filters/Solarize.ts
|
||||||
|
$this->image->solarizeImage(128 * 255);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function applyFilterClarendon()
|
||||||
|
{
|
||||||
|
$this->applyBaseFilterBrightness(0.1);
|
||||||
|
$this->applyBaseFilterContrast(0.1);
|
||||||
|
$this->applyBaseFilterSaturation(0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function applyFilterGingham()
|
||||||
|
{
|
||||||
|
$this->applyBaseFilterSepia(0.04);
|
||||||
|
$this->applyBaseFilterContrast(-0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function applyFilterMoon()
|
||||||
|
{
|
||||||
|
$this->applyBaseFilterGrayscale();
|
||||||
|
$this->applyBaseFilterBrightness(0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function applyFilterLark()
|
||||||
|
{
|
||||||
|
$this->applyBaseFilterBrightness(0.08);
|
||||||
|
$this->applyBaseFilterAdjustRGB(1, 1.03, 1.05);
|
||||||
|
$this->applyBaseFilterSaturation(0.12);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function applyFilterReyes()
|
||||||
|
{
|
||||||
|
$this->applyBaseFilterSepia(0.4);
|
||||||
|
$this->applyBaseFilterBrightness(0.13);
|
||||||
|
$this->applyBaseFilterContrast(-0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function applyFilterJuno()
|
||||||
|
{
|
||||||
|
$this->applyBaseFilterAdjustRGB(1.01, 1.04, 1);
|
||||||
|
$this->applyBaseFilterSaturation(0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function applyFilterSlumber()
|
||||||
|
{
|
||||||
|
$this->applyBaseFilterBrightness(0.1);
|
||||||
|
$this->applyBaseFilterSaturation(-0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function applyFilterCrema()
|
||||||
|
{
|
||||||
|
$this->applyBaseFilterAdjustRGB(1.04, 1, 1.02);
|
||||||
|
$this->applyBaseFilterSaturation(-0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function applyFilterLudwig()
|
||||||
|
{
|
||||||
|
$this->applyBaseFilterBrightness(0.05);
|
||||||
|
$this->applyBaseFilterSaturation(-0.03);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function applyFilterAden()
|
||||||
|
{
|
||||||
|
$this->applyBaseFilterColorFilter(228, 130, 225, 0.13);
|
||||||
|
$this->applyBaseFilterSaturation(-0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function applyFilterPerpetua()
|
||||||
|
{
|
||||||
|
$this->applyBaseFilterAdjustRGB(1.05, 1.1, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function applyFilterAmaro()
|
||||||
|
{
|
||||||
|
$this->applyBaseFilterSaturation(0.3);
|
||||||
|
$this->applyBaseFilterBrightness(0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function applyFilterMayfair()
|
||||||
|
{
|
||||||
|
$this->applyBaseFilterColorFilter(230, 115, 108, 0.05);
|
||||||
|
$this->applyBaseFilterSaturation(0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function applyFilterRise()
|
||||||
|
{
|
||||||
|
$this->applyBaseFilterColorFilter(255, 170, 0, 0.1);
|
||||||
|
$this->applyBaseFilterBrightness(0.09);
|
||||||
|
$this->applyBaseFilterSaturation(0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function applyFilterHudson()
|
||||||
|
{
|
||||||
|
$this->applyBaseFilterAdjustRGB(1, 1, 1.25);
|
||||||
|
$this->applyBaseFilterContrast(0.1);
|
||||||
|
$this->applyBaseFilterBrightness(0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function applyFilterValencia()
|
||||||
|
{
|
||||||
|
$this->applyBaseFilterColorFilter(255, 225, 80, 0.08);
|
||||||
|
$this->applyBaseFilterSaturation(0.1);
|
||||||
|
$this->applyBaseFilterContrast(0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function applyFilterXpro2()
|
||||||
|
{
|
||||||
|
$this->applyBaseFilterColorFilter(255, 255, 0, 0.07);
|
||||||
|
$this->applyBaseFilterSaturation(0.2);
|
||||||
|
$this->applyBaseFilterContrast(0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function applyFilterSierra()
|
||||||
|
{
|
||||||
|
$this->applyBaseFilterContrast(-0.15);
|
||||||
|
$this->applyBaseFilterSaturation(0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function applyFilterWillow()
|
||||||
|
{
|
||||||
|
$this->applyBaseFilterGrayscale();
|
||||||
|
$this->applyBaseFilterColorFilter(100, 28, 210, 0.03);
|
||||||
|
$this->applyBaseFilterBrightness(0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function applyFilterLoFi()
|
||||||
|
{
|
||||||
|
$this->applyBaseFilterContrast(0.15);
|
||||||
|
$this->applyBaseFilterSaturation(0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function applyFilterInkwell()
|
||||||
|
{
|
||||||
|
$this->applyBaseFilterGrayscale();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function applyFilterHefe()
|
||||||
|
{
|
||||||
|
$this->applyBaseFilterContrast(0.1);
|
||||||
|
$this->applyBaseFilterSaturation(0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function applyFilterNashville()
|
||||||
|
{
|
||||||
|
$this->applyBaseFilterColorFilter(220, 115, 188, 0.12);
|
||||||
|
$this->applyBaseFilterContrast(-0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function applyFilterStinson()
|
||||||
|
{
|
||||||
|
$this->applyBaseFilterBrightness(0.1);
|
||||||
|
$this->applyBaseFilterSepia(0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function applyFilterVesper()
|
||||||
|
{
|
||||||
|
$this->applyBaseFilterColorFilter(255, 225, 0, 0.05);
|
||||||
|
$this->applyBaseFilterBrightness(0.06);
|
||||||
|
$this->applyBaseFilterContrast(0.06);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function applyFilterEarlybird()
|
||||||
|
{
|
||||||
|
$this->applyBaseFilterColorFilter(255, 165, 40, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function applyFilterBrannan()
|
||||||
|
{
|
||||||
|
$this->applyBaseFilterContrast(0.2);
|
||||||
|
$this->applyBaseFilterColorFilter(140, 10, 185, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function applyFilterSutro()
|
||||||
|
{
|
||||||
|
$this->applyBaseFilterBrightness(-0.1);
|
||||||
|
$this->applyBaseFilterContrast(-0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function applyFilterToaster()
|
||||||
|
{
|
||||||
|
$this->applyBaseFilterSepia(0.1);
|
||||||
|
$this->applyBaseFilterColorFilter(255, 145, 0, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function applyFilterWalden()
|
||||||
|
{
|
||||||
|
$this->applyBaseFilterBrightness(0.1);
|
||||||
|
$this->applyBaseFilterColorFilter(255, 255, 0, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function applyFilterNinteenSeventySeven()
|
||||||
|
{
|
||||||
|
$this->applyBaseFilterColorFilter(255, 25, 0, 0.15);
|
||||||
|
$this->applyBaseFilterBrightness(0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function applyFilterKelvin()
|
||||||
|
{
|
||||||
|
$this->applyBaseFilterColorFilter(255, 140, 0, 0.1);
|
||||||
|
$this->applyBaseFilterAdjustRGB(1.15, 1.05, 1);
|
||||||
|
$this->applyBaseFilterSaturation(0.35);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function applyFilterMaven()
|
||||||
|
{
|
||||||
|
$this->applyBaseFilterColorFilter(225, 240, 0, 0.1);
|
||||||
|
$this->applyBaseFilterSaturation(0.25);
|
||||||
|
$this->applyBaseFilterContrast(0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function applyFilterGinza()
|
||||||
|
{
|
||||||
|
$this->applyBaseFilterSepia(0.06);
|
||||||
|
$this->applyBaseFilterBrightness(0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function applyFilterSkyline()
|
||||||
|
{
|
||||||
|
$this->applyBaseFilterSaturation(0.35);
|
||||||
|
$this->applyBaseFilterBrightness(0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function applyFilterDogpatch()
|
||||||
|
{
|
||||||
|
$this->applyBaseFilterContrast(0.15);
|
||||||
|
$this->applyBaseFilterBrightness(0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function applyFilterBrooklyn()
|
||||||
|
{
|
||||||
|
$this->applyBaseFilterColorFilter(25, 240, 252, 0.05);
|
||||||
|
$this->applyBaseFilterSepia(0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function applyFilterHelena()
|
||||||
|
{
|
||||||
|
$this->applyBaseFilterColorFilter(208, 208, 86, 0.2);
|
||||||
|
$this->applyBaseFilterContrast(0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function applyFilterAshby()
|
||||||
|
{
|
||||||
|
$this->applyBaseFilterColorFilter(255, 160, 25, 0.1);
|
||||||
|
$this->applyBaseFilterBrightness(0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function applyFilterCharmes()
|
||||||
|
{
|
||||||
|
$this->applyBaseFilterColorFilter(255, 50, 80, 0.12);
|
||||||
|
$this->applyBaseFilterContrast(0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function applyBaseFilterBrightness(float $value)
|
||||||
|
{
|
||||||
|
// https://github.com/scaleflex/filerobot-image-editor/blob/7113bf4968d97f41381f4a2965a59defd44562c8/packages/react-filerobot-image-editor/src/custom/filters/BaseFilters.js#L2
|
||||||
|
$this->applyBrighten($value);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function applyBaseFilterContrast(float $value)
|
||||||
|
{
|
||||||
|
// https://github.com/scaleflex/filerobot-image-editor/blob/7113bf4968d97f41381f4a2965a59defd44562c8/packages/react-filerobot-image-editor/src/custom/filters/BaseFilters.js#L14
|
||||||
|
$value *= 255;
|
||||||
|
|
||||||
|
// y = m * (x - 128) + 128
|
||||||
|
// y = m * x + (128 * (1 - m))
|
||||||
|
$m = (259 * ($value + 255)) / (255 * (259 - $value));
|
||||||
|
$c = 0.5 * (1 - $m);
|
||||||
|
|
||||||
|
$this->image->functionImage(\Imagick::FUNCTION_POLYNOMIAL, [$m, $c], \Imagick::CHANNEL_ALL);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function applyBaseFilterSaturation(float $value)
|
||||||
|
{
|
||||||
|
// https://github.com/scaleflex/filerobot-image-editor/blob/7113bf4968d97f41381f4a2965a59defd44562c8/packages/react-filerobot-image-editor/src/custom/filters/BaseFilters.js#L24
|
||||||
|
$this->applyHSV(0, $value, 0); // lazy
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function applyBaseFilterGrayscale()
|
||||||
|
{
|
||||||
|
// https://github.com/scaleflex/filerobot-image-editor/blob/7113bf4968d97f41381f4a2965a59defd44562c8/packages/react-filerobot-image-editor/src/custom/filters/BaseFilters.js#L38
|
||||||
|
// y = 0.2126 * r + 0.7152 * g + 0.0722 * b;
|
||||||
|
$this->image->colorMatrixImage([
|
||||||
|
0.2126, 0.7152, 0.0722, 0, 0,
|
||||||
|
0.2126, 0.7152, 0.0722, 0, 0,
|
||||||
|
0.2126, 0.7152, 0.0722, 0, 0,
|
||||||
|
0, 0, 0, 1, 0,
|
||||||
|
0, 0, 0, 0, 1,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function applyBaseFilterSepia(float $value)
|
||||||
|
{
|
||||||
|
// https://github.com/scaleflex/filerobot-image-editor/blob/7113bf4968d97f41381f4a2965a59defd44562c8/packages/react-filerobot-image-editor/src/custom/filters/BaseFilters.js#L46
|
||||||
|
$this->image->colorMatrixImage([
|
||||||
|
1 - 0.607 * $value, 0.769 * $value, 0.189 * $value, 0, 0,
|
||||||
|
0.349 * $value, 1 - 0.314 * $value, 0.168 * $value, 0, 0,
|
||||||
|
0.272 * $value, 0.534 * $value, 1 - 0.869 * $value, 0, 0,
|
||||||
|
0, 0, 0, 1, 0,
|
||||||
|
0, 0, 0, 0, 1,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function applyBaseFilterAdjustRGB(float $r, float $g, float $b)
|
||||||
|
{
|
||||||
|
// https://github.com/scaleflex/filerobot-image-editor/blob/7113bf4968d97f41381f4a2965a59defd44562c8/packages/react-filerobot-image-editor/src/custom/filters/BaseFilters.js#L57
|
||||||
|
$this->image->colorMatrixImage([
|
||||||
|
$r, 0, 0, 0, 0,
|
||||||
|
0, $g, 0, 0, 0,
|
||||||
|
0, 0, $b, 0, 0,
|
||||||
|
0, 0, 0, 1, 0,
|
||||||
|
0, 0, 0, 0, 1,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function applyBaseFilterColorFilter(float $r, float $g, float $b, float $v)
|
||||||
|
{
|
||||||
|
// https://github.com/scaleflex/filerobot-image-editor/blob/7113bf4968d97f41381f4a2965a59defd44562c8/packages/react-filerobot-image-editor/src/custom/filters/BaseFilters.js#L63
|
||||||
|
// y = x - (x - k) * v = (1 - v) * x + k * v
|
||||||
|
$this->image->evaluateImage(\Imagick::EVALUATE_MULTIPLY, 1 - $v, \Imagick::CHANNEL_ALL);
|
||||||
|
$this->image->evaluateImage(\Imagick::EVALUATE_ADD, $v * $r * 255, \Imagick::CHANNEL_RED);
|
||||||
|
$this->image->evaluateImage(\Imagick::EVALUATE_ADD, $v * $g * 255, \Imagick::CHANNEL_GREEN);
|
||||||
|
$this->image->evaluateImage(\Imagick::EVALUATE_ADD, $v * $b * 255, \Imagick::CHANNEL_BLUE);
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,7 +14,7 @@
|
||||||
"@nextcloud/sharing": "^0.1.0",
|
"@nextcloud/sharing": "^0.1.0",
|
||||||
"@nextcloud/vue": "7.8.0",
|
"@nextcloud/vue": "7.8.0",
|
||||||
"camelcase": "^7.0.1",
|
"camelcase": "^7.0.1",
|
||||||
"filerobot-image-editor": "^4.3.8",
|
"filerobot-image-editor": "^4.4.0",
|
||||||
"fuse.js": "^6.6.2",
|
"fuse.js": "^6.6.2",
|
||||||
"hammerjs": "^2.0.8",
|
"hammerjs": "^2.0.8",
|
||||||
"justified-layout": "^4.1.0",
|
"justified-layout": "^4.1.0",
|
||||||
|
@ -2446,8 +2446,7 @@
|
||||||
"node_modules/@types/prop-types": {
|
"node_modules/@types/prop-types": {
|
||||||
"version": "15.7.5",
|
"version": "15.7.5",
|
||||||
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz",
|
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz",
|
||||||
"integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==",
|
"integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w=="
|
||||||
"peer": true
|
|
||||||
},
|
},
|
||||||
"node_modules/@types/qs": {
|
"node_modules/@types/qs": {
|
||||||
"version": "6.9.7",
|
"version": "6.9.7",
|
||||||
|
@ -2467,13 +2466,20 @@
|
||||||
"version": "18.0.28",
|
"version": "18.0.28",
|
||||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.28.tgz",
|
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.28.tgz",
|
||||||
"integrity": "sha512-RD0ivG1kEztNBdoAK7lekI9M+azSnitIn85h4iOiaLjaTrMjzslhaqCGaI4IyCJ1RljWiLCEu4jyrLLgqxBTew==",
|
"integrity": "sha512-RD0ivG1kEztNBdoAK7lekI9M+azSnitIn85h4iOiaLjaTrMjzslhaqCGaI4IyCJ1RljWiLCEu4jyrLLgqxBTew==",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/prop-types": "*",
|
"@types/prop-types": "*",
|
||||||
"@types/scheduler": "*",
|
"@types/scheduler": "*",
|
||||||
"csstype": "^3.0.2"
|
"csstype": "^3.0.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/react-reconciler": {
|
||||||
|
"version": "0.28.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/react-reconciler/-/react-reconciler-0.28.2.tgz",
|
||||||
|
"integrity": "sha512-8tu6lHzEgYPlfDf/J6GOQdIc+gs+S2yAqlby3zTsB3SP2svlqTYe5fwZNtZyfactP74ShooP2vvi1BOp9ZemWw==",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/react": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@types/resolve": {
|
"node_modules/@types/resolve": {
|
||||||
"version": "1.17.1",
|
"version": "1.17.1",
|
||||||
"resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz",
|
"resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz",
|
||||||
|
@ -2493,8 +2499,7 @@
|
||||||
"node_modules/@types/scheduler": {
|
"node_modules/@types/scheduler": {
|
||||||
"version": "0.16.2",
|
"version": "0.16.2",
|
||||||
"resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz",
|
"resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz",
|
||||||
"integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==",
|
"integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew=="
|
||||||
"peer": true
|
|
||||||
},
|
},
|
||||||
"node_modules/@types/serve-index": {
|
"node_modules/@types/serve-index": {
|
||||||
"version": "1.9.1",
|
"version": "1.9.1",
|
||||||
|
@ -4796,19 +4801,65 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/filerobot-image-editor": {
|
"node_modules/filerobot-image-editor": {
|
||||||
"version": "4.3.8",
|
"version": "4.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/filerobot-image-editor/-/filerobot-image-editor-4.3.8.tgz",
|
"resolved": "https://registry.npmjs.org/filerobot-image-editor/-/filerobot-image-editor-4.4.0.tgz",
|
||||||
"integrity": "sha512-czOgrP/3cDBvDkiGFra/qu0hKesZcaTb9kUiyzCqbeUvZjMVlVcyHzumsETJZw4PDyYVzgJ120CcQYaYbgDhJw==",
|
"integrity": "sha512-B1lqUtPT0iEn+sguJ+XOrTLnjf5adBA60aNW6DPAwGCtHj4NjIHEGT7RMVeleCt9vsHwnTYEZVih0DvLcsVRcw==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.17.2",
|
"@babel/runtime": "^7.17.2",
|
||||||
"react": "18.2.0",
|
"react": "18.2.0",
|
||||||
"react-dom": "18.2.0",
|
"react-dom": "18.2.0",
|
||||||
|
"react-konva": "18.2.5",
|
||||||
"styled-components": "5.3.5"
|
"styled-components": "5.3.5"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"react-filerobot-image-editor": "^4.3.7"
|
"react-filerobot-image-editor": "^4.3.7"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/filerobot-image-editor/node_modules/react-konva": {
|
||||||
|
"version": "18.2.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-konva/-/react-konva-18.2.5.tgz",
|
||||||
|
"integrity": "sha512-lTqJStcHnpGSXB9RlV7p5at3MpRML/TujzbuUDZRIInsLocJ/I4Nhhg3w6yJm9UV05kcwr88OY6LO+2zRyzXog==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "patreon",
|
||||||
|
"url": "https://www.patreon.com/lavrton"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/konva"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/lavrton"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"dependencies": {
|
||||||
|
"@types/react-reconciler": "^0.28.2",
|
||||||
|
"its-fine": "^1.0.6",
|
||||||
|
"react-reconciler": "~0.29.0",
|
||||||
|
"scheduler": "^0.23.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"konva": "^8.0.1 || ^7.2.5",
|
||||||
|
"react": ">=18.0.0",
|
||||||
|
"react-dom": ">=18.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/filerobot-image-editor/node_modules/react-reconciler": {
|
||||||
|
"version": "0.29.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-reconciler/-/react-reconciler-0.29.0.tgz",
|
||||||
|
"integrity": "sha512-wa0fGj7Zht1EYMRhKWwoo1H9GApxYLBuhoAuXN0TlltESAjDssB+Apf0T/DngVqaMyPypDmabL37vw/2aRM98Q==",
|
||||||
|
"dependencies": {
|
||||||
|
"loose-envify": "^1.1.0",
|
||||||
|
"scheduler": "^0.23.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^18.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/filerobot-image-editor/node_modules/styled-components": {
|
"node_modules/filerobot-image-editor/node_modules/styled-components": {
|
||||||
"version": "5.3.5",
|
"version": "5.3.5",
|
||||||
"resolved": "https://registry.npmjs.org/styled-components/-/styled-components-5.3.5.tgz",
|
"resolved": "https://registry.npmjs.org/styled-components/-/styled-components-5.3.5.tgz",
|
||||||
|
@ -6111,6 +6162,17 @@
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/its-fine": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/its-fine/-/its-fine-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-nEoEt5EYSed1mmvwCRv3l1+6T7pyu4ltyBihzPjUtaSWhFhUPU/c7xkPDIutTh8FeIv0F1F5wOFYI8a2s5rlBA==",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/react-reconciler": "^0.28.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": ">=18.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/jake": {
|
"node_modules/jake": {
|
||||||
"version": "10.8.5",
|
"version": "10.8.5",
|
||||||
"resolved": "https://registry.npmjs.org/jake/-/jake-10.8.5.tgz",
|
"resolved": "https://registry.npmjs.org/jake/-/jake-10.8.5.tgz",
|
||||||
|
@ -13188,8 +13250,7 @@
|
||||||
"@types/prop-types": {
|
"@types/prop-types": {
|
||||||
"version": "15.7.5",
|
"version": "15.7.5",
|
||||||
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz",
|
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz",
|
||||||
"integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==",
|
"integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w=="
|
||||||
"peer": true
|
|
||||||
},
|
},
|
||||||
"@types/qs": {
|
"@types/qs": {
|
||||||
"version": "6.9.7",
|
"version": "6.9.7",
|
||||||
|
@ -13209,13 +13270,20 @@
|
||||||
"version": "18.0.28",
|
"version": "18.0.28",
|
||||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.28.tgz",
|
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.28.tgz",
|
||||||
"integrity": "sha512-RD0ivG1kEztNBdoAK7lekI9M+azSnitIn85h4iOiaLjaTrMjzslhaqCGaI4IyCJ1RljWiLCEu4jyrLLgqxBTew==",
|
"integrity": "sha512-RD0ivG1kEztNBdoAK7lekI9M+azSnitIn85h4iOiaLjaTrMjzslhaqCGaI4IyCJ1RljWiLCEu4jyrLLgqxBTew==",
|
||||||
"peer": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"@types/prop-types": "*",
|
"@types/prop-types": "*",
|
||||||
"@types/scheduler": "*",
|
"@types/scheduler": "*",
|
||||||
"csstype": "^3.0.2"
|
"csstype": "^3.0.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@types/react-reconciler": {
|
||||||
|
"version": "0.28.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/react-reconciler/-/react-reconciler-0.28.2.tgz",
|
||||||
|
"integrity": "sha512-8tu6lHzEgYPlfDf/J6GOQdIc+gs+S2yAqlby3zTsB3SP2svlqTYe5fwZNtZyfactP74ShooP2vvi1BOp9ZemWw==",
|
||||||
|
"requires": {
|
||||||
|
"@types/react": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@types/resolve": {
|
"@types/resolve": {
|
||||||
"version": "1.17.1",
|
"version": "1.17.1",
|
||||||
"resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz",
|
"resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz",
|
||||||
|
@ -13235,8 +13303,7 @@
|
||||||
"@types/scheduler": {
|
"@types/scheduler": {
|
||||||
"version": "0.16.2",
|
"version": "0.16.2",
|
||||||
"resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz",
|
"resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz",
|
||||||
"integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==",
|
"integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew=="
|
||||||
"peer": true
|
|
||||||
},
|
},
|
||||||
"@types/serve-index": {
|
"@types/serve-index": {
|
||||||
"version": "1.9.1",
|
"version": "1.9.1",
|
||||||
|
@ -15095,16 +15162,37 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"filerobot-image-editor": {
|
"filerobot-image-editor": {
|
||||||
"version": "4.3.8",
|
"version": "4.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/filerobot-image-editor/-/filerobot-image-editor-4.3.8.tgz",
|
"resolved": "https://registry.npmjs.org/filerobot-image-editor/-/filerobot-image-editor-4.4.0.tgz",
|
||||||
"integrity": "sha512-czOgrP/3cDBvDkiGFra/qu0hKesZcaTb9kUiyzCqbeUvZjMVlVcyHzumsETJZw4PDyYVzgJ120CcQYaYbgDhJw==",
|
"integrity": "sha512-B1lqUtPT0iEn+sguJ+XOrTLnjf5adBA60aNW6DPAwGCtHj4NjIHEGT7RMVeleCt9vsHwnTYEZVih0DvLcsVRcw==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@babel/runtime": "^7.17.2",
|
"@babel/runtime": "^7.17.2",
|
||||||
"react": "18.2.0",
|
"react": "18.2.0",
|
||||||
"react-dom": "18.2.0",
|
"react-dom": "18.2.0",
|
||||||
|
"react-konva": "18.2.5",
|
||||||
"styled-components": "5.3.5"
|
"styled-components": "5.3.5"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"react-konva": {
|
||||||
|
"version": "18.2.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-konva/-/react-konva-18.2.5.tgz",
|
||||||
|
"integrity": "sha512-lTqJStcHnpGSXB9RlV7p5at3MpRML/TujzbuUDZRIInsLocJ/I4Nhhg3w6yJm9UV05kcwr88OY6LO+2zRyzXog==",
|
||||||
|
"requires": {
|
||||||
|
"@types/react-reconciler": "^0.28.2",
|
||||||
|
"its-fine": "^1.0.6",
|
||||||
|
"react-reconciler": "~0.29.0",
|
||||||
|
"scheduler": "^0.23.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"react-reconciler": {
|
||||||
|
"version": "0.29.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-reconciler/-/react-reconciler-0.29.0.tgz",
|
||||||
|
"integrity": "sha512-wa0fGj7Zht1EYMRhKWwoo1H9GApxYLBuhoAuXN0TlltESAjDssB+Apf0T/DngVqaMyPypDmabL37vw/2aRM98Q==",
|
||||||
|
"requires": {
|
||||||
|
"loose-envify": "^1.1.0",
|
||||||
|
"scheduler": "^0.23.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"styled-components": {
|
"styled-components": {
|
||||||
"version": "5.3.5",
|
"version": "5.3.5",
|
||||||
"resolved": "https://registry.npmjs.org/styled-components/-/styled-components-5.3.5.tgz",
|
"resolved": "https://registry.npmjs.org/styled-components/-/styled-components-5.3.5.tgz",
|
||||||
|
@ -16056,6 +16144,14 @@
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"peer": true
|
"peer": true
|
||||||
},
|
},
|
||||||
|
"its-fine": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/its-fine/-/its-fine-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-nEoEt5EYSed1mmvwCRv3l1+6T7pyu4ltyBihzPjUtaSWhFhUPU/c7xkPDIutTh8FeIv0F1F5wOFYI8a2s5rlBA==",
|
||||||
|
"requires": {
|
||||||
|
"@types/react-reconciler": "^0.28.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"jake": {
|
"jake": {
|
||||||
"version": "10.8.5",
|
"version": "10.8.5",
|
||||||
"resolved": "https://registry.npmjs.org/jake/-/jake-10.8.5.tgz",
|
"resolved": "https://registry.npmjs.org/jake/-/jake-10.8.5.tgz",
|
||||||
|
|
|
@ -34,7 +34,7 @@
|
||||||
"@nextcloud/sharing": "^0.1.0",
|
"@nextcloud/sharing": "^0.1.0",
|
||||||
"@nextcloud/vue": "7.8.0",
|
"@nextcloud/vue": "7.8.0",
|
||||||
"camelcase": "^7.0.1",
|
"camelcase": "^7.0.1",
|
||||||
"filerobot-image-editor": "^4.3.8",
|
"filerobot-image-editor": "^4.4.0",
|
||||||
"fuse.js": "^6.6.2",
|
"fuse.js": "^6.6.2",
|
||||||
"hammerjs": "^2.0.8",
|
"hammerjs": "^2.0.8",
|
||||||
"justified-layout": "^4.1.0",
|
"justified-layout": "^4.1.0",
|
||||||
|
|
|
@ -9,9 +9,7 @@
|
||||||
>
|
>
|
||||||
<ImageEditor
|
<ImageEditor
|
||||||
v-if="editorOpen"
|
v-if="editorOpen"
|
||||||
:etag="currentPhoto.etag"
|
:photo="currentPhoto"
|
||||||
:src="editorSrc"
|
|
||||||
:fileid="currentPhoto.fileid"
|
|
||||||
@close="editorOpen = false"
|
@close="editorOpen = false"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
@ -186,7 +184,6 @@ import NcActions from "@nextcloud/vue/dist/Components/NcActions";
|
||||||
import NcActionButton from "@nextcloud/vue/dist/Components/NcActionButton";
|
import NcActionButton from "@nextcloud/vue/dist/Components/NcActionButton";
|
||||||
import axios from "@nextcloud/axios";
|
import axios from "@nextcloud/axios";
|
||||||
import { subscribe, unsubscribe } from "@nextcloud/event-bus";
|
import { subscribe, unsubscribe } from "@nextcloud/event-bus";
|
||||||
import { getRootUrl } from "@nextcloud/router";
|
|
||||||
|
|
||||||
import { getDownloadLink } from "../../services/DavRequests";
|
import { getDownloadLink } from "../../services/DavRequests";
|
||||||
import { API } from "../../services/API";
|
import { API } from "../../services/API";
|
||||||
|
@ -904,18 +901,7 @@ export default defineComponent({
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get DAV path
|
// Open editor
|
||||||
const fileInfo = (await dav.getFiles([this.currentPhoto]))[0];
|
|
||||||
if (!fileInfo) {
|
|
||||||
alert(this.t("memories", "Cannot edit this file"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.editorSrc =
|
|
||||||
window.location.origin +
|
|
||||||
getRootUrl() +
|
|
||||||
"/remote.php/dav" +
|
|
||||||
fileInfo.originalFilename;
|
|
||||||
this.editorOpen = true;
|
this.editorOpen = true;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -142,6 +142,10 @@ export class API {
|
||||||
return tok(API.Q(gen(`${BASE}/image/decodable/{id}`, { id }), { etag }));
|
return tok(API.Q(gen(`${BASE}/image/decodable/{id}`, { id }), { etag }));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static IMAGE_EDIT(id: number) {
|
||||||
|
return gen(`${BASE}/image/edit/{id}`, { id });
|
||||||
|
}
|
||||||
|
|
||||||
static VIDEO_TRANSCODE(fileid: number, file = "index.m3u8") {
|
static VIDEO_TRANSCODE(fileid: number, file = "index.m3u8") {
|
||||||
return tok(
|
return tok(
|
||||||
gen(`${BASE}/video/transcode/{videoClientId}/{fileid}/{file}`, {
|
gen(`${BASE}/video/transcode/{videoClientId}/{fileid}/{file}`, {
|
||||||
|
|
312
test.php
312
test.php
|
@ -1,312 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
class FileRobotImageState {
|
|
||||||
/** -1 to 1 */
|
|
||||||
public ?float $brightness = null;
|
|
||||||
/** -100 to 100 */
|
|
||||||
public ?float $contrast = null;
|
|
||||||
/** 0 to 259 */
|
|
||||||
public ?float $hue = null;
|
|
||||||
/** -2 to 10 */
|
|
||||||
public ?float $saturation = null;
|
|
||||||
/** -2 to 2 */
|
|
||||||
public ?float $value = null;
|
|
||||||
/** 0 to 100 */
|
|
||||||
public ?float $blurRadius = null;
|
|
||||||
/** 0 to 200 */
|
|
||||||
public ?float $warmth = null;
|
|
||||||
|
|
||||||
/** Order of filters */
|
|
||||||
public array $finetuneOrder = [];
|
|
||||||
|
|
||||||
/** Crop X coordinate */
|
|
||||||
public ?float $cropX = null;
|
|
||||||
/** Crop Y coordinate */
|
|
||||||
public ?float $cropY = null;
|
|
||||||
/** Crop width */
|
|
||||||
public ?float $cropWidth = null;
|
|
||||||
/** Crop height */
|
|
||||||
public ?float $cropHeight = null;
|
|
||||||
/** Rotation */
|
|
||||||
public ?int $rotation = null;
|
|
||||||
/** Flipped X */
|
|
||||||
public bool $isFlippedX = false;
|
|
||||||
/** Flipped Y */
|
|
||||||
public bool $isFlippedY = false;
|
|
||||||
|
|
||||||
/** Filter */
|
|
||||||
public ?string $filter = null;
|
|
||||||
|
|
||||||
public function __construct(array $json)
|
|
||||||
{
|
|
||||||
if ($order = $json['finetunes']) {
|
|
||||||
foreach ($order as $key) {
|
|
||||||
$this->finetuneOrder[] = $key;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($props = $json['finetuneProps']) {
|
|
||||||
$this->_set($props, 'brightness');
|
|
||||||
$this->_set($props, 'contrast');
|
|
||||||
$this->_set($props, 'hue');
|
|
||||||
$this->_set($props, 'saturation');
|
|
||||||
$this->_set($props, 'value');
|
|
||||||
$this->_set($props, 'blurRadius');
|
|
||||||
$this->_set($props, 'warmth');
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($props = $json['adjustments']) {
|
|
||||||
if ($crop = $props['crop']) {
|
|
||||||
$this->_set($crop, 'x', 'cropX');
|
|
||||||
$this->_set($crop, 'y', 'cropY');
|
|
||||||
$this->_set($crop, 'width', 'cropWidth');
|
|
||||||
$this->_set($crop, 'height', 'cropHeight');
|
|
||||||
}
|
|
||||||
$this->_set($props, 'rotation');
|
|
||||||
$this->_set($props, 'isFlippedX');
|
|
||||||
$this->_set($props, 'isFlippedY');
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($filter = $json['filter']) {
|
|
||||||
// https://github.com/scaleflex/filerobot-image-editor/blob/7113bf4968d97f41381f4a2965a59defd44562c8/packages/react-filerobot-image-editor/src/components/tools/Filters/Filters.constants.js#L8
|
|
||||||
$this->filter = $filter;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function _set(array $parent, string $key, string $ckey = null) {
|
|
||||||
$ckey ??= $key;
|
|
||||||
if (array_key_exists($key, $parent)) {
|
|
||||||
$this->$ckey = $parent[$key];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class FileRobotMagick {
|
|
||||||
private \Imagick $image;
|
|
||||||
private FileRobotImageState $state;
|
|
||||||
|
|
||||||
public function __construct(\Imagick $image, FileRobotImageState $state)
|
|
||||||
{
|
|
||||||
$this->image = $image;
|
|
||||||
$this->state = $state;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function apply() {
|
|
||||||
$this->applyCrop();
|
|
||||||
$this->applyFlipRotation();
|
|
||||||
|
|
||||||
foreach ($this->state->finetuneOrder as $key) {
|
|
||||||
$method = 'apply' . $key;
|
|
||||||
if (!method_exists($this, $method)) {
|
|
||||||
throw new \Exception('Unknown finetune: ' . $key);
|
|
||||||
}
|
|
||||||
$this->$method();
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->state->filter) {
|
|
||||||
$method = 'applyFilter' . $this->state->filter;
|
|
||||||
if (!method_exists($this, $method)) {
|
|
||||||
throw new \Exception('Unknown filter: ' . $this->state->filter);
|
|
||||||
}
|
|
||||||
$this->$method();
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->image;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function applyCrop() {
|
|
||||||
if ($this->state->cropX || $this->state->cropY || $this->state->cropWidth || $this->state->cropHeight) {
|
|
||||||
$iw = $this->image->getImageWidth();
|
|
||||||
$ih = $this->image->getImageHeight();
|
|
||||||
$this->image->cropImage(
|
|
||||||
(int) (($this->state->cropWidth ?? 1) * $iw),
|
|
||||||
(int) (($this->state->cropHeight ?? 1) * $ih),
|
|
||||||
(int) (($this->state->cropX ?? 0) * $iw),
|
|
||||||
(int) (($this->state->cropY ?? 0) * $ih)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function applyFlipRotation() {
|
|
||||||
if ($this->state->isFlippedX) {
|
|
||||||
$this->image->flopImage();
|
|
||||||
}
|
|
||||||
if ($this->state->isFlippedY) {
|
|
||||||
$this->image->flipImage();
|
|
||||||
}
|
|
||||||
if ($this->state->rotation) {
|
|
||||||
$this->image->rotateImage(new \ImagickPixel(), $this->state->rotation);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function applyBrighten(?float $value = null) {
|
|
||||||
$brightness = $value ?? $this->state->brightness ?? 0;
|
|
||||||
if ($brightness === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// https://github.com/konvajs/konva/blob/f0e18b09079175404a1026363689f8f89eae0749/src/filters/Brighten.ts#L15-L29
|
|
||||||
$this->image->evaluateImage(\Imagick::EVALUATE_ADD, $brightness * 255 * 255, \Imagick::CHANNEL_ALL);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function applyContrast(?float $value = null) {
|
|
||||||
$contrast = $value ?? $this->state->contrast ?? 0;
|
|
||||||
if ($contrast === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// https://github.com/konvajs/konva/blob/f0e18b09079175404a1026363689f8f89eae0749/src/filters/Contrast.ts#L15-L59
|
|
||||||
// m = ((a + 100) / 100) ** 2 // slope
|
|
||||||
// y = (x - 0.5) * m + 0.5
|
|
||||||
// y = mx + (0.5 * (1 - m)) // simplify
|
|
||||||
$m = (($contrast + 100) / 100) ** 2;
|
|
||||||
$c = 0.5 * (1 - $m);
|
|
||||||
|
|
||||||
$this->image->functionImage(\Imagick::FUNCTION_POLYNOMIAL, [$m, $c], \Imagick::CHANNEL_ALL);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function applyHSV(?float $hue = null, ?float $saturation = null, ?float $value = null) {
|
|
||||||
$hue ??= $this->state->hue ?? 0;
|
|
||||||
$saturation ??= $this->state->saturation ?? 0;
|
|
||||||
$value ??= $this->state->value ?? 0;
|
|
||||||
|
|
||||||
if ($hue === 0 && $saturation === 0 && $value === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$h = abs(($hue ?? 0) + 360) % 360;
|
|
||||||
$s = 2 ** ($saturation ?? 0);
|
|
||||||
$v = 2 ** ($value ?? 0);
|
|
||||||
|
|
||||||
// https://github.com/konvajs/konva/blob/f0e18b09079175404a1026363689f8f89eae0749/src/filters/HSV.ts#L17-L63
|
|
||||||
$vsu = $v * $s * cos(($h * pi()) / 180);
|
|
||||||
$vsw = $v * $s * sin(($h * pi()) / 180);
|
|
||||||
|
|
||||||
$rr = 0.299 * $v + 0.701 * $vsu + 0.167 * $vsw;
|
|
||||||
$rg = 0.587 * $v - 0.587 * $vsu + 0.33 * $vsw;
|
|
||||||
$rb = 0.114 * $v - 0.114 * $vsu - 0.497 * $vsw;
|
|
||||||
$gr = 0.299 * $v - 0.299 * $vsu - 0.328 * $vsw;
|
|
||||||
$gg = 0.587 * $v + 0.413 * $vsu + 0.035 * $vsw;
|
|
||||||
$gb = 0.114 * $v - 0.114 * $vsu + 0.293 * $vsw;
|
|
||||||
$br = 0.299 * $v - 0.3 * $vsu + 1.25 * $vsw;
|
|
||||||
$bg = 0.587 * $v - 0.586 * $vsu - 1.05 * $vsw;
|
|
||||||
$bb = 0.114 * $v + 0.886 * $vsu - 0.2 * $vsw;
|
|
||||||
|
|
||||||
$colorMatrix = [
|
|
||||||
$rr, $rg, $rb, 0, 0,
|
|
||||||
$gr, $gg, $gb, 0, 0,
|
|
||||||
$br, $bg, $bb, 0, 0,
|
|
||||||
0, 0, 0, 1, 0,
|
|
||||||
0, 0, 0, 0, 1,
|
|
||||||
];
|
|
||||||
|
|
||||||
$this->image->colorMatrixImage($colorMatrix);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function applyBlur() {
|
|
||||||
if ($this->state->blurRadius <= 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// https://github.com/konvajs/konva/blob/f0e18b09079175404a1026363689f8f89eae0749/src/filters/Blur.ts#L834
|
|
||||||
$sigma = min(round($this->state->blurRadius * 1.5), 100);
|
|
||||||
$this->image->blurImage(0, $sigma);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function applyWarmth() {
|
|
||||||
// https://github.com/scaleflex/filerobot-image-editor/blob/7113bf4968d97f41381f4a2965a59defd44562c8/packages/react-filerobot-image-editor/src/custom/finetunes/Warmth.js#L17-L28
|
|
||||||
$warmth = ($this->state->warmth ?? 0);
|
|
||||||
if ($warmth <= 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add to red channel, subtract from blue channel
|
|
||||||
$this->image->evaluateImage(\Imagick::EVALUATE_ADD, $warmth*255, \Imagick::CHANNEL_RED);
|
|
||||||
$this->image->evaluateImage(\Imagick::EVALUATE_SUBTRACT, $warmth*255, \Imagick::CHANNEL_BLUE);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function applyFilterInvert() {
|
|
||||||
$this->image->negateImage(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function applyFilterBlackAndWhite() {
|
|
||||||
// https://github.com/scaleflex/filerobot-image-editor/blob/7113bf4968d97f41381f4a2965a59defd44562c8/packages/react-filerobot-image-editor/src/custom/filters/BlackAndWhite.js
|
|
||||||
$this->image->thresholdImage(100 * 255);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function applyFilterSepia() {
|
|
||||||
// https://github.com/konvajs/konva/blob/master/src/filters/Sepia.ts
|
|
||||||
$this->image->colorMatrixImage([
|
|
||||||
0.393, 0.769, 0.189, 0, 0,
|
|
||||||
0.349, 0.686, 0.168, 0, 0,
|
|
||||||
0.272, 0.534, 0.131, 0, 0,
|
|
||||||
0, 0, 0, 1, 0,
|
|
||||||
0, 0, 0, 0, 1,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function applyFilterSolarize() {
|
|
||||||
// https://github.com/konvajs/konva/blob/master/src/filters/Solarize.ts
|
|
||||||
$this->image->solarizeImage(128 * 255);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function applyFilterClarendon() {
|
|
||||||
// https://github.com/scaleflex/filerobot-image-editor/blob/7113bf4968d97f41381f4a2965a59defd44562c8/packages/react-filerobot-image-editor/src/custom/filters/Clarendon.js
|
|
||||||
$this->applyBaseFilterBrightness(0.1);
|
|
||||||
$this->applyContrast(10); // TODO: this is wrong
|
|
||||||
$this->applyHSV(0, 0.15, 0); // TODO: this is wrong
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function applyFilterGingham() {
|
|
||||||
// https://github.com/scaleflex/filerobot-image-editor/blob/7113bf4968d97f41381f4a2965a59defd44562c8/packages/react-filerobot-image-editor/src/custom/filters/Gingham.js
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function applyBaseFilterBrightness(float $value) {
|
|
||||||
// https://github.com/scaleflex/filerobot-image-editor/blob/7113bf4968d97f41381f4a2965a59defd44562c8/packages/react-filerobot-image-editor/src/custom/filters/BaseFilters.js#L2
|
|
||||||
$this->applyBrighten($value);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function applyFilterTest() {
|
|
||||||
$this->applyFilterClarendon();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create new ImageState object
|
|
||||||
$imageState = new FileRobotImageState([
|
|
||||||
'finetunes' => ['Blur', 'Warmth', 'HSV', 'Contrast', 'Brighten'],
|
|
||||||
'finetuneProps' => [
|
|
||||||
'brightness' => 0,
|
|
||||||
'contrast' => 0,
|
|
||||||
'hue' => 0,
|
|
||||||
'saturation' => 0,
|
|
||||||
'value' => 0,
|
|
||||||
'blurRadius' => 0,
|
|
||||||
'warmth' => 0,
|
|
||||||
],
|
|
||||||
'filter' => 'Test',
|
|
||||||
'adjustments' =>[
|
|
||||||
// 'crop' => [
|
|
||||||
// 'x' => 0.04811054824217651,
|
|
||||||
// 'y' => 0.30121176094862184,
|
|
||||||
// 'width' => 0.47661152675402463,
|
|
||||||
// 'height' => 0.47661153565936554,
|
|
||||||
// ],
|
|
||||||
// 'rotation' => 0,
|
|
||||||
// 'isFlippedX' => false,
|
|
||||||
// 'isFlippedY' => false,
|
|
||||||
]
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Open test image file imagick
|
|
||||||
$image = new \Imagick('test.jpg');
|
|
||||||
$image->setResourceLimit(\Imagick::RESOURCETYPE_THREAD, 4);
|
|
||||||
|
|
||||||
// Apply image state
|
|
||||||
(new FileRobotMagick($image, $imageState))->apply();
|
|
||||||
|
|
||||||
//resize to max width
|
|
||||||
$image->resizeImage(800, 0, \Imagick::FILTER_LANCZOS, 1);
|
|
||||||
|
|
||||||
// Write to out.jpg
|
|
||||||
$image->writeImage('out.jpg');
|
|
Loading…
Reference in New Issue