Skip to content

Commit 50a1fc0

Browse files
committed
Fix leaky "Weird Al" gravity.
1 parent 29f4837 commit 50a1fc0

2 files changed

Lines changed: 93 additions & 47 deletions

File tree

src/imagetransforms/ImageTransformer.php

Lines changed: 3 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,7 @@ public function getTransformUrl(Asset $asset, \craft\models\ImageTransform $imag
4646

4747
// ImageTransform DI will not work on Craft 4, so we convert the object.
4848
// @see https://github.com/craftcms/cms/pull/15646
49-
if (!$imageTransform instanceof ImageTransform) {
50-
$imageTransform = Craft::createObject(ImageTransform::class, [$imageTransform->toArray()]);
51-
}
49+
$imageTransform = Craft::createObject(ImageTransform::class, [$imageTransform->toArray()]);
5250

5351
$this->applyAssetFocalPointGravity($asset, $imageTransform);
5452

@@ -66,53 +64,11 @@ public function invalidateAssetTransforms(Asset $asset): void
6664

6765
protected function applyAssetFocalPointGravity(Asset $asset, ImageTransform $imageTransform): void
6866
{
69-
if (!$asset->getHasFocalPoint() || isset($imageTransform->gravity) || $imageTransform->mode !== 'crop') {
70-
return;
71-
}
72-
73-
$sourceWidth = $asset->getWidth();
74-
$sourceHeight = $asset->getHeight();
75-
$targetWidth = $imageTransform->width;
76-
$targetHeight = $imageTransform->height;
77-
78-
if (!$sourceWidth || !$sourceHeight || !$targetWidth || !$targetHeight) {
79-
$imageTransform->gravity = $asset->getFocalPoint();
67+
if (!$asset->getHasFocalPoint() || isset($imageTransform->gravity)) {
8068
return;
8169
}
8270

83-
$focalPoint = $asset->getFocalPoint();
84-
$factor = min($sourceWidth / $targetWidth, $sourceHeight / $targetHeight);
85-
$newWidth = round($sourceWidth / $factor);
86-
$newHeight = round($sourceHeight / $factor);
87-
88-
$centerX = $newWidth * $focalPoint['x'];
89-
$centerY = $newHeight * $focalPoint['y'];
90-
$x1 = $centerX - $targetWidth / 2;
91-
$y1 = $centerY - $targetHeight / 2;
92-
$x2 = $x1 + $targetWidth;
93-
$y2 = $y1 + $targetHeight;
94-
95-
if ($x1 < 0) {
96-
$x2 -= $x1;
97-
$x1 = 0;
98-
}
99-
if ($y1 < 0) {
100-
$y2 -= $y1;
101-
$y1 = 0;
102-
}
103-
if ($x2 > $newWidth) {
104-
$x1 -= ($x2 - $newWidth);
105-
$x2 = $newWidth;
106-
}
107-
if ($y2 > $newHeight) {
108-
$y1 -= ($y2 - $newHeight);
109-
$y2 = $newHeight;
110-
}
111-
112-
$imageTransform->gravity = [
113-
'x' => $newWidth > $targetWidth ? $x1 / ($newWidth - $targetWidth) : $focalPoint['x'],
114-
'y' => $newHeight > $targetHeight ? $y1 / ($newHeight - $targetHeight) : $focalPoint['y'],
115-
];
71+
$imageTransform->gravity = $asset->getFocalPoint();
11672
}
11773

11874
private function sign(UriInterface $uri): UriInterface

tests/unit/ImageTransformTest.php

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,29 @@ public function testCropModeMapsFocalPointToClampedCropOriginGravity(): void
7575
], $transform->gravity, 0.0001);
7676
}
7777

