1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Imagecow; |
4
|
|
|
|
5
|
|
|
use Imagecow\Utils\Dimmensions; |
6
|
|
|
|
7
|
|
|
class Image |
8
|
|
|
{ |
9
|
|
|
const LIB_GD = 'Gd'; |
10
|
|
|
const LIB_IMAGICK = 'Imagick'; |
11
|
|
|
|
12
|
|
|
const CROP_ENTROPY = 'Entropy'; |
13
|
|
|
const CROP_BALANCED = 'Balanced'; |
14
|
|
|
const CROP_FACE = 'Face'; |
15
|
|
|
|
16
|
|
|
protected $image; |
17
|
|
|
protected $filename; |
18
|
|
|
protected $clientHints = [ |
19
|
|
|
'dpr' => null, |
20
|
|
|
'viewport-width' => null, |
21
|
|
|
'width' => null, |
22
|
|
|
]; |
23
|
|
|
|
24
|
|
|
/** |
25
|
|
|
* Static function to create a new Imagecow instance from an image file. |
26
|
|
|
* |
27
|
|
|
* @param string $filename The path of the file |
28
|
|
|
* @param string $library The name of the image library to use (Gd or Imagick). If it's not defined, detects automatically the library to use. |
29
|
|
|
* |
30
|
|
|
* @return Image |
31
|
|
|
*/ |
32
|
|
|
public static function fromFile($filename, $library = null) |
33
|
|
|
{ |
34
|
|
|
$class = self::getLibraryClass($library); |
35
|
|
|
|
36
|
|
|
$image = new static($class::createFromFile($filename), $filename); |
37
|
|
|
|
38
|
|
|
if ($image->getMimeType() !== 'image/gif') { |
39
|
|
|
return $image; |
40
|
|
|
} |
41
|
|
|
|
42
|
|
|
$stream = fopen($filename, 'rb'); |
43
|
|
|
|
44
|
|
|
if (self::isAnimatedGif($stream)) { |
45
|
|
|
$image->image->setAnimated(true); |
46
|
|
|
} |
47
|
|
|
|
48
|
|
|
fclose($stream); |
49
|
|
|
|
50
|
|
|
return $image; |
51
|
|
|
} |
52
|
|
|
|
53
|
|
|
/** |
54
|
|
|
* Static function to create a new Imagecow instance from a binary string. |
55
|
|
|
* |
56
|
|
|
* @param string $string The string of the image |
57
|
|
|
* @param string $library The name of the image library to use (Gd or Imagick). If it's not defined, detects automatically the library to use. |
58
|
|
|
* |
59
|
|
|
* @return Image |
60
|
|
|
*/ |
61
|
|
|
public static function fromString($string, $library = null) |
62
|
|
|
{ |
63
|
|
|
$class = self::getLibraryClass($library); |
64
|
|
|
|
65
|
|
|
$image = new static($class::createFromString($string)); |
66
|
|
|
|
67
|
|
|
if ($image->getMimeType() !== 'image/gif') { |
68
|
|
|
return $image; |
69
|
|
|
} |
70
|
|
|
|
71
|
|
|
$stream = fopen('php://temp', 'r+'); |
72
|
|
|
|
73
|
|
|
fwrite($stream, $string); |
74
|
|
|
rewind($stream); |
75
|
|
|
|
76
|
|
|
if (self::isAnimatedGif($stream)) { |
77
|
|
|
$image->image->setAnimated(true); |
78
|
|
|
} |
79
|
|
|
|
80
|
|
|
fclose($stream); |
81
|
|
|
|
82
|
|
|
return $image; |
83
|
|
|
} |
84
|
|
|
|
85
|
|
|
/** |
86
|
|
|
* Constructor. |
87
|
|
|
* |
88
|
|
|
* @param Libs\LibInterface $image |
89
|
|
|
* @param string $filename Original filename (used to overwrite) |
90
|
|
|
*/ |
91
|
|
|
public function __construct(Libs\LibInterface $image, $filename = null) |
92
|
|
|
{ |
93
|
|
|
$this->image = $image; |
94
|
|
|
$this->filename = $filename; |
95
|
|
|
} |
96
|
|
|
|
97
|
|
|
/** |
98
|
|
|
* Set the available client hints. |
99
|
|
|
* |
100
|
|
|
* @param array $clientHints |
101
|
|
|
* |
102
|
|
|
* @return self |
103
|
|
|
*/ |
104
|
|
|
public function setClientHints(array $clientHints) |
105
|
|
|
{ |
106
|
|
|
$normalize = []; |
107
|
|
|
|
108
|
|
|
foreach ($clientHints as $key => $value) { |
109
|
|
|
$normalize[strtolower($key)] = is_null($value) ? null : (float) $value; |
110
|
|
|
} |
111
|
|
|
|
112
|
|
|
if (array_diff_key($normalize, $this->clientHints)) { |
113
|
|
|
throw new \InvalidArgumentException('Invalid client hints'); |
114
|
|
|
} |
115
|
|
|
|
116
|
|
|
$this->clientHints = array_replace($this->clientHints, $normalize); |
117
|
|
|
|
118
|
|
|
return $this; |
119
|
|
|
} |
120
|
|
|
|
121
|
|
|
/** |
122
|
|
|
* Set a default background color used to fill in some transformation functions. |
123
|
|
|
* |
124
|
|
|
* @param array $background The color in rgb, for example: array(0,127,34) |
125
|
|
|
* |
126
|
|
|
* @return self |
127
|
|
|
*/ |
128
|
|
|
public function setBackground(array $background) |
129
|
|
|
{ |
130
|
|
|
$this->image->setBackground($background); |
131
|
|
|
|
132
|
|
|
return $this; |
133
|
|
|
} |
134
|
|
|
|
135
|
|
|
/** |
136
|
|
|
* Define the image compression quality for jpg images. |
137
|
|
|
* |
138
|
|
|
* @param int $quality The quality (from 0 to 100) |
139
|
|
|
* |
140
|
|
|
* @deprecated Use quality instead |
141
|
|
|
* |
142
|
|
|
* @return self |
143
|
|
|
*/ |
144
|
|
|
public function setCompressionQuality($quality) |
145
|
|
|
{ |
146
|
|
|
error_log('The method `setCompressionQuality()` is deprecated. Use `quality()` instead.'); |
147
|
|
|
|
148
|
|
|
return $this->quality($quality); |
149
|
|
|
} |
150
|
|
|
|
151
|
|
|
/** |
152
|
|
|
* Get the fixed size according with the client hints. |
153
|
|
|
* |
154
|
|
|
* @param int $width |
155
|
|
|
* @param int $height |
156
|
|
|
* |
157
|
|
|
* @return array |
158
|
|
|
*/ |
159
|
|
|
private function calculateClientSize($width, $height) |
160
|
|
|
{ |
161
|
|
View Code Duplication |
if ($this->clientHints['width'] !== null && $this->clientHints['width'] < $width) { |
|
|
|
|
162
|
|
|
return Dimmensions::getResizeDimmensions($width, $height, $this->clientHints['width'], null); |
163
|
|
|
} |
164
|
|
|
|
165
|
|
View Code Duplication |
if ($this->clientHints['viewport-width'] !== null && $this->clientHints['viewport-width'] < $width) { |
|
|
|
|
166
|
|
|
return Dimmensions::getResizeDimmensions($width, $height, $this->clientHints['viewport-width'], null); |
167
|
|
|
} |
168
|
|
|
|
169
|
|
|
if ($this->clientHints['dpr'] !== null) { |
170
|
|
|
$width *= $this->clientHints['dpr']; |
171
|
|
|
$height *= $this->clientHints['dpr']; |
172
|
|
|
} |
173
|
|
|
|
174
|
|
|
return [$width, $height]; |
175
|
|
|
} |
176
|
|
|
|
177
|
|
|
/** |
178
|
|
|
* Inverts the image vertically. |
179
|
|
|
* |
180
|
|
|
* @return self |
181
|
|
|
*/ |
182
|
|
|
public function flip() |
183
|
|
|
{ |
184
|
|
|
$this->image->flip(); |
185
|
|
|
|
186
|
|
|
return $this; |
187
|
|
|
} |
188
|
|
|
|
189
|
|
|
/** |
190
|
|
|
* Inverts the image horizontally. |
191
|
|
|
* |
192
|
|
|
* @return self |
193
|
|
|
*/ |
194
|
|
|
public function flop() |
195
|
|
|
{ |
196
|
|
|
$this->image->flop(); |
197
|
|
|
|
198
|
|
|
return $this; |
199
|
|
|
} |
200
|
|
|
|
201
|
|
|
/** |
202
|
|
|
* Saves the image in a file. |
203
|
|
|
* |
204
|
|
|
* @param string $filename Name of the file where the image will be saved. If it's not defined, The original file will be overwritten. |
205
|
|
|
* |
206
|
|
|
* @return self |
207
|
|
|
*/ |
208
|
|
|
public function save($filename = null) |
209
|
|
|
{ |
210
|
|
|
$this->image->save($filename ?: $this->filename); |
211
|
|
|
|
212
|
|
|
return $this; |
213
|
|
|
} |
214
|
|
|
|
215
|
|
|
/** |
216
|
|
|
* Returns the image instance. |
217
|
|
|
* |
218
|
|
|
* @return Libs\LibInterface |
219
|
|
|
*/ |
220
|
|
|
public function getImage() |
221
|
|
|
{ |
222
|
|
|
return $this->image; |
223
|
|
|
} |
224
|
|
|
|
225
|
|
|
/** |
226
|
|
|
* Gets the image data in a string. |
227
|
|
|
* |
228
|
|
|
* @return string The image data |
229
|
|
|
*/ |
230
|
|
|
public function getString() |
231
|
|
|
{ |
232
|
|
|
return $this->image->getString(); |
233
|
|
|
} |
234
|
|
|
|
235
|
|
|
/** |
236
|
|
|
* Gets the mime type of the image. |
237
|
|
|
* |
238
|
|
|
* @return string The mime type |
239
|
|
|
*/ |
240
|
|
|
public function getMimeType() |
241
|
|
|
{ |
242
|
|
|
return $this->image->getMimeType(); |
243
|
|
|
} |
244
|
|
|
|
245
|
|
|
/** |
246
|
|
|
* Gets the width of the image. |
247
|
|
|
* |
248
|
|
|
* @return int The width in pixels |
249
|
|
|
*/ |
250
|
|
|
public function getWidth() |
251
|
|
|
{ |
252
|
|
|
return $this->image->getWidth(); |
253
|
|
|
} |
254
|
|
|
|
255
|
|
|
/** |
256
|
|
|
* Gets the height of the image. |
257
|
|
|
* |
258
|
|
|
* @return int The height in pixels |
259
|
|
|
*/ |
260
|
|
|
public function getHeight() |
261
|
|
|
{ |
262
|
|
|
return $this->image->getHeight(); |
263
|
|
|
} |
264
|
|
|
|
265
|
|
|
/** |
266
|
|
|
* Converts the image to other format. |
267
|
|
|
* |
268
|
|
|
* @param string $format The new format: png, jpg, gif |
269
|
|
|
* |
270
|
|
|
* @return self |
271
|
|
|
*/ |
272
|
|
|
public function format($format) |
273
|
|
|
{ |
274
|
|
|
$this->image->format($format); |
275
|
|
|
|
276
|
|
|
return $this; |
277
|
|
|
} |
278
|
|
|
|
279
|
|
|
/** |
280
|
|
|
* Resizes the image maintaining the proportion (A 800x600 image resized to 400x400 becomes to 400x300). |
281
|
|
|
* |
282
|
|
|
* @param int|string $width The max width of the image. It can be a number (pixels) or percentaje |
283
|
|
|
* @param int|string $height The max height of the image. It can be a number (pixels) or percentaje |
284
|
|
|
* @param bool $cover |
285
|
|
|
* |
286
|
|
|
* @return self |
287
|
|
|
*/ |
288
|
|
|
public function resize($width, $height = 0, $cover = false) |
289
|
|
|
{ |
290
|
|
|
$imageWidth = $this->getWidth(); |
291
|
|
|
$imageHeight = $this->getHeight(); |
292
|
|
|
|
293
|
|
|
$width = Dimmensions::getIntegerValue('x', $width, $imageWidth); |
294
|
|
|
$height = Dimmensions::getIntegerValue('y', $height, $imageHeight); |
295
|
|
|
|
296
|
|
|
list($width, $height) = Dimmensions::getResizeDimmensions($imageWidth, $imageHeight, $width, $height, $cover); |
297
|
|
|
list($width, $height) = $this->calculateClientSize($width, $height); |
298
|
|
|
|
299
|
|
|
if ($width >= $imageWidth && !$cover) { |
300
|
|
|
return $this; |
301
|
|
|
} |
302
|
|
|
|
303
|
|
|
$this->image->resize($width, $height); |
304
|
|
|
|
305
|
|
|
return $this; |
306
|
|
|
} |
307
|
|
|
|
308
|
|
|
/** |
309
|
|
|
* Crops the image. |
310
|
|
|
* |
311
|
|
|
* @param int|string $width The new width of the image. It can be a number (pixels) or percentaje |
312
|
|
|
* @param int|string $height The new height of the image. It can be a number (pixels) or percentaje |
313
|
|
|
* @param int|string $x The "x" position to crop. It can be number (pixels), percentaje, [left, center, right] or one of the Image::CROP_* constants |
314
|
|
|
* @param int|string $y The "y" position to crop. It can be number (pixels), percentaje or [top, middle, bottom] |
315
|
|
|
* |
316
|
|
|
* @return self |
317
|
|
|
*/ |
318
|
|
|
public function crop($width, $height, $x = 'center', $y = 'middle') |
319
|
|
|
{ |
320
|
|
|
$imageWidth = $this->getWidth(); |
321
|
|
|
$imageHeight = $this->getHeight(); |
322
|
|
|
|
323
|
|
|
$width = Dimmensions::getIntegerValue('x', $width, $imageWidth); |
324
|
|
|
$height = Dimmensions::getIntegerValue('y', $height, $imageHeight); |
325
|
|
|
|
326
|
|
|
list($width, $height) = $this->calculateClientSize($width, $height); |
327
|
|
|
|
328
|
|
|
if (in_array($x, [self::CROP_BALANCED, self::CROP_ENTROPY, self::CROP_FACE], true)) { |
329
|
|
|
list($x, $y) = $this->image->getCropOffsets($width, $height, $x); |
330
|
|
|
} |
331
|
|
|
|
332
|
|
|
$x = Dimmensions::getPositionValue('x', $x, $width, $imageWidth); |
333
|
|
|
$y = Dimmensions::getPositionValue('y', $y, $height, $imageHeight); |
334
|
|
|
|
335
|
|
|
$this->image->crop($width, $height, $x, $y); |
336
|
|
|
|
337
|
|
|
return $this; |
338
|
|
|
} |
339
|
|
|
|
340
|
|
|
/** |
341
|
|
|
* Adjust the image to the given dimmensions. Resizes and crops the image maintaining the proportions. |
342
|
|
|
* |
343
|
|
|
* @param int|string $width The new width in number (pixels) or percentaje |
344
|
|
|
* @param int|string $height The new height in number (pixels) or percentaje |
345
|
|
|
* @param int|string $x The "x" position to crop. It can be number (pixels), percentaje, [left, center, right] or one of the Image::CROP_* constants |
346
|
|
|
* @param int|string $y The "y" position to crop. It can be number (pixels), percentaje or [top, middle, bottom] |
347
|
|
|
* |
348
|
|
|
* @return self |
349
|
|
|
*/ |
350
|
|
|
public function resizeCrop($width, $height, $x = 'center', $y = 'middle') |
351
|
|
|
{ |
352
|
|
|
$this->resize($width, $height, true); |
353
|
|
|
$this->crop($width, $height, $x, $y); |
354
|
|
|
|
355
|
|
|
return $this; |
356
|
|
|
} |
357
|
|
|
|
358
|
|
|
/** |
359
|
|
|
* Rotates the image. |
360
|
|
|
* |
361
|
|
|
* @param int $angle Rotation angle in degrees (anticlockwise) |
362
|
|
|
* |
363
|
|
|
* @return self |
364
|
|
|
*/ |
365
|
|
|
public function rotate($angle) |
366
|
|
|
{ |
367
|
|
|
if (($angle = intval($angle)) !== 0) { |
368
|
|
|
$this->image->rotate($angle); |
369
|
|
|
} |
370
|
|
|
|
371
|
|
|
return $this; |
372
|
|
|
} |
373
|
|
|
|
374
|
|
|
/** |
375
|
|
|
* Apply blur to image |
376
|
|
|
* |
377
|
|
|
* @param int $loops Quantity of blur effect loop |
378
|
|
|
* |
379
|
|
|
* @return self |
380
|
|
|
*/ |
381
|
|
|
public function blur($loops = 4) |
382
|
|
|
{ |
383
|
|
|
$this->image->blur($loops); |
384
|
|
|
|
385
|
|
|
return $this; |
386
|
|
|
} |
387
|
|
|
|
388
|
|
|
/** |
389
|
|
|
* Define the image compression quality for jpg images. |
390
|
|
|
* |
391
|
|
|
* @param int $quality The quality (from 0 to 100) |
392
|
|
|
* |
393
|
|
|
* @return self |
394
|
|
|
*/ |
395
|
|
|
public function quality($quality) |
396
|
|
|
{ |
397
|
|
|
$quality = intval($quality); |
398
|
|
|
|
399
|
|
|
if ($quality < 0) { |
400
|
|
|
$quality = 0; |
401
|
|
|
} elseif ($quality > 100) { |
402
|
|
|
$quality = 100; |
403
|
|
|
} |
404
|
|
|
|
405
|
|
|
$this->image->setCompressionQuality($quality); |
406
|
|
|
|
407
|
|
|
return $this; |
408
|
|
|
} |
409
|
|
|
|
410
|
|
|
/** |
411
|
|
|
* Add a watermark to current image. |
412
|
|
|
* |
413
|
|
|
* @param string $file Image to set as watermark |
|
|
|
|
414
|
|
|
* @param mixed $x Horizontal position |
415
|
|
|
* @param mixed $y Vertical position |
416
|
|
|
* |
417
|
|
|
* @return self |
418
|
|
|
*/ |
419
|
|
|
public function watermark(Image $image, $x = 'right', $y = 'bottom') |
420
|
|
|
{ |
421
|
|
|
$imageWidth = $this->getWidth(); |
422
|
|
|
$imageHeight = $this->getHeight(); |
423
|
|
|
|
424
|
|
|
$width = $image->getWidth(); |
425
|
|
|
$height = $image->getHeight(); |
426
|
|
|
|
427
|
|
|
$x = Dimmensions::getPositionValue('x', $x, $width, $imageWidth); |
428
|
|
|
$y = Dimmensions::getPositionValue('y', $y, $height, $imageHeight); |
429
|
|
|
|
430
|
|
|
$this->image->watermark($image->getImage(), $x, $y); |
431
|
|
|
|
432
|
|
|
return $this; |
433
|
|
|
} |
434
|
|
|
|
435
|
|
|
/** |
436
|
|
|
* Add opacity to image from 0 (transparent) to 100 (opaque). |
437
|
|
|
* |
438
|
|
|
* @param int $opacity Opacity value |
439
|
|
|
* |
440
|
|
|
* @return self |
441
|
|
|
*/ |
442
|
|
|
public function opacity($opacity) |
443
|
|
|
{ |
444
|
|
|
$this->image->opacity($opacity); |
445
|
|
|
|
446
|
|
|
return $this; |
447
|
|
|
} |
448
|
|
|
|
449
|
|
|
/** |
450
|
|
|
* Set the image progressive or not |
451
|
|
|
* |
452
|
|
|
* @param bool $progressive |
453
|
|
|
* |
454
|
|
|
* @return self |
455
|
|
|
*/ |
456
|
|
|
public function progressive($progressive = true) |
457
|
|
|
{ |
458
|
|
|
$this->image->setProgressive((bool) $progressive); |
459
|
|
|
|
460
|
|
|
return $this; |
461
|
|
|
} |
462
|
|
|
|
463
|
|
|
/** |
464
|
|
|
* Reads the EXIF data from a JPEG and returns an associative array |
465
|
|
|
* (requires the exif PHP extension enabled). |
466
|
|
|
* |
467
|
|
|
* @param null|string $key |
468
|
|
|
* |
469
|
|
|
* @return null|array |
470
|
|
|
*/ |
471
|
|
|
public function getExifData($key = null) |
472
|
|
|
{ |
473
|
|
|
if ($this->filename !== null && ($this->getMimeType() === 'image/jpeg')) { |
474
|
|
|
$exif = exif_read_data($this->filename); |
475
|
|
|
|
476
|
|
|
if ($key !== null) { |
477
|
|
|
return isset($exif[$key]) ? $exif[$key] : null; |
478
|
|
|
} |
479
|
|
|
|
480
|
|
|
return $exif; |
481
|
|
|
} |
482
|
|
|
} |
483
|
|
|
|
484
|
|
|
/** |
485
|
|
|
* Transform the image executing various operations of crop, resize, resizeCrop and format. |
486
|
|
|
* |
487
|
|
|
* @param string $operations The string with all operations separated by "|". |
488
|
|
|
* |
489
|
|
|
* @return self |
490
|
|
|
*/ |
491
|
|
|
public function transform($operations = null) |
492
|
|
|
{ |
493
|
|
|
//No transform operations, resize to fix the client size |
494
|
|
|
if (empty($operations)) { |
495
|
|
|
return $this->resize($this->getWidth(), $this->getHeight()); |
496
|
|
|
} |
497
|
|
|
|
498
|
|
|
$operations = self::parseOperations($operations); |
499
|
|
|
|
500
|
|
|
foreach ($operations as $operation) { |
501
|
|
|
switch ($operation['function']) { |
502
|
|
|
case 'crop': |
503
|
|
|
case 'resizecrop': |
504
|
|
|
if (empty($operation['params'][2])) { |
505
|
|
|
break; |
506
|
|
|
} |
507
|
|
|
|
508
|
|
|
switch ($operation['params'][2]) { |
509
|
|
|
case 'CROP_ENTROPY': |
510
|
|
|
$operation['params'][2] = self::CROP_ENTROPY; |
511
|
|
|
break; |
512
|
|
|
|
513
|
|
|
case 'CROP_BALANCED': |
514
|
|
|
$operation['params'][2] = self::CROP_BALANCED; |
515
|
|
|
break; |
516
|
|
|
|
517
|
|
|
case 'CROP_FACE': |
518
|
|
|
$operation['params'][2] = self::CROP_FACE; |
519
|
|
|
break; |
520
|
|
|
} |
521
|
|
|
|
522
|
|
|
break; |
523
|
|
|
} |
524
|
|
|
|
525
|
|
|
call_user_func_array([$this, $operation['function']], $operation['params']); |
526
|
|
|
} |
527
|
|
|
|
528
|
|
|
return $this; |
529
|
|
|
} |
530
|
|
|
|
531
|
|
|
/** |
532
|
|
|
* Send the HTTP header with the content-type, output the image data and die. |
533
|
|
|
*/ |
534
|
|
View Code Duplication |
public function show() |
535
|
|
|
{ |
536
|
|
|
if (($string = $this->getString()) && ($mimetype = $this->getMimeType())) { |
537
|
|
|
header('Content-Type: '.$mimetype); |
538
|
|
|
die($string); |
539
|
|
|
} |
540
|
|
|
} |
541
|
|
|
|
542
|
|
|
/** |
543
|
|
|
* Returns the image as base64 url. |
544
|
|
|
* |
545
|
|
|
* @return string|null |
546
|
|
|
*/ |
547
|
|
View Code Duplication |
public function base64() |
548
|
|
|
{ |
549
|
|
|
if (($string = $this->getString()) && ($mimetype = $this->getMimeType())) { |
550
|
|
|
$string = base64_encode($string); |
551
|
|
|
|
552
|
|
|
return "data:{$mimetype};base64,{$string}"; |
553
|
|
|
} |
554
|
|
|
} |
555
|
|
|
|
556
|
|
|
/** |
557
|
|
|
* Auto-rotate the image according with its exif data |
558
|
|
|
* Taken from: http://php.net/manual/en/function.exif-read-data.php#76964. |
559
|
|
|
* |
560
|
|
|
* @return self |
561
|
|
|
*/ |
562
|
|
|
public function autoRotate() |
563
|
|
|
{ |
564
|
|
|
switch ($this->getExifData('Orientation')) { |
565
|
|
|
case 2: |
566
|
|
|
$this->flop(); |
567
|
|
|
break; |
568
|
|
|
|
569
|
|
|
case 3: |
570
|
|
|
$this->rotate(180); |
571
|
|
|
break; |
572
|
|
|
|
573
|
|
|
case 4: |
574
|
|
|
$this->flip(); |
575
|
|
|
break; |
576
|
|
|
|
577
|
|
|
case 5: |
578
|
|
|
$this->flip()->rotate(90); |
579
|
|
|
break; |
580
|
|
|
|
581
|
|
|
case 6: |
582
|
|
|
$this->rotate(90); |
583
|
|
|
break; |
584
|
|
|
|
585
|
|
|
case 7: |
586
|
|
|
$this->flop()->rotate(90); |
587
|
|
|
break; |
588
|
|
|
|
589
|
|
|
case 8: |
590
|
|
|
$this->rotate(-90); |
591
|
|
|
break; |
592
|
|
|
} |
593
|
|
|
|
594
|
|
|
return $this; |
595
|
|
|
} |
596
|
|
|
|
597
|
|
|
/** |
598
|
|
|
* Check whether the image is an animated gif. |
599
|
|
|
* Copied from: https://github.com/Sybio/GifFrameExtractor/blob/master/src/GifFrameExtractor/GifFrameExtractor.php#L181. |
600
|
|
|
* |
601
|
|
|
* @param resource A stream pointer opened by fopen() |
602
|
|
|
* |
603
|
|
|
* @return bool |
604
|
|
|
*/ |
605
|
|
|
private static function isAnimatedGif($stream) |
606
|
|
|
{ |
607
|
|
|
$count = 0; |
608
|
|
|
|
609
|
|
|
while (!feof($stream) && $count < 2) { |
610
|
|
|
$chunk = fread($stream, 1024 * 100); //read 100kb at a time |
611
|
|
|
$count += preg_match_all('#\x00\x21\xF9\x04.{4}\x00(\x2C|\x21)#s', $chunk, $matches); |
612
|
|
|
} |
613
|
|
|
|
614
|
|
|
return $count > 1; |
615
|
|
|
} |
616
|
|
|
|
617
|
|
|
/** |
618
|
|
|
* Converts a string with operations in an array. |
619
|
|
|
* |
620
|
|
|
* @param string $operations The operations string |
621
|
|
|
* |
622
|
|
|
* @return array |
623
|
|
|
*/ |
624
|
|
|
private static function parseOperations($operations) |
625
|
|
|
{ |
626
|
|
|
$valid_operations = ['resize', 'resizecrop', 'crop', 'format', 'quality']; |
627
|
|
|
$operations = explode('|', str_replace(' ', '', $operations)); |
628
|
|
|
$return = []; |
629
|
|
|
|
630
|
|
|
foreach ($operations as $operations) { |
631
|
|
|
$params = explode(',', $operations); |
632
|
|
|
$function = strtolower(trim(array_shift($params))); |
633
|
|
|
|
634
|
|
|
if (!in_array($function, $valid_operations, true)) { |
635
|
|
|
throw new ImageException("The transform function '{$function}' is not valid"); |
636
|
|
|
} |
637
|
|
|
|
638
|
|
|
$return[] = [ |
639
|
|
|
'function' => $function, |
640
|
|
|
'params' => $params, |
641
|
|
|
]; |
642
|
|
|
} |
643
|
|
|
|
644
|
|
|
return $return; |
645
|
|
|
} |
646
|
|
|
|
647
|
|
|
/** |
648
|
|
|
* Checks the library to use and returns its class. |
649
|
|
|
* |
650
|
|
|
* @param string $library The library name (Gd, Imagick) |
651
|
|
|
* |
652
|
|
|
* @throws ImageException if the image library does not exists. |
653
|
|
|
* |
654
|
|
|
* @return string |
655
|
|
|
*/ |
656
|
|
|
private static function getLibraryClass($library) |
657
|
|
|
{ |
658
|
|
|
if (!$library) { |
659
|
|
|
$library = Libs\Imagick::checkCompatibility() ? self::LIB_IMAGICK : self::LIB_GD; |
660
|
|
|
} |
661
|
|
|
|
662
|
|
|
$class = 'Imagecow\\Libs\\'.$library; |
663
|
|
|
|
664
|
|
|
if (!class_exists($class)) { |
665
|
|
|
throw new ImageException('The image library is not valid'); |
666
|
|
|
} |
667
|
|
|
|
668
|
|
|
if (!$class::checkCompatibility()) { |
669
|
|
|
throw new ImageException("The image library '$library' is not installed in this computer"); |
670
|
|
|
} |
671
|
|
|
|
672
|
|
|
return $class; |
673
|
|
|
} |
674
|
|
|
} |
675
|
|
|
|
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.