PluginProbe ʕ •ᴥ•ʔ
Booking for Appointments and Events Calendar – Amelia / 2.4.4
Booking for Appointments and Events Calendar – Amelia v2.4.4
2.4.4 2.4.3 2.4.2 2.4.1 2.4 trunk 1.2.1 1.2.10 1.2.11 1.2.12 1.2.13 1.2.14 1.2.15 1.2.16 1.2.17 1.2.18 1.2.19 1.2.2 1.2.20 1.2.21 1.2.22 1.2.23 1.2.24 1.2.25 1.2.26 1.2.27 1.2.28 1.2.29 1.2.3 1.2.30 1.2.31 1.2.32 1.2.33 1.2.34 1.2.35 1.2.36 1.2.37 1.2.38 1.2.4 1.2.5 1.2.6 1.2.7 1.2.8 1.2.9 2.0 2.0.1 2.0.2 2.1 2.1.1 2.1.2 2.1.3 2.2 2.2.1 2.3
ameliabooking / vendor / bacon / bacon-qr-code / src / Renderer / Image / ImagickImageBackEnd.php
ameliabooking / vendor / bacon / bacon-qr-code / src / Renderer / Image Last commit date
EpsImageBackEnd.php 8 months ago ImageBackEndInterface.php 8 months ago ImagickImageBackEnd.php 8 months ago SvgImageBackEnd.php 8 months ago TransformationMatrix.php 8 months ago
ImagickImageBackEnd.php
337 lines
1 <?php
2 declare(strict_types = 1);
3
4 namespace BaconQrCode\Renderer\Image;
5
6 use BaconQrCode\Exception\RuntimeException;
7 use BaconQrCode\Renderer\Color\Alpha;
8 use BaconQrCode\Renderer\Color\Cmyk;
9 use BaconQrCode\Renderer\Color\ColorInterface;
10 use BaconQrCode\Renderer\Color\Gray;
11 use BaconQrCode\Renderer\Color\Rgb;
12 use BaconQrCode\Renderer\Path\Close;
13 use BaconQrCode\Renderer\Path\Curve;
14 use BaconQrCode\Renderer\Path\EllipticArc;
15 use BaconQrCode\Renderer\Path\Line;
16 use BaconQrCode\Renderer\Path\Move;
17 use BaconQrCode\Renderer\Path\Path;
18 use BaconQrCode\Renderer\RendererStyle\Gradient;
19 use BaconQrCode\Renderer\RendererStyle\GradientType;
20 use Imagick;
21 use ImagickDraw;
22 use ImagickPixel;
23
24 final class ImagickImageBackEnd implements ImageBackEndInterface
25 {
26 /**
27 * @var string
28 */
29 private $imageFormat;
30
31 /**
32 * @var int
33 */
34 private $compressionQuality;
35
36 /**
37 * @var Imagick|null
38 */
39 private $image;
40
41 /**
42 * @var ImagickDraw|null
43 */
44 private $draw;
45
46 /**
47 * @var int|null
48 */
49 private $gradientCount;
50
51 /**
52 * @var TransformationMatrix[]|null
53 */
54 private $matrices;
55
56 /**
57 * @var int|null
58 */
59 private $matrixIndex;
60
61 public function __construct(string $imageFormat = 'png', int $compressionQuality = 100)
62 {
63 if (! class_exists(Imagick::class)) {
64 throw new RuntimeException('You need to install the imagick extension to use this back end');
65 }
66
67 $this->imageFormat = $imageFormat;
68 $this->compressionQuality = $compressionQuality;
69 }
70
71 public function new(int $size, ColorInterface $backgroundColor) : void
72 {
73 $this->image = new Imagick();
74 $this->image->newImage($size, $size, $this->getColorPixel($backgroundColor));
75 $this->image->setImageFormat($this->imageFormat);
76 $this->image->setCompressionQuality($this->compressionQuality);
77 $this->draw = new ImagickDraw();
78 $this->gradientCount = 0;
79 $this->matrices = [new TransformationMatrix()];
80 $this->matrixIndex = 0;
81 }
82
83 public function scale(float $size) : void
84 {
85 if (null === $this->draw) {
86 throw new RuntimeException('No image has been started');
87 }
88
89 $this->draw->scale($size, $size);
90 $this->matrices[$this->matrixIndex] = $this->matrices[$this->matrixIndex]
91 ->multiply(TransformationMatrix::scale($size));
92 }
93
94 public function translate(float $x, float $y) : void
95 {
96 if (null === $this->draw) {
97 throw new RuntimeException('No image has been started');
98 }
99
100 $this->draw->translate($x, $y);
101 $this->matrices[$this->matrixIndex] = $this->matrices[$this->matrixIndex]
102 ->multiply(TransformationMatrix::translate($x, $y));
103 }
104
105 public function rotate(int $degrees) : void
106 {
107 if (null === $this->draw) {
108 throw new RuntimeException('No image has been started');
109 }
110
111 $this->draw->rotate($degrees);
112 $this->matrices[$this->matrixIndex] = $this->matrices[$this->matrixIndex]
113 ->multiply(TransformationMatrix::rotate($degrees));
114 }
115
116 public function push() : void
117 {
118 if (null === $this->draw) {
119 throw new RuntimeException('No image has been started');
120 }
121
122 $this->draw->push();
123 $this->matrices[++$this->matrixIndex] = $this->matrices[$this->matrixIndex - 1];
124 }
125
126 public function pop() : void
127 {
128 if (null === $this->draw) {
129 throw new RuntimeException('No image has been started');
130 }
131
132 $this->draw->pop();
133 unset($this->matrices[$this->matrixIndex--]);
134 }
135
136 public function drawPathWithColor(Path $path, ColorInterface $color) : void
137 {
138 if (null === $this->draw) {
139 throw new RuntimeException('No image has been started');
140 }
141
142 $this->draw->setFillColor($this->getColorPixel($color));
143 $this->drawPath($path);
144 }
145
146 public function drawPathWithGradient(
147 Path $path,
148 Gradient $gradient,
149 float $x,
150 float $y,
151 float $width,
152 float $height
153 ) : void {
154 if (null === $this->draw) {
155 throw new RuntimeException('No image has been started');
156 }
157
158 $this->draw->setFillPatternURL('#' . $this->createGradientFill($gradient, $x, $y, $width, $height));
159 $this->drawPath($path);
160 }
161
162 public function done() : string
163 {
164 if (null === $this->draw) {
165 throw new RuntimeException('No image has been started');
166 }
167
168 $this->image->drawImage($this->draw);
169 $blob = $this->image->getImageBlob();
170 $this->draw->clear();
171 $this->image->clear();
172 $this->draw = null;
173 $this->image = null;
174 $this->gradientCount = null;
175
176 return $blob;
177 }
178
179 private function drawPath(Path $path) : void
180 {
181 $this->draw->pathStart();
182
183 foreach ($path as $op) {
184 switch (true) {
185 case $op instanceof Move:
186 $this->draw->pathMoveToAbsolute($op->getX(), $op->getY());
187 break;
188
189 case $op instanceof Line:
190 $this->draw->pathLineToAbsolute($op->getX(), $op->getY());
191 break;
192
193 case $op instanceof EllipticArc:
194 $this->draw->pathEllipticArcAbsolute(
195 $op->getXRadius(),
196 $op->getYRadius(),
197 $op->getXAxisAngle(),
198 $op->isLargeArc(),
199 $op->isSweep(),
200 $op->getX(),
201 $op->getY()
202 );
203 break;
204
205 case $op instanceof Curve:
206 $this->draw->pathCurveToAbsolute(
207 $op->getX1(),
208 $op->getY1(),
209 $op->getX2(),
210 $op->getY2(),
211 $op->getX3(),
212 $op->getY3()
213 );
214 break;
215
216 case $op instanceof Close:
217 $this->draw->pathClose();
218 break;
219
220 default:
221 throw new RuntimeException('Unexpected draw operation: ' . get_class($op));
222 }
223 }
224
225 $this->draw->pathFinish();
226 }
227
228 private function createGradientFill(Gradient $gradient, float $x, float $y, float $width, float $height) : string
229 {
230 list($width, $height) = $this->matrices[$this->matrixIndex]->apply($width, $height);
231
232 $startColor = $this->getColorPixel($gradient->getStartColor())->getColorAsString();
233 $endColor = $this->getColorPixel($gradient->getEndColor())->getColorAsString();
234 $gradientImage = new Imagick();
235
236 switch ($gradient->getType()) {
237 case GradientType::HORIZONTAL():
238 $gradientImage->newPseudoImage((int) $height, (int) $width, sprintf(
239 'gradient:%s-%s',
240 $startColor,
241 $endColor
242 ));
243 $gradientImage->rotateImage('transparent', -90);
244 break;
245
246 case GradientType::VERTICAL():
247 $gradientImage->newPseudoImage((int) $width, (int) $height, sprintf(
248 'gradient:%s-%s',
249 $startColor,
250 $endColor
251 ));
252 break;
253
254 case GradientType::DIAGONAL():
255 case GradientType::INVERSE_DIAGONAL():
256 $gradientImage->newPseudoImage((int) ($width * sqrt(2)), (int) ($height * sqrt(2)), sprintf(
257 'gradient:%s-%s',
258 $startColor,
259 $endColor
260 ));
261
262 if (GradientType::DIAGONAL() === $gradient->getType()) {
263 $gradientImage->rotateImage('transparent', -45);
264 } else {
265 $gradientImage->rotateImage('transparent', -135);
266 }
267
268 $rotatedWidth = $gradientImage->getImageWidth();
269 $rotatedHeight = $gradientImage->getImageHeight();
270
271 $gradientImage->setImagePage($rotatedWidth, $rotatedHeight, 0, 0);
272 $gradientImage->cropImage(
273 intdiv($rotatedWidth, 2) - 2,
274 intdiv($rotatedHeight, 2) - 2,
275 intdiv($rotatedWidth, 4) + 1,
276 intdiv($rotatedWidth, 4) + 1
277 );
278 break;
279
280 case GradientType::RADIAL():
281 $gradientImage->newPseudoImage((int) $width, (int) $height, sprintf(
282 'radial-gradient:%s-%s',
283 $startColor,
284 $endColor
285 ));
286 break;
287 }
288
289 $id = sprintf('g%d', ++$this->gradientCount);
290 $this->draw->pushPattern($id, 0, 0, $width, $height);
291 $this->draw->composite(Imagick::COMPOSITE_COPY, 0, 0, $width, $height, $gradientImage);
292 $this->draw->popPattern();
293 return $id;
294 }
295
296 private function getColorPixel(ColorInterface $color) : ImagickPixel
297 {
298 $alpha = 100;
299
300 if ($color instanceof Alpha) {
301 $alpha = $color->getAlpha();
302 $color = $color->getBaseColor();
303 }
304
305 if ($color instanceof Rgb) {
306 return new ImagickPixel(sprintf(
307 'rgba(%d, %d, %d, %F)',
308 $color->getRed(),
309 $color->getGreen(),
310 $color->getBlue(),
311 $alpha / 100
312 ));
313 }
314
315 if ($color instanceof Cmyk) {
316 return new ImagickPixel(sprintf(
317 'cmyka(%d, %d, %d, %d, %F)',
318 $color->getCyan(),
319 $color->getMagenta(),
320 $color->getYellow(),
321 $color->getBlack(),
322 $alpha / 100
323 ));
324 }
325
326 if ($color instanceof Gray) {
327 return new ImagickPixel(sprintf(
328 'graya(%d%%, %F)',
329 $color->getGray(),
330 $alpha / 100
331 ));
332 }
333
334 return $this->getColorPixel(new Alpha($alpha, $color->toRgb()));
335 }
336 }
337