• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

tito10047 / progressive-image-bundle / 21093331125

17 Jan 2026 11:10AM UTC coverage: 90.436% (-0.5%) from 90.889%
21093331125

push

github

web-flow
Merge pull request #4 from tito10047/responsive-strategy

change rendering stratety from <img> to <picture><source><img> for better responsive strategy

268 of 332 new or added lines in 19 files covered. (80.72%)

5 existing lines in 2 files now uncovered.

851 of 941 relevant lines covered (90.44%)

318.39 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

90.91
/src/Analyzer/GdImageAnalyzer.php
1
<?php
2

3
/*
4
 * This file is part of the Progressive Image Bundle.
5
 *
6
 * (c) Jozef Môstka <https://github.com/tito10047/progressive-image-bundle>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11

12
namespace Tito10047\ProgressiveImageBundle\Analyzer;
13

14
use kornrunner\Blurhash\Blurhash;
15
use Tito10047\ProgressiveImageBundle\DTO\ImageMetadata;
16
use Tito10047\ProgressiveImageBundle\Exception\ImageProcessingException;
17
use Tito10047\ProgressiveImageBundle\Loader\LoaderInterface;
18

19
final class GdImageAnalyzer implements ImageAnalyzerInterface
20
{
21
    public function __construct(
22
        private readonly int $componentsX = 4,
23
        private readonly int $componentsY = 3,
24
    ) {
25
    }
590✔
26

27
    public function analyze(LoaderInterface $loader, string $path): ImageMetadata
28
    {
29
        $stream = $loader->load($path);
299✔
30
        $data = stream_get_contents($stream);
299✔
31

32
        if (false === $data) {
299✔
NEW
33
            throw new ImageProcessingException('Failed to load data from loader for path: '.$path);
×
34
        }
35

36
        $image = @imagecreatefromstring($data);
299✔
37
        if (false === $image) {
299✔
NEW
38
            throw new ImageProcessingException('GD could not load image from data for path: '.$path);
×
39
        }
40

41
        $width = imagesx($image);
299✔
42
        $height = imagesy($image);
299✔
43

44
        // Downscale to max 64x64 for Blurhash (preserving aspect ratio)
45
        $targetWidth = 64;
299✔
46
        $targetHeight = 64;
299✔
47

48
        if ($width > $height) {
299✔
49
            $targetHeight = (int) ($height * (64 / $width));
×
50
        } else {
51
            $targetWidth = (int) ($width * (64 / $height));
299✔
52
        }
53

54
        $resizedImage = imagecreatetruecolor($targetWidth, $targetHeight);
299✔
55
        imagecopyresampled($resizedImage, $image, 0, 0, 0, 0, $targetWidth, $targetHeight, $width, $height);
299✔
56

57
        $pixels = [];
299✔
58
        for ($y = 0; $y < $targetHeight; ++$y) {
299✔
59
            $row = [];
299✔
60
            for ($x = 0; $x < $targetWidth; ++$x) {
299✔
61
                $rgb = imagecolorat($resizedImage, $x, $y);
299✔
62
                $r = ($rgb >> 16) & 0xFF;
299✔
63
                $g = ($rgb >> 8) & 0xFF;
299✔
64
                $b = $rgb & 0xFF;
299✔
65
                $row[] = [$r, $g, $b];
299✔
66
            }
67
            $pixels[] = $row;
299✔
68
        }
69

70
        $hash = Blurhash::encode($pixels, $this->componentsX, $this->componentsY);
299✔
71

72
        return new ImageMetadata(
299✔
73
            $hash,
299✔
74
            $width,
299✔
75
            $height
299✔
76
        );
299✔
77
    }
78
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc