Passed
Push — color ( 10794c )
by Arnaud
05:22 queued 01:34
created

Image::getDominantColor()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 13
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 8
nc 2
nop 1
dl 0
loc 13
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of Cecil.
7
 *
8
 * Copyright (c) Arnaud Ligny <[email protected]>
9
 *
10
 * For the full copyright and license information, please view the LICENSE
11
 * file that was distributed with this source code.
12
 */
13
14
namespace Cecil\Assets;
15
16
use Cecil\Exception\RuntimeException;
17
use Intervention\Image\ImageManagerStatic as ImageManager;
18
use Spatie\ImageOptimizer\OptimizerChain;
19
use Spatie\ImageOptimizer\Optimizers\Cwebp;
20
use Spatie\ImageOptimizer\Optimizers\Gifsicle;
21
use Spatie\ImageOptimizer\Optimizers\Jpegoptim;
22
use Spatie\ImageOptimizer\Optimizers\Optipng;
23
use Spatie\ImageOptimizer\Optimizers\Pngquant;
24
use Spatie\ImageOptimizer\Optimizers\Svgo;
25
26
class Image
27
{
28
    /**
29
     * Image Optimizer Chain.
30
     */
31
    public static function optimizer(int $quality): OptimizerChain
32
    {
33
        return (new OptimizerChain())
34
            ->addOptimizer(new Jpegoptim([
35
                "--max=$quality",
36
                '--strip-all',
37
                '--all-progressive',
38
            ]))
39
            ->addOptimizer(new Pngquant([
40
                "--quality=$quality",
41
                '--force',
42
                '--skip-if-larger',
43
            ]))
44
            ->addOptimizer(new Optipng([
45
                '-i0',
46
                '-o2',
47
                '-quiet',
48
            ]))
49
            ->addOptimizer(new Svgo([
50
                '--disable={cleanupIDs,removeViewBox}',
51
            ]))
52
            ->addOptimizer(new Gifsicle([
53
                '-b',
54
                '-O3',
55
            ]))
56
            ->addOptimizer(new Cwebp([
57
                '-m 6',
58
                '-pass 10',
59
                '-mt',
60
                '-q $quality',
61
            ]));
62
    }
63
64
    /**
65
     * Build the `srcset` attribute for responsive images.
66
     * ie: srcset="/img-480.jpg 480w, /img-800.jpg 800w".
67
     *
68
     * @throws RuntimeException
69
     */
70
    public static function buildSrcset(Asset $asset, array $widths): string
71
    {
72
        if ($asset['type'] !== 'image') {
73
            throw new RuntimeException(\sprintf('Can not build "srcset" of "%s": it\'s not an image file.', $asset['path']));
74
        }
75
76
        $srcset = '';
77
        foreach ($widths as $width) {
78
            if ($asset->getWidth() < $width) {
79
                break;
80
            }
81
            $img = $asset->resize($width);
82
            $srcset .= \sprintf('%s %sw, ', $img, $width);
83
        }
84
        rtrim($srcset, ', ');
85
        // add reference image
86
        if (!empty($srcset)) {
87
            $srcset .= \sprintf('%s %sw', (string) $asset, $asset->getWidth());
88
        }
89
90
        return $srcset;
91
    }
92
93
    /**
94
     * Converts an asset to WebP format.
95
     *
96
     * @throws RuntimeException
97
     */
98
    public static function convertTopWebp(Asset $asset, int $quality): Asset
99
    {
100
        if ($asset['type'] !== 'image') {
101
            throw new RuntimeException(\sprintf('Can not convert "%s" to WebP: it\'s not an image file.', $asset['path']));
102
        }
103
104
        $assetWebp = clone $asset;
105
        $format = 'webp';
106
        $image = ImageManager::make($assetWebp['content']);
107
        $assetWebp['content'] = (string) $image->encode($format, $quality);
108
        $assetWebp['path'] = preg_replace('/\.'.$asset['ext'].'$/m', ".$format", $asset['path']);
109
        $assetWebp['ext'] = $format;
110
111
        return $assetWebp;
112
    }
113
114
    /**
115
     * Checks if an asset is an animated gif.
116
     */
117
    public static function isAnimatedGif(Asset $asset): bool
118
    {
119
        // an animated gif contains multiple "frames", with each frame having a header made up of:
120
        // * a static 4-byte sequence (\x00\x21\xF9\x04)
121
        // * 4 variable bytes
122
        // * a static 2-byte sequence (\x00\x2C)
123
        $count = preg_match_all('#\x00\x21\xF9\x04.{4}\x00[\x2C\x21]#s', (string) $asset['content_source']);
124
125
        return $count > 1;
126
    }
127
128
    /**
129
     * Returns the dominant hex color of an image asset.
130
     *
131
     * @throws RuntimeException
132
     */
133
    public static function getDominantColor(Asset $asset): string
134
    {
135
        if ($asset['type'] !== 'image') {
136
            throw new RuntimeException(\sprintf('Can not get dominant color of "%s": it\'s not an image file.', $asset['path']));
137
        }
138
139
        $assetColor = clone $asset;
140
        $assetColor = $assetColor->resize(100);
141
        $image = ImageManager::make($assetColor['content']);
142
        $color = $image->limitColors(1)->pickColor(0, 0, 'hex');
143
        $image->destroy();
144
145
        return $color;
146
    }
147
}
148