Skip to content

Commit

Permalink
feature #1766 [LazyImage] refactor to twig.runtime & support `inter…
Browse files Browse the repository at this point in the history
…vention/image` 3 (kbond)

This PR was merged into the 2.x branch.

Discussion
----------

[LazyImage] refactor to `twig.runtime` & support `intervention/image` 3

| Q             | A
| ------------- | ---
| Bug fix?      | no
| New feature?  | yes
| Issues        | Fix #1465
| License       | MIT

- Refactored the code to use `twig.runtime`
- Support `intervention/image` 3

Commits
-------

83f3e2f [LazyImage] support `intervention/image` 3.0
  • Loading branch information
kbond committed Apr 23, 2024
2 parents a93dd4d + 83f3e2f commit 80b36bc
Show file tree
Hide file tree
Showing 5 changed files with 106 additions and 31 deletions.
4 changes: 4 additions & 0 deletions src/LazyImage/CHANGELOG.md
@@ -1,5 +1,9 @@
# CHANGELOG

## 2.17.0

- Add support for `intervention/image` 3.0+

## 2.13.2

- Revert "Change JavaScript package to `type: module`"
Expand Down
2 changes: 1 addition & 1 deletion src/LazyImage/composer.json
Expand Up @@ -34,7 +34,7 @@
"symfony/dependency-injection": "^5.4|^6.0|^7.0"
},
"require-dev": {
"intervention/image": "^2.5",
"intervention/image": "^2.5|^3.0",
"kornrunner/blurhash": "^1.1",
"symfony/cache-contracts": "^2.2",
"symfony/framework-bundle": "^5.4|^6.0|^7.0",
Expand Down
91 changes: 67 additions & 24 deletions src/LazyImage/src/BlurHash/BlurHash.php
Expand Up @@ -11,7 +11,10 @@

namespace Symfony\UX\LazyImage\BlurHash;

use Intervention\Image\Colors\Rgb\Color;
use Intervention\Image\Drivers\Gd\Encoders\JpegEncoder;
use Intervention\Image\ImageManager;
use Intervention\Image\ImageManagerStatic;
use kornrunner\Blurhash\Blurhash as BlurhashEncoder;
use Symfony\Contracts\Cache\CacheInterface;

Expand All @@ -28,40 +31,24 @@ public function __construct(
) {
}

public function createDataUriThumbnail(string $filename, int $width, int $height, int $encodingWidth = 75, int $encodingHeight = 75): string
public static function intervention3(): bool
{
if (!$this->imageManager) {
throw new \LogicException('To use the Blurhash feature, install intervention/image.');
}
if (!class_exists(BlurhashEncoder::class)) {
throw new \LogicException('To use the Blurhash feature, install kornrunner/blurhash.');
}
return !class_exists(ImageManagerStatic::class);
}

public function createDataUriThumbnail(string $filename, int $width, int $height, int $encodingWidth = 75, int $encodingHeight = 75): string
{
// Resize and encode
$encoded = $this->encode($filename, $encodingWidth, $encodingHeight);

// Create a new blurred thumbnail from encoded BlurHash
$pixels = BlurhashEncoder::decode($encoded, $width, $height);

$thumbnail = $this->imageManager->canvas($width, $height);
for ($y = 0; $y < $height; ++$y) {
for ($x = 0; $x < $width; ++$x) {
$thumbnail->pixel($pixels[$y][$x], $x, $y);
}
}

return 'data:image/jpeg;base64,'.base64_encode($thumbnail->encode('jpg', 80));
return $this->encodeImage($pixels, $width, $height);
}

public function encode(string $filename, int $encodingWidth = 75, int $encodingHeight = 75): string
{
if (!$this->imageManager) {
throw new \LogicException('To use the Blurhash feature, install intervention/image.');
}
if (!class_exists(BlurhashEncoder::class)) {
throw new \LogicException('To use the Blurhash feature, install kornrunner/blurhash.');
}

if ($this->cache) {
return $this->cache->get(
'blurhash.'.hash('xxh3', $filename.$encodingWidth.$encodingHeight),
Expand All @@ -74,6 +61,37 @@ public function encode(string $filename, int $encodingWidth = 75, int $encodingH

private function doEncode(string $filename, int $encodingWidth = 75, int $encodingHeight = 75): string
{
if (!$this->imageManager) {
throw new \LogicException('To use the Blurhash feature, install intervention/image.');
}

if (!class_exists(BlurhashEncoder::class)) {
throw new \LogicException('To use the Blurhash feature, install kornrunner/blurhash.');
}

return BlurhashEncoder::encode($this->generatePixels($filename, $encodingWidth, $encodingHeight), 4, 3);
}

private function generatePixels(string $filename, int $encodingWidth, int $encodingHeight): array
{
if (self::intervention3()) {
$image = $this->imageManager->read($filename)->scale($encodingWidth, $encodingHeight);
$width = $image->width();
$height = $image->height();
$pixels = [];

for ($y = 0; $y < $height; ++$y) {
$row = [];
for ($x = 0; $x < $width; ++$x) {
$row[] = $image->pickColor($x, $y)->toArray();
}

$pixels[] = $row;
}

return $pixels;
}

// Resize image to increase encoding performance
$image = $this->imageManager->make(file_get_contents($filename));
$image->resize($encodingWidth, $encodingHeight, static function ($constraint) {
Expand All @@ -84,8 +102,8 @@ private function doEncode(string $filename, int $encodingWidth = 75, int $encodi
// Encode using BlurHash
$width = $image->getWidth();
$height = $image->getHeight();

$pixels = [];

for ($y = 0; $y < $height; ++$y) {
$row = [];
for ($x = 0; $x < $width; ++$x) {
Expand All @@ -96,6 +114,31 @@ private function doEncode(string $filename, int $encodingWidth = 75, int $encodi
$pixels[] = $row;
}

return BlurhashEncoder::encode($pixels, 4, 3);
return $pixels;
}

private function encodeImage(array $pixels, int $width, int $height): string
{
if (self::intervention3()) {
$thumbnail = $this->imageManager->create($width, $height);

for ($y = 0; $y < $height; ++$y) {
for ($x = 0; $x < $width; ++$x) {
$thumbnail->drawPixel($x, $y, new Color($pixels[$y][$x][0], $pixels[$y][$x][1], $pixels[$y][$x][2]));
}
}

return $thumbnail->encode(new JpegEncoder(80))->toDataUri();
}

$thumbnail = $this->imageManager->canvas($width, $height);

for ($y = 0; $y < $height; ++$y) {
for ($x = 0; $x < $width; ++$x) {
$thumbnail->pixel($pixels[$y][$x], $x, $y);
}
}

return 'data:image/jpeg;base64,'.base64_encode($thumbnail->encode('jpg', 80));
}
}
3 changes: 2 additions & 1 deletion src/LazyImage/src/DependencyInjection/LazyImageExtension.php
Expand Up @@ -11,6 +11,7 @@

namespace Symfony\UX\LazyImage\DependencyInjection;

use Intervention\Image\Drivers\Gd\Driver;
use Intervention\Image\ImageManager;
use Symfony\Component\AssetMapper\AssetMapperInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
Expand Down Expand Up @@ -38,14 +39,14 @@ public function load(array $configs, ContainerBuilder $container)
if (class_exists(ImageManager::class)) {
$container
->setDefinition('lazy_image.image_manager', new Definition(ImageManager::class))
->addArgument(BlurHash::intervention3() ? Driver::class : [])
->setPublic(false)
;
}

$container
->setDefinition('lazy_image.blur_hash', new Definition(BlurHash::class))
->setArgument(0, new Reference('lazy_image.image_manager', ContainerInterface::NULL_ON_INVALID_REFERENCE))
->setPublic(false)
;

if (isset($config['cache'])) {
Expand Down

0 comments on commit 80b36bc

Please sign in to comment.