Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion composer.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -15,12 +15,13 @@
"composer/semver": "^3.4",
"guzzlehttp/guzzle": "^6.3 || ^7.0",
"inertiajs/inertia-laravel": "^2.0",
"intervention/image": "^3.9.1 || ^4.0",
"james-heinrich/getid3": "^1.9.21",
"laravel/framework": "^12.40.0 || ^13.0",
"laravel/prompts": "^0.3.0",
"league/commonmark": "^2.2",
"league/csv": "^9.1",
"league/glide": "^3.0",
"league/glide": "^3.0 || ^4.0",
"maennchen/zipstream-php": "^3.1",
"michelf/php-smartypants": "^1.8.1",
"nesbot/carbon": "^3.0",
Expand Down
19 changes: 4 additions & 15 deletions src/CP/Assets/CropProcessor.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,7 +2,7 @@

namespace Statamic\CP\Assets;

use Intervention\Image\ImageManager;
use Statamic\Imaging\Intervention;

/**
* @internal
Expand All@@ -13,7 +13,7 @@ public function crop(string $contents, string $extension, int $x, int $y, int $w
{
// Bake in EXIF orientation so the crop coordinates, which come from the
// auto-oriented image the user saw in the browser, line up with the source.
$image = $this->manager()->read($contents)->orient();
$image = Intervention::decode(Intervention::manager(), $contents)->orient();

$x = max(0, min($x, $image->width() - 1));
$y = max(0, min($y, $image->height() - 1));
Expand All@@ -25,10 +25,10 @@ public function crop(string $contents, string $extension, int $x, int $y, int $w
// JPEG has no alpha channel, so flatten any transparency onto a
// background colour rather than letting it default to black.
if (in_array(strtolower($extension), ['jpg', 'jpeg'])) {
$image->blendTransparency($background ?? 'ffffff');
Intervention::fillTransparentAreas($image, $background ?? 'ffffff');
}

return (string) $image->encodeByExtension($extension, quality: $quality ?? self::defaultQuality());
return (string) Intervention::encodeByExtension($image, $extension, $quality ?? self::defaultQuality());
}

public static function defaultQuality(): int
Expand All@@ -37,15 +37,4 @@ public static function defaultQuality(): int
?? config('statamic.assets.image_manipulation.defaults.quality')
?? 90;
}

protected function manager(): ImageManager
{
$driver = config('statamic.assets.image_manipulation.driver', 'gd');

return match ($driver) {
'gd' => ImageManager::gd(),
'imagick' => ImageManager::imagick(),
default => ImageManager::withDriver($driver),
};
}
}
85 changes: 85 additions & 0 deletions src/Imaging/Intervention.php
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
<?php

namespace Statamic\Imaging;

use Intervention\Image\Drivers\Gd\Driver as GdDriver;
use Intervention\Image\Drivers\Imagick\Driver as ImagickDriver;
use Intervention\Image\ImageManager;
use Intervention\Image\Interfaces\ColorInterface;
use Intervention\Image\Interfaces\DriverInterface;
use Intervention\Image\Interfaces\EncodedImageInterface;
use Intervention\Image\Interfaces\ImageInterface;

/**
* Smooths over the differences between Intervention Image v3 and v4.
*
* Glide 3 requires Intervention v3 and Glide 4 requires v4, and we support both. Most of the
* API is identical across the two, but v4 renamed a handful of the methods we rely on.
*
* @internal
*/
class Intervention
{
public static function isV4(): bool
{
// v4 dropped the driver-specific named constructors in favour of usingDriver().
return ! method_exists(ImageManager::class, 'withDriver');
}

public static function driver(?string $driver = null): DriverInterface
{
$driver ??= config('statamic.assets.image_manipulation.driver', 'gd');

$class = match ($driver) {
'gd' => GdDriver::class,
'imagick' => ImagickDriver::class,
default => $driver,
};

return new $class;
}

public static function manager(?string $driver = null): ImageManager
{
return new ImageManager(static::driver($driver));
}

public static function decode(ImageManager $manager, string $contents): ImageInterface
{
return static::isV4()
? $manager->decodeBinary($contents)
: $manager->read($contents);
}

public static function create(ImageManager $manager, int $width, int $height): ImageInterface
{
return static::isV4()
? $manager->createImage($width, $height)
: $manager->create($width, $height);
}

public static function colorAt(ImageInterface $image, int $x, int $y): ColorInterface
{
return static::isV4()
? $image->colorAt($x, $y)
: $image->pickColor($x, $y);
}

public static function fillTransparentAreas(ImageInterface $image, string $color): ImageInterface
{
return static::isV4()
? $image->fillTransparentAreas($color)
: $image->blendTransparency($color);
}

public static function encodeByExtension(ImageInterface $image, string $extension, ?int $quality = null): EncodedImageInterface
{
// v4's encoders type the quality as an int, so leave it out entirely rather
// than passing null through and letting the format's default apply.
$options = $quality === null ? [] : ['quality' => $quality];

return static::isV4()
? $image->encodeUsingFileExtension($extension, ...$options)
: $image->encodeByExtension($extension, ...$options);
}
}
14 changes: 3 additions & 11 deletions src/Providers/GlideServiceProvider.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -3,7 +3,6 @@
namespace Statamic\Providers;

use Illuminate\Support\ServiceProvider;
use Intervention\Image\ImageManager;
use League\Glide\Server;
use Statamic\Contracts\Imaging\ImageManipulator;
use Statamic\Contracts\Imaging\UrlBuilder;
Expand All@@ -13,6 +12,7 @@
use Statamic\Imaging\GlideUrlBuilder;
use Statamic\Imaging\ImageGenerator;
use Statamic\Imaging\ImageValidator;
use Statamic\Imaging\Intervention;
use Statamic\Imaging\PresetGenerator;
use Statamic\Imaging\StaticUrlBuilder;

Expand DownExpand Up@@ -42,16 +42,8 @@ public function register()
);
});

$this->app->bind(ImageValidator::class, function ($app) {
$driver = config('statamic.assets.image_manipulation.driver', 'gd');

$imageManager = match ($driver) {
'gd' => ImageManager::gd(),
'imagick' => ImageManager::imagick(),
default => ImageManager::withDriver($driver),
};

return new ImageValidator($imageManager->driver());
$this->app->bind(ImageValidator::class, function () {
return new ImageValidator(Intervention::driver());
});
}

Expand Down
14 changes: 10 additions & 4 deletions tests/Feature/Assets/CropAssetTest.php
Original file line numberDiff line numberDiff line change
Expand Up@@ -5,12 +5,12 @@
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Storage;
use Intervention\Image\ImageManager;
use PHPUnit\Framework\Attributes\Test;
use Statamic\Assets\AssetContainer;
use Statamic\CP\Assets\CropProcessor;
use Statamic\Events\AssetUploaded;
use Statamic\Facades;
use Statamic\Imaging\Intervention;
use Tests\FakesRoles;
use Tests\PreventSavingStacheItemsToDisk;
use Tests\TestCase;
Expand DownExpand Up@@ -308,17 +308,23 @@ private function cropRoute($path)

private function makeImage($width, $height)
{
return (string) ImageManager::gd()->create($width, $height)->fill('ff0000')->encodeByExtension('jpg');
$image = Intervention::create(Intervention::manager('gd'), $width, $height)->fill('ff0000');

return (string) Intervention::encodeByExtension($image, 'jpg');
}

private function makeTransparentImage($width, $height)
{
return (string) ImageManager::gd()->create($width, $height)->encodeByExtension('png');
$image = Intervention::create(Intervention::manager('gd'), $width, $height);

return (string) Intervention::encodeByExtension($image, 'png');
}

private function redChannel($path)
{
$hex = ImageManager::gd()->read(Storage::disk('test')->get($path))->pickColor(5, 5)->toHex();
$image = Intervention::decode(Intervention::manager('gd'), Storage::disk('test')->get($path));

$hex = Intervention::colorAt($image, 5, 5)->toHex();

return hexdec(substr($hex, 0, 2));
}
Expand Down
Loading