-
Notifications
You must be signed in to change notification settings - Fork 638
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fill image transform setting #12214
Fill image transform setting #12214
Changes from 9 commits
cc244b1
467c095
3a67351
07d7599
2cf7b91
52df321
62a22ce
8aa9095
232b30a
ff77b8e
9d7d47c
b250795
79ae641
87c5abc
2318b6f
790ff68
6cf72ab
1cd1a9b
72b0b59
e1a1e16
75fba52
800c76f
162385e
29fb7ea
fffe2c1
5e86abc
32b8895
3c1aa6d
d8722f3
196a613
f077af3
f12b320
169212f
d0f4181
adf4936
d402ee5
13703ff
281b91f
fab0774
62164a6
855a18c
d81e156
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,6 +10,7 @@ | |
use Craft; | ||
use craft\helpers\Image; | ||
use craft\models\ImageTransform; | ||
use craft\validators\ColorValidator; | ||
use craft\web\assets\edittransform\EditTransformAsset; | ||
use craft\web\Controller; | ||
use yii\web\NotFoundHttpException; | ||
|
@@ -133,6 +134,7 @@ public function actionSave(): ?Response | |
$transform->quality = $this->request->getBodyParam('quality') ?: null; | ||
$transform->interlace = $this->request->getBodyParam('interlace'); | ||
$transform->format = $this->request->getBodyParam('format'); | ||
$transform->fill = $this->request->getBodyParam('fill'); | ||
|
||
if (empty($transform->format)) { | ||
$transform->format = null; | ||
|
@@ -156,6 +158,13 @@ public function actionSave(): ?Response | |
$errors = true; | ||
} | ||
|
||
$useFill = $this->request->getBodyParam('useFill'); | ||
if ($useFill && $transform->mode === 'fit') { | ||
$transform->fill = $transform->fill ? ColorValidator::normalizeColor($transform->fill) : 'transparent'; | ||
} else { | ||
$transform->fill = null; | ||
} | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not wild about this, but I couldn't think of a better way to allow users to specify if they wanted the current behavior (image is sized proportionately to fit within a window) or the new behavior (image is placed within an area specified by the transform). In code, the fact that |
||
if (!$errors) { | ||
$success = Craft::$app->getImageTransforms()->saveTransform($transform); | ||
} else { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -24,6 +24,7 @@ | |
use Imagine\Image\BoxInterface; | ||
use Imagine\Image\ImageInterface; | ||
use Imagine\Image\Metadata\ExifMetadataReader; | ||
use Imagine\Image\Palette\Color\ColorInterface; | ||
use Imagine\Image\Palette\RGB; | ||
use Imagine\Image\Point; | ||
use Imagine\Imagick\Imagine as ImagickImagine; | ||
|
@@ -59,9 +60,9 @@ class Raster extends Image | |
private int $_quality = 0; | ||
|
||
/** | ||
* @var AbstractImage|null | ||
* @var ImageInterface|null | ||
*/ | ||
private ?AbstractImage $_image = null; | ||
private ?ImageInterface $_image = null; | ||
|
||
/** | ||
* @var AbstractImagine|null | ||
|
@@ -78,6 +79,11 @@ class Raster extends Image | |
*/ | ||
private ?Font $_font = null; | ||
|
||
/** | ||
* @var ColorInterface|null | ||
*/ | ||
private ?ColorInterface $_fill = null; | ||
|
||
/** | ||
* @inheritdoc | ||
*/ | ||
|
@@ -234,7 +240,7 @@ public function crop(int $x1, int $x2, int $y1, int $y2): self | |
/** | ||
* @inheritdoc | ||
*/ | ||
public function scaleToFit(?int $targetWidth, ?int $targetHeight, bool $scaleIfSmaller = true): self | ||
public function scaleToFit(?int $targetWidth, ?int $targetHeight, bool $scaleIfSmaller = true, array|string $position = 'center-center'): self | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
{ | ||
$this->normalizeDimensions($targetWidth, $targetHeight); | ||
|
||
|
@@ -243,11 +249,67 @@ public function scaleToFit(?int $targetWidth, ?int $targetHeight, bool $scaleIfS | |
if ($scaleIfSmaller || $this->getWidth() > $targetWidth || $this->getHeight() > $targetHeight) { | ||
$factor = max($this->getWidth() / $targetWidth, $this->getHeight() / $targetHeight); | ||
$this->resize(round($this->getWidth() / $factor), round($this->getHeight() / $factor)); | ||
|
||
if ($this->_fill) { | ||
$this->applyFill($targetWidth, $targetHeight, $position); | ||
} | ||
} | ||
|
||
return $this; | ||
} | ||
|
||
protected function applyFill(?int $targetWidth, ?int $targetHeight, string $position = 'center-center') | ||
{ | ||
$box = new Box($targetWidth, $targetHeight); | ||
$canvas = $this->_instance->create($box, $this->_fill); | ||
|
||
[$verticalPosition, $horizontalPosition] = explode('-', $position); | ||
|
||
switch ($verticalPosition) { | ||
case 'top': | ||
$y = 0; | ||
break; | ||
case 'bottom': | ||
$y = ($box->getHeight() - $this->getHeight()); | ||
break; | ||
default: | ||
$y = ($box->getHeight() - $this->getHeight()) / 2; | ||
break; | ||
} | ||
|
||
switch ($horizontalPosition) { | ||
case 'left': | ||
$x = 0; | ||
break; | ||
case 'right': | ||
$x = ($box->getWidth() - $this->getWidth()); | ||
break; | ||
default: | ||
$x = ($box->getWidth() - $this->getWidth()) / 2; | ||
break; | ||
} | ||
|
||
$point = new Point($x, $y); | ||
|
||
if ($this->_isAnimated) { | ||
$canvas->layers()->remove(0); | ||
$this->_image->layers()->coalesce(); | ||
|
||
foreach ($this->_image->layers() as $layer) { | ||
$newLayer = $this->_instance->create($box, $this->_fill); | ||
$newLayer->paste($layer, $point); | ||
$canvas->layers()->add($newLayer); | ||
|
||
// Hopefully this doesn't take _too_ long, but it might | ||
$this->heartbeat(); | ||
} | ||
} else { | ||
$canvas->paste($this->_image, $point); | ||
} | ||
|
||
$this->_image = $canvas; | ||
} | ||
|
||
/** | ||
* @inheritdoc | ||
*/ | ||
|
@@ -458,6 +520,23 @@ public function setInterlace(string $interlace): self | |
return $this; | ||
} | ||
|
||
/** | ||
* Sets the fill color based on the image's palette. | ||
* | ||
* @param string $fill Hex color of the fill. | ||
* @return $this Self reference | ||
*/ | ||
public function setFill(string $fill): self | ||
{ | ||
if ($fill === 'transparent') { | ||
$this->_fill = $this->_image->palette()->color('#ffffff', 0); | ||
} else { | ||
$this->_fill = $this->_image->palette()->color($fill); | ||
} | ||
|
||
return $this; | ||
} | ||
|
||
/** | ||
* @inheritdoc | ||
*/ | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
<?php | ||
|
||
namespace craft\migrations; | ||
|
||
use craft\db\Migration; | ||
use craft\db\Table; | ||
|
||
/** | ||
* m221027_160703_add_image_transform_fill migration. | ||
*/ | ||
class m221027_160703_add_image_transform_fill extends Migration | ||
{ | ||
/** | ||
* @inheritdoc | ||
*/ | ||
public function safeUp(): bool | ||
{ | ||
// Place migration code here... | ||
$this->addColumn(Table::IMAGETRANSFORMS, 'fill', $this->string(11)->null()->after('interlace')); | ||
|
||
return true; | ||
} | ||
|
||
/** | ||
* @inheritdoc | ||
*/ | ||
public function safeDown(): bool | ||
{ | ||
echo "m221027_160703_add_image_transform_fill cannot be reverted.\n"; | ||
return false; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing
@param
for$position
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also if the fill stuff is exclusive to
Raster
, it feels weird to have$position
defined here in the base class. MaybeRaster
should just be defining its ownscaleToFitAndFill()
method that combinesscaleToFit()
andsetFill()
?