1
|
|
|
<?php declare(strict_types=1); |
2
|
|
|
|
3
|
|
|
namespace Compolomus\Compomage; |
4
|
|
|
|
5
|
|
|
use Compolomus\Compomage\Interfaces\ImageInterface; |
6
|
|
|
use Exception; |
7
|
|
|
use Imagick; |
8
|
|
|
use ImagickDraw; |
9
|
|
|
use ImagickException; |
10
|
|
|
use ImagickPixel; |
11
|
|
|
use InvalidArgumentException; |
12
|
|
|
|
13
|
|
|
class Imagick2 extends AbstractImage |
14
|
|
|
{ |
15
|
|
|
/** |
16
|
|
|
* Imagick constructor. |
17
|
|
|
* @param string $image |
18
|
|
|
* @throws Exception |
19
|
|
|
*/ |
20
|
9 |
|
public function __construct(string $image) |
21
|
|
|
{ |
22
|
9 |
|
$this->init($image); |
23
|
9 |
|
} |
24
|
|
|
|
25
|
|
|
/** |
26
|
|
|
* @param int $width |
27
|
|
|
* @param int $height |
28
|
|
|
* @return ImageInterface |
29
|
|
|
* @throws ImagickException |
30
|
|
|
*/ |
31
|
1 |
|
public function resize(int $width, int $height): ImageInterface |
32
|
|
|
{ |
33
|
1 |
|
$this->getImage()->scaleImage($width, $height, false); |
34
|
1 |
|
$this->setSizes(); |
35
|
|
|
|
36
|
1 |
|
return $this; |
37
|
|
|
} |
38
|
|
|
|
39
|
9 |
|
protected function setSizes(): void |
40
|
|
|
{ |
41
|
9 |
|
$args = $this->getImage()->getImageGeometry(); |
42
|
9 |
|
$this->setWidth($args['width']); |
43
|
9 |
|
$this->setHeight($args['height']); |
44
|
9 |
|
$this->setOrientation(); |
45
|
9 |
|
} |
46
|
|
|
|
47
|
|
|
/** |
48
|
|
|
* @param int $angle |
49
|
|
|
* @return ImageInterface |
50
|
|
|
*/ |
51
|
1 |
|
public function rotate(int $angle = 90): ImageInterface |
52
|
|
|
{ |
53
|
1 |
|
$this->getImage()->rotateImage(new ImagickPixel('transparent'), $angle); |
54
|
1 |
|
$this->setSizes(); |
55
|
|
|
|
56
|
1 |
|
return $this; |
57
|
|
|
} |
58
|
|
|
|
59
|
|
|
/** |
60
|
|
|
* @return ImageInterface |
61
|
|
|
*/ |
62
|
|
|
public function flip(): ImageInterface |
63
|
|
|
{ |
64
|
|
|
$this->getImage()->flipImage(); |
65
|
|
|
|
66
|
|
|
return $this; |
67
|
|
|
} |
68
|
|
|
|
69
|
|
|
/** |
70
|
|
|
* @return ImageInterface |
71
|
|
|
*/ |
72
|
|
|
public function flop(): ImageInterface |
73
|
|
|
{ |
74
|
|
|
$this->getImage()->flopImage(); |
75
|
|
|
|
76
|
|
|
return $this; |
77
|
|
|
} |
78
|
|
|
|
79
|
|
|
public function grayscale(): ImageInterface |
80
|
|
|
{ |
81
|
|
|
$this->getImage()->transformimagecolorspace(Imagick::COLORSPACE_GRAY); |
82
|
|
|
$this->getImage()->separateImageChannel(1); |
83
|
|
|
|
84
|
|
|
// modulateImage(100, 0, 100); |
85
|
|
|
|
86
|
|
|
return $this; |
87
|
|
|
} |
88
|
|
|
|
89
|
|
|
/** |
90
|
|
|
* @param string $text |
91
|
|
|
* @param string $position |
92
|
|
|
* @param string $font |
93
|
|
|
* @return ImageInterface |
94
|
|
|
* @throws InvalidArgumentException |
95
|
|
|
* @throws ImagickException |
96
|
|
|
*/ |
97
|
1 |
|
public function copyright(string $text, string $font = 'Courier', string $position = 'SouthWest'): ImageInterface |
98
|
|
|
{ |
99
|
|
|
$positions = [ |
100
|
1 |
|
'NORTHWEST' => Imagick::GRAVITY_NORTHWEST, |
101
|
|
|
'NORTH' => Imagick::GRAVITY_NORTH, |
102
|
|
|
'NORTHEAST' => Imagick::GRAVITY_NORTHEAST, |
103
|
|
|
'WEST' => Imagick::GRAVITY_WEST, |
104
|
|
|
'CENTER' => Imagick::GRAVITY_CENTER, |
105
|
|
|
'SOUTHWEST' => Imagick::GRAVITY_SOUTHWEST, |
106
|
|
|
'SOUTH' => Imagick::GRAVITY_SOUTH, |
107
|
|
|
'SOUTHEAST' => Imagick::GRAVITY_SOUTHEAST, |
108
|
|
|
'EAST' => Imagick::GRAVITY_EAST |
109
|
|
|
]; |
110
|
1 |
|
if (!array_key_exists(strtoupper($position), $positions) || !in_array($font, $this->getFontsList(), true)) { |
111
|
1 |
|
throw new InvalidArgumentException('Does not support font or wrong position'); |
112
|
|
|
} |
113
|
|
|
$this->getImage()->compositeImage($this->prepareImage($text, $positions[strtoupper($position)], $font), |
114
|
|
|
Imagick::COMPOSITE_DISSOLVE, 0, 0); |
115
|
|
|
|
116
|
|
|
return $this; |
117
|
|
|
} |
118
|
|
|
|
119
|
1 |
|
public function getFontsList(): array |
120
|
|
|
{ |
121
|
1 |
|
return $this->getImage()->queryFonts(); |
122
|
|
|
} |
123
|
|
|
|
124
|
|
|
/** |
125
|
|
|
* @param string $text |
126
|
|
|
* @param int $position |
127
|
|
|
* @param string $font |
128
|
|
|
* @return Imagick |
129
|
|
|
* @throws ImagickException |
130
|
|
|
*/ |
131
|
|
|
private function prepareImage(string $text, int $position, string $font): Imagick |
132
|
|
|
{ |
133
|
|
|
$image = new Imagick(); |
134
|
|
|
$mask = new Imagick(); |
135
|
|
|
$draw = new ImagickDraw(); |
136
|
|
|
$image->newImage($this->getWidth(), $this->getHeight(), new ImagickPixel('grey30')); |
137
|
|
|
$mask->newImage($this->getWidth(), $this->getHeight(), new ImagickPixel('black')); |
138
|
|
|
$draw->setFont($font); |
139
|
|
|
$draw->setFontSize(20); |
140
|
|
|
$draw->setFillColor(new ImagickPixel('grey70')); |
141
|
|
|
$draw->setGravity($position); |
142
|
|
|
$image->annotateImage($draw, 10, 12, 0, $text); |
143
|
|
|
$draw->setFillColor(new ImagickPixel('white')); |
144
|
|
|
$mask->annotateImage($draw, 11, 13, 0, $text); |
145
|
|
|
$mask->annotateImage($draw, 10, 12, 0, $text); |
146
|
|
|
$draw->setFillColor(new ImagickPixel('black')); |
147
|
|
|
$mask->annotateImage($draw, 9, 11, 0, $text); |
148
|
|
|
$mask->setImageMatte(false); |
149
|
|
|
$image->compositeImage($mask, Imagick::COMPOSITE_COPYOPACITY, 0, 0); |
150
|
|
|
|
151
|
|
|
return $image; |
152
|
|
|
} |
153
|
|
|
|
154
|
|
|
/** |
155
|
|
|
* @param int $width |
156
|
|
|
* @param int $height |
157
|
|
|
* @param int $startX |
158
|
|
|
* @param int $startY |
159
|
|
|
* @return ImageInterface |
160
|
|
|
*/ |
161
|
|
|
public function crop(int $width, int $height, int $startX, int $startY): ImageInterface |
162
|
|
|
{ |
163
|
|
|
$this->getImage()->cropImage($width, $height, $startX, $startY); |
164
|
|
|
$this->setSizes(); |
165
|
|
|
|
166
|
|
|
return $this; |
167
|
|
|
} |
168
|
|
|
|
169
|
1 |
|
public function save(string $filename, $quality = 100): bool |
170
|
|
|
{ |
171
|
1 |
|
$this->getImage()->setImageCompressionQuality($quality); |
172
|
1 |
|
$this->getImage()->writeImage($filename . '.png'); |
173
|
|
|
|
174
|
1 |
|
return true; |
175
|
|
|
} |
176
|
|
|
|
177
|
1 |
|
public function __toString(): string |
178
|
|
|
{ |
179
|
1 |
|
return trim($this->getImage()->getImageBlob()); |
180
|
|
|
} |
181
|
|
|
|
182
|
|
|
/** |
183
|
|
|
* @param int $width |
184
|
|
|
* @param int $height |
185
|
|
|
* @return ImageInterface |
186
|
|
|
* @throws ImagickException |
187
|
|
|
*/ |
188
|
1 |
|
protected function prepareThumbnail(int $width, int $height): ImageInterface |
189
|
|
|
{ |
190
|
1 |
|
$this->getImage()->cropThumbnailImage($width, $height); |
191
|
|
|
|
192
|
1 |
|
return $this; |
193
|
|
|
} |
194
|
|
|
|
195
|
|
|
/** |
196
|
|
|
* @param string $source |
197
|
|
|
* @return ImageInterface |
198
|
|
|
* @throws ImagickException |
199
|
|
|
*/ |
200
|
9 |
|
protected function tmp(string $source): ImageInterface |
201
|
|
|
{ |
202
|
9 |
|
$image = new Imagick; |
203
|
9 |
|
if ($image->readImageBlob($source)) { |
204
|
9 |
|
if ($image->getImageAlphaChannel() !== Imagick::ALPHACHANNEL_ACTIVATE) { |
205
|
8 |
|
$image->setImageAlphaChannel(Imagick::ALPHACHANNEL_SET); |
206
|
|
|
} |
207
|
|
|
} |
208
|
9 |
|
$background = $this->newImage($image->getImageWidth(), $image->getImageHeight()); |
209
|
9 |
|
$image->compositeImage($background, Imagick::COMPOSITE_OVER, 0, 0); //Imagick::COMPOSITE_DISSOLVE |
210
|
9 |
|
$this->setImage($image); |
211
|
9 |
|
$this->getImage()->setFormat('png'); // save transparent |
212
|
9 |
|
$this->setSizes(); |
213
|
|
|
|
214
|
9 |
|
return $this; |
215
|
|
|
} |
216
|
|
|
|
217
|
|
|
/** |
218
|
|
|
* @param int $width |
219
|
|
|
* @param int $height |
220
|
|
|
* @return Imagick |
221
|
|
|
* @throws ImagickException |
222
|
|
|
*/ |
223
|
9 |
|
protected function newImage(int $width, int $height): Imagick |
224
|
|
|
{ |
225
|
9 |
|
$background = new Imagick; |
226
|
9 |
|
$background->newImage($width, $height, new ImagickPixel('transparent')); |
227
|
9 |
|
$background->setImageBackgroundColor(new ImagickPixel('transparent')); |
228
|
|
|
|
229
|
9 |
|
return $background; |
230
|
|
|
} |
231
|
|
|
|
232
|
|
|
/** |
233
|
|
|
* @param Image $watermark |
234
|
|
|
* @param int $x |
235
|
|
|
* @param int $y |
236
|
|
|
* @return ImageInterface |
237
|
|
|
* @throws Exception |
238
|
|
|
*/ |
239
|
|
|
protected function prepareWatermark($watermark, int $x, int $y): ImageInterface |
240
|
|
|
{ |
241
|
|
|
$watermark = $watermark->getImage(); |
242
|
|
|
$watermark->evaluateImage(Imagick::EVALUATE_MULTIPLY, 1, Imagick::CHANNEL_ALPHA); |
243
|
|
|
$this->getImage()->compositeImage($watermark, Imagick::COMPOSITE_DISSOLVE, $x, $y); |
244
|
|
|
|
245
|
|
|
return $this; |
246
|
|
|
} |
247
|
|
|
|
248
|
|
|
public function resizeByTransparentBackground(int $width, int $height): ImageInterface |
249
|
|
|
{ |
250
|
|
|
$background = $this->newImage($width, $height); |
251
|
|
|
$background->setImageFormat('png'); |
252
|
|
|
|
253
|
|
|
return $this->setBackground($width, $height, new Image(base64_encode((string) $background), Image::IMAGICK)); |
254
|
|
|
} |
255
|
|
|
|
256
|
|
View Code Duplication |
public function resizeByBlurBackground(int $width, int $height): ImageInterface |
|
|
|
|
257
|
|
|
{ |
258
|
|
|
$background = new Image(base64_encode((string) $this)); |
259
|
|
|
$background->resize($width, $height)->blur(); |
260
|
|
|
|
261
|
|
|
return $this->setBackground($width, $height, $background); |
262
|
|
|
} |
263
|
|
|
|
264
|
|
|
/** |
265
|
|
|
* @param int $level |
266
|
|
|
* @return ImageInterface |
267
|
|
|
*/ |
268
|
|
View Code Duplication |
public function brightness(int $level): ImageInterface |
|
|
|
|
269
|
|
|
{ |
270
|
|
|
if (!$this->compareRangeValue($level, 200)) |
271
|
|
|
{ |
272
|
|
|
throw new InvalidArgumentException('Wrong brightness level, range 0 - 200, ' . $level . ' given'); |
273
|
|
|
} |
274
|
|
|
$this->getImage()->modulateImage(abs($level), 100, 100); |
275
|
|
|
|
276
|
|
|
return $this; |
277
|
|
|
} |
278
|
|
|
|
279
|
|
|
/** |
280
|
|
|
* @param int $level |
281
|
|
|
* @return ImageInterface |
282
|
|
|
*/ |
283
|
|
View Code Duplication |
public function contrast(int $level): ImageInterface |
|
|
|
|
284
|
|
|
{ |
285
|
|
|
if (!$this->compareRangeValue($level, 100)) |
286
|
|
|
{ |
287
|
|
|
throw new InvalidArgumentException('Wrong contrast level, range 0 - 100, ' . $level . ' given'); |
288
|
|
|
} |
289
|
|
|
$this->getImage()->brightnessContrastImage(0, abs($level)); |
|
|
|
|
290
|
|
|
|
291
|
|
|
return $this; |
292
|
|
|
} |
293
|
|
|
|
294
|
|
|
/** |
295
|
|
|
* @return ImageInterface |
296
|
|
|
*/ |
297
|
|
|
public function negate(): ImageInterface |
298
|
|
|
{ |
299
|
|
|
$this->getImage()->negateImage(false); |
300
|
|
|
|
301
|
|
|
return $this; |
302
|
|
|
} |
303
|
|
|
|
304
|
|
|
/** |
305
|
|
|
* @return ImageInterface |
306
|
|
|
*/ |
307
|
|
|
public function blur(): ImageInterface |
308
|
|
|
{ |
309
|
|
|
$this->getImage()->blurImage(7, 5); |
310
|
|
|
|
311
|
|
|
return $this; |
312
|
|
|
} |
313
|
|
|
} |
314
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.