78+
public function testGetTransformUrlDoesNotLeakGravityBetweenAssets(): void
79+
{
80+
$transform = new ImageTransform([
81+
'mode' => 'crop',
82+
'position' => 'top-center',
83+
'width' => 1200,
84+
'height' => 750,
85+
]);
86+
87+
$firstAsset = $this->makeUrlAssetStub(1, 'first.jpg', 3402, 4253, ['x' => 0.57, 'y' => 0.7707]);
88+
$secondAsset = $this->makeUrlAssetStub(2, 'second.jpg', 3402, 4253, ['x' => 0.4631, 'y' => 0.308]);
89+
90+
$transformer = new UrlTestImageTransformer();
91+
92+
$firstUrl = $transformer->getTransformUrl($firstAsset, $transform, true);
93+
$secondUrl = $transformer->getTransformUrl($secondAsset, $transform, true);
94+
95+
$this->assertStringContainsString('gravity%5Bx%5D=0.57', $firstUrl);
96+
$this->assertStringContainsString('gravity%5By%5D=0.9999999999999992', $firstUrl);
97+
$this->assertStringContainsString('gravity%5Bx%5D=0.4631', $secondUrl);
98+
$this->assertStringNotContainsString('gravity%5Bx%5D=0.57', $secondUrl);
99+
}
100+
78101
private function makeAssetStub(int $width, int $height, array $focalPoint): Asset
79102
{
80103
return new class($width, $height, $focalPoint) extends Asset {
@@ -109,6 +132,62 @@ public function getHeight(mixed $transform = null): ?int
109132
};
110133
}
111134

135+
private function makeUrlAssetStub(int $id, string $filename, int $width, int $height, array $focalPoint): Asset
136+
{
137+
return new class($id, $filename, $width, $height, $focalPoint) extends Asset {
138+
public function __construct(
139+
int $id,
140+
private string $filenameValue,
141+
private int $widthValue,
142+
private int $heightValue,
143+
private array $focalPointValue,
144+
) {
145+
parent::__construct();
146+
$this->id = $id;
147+
$this->kind = self::KIND_IMAGE;
148+
}
149+
150+
public function getHasFocalPoint(): bool
151+
{
152+
return true;
153+
}
154+
155+
public function getFocalPoint(bool $asCss = false): array|string|null
156+
{
157+
if ($asCss) {
158+
return ($this->focalPointValue['x'] * 100) . '% ' . ($this->focalPointValue['y'] * 100) . '%';
159+
}
160+
161+
return $this->focalPointValue;
162+
}
163+
164+
public function getWidth(array|string|\craft\models\ImageTransform $transform = null): ?int
165+
{
166+
return $this->widthValue;
167+
}
168+
169+
public function getHeight(mixed $transform = null): ?int
170+
{
171+
return $this->heightValue;
172+
}
173+
174+
public function getFilename(bool $withExtension = true): string
175+
{
176+
return $this->filenameValue;
177+
}
178+
179+
public function getPath(): ?string
180+
{
181+
return 'tests/' . $this->filenameValue;
182+
}
183+
184+
public function getMimeType(mixed $transform = null): ?string
185+
{
186+
return 'image/jpeg';
187+
}
188+
};
189+
}
190+
112191
}
113192

114193
class TestImageTransformer extends ImageTransformer
@@ -118,3 +197,14 @@ public function applyFocalPointGravity(Asset $asset, ImageTransform $imageTransf
118197
$this->applyAssetFocalPointGravity($asset, $imageTransform);
119198
}
120199
}
200+
201+
class UrlTestImageTransformer extends ImageTransformer
202+
{
203+
public function getTransformUrl(Asset $asset, \craft\models\ImageTransform $imageTransform, bool $immediately): string
204+
{
205+
$imageTransform = \Craft::createObject(ImageTransform::class, [$imageTransform->toArray()]);
206+
$this->applyAssetFocalPointGravity($asset, $imageTransform);
207+
208+
return (string) \League\Uri\Components\Query::fromVariable($imageTransform->toOptions());
209+
}
210+
}

0 commit comments

Comments
 (0)