Zebra_Image::resize()   F
last analyzed

Complexity

Conditions 55
Paths 265

Size

Total Lines 338
Code Lines 149

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 55
eloc 149
nc 265
nop 4
dl 0
loc 338
rs 2.0166
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 *  Methods used with the {@link resize()} method.
5
 */
6
define('ZEBRA_IMAGE_BOXED', 0);
7
define('ZEBRA_IMAGE_NOT_BOXED', 1);
8
define('ZEBRA_IMAGE_CROP_TOPLEFT', 2);
9
define('ZEBRA_IMAGE_CROP_TOPCENTER', 3);
10
define('ZEBRA_IMAGE_CROP_TOPRIGHT', 4);
11
define('ZEBRA_IMAGE_CROP_MIDDLELEFT', 5);
12
define('ZEBRA_IMAGE_CROP_CENTER', 6);
13
define('ZEBRA_IMAGE_CROP_MIDDLERIGHT', 7);
14
define('ZEBRA_IMAGE_CROP_BOTTOMLEFT', 8);
15
define('ZEBRA_IMAGE_CROP_BOTTOMCENTER', 9);
16
define('ZEBRA_IMAGE_CROP_BOTTOMRIGHT', 10);
17
18
// this enables handling of partially broken JPEG files without warnings/errors
19
ini_set('gd.jpeg_ignore_warning', true);
0 ignored issues
show
Bug introduced by
true of type true is incompatible with the type string expected by parameter $value of ini_set(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

19
ini_set('gd.jpeg_ignore_warning', /** @scrutinizer ignore-type */ true);
Loading history...
20
21
/**
22
 *  A compact (one-file only) and lightweight PHP library for image manipulation providing methods for performing several
23
 *  types of image manipulation operations and applying filters to images.
24
 *
25
 *  Read more {@link https://github.com/stefangabos/Zebra_Image/ here}
26
 *
27
 * @author         Stefan Gabos <[email protected]>
28
 * @version        2.3.0 (last revision: June 06, 2019)
29
 * @copyright  (c) 2006 - 2019 Stefan Gabos
30
 * @license        https://www.gnu.org/licenses/lgpl-3.0.txt GNU LESSER GENERAL PUBLIC LICENSE
31
 * @package        Zebra_Image
32
 */
33
class Zebra_Image
34
{
35
    /**
36
     *  Indicates the file system permissions to be set for newly created images.
37
     *
38
     *  Better is to leave this setting as it is.
39
     *
40
     *  If you know what you are doing, here is how you can calculate the permission levels:
41
     *
42
     *  - 400 Owner Read
43
     *  - 200 Owner Write
44
     *  - 100 Owner Execute
45
     *  - 40 Group Read
46
     *  - 20 Group Write
47
     *  - 10 Group Execute
48
     *  - 4 Global Read
49
     *  - 2 Global Write
50
     *  - 1 Global Execute
51
     *
52
     *  Default is 0755
53
     *
54
     * @var integer
55
     */
56
    public $chmod_value;
57
    /**
58
     *  If set to FALSE, images having both width and height smaller than the required width and height, will be left
59
     *  untouched ({@link jpeg_quality} and {@link png_compression} will still apply).
60
     *
61
     *  Available only for the {@link resize()} method
62
     *
63
     *  Default is TRUE
64
     *
65
     * @var boolean
66
     */
67
    public $enlarge_smaller_images;
68
    /**
69
     *  In case of an error read this property's value to see the error's code.
70
     *
71
     *  Possible error codes are:
72
     *
73
     *  - 1:  source file could not be found
74
     *  - 2:  source file is not readable
75
     *  - 3:  could not write target file
76
     *  - 4:  unsupported source file format
77
     *  - 5:  unsupported target file format
78
     *  - 6:  GD library version does not support target file format
79
     *  - 7:  GD library is not installed!
80
     *  - 8:  "chmod" command is disabled via configuration
81
     *  - 9:  "exif_read_data" function is not available
82
     *
83
     *  Default is 0 (no error).
84
     *
85
     * @var integer
86
     */
87
    public $error;
88
    /**
89
     *  If set to TRUE, JPEG images will be auto-rotated according to the {@link http://keyj.emphy.de/exif-orientation-rant/ Exif Orientation Tag}
90
     *  so that they are always shown correctly.
91
     *
92
     *  <samp>If you set this to TRUE you must also enable exif-support with --enable-exif. Windows users must enable both
93
     *  the php_mbstring.dll and php_exif.dll DLL's in php.ini. The php_mbstring.dll DLL must be loaded before the
94
     *  php_exif.dll DLL so adjust your php.ini accordingly. See {@link http://php.net/manual/en/exif.installation.php the PHP manual}</samp>
95
     *
96
     *  Default is FALSE
97
     *
98
     * @since 2.2.4
99
     *
100
     * @var boolean
101
     */
102
    public $auto_handle_exif_orientation;
103
    /**
104
     *  Indicates the quality of the output image (better quality means bigger file size).
105
     *
106
     *  Used only if the file at {@link target_path} is a JPG/JPEG image.
107
     *
108
     *  Range is 0 - 100
109
     *
110
     *  Default is 85
111
     *
112
     * @var integer
113
     */
114
    public $jpeg_quality;
115
    /**
116
     *  Indicates the compression level of the output image (lower compression means bigger file size).
117
     *
118
     *  Available only if PHP version is 5.1.2+, and only if the file at {@link target_path} is a PNG image. It will be
119
     *  ignored otherwise.
120
     *
121
     *  Range is 0 - 9
122
     *
123
     *  Default is 9
124
     *
125
     * @since 2.2
126
     *
127
     * @var integer
128
     */
129
    public $png_compression;
130
    /**
131
     *  Specifies whether, upon resizing, images should preserve their aspect ratio.
132
     *
133
     *  Available only for the {@link resize()} method
134
     *
135
     *  Default is TRUE
136
     *
137
     * @var boolean
138
     */
139
    public $preserve_aspect_ratio;
140
    /**
141
     *  Indicates whether a target files should preserve the source file's date/time.
142
     *
143
     *  Default is TRUE
144
     *
145
     * @since 1.0.4
146
     *
147
     * @var boolean
148
     */
149
    public $preserve_time;
150
    /**
151
     *  Indicates whether the target image should have a "sharpen" filter applied to it.
152
     *
153
     *  Can be very useful when creating thumbnails and should be used only when creating thumbnails.
154
     *
155
     *  <i>The sharpen filter relies on the "imageconvolution" PHP function which is available only for PHP version
156
     *  5.1.0+, and will leave the images unaltered for older versions!</i>
157
     *
158
     *  Default is FALSE
159
     *
160
     * @since 2.2
161
     *
162
     * @var boolean
163
     */
164
    public $sharpen_images;
165
    /**
166
     *  Path to an image file to apply the transformations to.
167
     *
168
     *  Supported file types are <b>GIF</b>, <b>PNG</b> and <b>JPEG</b>.
169
     *
170
     * @var    string
171
     */
172
    public $source_path;
173
    /**
174
     *  Path (including file name) to where to save the transformed image.
175
     *
176
     *  <i>Can be a different than {@link source_path} - the type of the transformed image will be as indicated by the
177
     *  file's extension (supported file types are GIF, PNG and JPEG)</i>.
178
     *
179
     * @var    string
180
     */
181
    public $target_path;
182
183
    /**
184
     *  Constructor of the class.
185
     *
186
     *  Initializes the class and the default properties
187
     *
188
     * @return void
189
     */
190
    public function __construct()
191
    {
192
        // set default values for properties
193
        $this->chmod_value = 0755;
194
195
        $this->error = 0;
196
197
        $this->jpeg_quality = 85;
198
199
        $this->png_compression = 9;
200
201
        $this->preserve_aspect_ratio = $this->preserve_time = $this->enlarge_smaller_images = true;
202
203
        $this->sharpen_images = $this->auto_handle_exif_orientation = false;
204
205
        $this->source_path = $this->target_path = '';
206
    }
207
208
    /**
209
     *  Applies one or more filters to the image given as {@link source_path} and outputs it as the file specified as
210
     *  {@link target_path}.
211
     *
212
     *  <samp>This method is available only if the {@link http://php.net/manual/en/function.imagefilter.php imagefilter}
213
     *  function is available (available from PHP 5+), and will leave images unaltered otherwise.</samp>
214
     *
215
     *  <code>
216
     *  // include the Zebra_Image library
217
     *  require 'path/to/Zebra_Image.php';
218
     *
219
     *  // instantiate the class
220
     *  $img = new Zebra_Image();
221
     *
222
     *  // a source image
223
     *  $img->source_path = 'path/to/source.ext';
224
     *
225
     *  // path to where should the resulting image be saved
226
     *  // note that by simply setting a different extension to the file will
227
     *  // instruct the script to create an image of that particular type
228
     *  $img->target_path = 'path/to/target.ext';
229
     *
230
     *  // apply the "grayscale" filter
231
     *  $img->apply_filter('grayscale');
232
     *
233
     *  // apply the "contrast" filter
234
     *  $img->apply_filter('contrast', -20);
235
     *  </code>
236
     *
237
     *  You can also apply multiple filters at once. In this case, the method requires a single argument, an array of
238
     *  arrays, containing the filters and associated arguments, where applicable:
239
     *
240
     *  <code>
241
     *  // create a sepia effect
242
     *  // note how we're applying multiple filters at once
243
     *  // each filter is in its own array
244
     *  $img->apply_filter(array(
245
     *
246
     *      // first we apply the "grayscale" filter
247
     *      array('grayscale'),
248
     *
249
     *      // then we apply the "colorize" filter with 90, 60, 40 as
250
     *      // the values for red, green and blue
251
     *      array('colorize', 90, 60, 40),
252
     *
253
     *  ));
254
     *  </code>
255
     *
256
     * @param string $filter                                        The (case-insensitive) name of the filter to apply. Can be one of the following:
257
     *
258
     *                              -   <b>brightness</b>       -   changes the brightness of the image; use <b>arg1</b>
259
     *                                                              to set the level of brightness; the range of brightness
260
     *                                                              is -255 to 255;
261
     *                              -   <b>colorize</b>         -   adds (subtracts) specified RGB values to each pixel;
262
     *                                                              use <b>arg1</b>, <b>arg2</b> and <b>arg3</b> in the
263
     *                                                              form of red, green, blue and <b>arg4</b> for the alpha
264
     *                                                              channel. the range for each color is -255 to 255 and
265
     *                                                              0 to 127 for alpha; <i>alpha support is available only
266
     *                                                              for PHP 5.2.5+</i>;
267
     *                              -   <b>contrast</b>         -   changes the contrast of the image; use <b>arg1</b>
268
     *                                                              to set the level of contrast; the range of contrast
269
     *                                                              is -100 to 100;
270
     *                              -   <b>gausian_blur</b>     -   blurs the image using the Gaussian method;
271
     *                              -   <b>grayscale</b>        -   converts the image into grayscale;
272
     *                              -   <b>edgedetect</b>       -   uses edge detection to highlight the edges in the image;
273
     *                              -   <b>emboss</b>           -   embosses the image;
274
     *                              -   <b>mean_removal</b>     -   uses mean removal to achieve a "sketchy" effect;
275
     *                              -   <b>negate</b>           -   reverses all the colors of the image;
276
     *                              -   <b>pixelate</b>         -   applies pixelation effect to the image, use <b>arg1</b>
277
     *                                                              to set the block size and <b>arg2</b> to set the
278
     *                                                              pixelation effect mode; <i>this filter is available
279
     *                                                              only for PHP 5.3.0+</i>;
280
     *                              -   <b>selective_blur</b>   -   blurs the image;
281
     *                              -   <b>smooth</b>           -   makes the image smoother. Use <b>arg1</b> to set the
282
     *                                                              level of smoothness. applies a 9-cell convolution matrix
283
     *                                                              where center pixel has the weight of <b>arg1</b> and
284
     *                                                              others weight of 1.0. the result is normalized by dividing
285
     *                                                              the sum with <b>arg1</b> + 8.0 (sum of the matrix).
286
     *                                                              any float is accepted;
287
     *
288
     * @param mixed  $arg1                                          Used by the following filters:
289
     *                                                              -   <b>brightness</b>       -   sets the brightness level (-255 to 255)
290
     *                                                              -   <b>contrast</b>         -   sets the contrast level (-100 to 100)
291
     *                                                              -   <b>colorize</b>         -   sets the value of the red component (-255 to 255)
292
     *                                                              -   <b>smooth</b>           -   sets the smoothness level
293
     *                                                              -   <b>pixelate</b>         -   sets the block size, in pixels
294
     *
295
     * @param mixed  $arg2                                          Used by the following filters:
296
     *                                                              -   <b>colorize</b>         -   sets the value of the green component (-255 to 255)
297
     *                                                              -   <b>pixelate</b>         -   whether to use advanced pixelation effect or not (defaults to FALSE).
298
     *
299
     * @param mixed  $arg3                                          Used by the following filters:
300
     *                                                              -   <b>colorize</b>         -   sets the value of the blue component (-255 to 255)
301
     *
302
     * @param mixed  $arg4                                          Used by the following filters:
303
     *                                                              -   <b>colorize</b>         -   alpha channel; a value between 0 and 127. 0 indicates
304
     *                                                              completely opaque while 127 indicates completely
305
     *                                                              transparent.
306
     *
307
     * @return boolean             Returns TRUE on success or FALSE on error.
308
     *
309
     *                              If {@link http://php.net/manual/en/function.imagefilter.php imagefilter} is not
310
     *                              available the method will return FALSE without setting an {@link error} code.
311
     *
312
     *                              If the requested filter doesn't exist, or invalid arguments are passed, the method
313
     *                              will trigger a warning.
314
     *
315
     *                              If FALSE is returned and you are sure that
316
     *                              {@link http://php.net/manual/en/function.imagefilter.php imagefilter} exists and that
317
     *                              the requested filter is valid, check the {@link error} property to see the error code.
318
     * @since 2.2.2
319
     *
320
     */
321
    public function apply_filter($filter, $arg1 = '', $arg2 = '', $arg3 = '', $arg4 = '')
322
    {
323
        // if "imagefilter" function exists and the requested filter exists
324
        if (function_exists('imagefilter')) // if image resource was successfully created
325
        {
326
            if ($this->_create_from_source()) {
327
                // prepare the target image
328
                $target_identifier = $this->_prepare_image($this->source_width, $this->source_height, -1);
329
330
                // copy the original image
331
                imagecopyresampled(
332
333
                    $target_identifier,
334
                    $this->source_identifier,
335
                    0,
336
                    0,
337
                    0,
338
                    0,
339
                    $this->source_width,
340
                    $this->source_height,
341
                    $this->source_width,
342
                    $this->source_height
343
344
                );
345
346
                // if multiple filters are to be applied at once
347
                if (is_array($filter)) {
348
                    // iterate through the filters
349
                    foreach ($filter as $arguments) // if filter exists
350
                    {
351
                        if (defined('IMG_FILTER_' . strtoupper($arguments[0]))) {
352
                            // try to apply the filter...
353
                            if (!@call_user_func_array('imagefilter', array_merge([$target_identifier, constant('IMG_FILTER_' . strtoupper($arguments[0]))], array_slice($arguments, 1)))) // ...and trigger an error if the filter could not be applied
354
                            {
355
                                trigger_error('Invalid arguments used for "' . strtoupper($arguments[0]) . '" filter', E_USER_WARNING);
356
                            }
357
                            // if filter doesn't exists, trigger an error
358
                        } else {
359
                            trigger_error('Filter "' . strtoupper($arguments[0]) . '" is not available', E_USER_WARNING);
360
                        }
361
                    }
362
                    // if a single filter is to be applied and it is available
363
                } elseif (defined('IMG_FILTER_' . strtoupper($filter))) {
364
                    // get all the arguments passed to the method
365
                    $arguments = func_get_args();
366
367
                    // try to apply the filter...
368
                    if (!@call_user_func_array('imagefilter', array_merge([$target_identifier, constant('IMG_FILTER_' . strtoupper($filter))], array_slice($arguments, 1)))) // ...and trigger an error if the filter could not be applied
369
                    {
370
                        trigger_error('Invalid arguments used for "' . strtoupper($arguments[0]) . '" filter', E_USER_WARNING);
371
                    }
372
                    // if filter doesn't exists, trigger an error
373
                } else {
374
                    trigger_error('Filter "' . strtoupper($arguments[0]) . '" is not available', E_USER_WARNING);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $arguments seems to be never defined.
Loading history...
375
                }
376
377
                // write image
378
                return $this->_write_image($target_identifier);
379
            }
380
        }
381
382
        // if script gets this far, return false
383
        // note that we do not set the error level as it has been already set
384
        // by the _create_from_source() method earlier, if the case
385
        return false;
386
    }
387
388
    /**
389
     *  Crops a portion of the image given as {@link source_path} and outputs it as the file specified as {@link target_path}.
390
     *
391
     *  <code>
392
     *  // include the Zebra_Image library
393
     *  require 'path/to/Zebra_Image.php';
394
     *
395
     *  // instantiate the class
396
     *  $img = new Zebra_Image();
397
     *
398
     *  // a source image
399
     *  $img->source_path = 'path/to/source.ext';
400
     *
401
     *  // path to where should the resulting image be saved
402
     *  // note that by simply setting a different extension to the file will
403
     *  // instruct the script to create an image of that particular type
404
     *  $img->target_path = 'path/to/target.ext';
405
     *
406
     *  // crop a rectangle of 100x100 pixels, starting from the top-left corner
407
     *  $img->crop(0, 0, 100, 100);
408
     *  </code>
409
     *
410
     * @param integer $start_x x coordinate to start cropping from
411
     *
412
     * @param integer $start_y y coordinate to start cropping from
413
     *
414
     * @param integer $end_x   x coordinate where to end the cropping
415
     *
416
     * @param integer $end_y   y coordinate where to end the cropping
417
     *
418
     * @return boolean     Returns TRUE on success or FALSE on error.
419
     *
420
     *                      If FALSE is returned, check the {@link error} property to see the error code.
421
     * @since  1.0.4
422
     *
423
     */
424
    public function crop($start_x, $start_y, $end_x, $end_y)
425
    {
426
        // this method might be also called internally
427
        // in this case, there's a fifth argument that points to an already existing image identifier
428
        $args = func_get_args();
429
430
        // if fifth argument exists
431
        if (isset($args[4]) && is_resource($args[4])) {
432
            // that it is the image identifier that we'll be using further on
433
            $this->source_identifier = $args[4];
0 ignored issues
show
Bug Best Practice introduced by
The property source_identifier does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
434
435
            // set this to true so that the script will continue to execute at the next IF
436
            $result = true;
437
438
            // if method is called as usually
439
            // try to create an image resource from source path
440
        } else {
441
            $result = $this->_create_from_source();
442
        }
443
444
        // if image resource was successfully created
445
        if ($result !== false) {
446
            // compute width and height
447
            $width  = $end_x - $start_x;
448
            $height = $end_y - $start_y;
449
450
            // prepare the target image
451
            $target_identifier = $this->_prepare_image($width, $height, -1);
452
453
            // crop the image
454
            imagecopyresampled(
455
456
                $target_identifier,
457
                $this->source_identifier,
458
                0,
459
                0,
460
                $start_x,
461
                $start_y,
462
                $width,
463
                $height,
464
                $width,
465
                $height
466
467
            );
468
469
            // write image
470
            return $this->_write_image($target_identifier);
471
        }
472
473
        // if script gets this far, return false
474
        // note that we do not set the error level as it has been already set
475
        // by the _create_from_source() method earlier
476
        return false;
477
    }
478
479
    /**
480
     *  Flips both horizontally and vertically the image given as {@link source_path} and outputs the resulted image as
481
     *  {@link target_path}
482
     *
483
     *  <code>
484
     *  // include the Zebra_Image library
485
     *  require 'path/to/Zebra_Image.php';
486
     *
487
     *  // instantiate the class
488
     *  $img = new Zebra_Image();
489
     *
490
     *  // a source image
491
     *  $img->source_path = 'path/to/source.ext';
492
     *
493
     *  // path to where should the resulting image be saved
494
     *  // note that by simply setting a different extension to the file will
495
     *  // instruct the script to create an image of that particular type
496
     *  $img->target_path = 'path/to/target.ext';
497
     *
498
     *  // flip the image both horizontally and vertically
499
     *  $img->flip_both();
500
     *  </code>
501
     *
502
     * @return boolean     Returns TRUE on success or FALSE on error.
503
     *
504
     *                      If FALSE is returned, check the {@link error} property to see the error code.
505
     * @since 2.1
506
     *
507
     */
508
    public function flip_both()
509
    {
510
        return $this->_flip('both');
511
    }
512
513
    /**
514
     *  Flips horizontally the image given as {@link source_path} and outputs the resulted image as {@link target_path}
515
     *
516
     *  <code>
517
     *  // include the Zebra_Image library
518
     *  require 'path/to/Zebra_Image.php';
519
     *
520
     *  // instantiate the class
521
     *  $img = new Zebra_Image();
522
     *
523
     *  // a source image
524
     *  $img->source_path = 'path/to/source.ext';
525
     *
526
     *  // path to where should the resulting image be saved
527
     *  // note that by simply setting a different extension to the file will
528
     *  // instruct the script to create an image of that particular type
529
     *  $img->target_path = 'path/to/target.ext';
530
     *
531
     *  // flip the image horizontally
532
     *  $img->flip_horizontal();
533
     *  </code>
534
     *
535
     * @return boolean     Returns TRUE on success or FALSE on error.
536
     *
537
     *                      If FALSE is returned, check the {@link error} property to see the error code.
538
     */
539
    public function flip_horizontal()
540
    {
541
        return $this->_flip('horizontal');
542
    }
543
544
    /**
545
     *  Flips vertically the image given as {@link source_path} and outputs the resulted image as {@link target_path}
546
     *
547
     *  <code>
548
     *  // include the Zebra_Image library
549
     *  require 'path/to/Zebra_Image.php';
550
     *
551
     *  // instantiate the class
552
     *  $img = new Zebra_Image();
553
     *
554
     *  // a source image
555
     *  $img->source_path = 'path/to/source.ext';
556
     *
557
     *  // path to where should the resulting image be saved
558
     *  // note that by simply setting a different extension to the file will
559
     *  // instruct the script to create an image of that particular type
560
     *  $img->target_path = 'path/to/target.ext';
561
     *
562
     *  // flip the image vertically
563
     *  $img->flip_vertical();
564
     *  </code>
565
     *
566
     * @return boolean     Returns TRUE on success or FALSE on error.
567
     *
568
     *                      If FALSE is returned, check the {@link error} property to see the error code.
569
     */
570
    public function flip_vertical()
571
    {
572
        return $this->_flip('vertical');
573
    }
574
575
    /**
576
     *  Resizes the image given as {@link source_path} and outputs the resulted image as {@link target_path}.
577
     *
578
     *  <code>
579
     *  // include the Zebra_Image library
580
     *  require 'path/to/Zebra_Image.php';
581
     *
582
     *  // instantiate the class
583
     *  $img = new Zebra_Image();
584
     *
585
     *  // a source image
586
     *  $img->source_path = 'path/to/source.ext';
587
     *
588
     *  // path to where should the resulting image be saved
589
     *  // note that by simply setting a different extension to the file will
590
     *  // instruct the script to create an image of that particular type
591
     *  $img->target_path = 'path/to/target.ext';
592
     *
593
     *  // apply a "sharpen" filter to the resulting images
594
     *  $img->sharpen_images = true;
595
     *
596
     *  // resize the image to exactly 150x150 pixels, without altering
597
     *  // aspect ratio, by using the CROP_CENTER method
598
     *  $img->resize(150, 150, ZEBRA_IMAGE_CROP_CENTER);
599
     *  </code>
600
     *
601
     * @param integer     $width                The width to resize the image to.
602
     *
603
     *                                          If set to <b>0</b>, the width will be automatically adjusted, depending
604
     *                                          on the value of the <b>height</b> argument so that the image preserves
605
     *                                          its aspect ratio.
606
     *
607
     *                                          If {@link preserve_aspect_ratio} is set to TRUE and both this and the
608
     *                                          <b>height</b> arguments are values greater than <b>0</b>, the image will
609
     *                                          be resized to the exact required width and height and the aspect ratio
610
     *                                          will be preserved - (also see the description for the <b>method</b>
611
     *                                          argument below on how can this be done).
612
     *
613
     *                                          If {@link preserve_aspect_ratio} is set to FALSE, the image will be
614
     *                                          resized to the required width and the aspect ratio will be ignored.
615
     *
616
     *                                          If both <b>width</b> and <b>height</b> are set to <b>0</b>, a copy of
617
     *                                          the source image will be created ({@link jpeg_quality} and
618
     *                                          {@link png_compression} will still apply).
619
     *
620
     *                                          If either <b>width</b> or <b>height</b> are set to <b>0</b>, the script
621
     *                                          will consider the value of the {@link preserve_aspect_ratio} to bet set
622
     *                                          to TRUE regardless of its actual value!
623
     *
624
     * @param integer     $height               The height to resize the image to.
625
     *
626
     *                                          If set to <b>0</b>, the height will be automatically adjusted, depending
627
     *                                          on the value of the <b>width</b> argument so that the image preserves
628
     *                                          its aspect ratio.
629
     *
630
     *                                          If {@link preserve_aspect_ratio} is set to TRUE and both this and the
631
     *                                          <b>width</b> arguments are values greater than <b>0</b>, the image will
632
     *                                          be resized to the exact required width and height and the aspect ratio
633
     *                                          will be preserved - (also see the description for the <b>method</b>
634
     *                                          argument below on how can this be done).
635
     *
636
     *                                          If {@link preserve_aspect_ratio} is set to FALSE, the image will be
637
     *                                          resized to the required height and the aspect ratio will be ignored.
638
     *
639
     *                                          If both <b>width</b> and <b>height</b> are set to <b>0</b>, a copy of
640
     *                                          the source image will be created ({@link jpeg_quality} and
641
     *                                          {@link png_compression} will still apply).
642
     *
643
     *                                          If either <b>height</b> or <b>width</b> are set to <b>0</b>, the script
644
     *                                          will consider the value of the {@link preserve_aspect_ratio} to bet set
645
     *                                          to TRUE regardless of its actual value!
646
     *
647
     * @param int         $method               (Optional) Method to use when resizing images to exact width and height
648
     *                                          while preserving aspect ratio.
649
     *
650
     *                                          If the {@link preserve_aspect_ratio} property is set to TRUE and both the
651
     *                                          <b>width</b> and <b>height</b> arguments are values greater than <b>0</b>,
652
     *                                          the image will be resized to the exact given width and height and the
653
     *                                          aspect ratio will be preserved by using on of the following methods:
654
     *
655
     *                                          -   <b>ZEBRA_IMAGE_BOXED</b> - the image will be scaled so that it will
656
     *                                              fit in a box with the given width and height (both width/height will
657
     *                                              be smaller or equal to the required width/height) and then it will
658
     *                                              be centered both horizontally and vertically. The blank area will be
659
     *                                              filled with the color specified by the <b>bgcolor</b> argument. (the
660
     *                                              blank area will be filled only if the image is not transparent!)
661
     *
662
     *                                          -   <b>ZEBRA_IMAGE_NOT_BOXED</b> - the image will be scaled so that it
663
     *                                              <i>could</i> fit in a box with the given width and height but will
664
     *                                              not be enclosed in a box with given width and height. The new width/
665
     *                                              height will be both smaller or equal to the required width/height
666
     *
667
     *                                          -   <b>ZEBRA_IMAGE_CROP_TOPLEFT</b>
668
     *                                          -   <b>ZEBRA_IMAGE_CROP_TOPCENTER</b>
669
     *                                          -   <b>ZEBRA_IMAGE_CROP_TOPRIGHT</b>
670
     *                                          -   <b>ZEBRA_IMAGE_CROP_MIDDLELEFT</b>
671
     *                                          -   <b>ZEBRA_IMAGE_CROP_CENTER</b>
672
     *                                          -   <b>ZEBRA_IMAGE_CROP_MIDDLERIGHT</b>
673
     *                                          -   <b>ZEBRA_IMAGE_CROP_BOTTOMLEFT</b>
674
     *                                          -   <b>ZEBRA_IMAGE_CROP_BOTTOMCENTER</b>
675
     *                                          -   <b>ZEBRA_IMAGE_CROP_BOTTOMRIGHT</b>
676
     *
677
     *                                          For the methods involving crop, first the image is scaled so that both
678
     *                                          its sides are equal or greater than the respective sizes of the bounding
679
     *                                          box; next, a region of required width and height will be cropped from
680
     *                                          indicated region of the resulted image.
681
     *
682
     *                                          Default is ZEBRA_IMAGE_CROP_CENTER
683
     *
684
     * @param hexadecimal $background_color     (Optional) The hexadecimal color (like "#FFFFFF" or "#FFF") of the
0 ignored issues
show
Bug introduced by
The type hexadecimal was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
685
     *                                          blank area. See the <b>method</b> argument.
686
     *
687
     *                                          When set to -1 the script will preserve transparency for transparent GIF
688
     *                                          and PNG images. For non-transparent images the background will be white
689
     *                                          (#FFFFFF) in this case.
690
     *
691
     *                                          Default is -1
692
     *
693
     * @return boolean                         Returns TRUE on success or FALSE on error.
694
     *
695
     *                                          If FALSE is returned, check the {@link error} property to see what went
696
     *                                          wrong
697
     */
698
    public function resize($width = 0, $height = 0, $method = ZEBRA_IMAGE_CROP_CENTER, $background_color = -1)
699
    {
700
        // if image resource was successfully created
701
        if ($this->_create_from_source()) {
702
            // if either width or height is to be adjusted automatically
703
            // set a flag telling the script that, even if $preserve_aspect_ratio is set to false
704
            // treat everything as if it was set to true
705
            if ($width == 0 || $height == 0) {
706
                $auto_preserve_aspect_ratio = true;
707
            }
708
709
            // if aspect ratio needs to be preserved
710
            if ($this->preserve_aspect_ratio || isset($auto_preserve_aspect_ratio)) {
711
                // if height is given and width is to be computed accordingly
712
                if ($width == 0 && $height > 0) {
713
                    // get the original image's aspect ratio
714
                    $aspect_ratio = $this->source_width / $this->source_height;
715
716
                    // the target image's height is as given as argument to the method
717
                    $target_height = $height;
718
719
                    // compute the target image's width, preserving the aspect ratio
720
                    $target_width = round($height * $aspect_ratio);
721
                    // if width is given and height is to be computed accordingly
722
                } elseif ($width > 0 && $height == 0) {
723
                    // get the original image's aspect ratio
724
                    $aspect_ratio = $this->source_height / $this->source_width;
725
726
                    // the target image's width is as given as argument to the method
727
                    $target_width = $width;
728
729
                    // compute the target image's height, preserving the aspect ratio
730
                    $target_height = round($width * $aspect_ratio);
731
                    // if both width and height are given and ZEBRA_IMAGE_BOXED or ZEBRA_IMAGE_NOT_BOXED methods are to be used
732
                } elseif ($width > 0 && $height > 0 && ($method == 0 || $method == 1)) {
733
                    // compute the horizontal and vertical aspect ratios
734
                    $vertical_aspect_ratio   = $height / $this->source_height;
735
                    $horizontal_aspect_ratio = $width / $this->source_width;
736
737
                    // if the image's newly computed height would be inside the bounding box
738
                    if (round($horizontal_aspect_ratio * $this->source_height < $height)) {
0 ignored issues
show
Bug introduced by
$horizontal_aspect_ratio...source_height < $height of type boolean is incompatible with the type double|integer expected by parameter $num of round(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

738
                    if (round(/** @scrutinizer ignore-type */ $horizontal_aspect_ratio * $this->source_height < $height)) {
Loading history...
739
                        // the target image's width is as given as argument to the method
740
                        $target_width = $width;
741
742
                        // compute the target image's height so that the image will stay inside the bounding box
743
                        $target_height = round($horizontal_aspect_ratio * $this->source_height);
744
                        // otherwise
745
                    } else {
746
                        // the target image's height is as given as argument to the method
747
                        $target_height = $height;
748
749
                        // compute the target image's width so that the image will stay inside the bounding box
750
                        $target_width = round($vertical_aspect_ratio * $this->source_width);
751
                    }
752
                    // if both width and height are given and image is to be cropped in order to get to the required size
753
                } elseif ($width > 0 && $height > 0 && $method > 1 && $method < 11) {
754
                    // compute the horizontal and vertical aspect ratios
755
                    $vertical_aspect_ratio   = $this->source_height / $height;
756
                    $horizontal_aspect_ratio = $this->source_width / $width;
757
758
                    // we'll use one of the two
759
                    $aspect_ratio =
760
761
                        $vertical_aspect_ratio < $horizontal_aspect_ratio ?
762
763
                            $vertical_aspect_ratio :
764
765
                            $horizontal_aspect_ratio;
766
767
                    // compute the target image's width, preserving the aspect ratio
768
                    $target_width = round($this->source_width / $aspect_ratio);
769
770
                    // compute the target image's height, preserving the aspect ratio
771
                    $target_height = round($this->source_height / $aspect_ratio);
772
                    // for any other case
773
                } else {
774
                    // we will create a copy of the source image
775
                    $target_width  = $this->source_width;
776
                    $target_height = $this->source_height;
777
                }
778
                // if aspect ratio does not need to be preserved
779
            } else {
780
                // compute the target image's width
781
                $target_width = ($width > 0 ? $width : $this->source_width);
782
783
                // compute the target image's height
784
                $target_height = ($height > 0 ? $height : $this->source_height);
785
            }
786
787
            // if
788
            if (
789
790
                // all images are to be resized - including images that are smaller than the given width/height
791
                $this->enlarge_smaller_images
792
                || // smaller images than the given width/height are to be left untouched
793
                // but current image has at leas one side that is larger than the required width/height
794
                ($width > 0 && $height > 0 ?
795
796
                    ($this->source_width > $width || $this->source_height > $height) :
797
798
                    ($this->source_width > $target_width || $this->source_height > $target_height)
799
800
                )
801
802
            ) {
803
                // if
804
                if (
805
806
                    // aspect ratio needs to be preserved AND
807
                    ($this->preserve_aspect_ratio || isset($auto_preserve_aspect_ratio))
808
                    && // both width and height are given
809
                    ($width > 0 && $height > 0)
810
                    && // images are to be cropped
811
                    ($method > 1 && $method < 11)
812
813
                ) {
814
                    // prepare the target image
815
                    $target_identifier = $this->_prepare_image($target_width, $target_height, $background_color);
816
817
                    imagecopyresampled(
818
819
                        $target_identifier,
820
                        $this->source_identifier,
821
                        0,
822
                        0,
823
                        0,
824
                        0,
825
                        $target_width,
826
                        $target_height,
827
                        $this->source_width,
828
                        $this->source_height
829
830
                    );
831
832
                    // do the crop according to the required method
833
                    switch ($method) {
834
                        // if image needs to be cropped from the top-left corner
835
                        case ZEBRA_IMAGE_CROP_TOPLEFT:
836
837
                            // crop accordingly
838
                            return $this->crop(
839
                                0,
840
                                0,
841
                                $width,
842
                                $height,
843
                                $target_identifier // crop this resource instead
844
                            );
845
846
                            break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
847
848
                        // if image needs to be cropped from the top-center
849
                        case ZEBRA_IMAGE_CROP_TOPCENTER:
850
851
                            // crop accordingly
852
                            return $this->crop(
853
                                floor(($target_width - $width) / 2),
854
                                0,
855
                                floor(($target_width - $width) / 2) + $width,
856
                                $height,
857
                                $target_identifier // crop this resource instead
858
                            );
859
860
                            break;
861
862
                        // if image needs to be cropped from the top-right corner
863
                        case ZEBRA_IMAGE_CROP_TOPRIGHT:
864
865
                            // crop accordingly
866
                            return $this->crop(
867
                                $target_width - $width,
868
                                0,
869
                                $target_width,
870
                                $height,
871
                                $target_identifier // crop this resource instead
872
                            );
873
874
                            break;
875
876
                        // if image needs to be cropped from the middle-left
877
                        case ZEBRA_IMAGE_CROP_MIDDLELEFT:
878
879
                            // crop accordingly
880
                            return $this->crop(
881
882
                                0,
883
                                floor(($target_height - $height) / 2),
884
                                $width,
885
                                floor(($target_height - $height) / 2) + $height,
886
                                $target_identifier // crop this resource instead
887
888
                            );
889
890
                            break;
891
892
                        // if image needs to be cropped from the center of the image
893
                        case ZEBRA_IMAGE_CROP_CENTER:
894
895
                            // crop accordingly
896
                            return $this->crop(
897
898
                                floor(($target_width - $width) / 2),
899
                                floor(($target_height - $height) / 2),
900
                                floor(($target_width - $width) / 2) + $width,
901
                                floor(($target_height - $height) / 2) + $height,
902
                                $target_identifier // crop this resource instead
903
904
                            );
905
906
                            break;
907
908
                        // if image needs to be cropped from the middle-right
909
                        case ZEBRA_IMAGE_CROP_MIDDLERIGHT:
910
911
                            // crop accordingly
912
                            return $this->crop(
913
914
                                $target_width - $width,
915
                                floor(($target_height - $height) / 2),
916
                                $target_width,
917
                                floor(($target_height - $height) / 2) + $height,
918
                                $target_identifier // crop this resource instead
919
920
                            );
921
922
                            break;
923
924
                        // if image needs to be cropped from the bottom-left corner
925
                        case ZEBRA_IMAGE_CROP_BOTTOMLEFT:
926
927
                            // crop accordingly
928
                            return $this->crop(
929
930
                                0,
931
                                $target_height - $height,
932
                                $width,
933
                                $target_height,
934
                                $target_identifier // crop this resource instead
935
936
                            );
937
938
                            break;
939
940
                        // if image needs to be cropped from the bottom-center
941
                        case ZEBRA_IMAGE_CROP_BOTTOMCENTER:
942
943
                            // crop accordingly
944
                            return $this->crop(
945
946
                                floor(($target_width - $width) / 2),
947
                                $target_height - $height,
948
                                floor(($target_width - $width) / 2) + $width,
949
                                $target_height,
950
                                $target_identifier // crop this resource instead
951
952
                            );
953
954
                            break;
955
956
                        // if image needs to be cropped from the bottom-right corner
957
                        case ZEBRA_IMAGE_CROP_BOTTOMRIGHT:
958
959
                            // crop accordingly
960
                            return $this->crop(
961
962
                                $target_width - $width,
963
                                $target_height - $height,
964
                                $target_width,
965
                                $target_height,
966
                                $target_identifier // crop this resource instead
967
968
                            );
969
970
                            break;
971
                    }
972
973
                    // if aspect ratio doesn't need to be preserved or
974
                    // it needs to be preserved and method is ZEBRA_IMAGE_BOXED or ZEBRA_IMAGE_NOT_BOXED
975
                } else {
976
                    // prepare the target image
977
                    $target_identifier = $this->_prepare_image(
978
                        ($width > 0 && $height > 0 && $method != ZEBRA_IMAGE_NOT_BOXED ? $width : $target_width),
979
                        ($width > 0 && $height > 0 && $method != ZEBRA_IMAGE_NOT_BOXED ? $height : $target_height),
980
                        $background_color
981
                    );
982
983
                    imagecopyresampled(
984
985
                        $target_identifier,
986
                        $this->source_identifier,
987
                        ($width > 0 && $height > 0 && $method != ZEBRA_IMAGE_NOT_BOXED ? ($width - $target_width) / 2 : 0),
988
                        ($width > 0 && $height > 0 && $method != ZEBRA_IMAGE_NOT_BOXED ? ($height - $target_height) / 2 : 0),
989
                        0,
990
                        0,
991
                        $target_width,
992
                        $target_height,
993
                        $this->source_width,
994
                        $this->source_height
995
996
                    );
997
998
                    // if script gets this far, write the image to disk
999
                    return $this->_write_image($target_identifier);
1000
                }
1001
1002
                // if we get here it means that
1003
                // smaller images than the given width/height are to be left untouched
1004
                // therefore, we save the image as it is
1005
            } else {
1006
                // prepare the target image
1007
                $target_identifier = $this->_prepare_image($this->source_width, $this->source_height, $background_color);
1008
1009
                imagecopyresampled(
1010
1011
                    $target_identifier,
1012
                    $this->source_identifier,
1013
                    0,
1014
                    0,
1015
                    0,
1016
                    0,
1017
                    $this->source_width,
1018
                    $this->source_height,
1019
                    $this->source_width,
1020
                    $this->source_height
1021
1022
                );
1023
1024
                // previously to 2.2.7 I was simply calling the _write_images() method without the code from above this
1025
                // comment and therefore, when resizing transparent images to a format which doesn't support transparency
1026
                // and the "enlarge_smaller_images" property being set to FALSE, the "background_color" argument was not
1027
                // applied and lead to unexpected background colors for the resulting images
1028
                return $this->_write_image($target_identifier);
1029
            }
1030
        }
1031
1032
        // if script gets this far return false
1033
        // note that we do not set the error level as it has been already set
1034
        // by the _create_from_source() method earlier
1035
        return false;
1036
    }
1037
1038
    /**
1039
     *  Rotates the image given as {@link source_path} and outputs the resulted image as {@link target_path}.
1040
     *
1041
     *  <code>
1042
     *  // include the Zebra_Image library
1043
     *  require 'path/to/Zebra_Image.php';
1044
     *
1045
     *  // instantiate the class
1046
     *  $img = new Zebra_Image();
1047
     *
1048
     *  // a source image
1049
     *  $img->source_path = 'path/to/source.ext';
1050
     *
1051
     *  // path to where should the resulting image be saved
1052
     *  // note that by simply setting a different extension to the file will
1053
     *  // instruct the script to create an image of that particular type
1054
     *  $img->target_path = 'path/to/target.ext';
1055
     *
1056
     *  // rotate the image 45 degrees, clockwise
1057
     *  $img->rotate(45);
1058
     *  </code>
1059
     *
1060
     * @param double $angle                     Angle by which to rotate the image clockwise.
1061
     *
1062
     *                                          Between 0 and 360.
1063
     *
1064
     * @param mixed  $background_color          (Optional) The hexadecimal color (like "#FFFFFF" or "#FFF") of the
1065
     *                                          uncovered zone after the rotation.
1066
     *
1067
     *                                          When set to -1 the script will preserve transparency for transparent GIF
1068
     *                                          and PNG images. For non-transparent images the background will be white
1069
     *                                          (#FFFFFF) in this case.
1070
     *
1071
     *                                          Default is -1.
1072
     *
1073
     * @return boolean                         Returns TRUE on success or FALSE on error.
1074
     *
1075
     *                                          If FALSE is returned, check the {@link error} property to see the error
1076
     *                                          code.
1077
     */
1078
    public function rotate($angle, $background_color = -1)
1079
    {
1080
        // get function arguments
1081
        $arguments = func_get_args();
1082
1083
        // if a third argument exists
1084
        $use_existing_source = (isset($arguments[2]) && $arguments[2] === false);
1085
1086
        // if we came here just to fix orientation or if image resource was successfully created
1087
        if ($use_existing_source || $this->_create_from_source()) {
1088
            // angles are given clockwise but imagerotate works counterclockwise so we need to negate our value
1089
            $angle = -$angle;
1090
1091
            // if the uncovered zone after the rotation is to be transparent
1092
            if ($background_color == -1) {
1093
                // if target image is a PNG
1094
                if ($this->target_type == 'png') {
1095
                    // allocate a transparent color
1096
                    $background_color = imagecolorallocatealpha($this->source_identifier, 0, 0, 0, 127);
1097
                    // if target image is a GIF
1098
                } elseif ($this->target_type == 'gif') {
1099
                    // if source image was a GIF and a transparent color existed
1100
                    if ($this->source_type == IMAGETYPE_GIF && $this->source_transparent_color_index >= 0) {
1101
                        // use that color
1102
                        $background_color = imagecolorallocate(
1103
                            $this->source_identifier,
1104
                            $this->source_transparent_color['red'],
1105
                            $this->source_transparent_color['green'],
1106
                            $this->source_transparent_color['blue']
1107
                        );
1108
                        // if image had no transparent color
1109
                    } else {
1110
                        // allocate a transparent color
1111
                        $background_color = imagecolorallocate($this->source_identifier, 255, 255, 255);
1112
1113
                        // make color transparent
1114
                        imagecolortransparent($this->source_identifier, $background_color);
1115
                    }
1116
                    // for other image types
1117
                } else {
1118
                    // use white as the color of uncovered zone after the rotation
1119
                    $background_color = imagecolorallocate($this->source_identifier, 255, 255, 255);
1120
                }
1121
                // if a background color is given
1122
            } else {
1123
                // convert the color to RGB values
1124
                $background_color = $this->_hex2rgb($background_color);
1125
1126
                // allocate the color to the image identifier
1127
                $background_color = imagecolorallocate(
1128
1129
                    $this->source_identifier,
1130
                    $background_color['r'],
1131
                    $background_color['g'],
1132
                    $background_color['b']
1133
1134
                );
1135
            }
1136
1137
            // rotate the image
1138
            $target_identifier = imagerotate($this->source_identifier, $angle, $background_color);
1139
1140
            // if we called this method from the _create_from_source() method
1141
            // because we are fixing orientation
1142
            if ($use_existing_source) {
1143
                // make any further method work on the rotated image
1144
                $this->source_identifier = $target_identifier;
0 ignored issues
show
Bug Best Practice introduced by
The property source_identifier does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
1145
1146
                // update the width and height of the image to the values
1147
                // of the rotated image
1148
                $this->source_width  = imagesx($target_identifier);
0 ignored issues
show
Bug Best Practice introduced by
The property source_width does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
1149
                $this->source_height = imagesy($target_identifier);
0 ignored issues
show
Bug Best Practice introduced by
The property source_height does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
1150
1151
                return true;
1152
                // write image otherwise
1153
            } else {
1154
                return $this->_write_image($target_identifier);
1155
            }
1156
        }
1157
1158
        // if script gets this far return false
1159
        // note that we do not set the error level as it has been already set
1160
        // by the _create_from_source() method earlier
1161
        return false;
1162
    }
1163
1164
    /**
1165
     *  Returns an array containing the image identifier representing the image obtained from {@link $source_path}, the
1166
     *  image's width and height and the image's type
1167
     *
1168
     * @access private
1169
     */
1170
    private function _create_from_source()
1171
    {
1172
        // perform some error checking first
1173
        // if the GD library is not installed
1174
        if (!function_exists('gd_info')) {
1175
            // save the error level and stop the execution of the script
1176
            $this->error = 7;
1177
1178
            return false;
1179
            // if source file does not exist
1180
        } elseif (!is_file($this->source_path)) {
1181
            // save the error level and stop the execution of the script
1182
            $this->error = 1;
1183
1184
            return false;
1185
            // if source file is not readable
1186
        } elseif (!is_readable($this->source_path)) {
1187
            // save the error level and stop the execution of the script
1188
            $this->error = 2;
1189
1190
            return false;
1191
            // if target file is same as source file and source file is not writable
1192
        } elseif ($this->target_path == $this->source_path && !is_writable($this->source_path)) {
1193
            // save the error level and stop the execution of the script
1194
            $this->error = 3;
1195
1196
            return false;
1197
1198
            // try to get source file width, height and type
1199
            // and if it founds an unsupported file type
1200
        } elseif (!list($this->source_width, $this->source_height, $this->source_type) = @getimagesize($this->source_path)) {
0 ignored issues
show
Bug Best Practice introduced by
The property source_height does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
Bug Best Practice introduced by
The property source_width does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
Bug Best Practice introduced by
The property source_type does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
1201
            // save the error level and stop the execution of the script
1202
            $this->error = 4;
1203
1204
            return false;
1205
            // if no errors so far
1206
        } else {
1207
            // get target file's type based on the file extension
1208
            $this->target_type = strtolower(substr($this->target_path, strrpos($this->target_path, '.') + 1));
0 ignored issues
show
Bug Best Practice introduced by
The property target_type does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
1209
1210
            // create an image from file using extension dependant function
1211
            // checks for file extension
1212
            switch ($this->source_type) {
1213
                // if GIF
1214
                case IMAGETYPE_GIF:
1215
1216
                    // create an image from file
1217
                    $identifier = imagecreatefromgif($this->source_path);
1218
1219
                    // get the index of the transparent color (if any)
1220
                    if (($this->source_transparent_color_index = imagecolortransparent($identifier)) >= 0)
0 ignored issues
show
Bug Best Practice introduced by
The property source_transparent_color_index does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
1221
1222
                        // get the transparent color's RGB values
1223
                        // we have to mute errors because there are GIF images which *are* transparent and everything
1224
                        // works as expected, but imagecolortransparent() returns a color that is outside the range of
1225
                        // colors in the image's pallette...
1226
                    {
1227
                        $this->source_transparent_color = @imagecolorsforindex($identifier, $this->source_transparent_color_index);
0 ignored issues
show
Bug Best Practice introduced by
The property source_transparent_color does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
1228
                    }
1229
1230
                    break;
1231
1232
                // if JPEG
1233
                case IMAGETYPE_JPEG:
1234
1235
                    // create an image from file
1236
                    $identifier = imagecreatefromjpeg($this->source_path);
1237
1238
                    break;
1239
1240
                // if PNG
1241
                case IMAGETYPE_PNG:
1242
1243
                    // create an image from file
1244
                    $identifier = imagecreatefrompng($this->source_path);
1245
1246
                    // disable blending
1247
                    imagealphablending($identifier, false);
1248
1249
                    // save full alpha channel information
1250
                    imagesavealpha($identifier, true);
1251
1252
                    break;
1253
1254
                default:
1255
1256
                    // if unsupported file type
1257
                    // note that we call this if the file is not GIF, JPG or PNG even though the getimagesize function
1258
                    // handles more image types
1259
                    $this->error = 4;
1260
1261
                    return false;
1262
            }
1263
        }
1264
1265
        // if target file has to have the same timestamp as the source image
1266
        // save it as a global property of the class
1267
        if ($this->preserve_time) {
1268
            $this->source_image_time = filemtime($this->source_path);
0 ignored issues
show
Bug Best Practice introduced by
The property source_image_time does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
1269
        }
1270
1271
        // make available the source image's identifier
1272
        $this->source_identifier = $identifier;
0 ignored issues
show
Bug Best Practice introduced by
The property source_identifier does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
1273
1274
        // for JPEG files, if we need to handle exif orientation automatically
1275
        if ($this->auto_handle_exif_orientation && $this->source_type === IMAGETYPE_JPEG) // if "exif_read_data" function is not available, return false
1276
        {
1277
            if (!function_exists('exif_read_data')) {
1278
                // save the error level and stop the execution of the script
1279
                $this->error = 9;
1280
1281
                return false;
1282
                // if "exif_read_data" function is available, EXIF information is available, orientation information is available and orientation needs fixing
1283
            } elseif (($exif = exif_read_data($this->source_path)) && isset($exif['Orientation']) && in_array($exif['Orientation'], [3, 6, 8])) {
1284
                // fix the orientation
1285
                switch ($exif['Orientation']) {
1286
                    case 3:
1287
1288
                        // 180 rotate left
1289
                        $this->rotate(180, -1, false);
1290
                        break;
1291
1292
                    case 6:
1293
1294
                        // 90 rotate right
1295
                        $this->rotate(90, -1, false);
1296
                        break;
1297
1298
                    case 8:
1299
1300
                        // 90 rotate left
1301
                        $this->rotate(-90, -1, false);
1302
                        break;
1303
                }
1304
            }
1305
        }
1306
1307
        return true;
1308
    }
1309
1310
    /**
1311
     *  Flips horizontally or vertically or both ways the image given as {@link source_path}.
1312
     *
1313
     * @return boolean     Returns TRUE on success or FALSE on error.
1314
     *
1315
     *                      If FALSE is returned, check the {@link error} property to see the error code.
1316
     * @since  2.1
1317
     *
1318
     * @access private
1319
     *
1320
     */
1321
    private function _flip($orientation)
1322
    {
1323
        // if image resource was successfully created
1324
        if ($this->_create_from_source()) {
1325
            // prepare the target image
1326
            $target_identifier = $this->_prepare_image($this->source_width, $this->source_height, -1);
1327
1328
            // flip according to $orientation
1329
            switch ($orientation) {
1330
                case 'horizontal':
1331
1332
                    imagecopyresampled(
1333
1334
                        $target_identifier,
1335
                        $this->source_identifier,
1336
                        0,
1337
                        0,
1338
                        ($this->source_width - 1),
1339
                        0,
1340
                        $this->source_width,
1341
                        $this->source_height,
1342
                        -$this->source_width,
1343
                        $this->source_height
1344
1345
                    );
1346
1347
                    break;
1348
1349
                case 'vertical':
1350
1351
                    imagecopyresampled(
1352
1353
                        $target_identifier,
1354
                        $this->source_identifier,
1355
                        0,
1356
                        0,
1357
                        0,
1358
                        ($this->source_height - 1),
1359
                        $this->source_width,
1360
                        $this->source_height,
1361
                        $this->source_width,
1362
                        -$this->source_height
1363
1364
                    );
1365
1366
                    break;
1367
1368
                case 'both':
1369
1370
                    imagecopyresampled(
1371
1372
                        $target_identifier,
1373
                        $this->source_identifier,
1374
                        0,
1375
                        0,
1376
                        ($this->source_width - 1),
1377
                        ($this->source_height - 1),
1378
                        $this->source_width,
1379
                        $this->source_height,
1380
                        -$this->source_width,
1381
                        -$this->source_height
1382
1383
                    );
1384
1385
                    break;
1386
            }
1387
1388
            // write image
1389
            return $this->_write_image($target_identifier);
1390
        }
1391
1392
        // if script gets this far, return false
1393
        // note that we do not set the error level as it has been already set
1394
        // by the _create_from_source() method earlier
1395
        return false;
1396
    }
1397
1398
    /**
1399
     *  Converts a hexadecimal representation of a color (i.e. #123456 or #AAA) to a RGB representation.
1400
     *
1401
     *  The RGB values will be a value between 0 and 255 each.
1402
     *
1403
     * @param string $color                 Hexadecimal representation of a color (i.e. #123456 or #AAA).
1404
     *
1405
     * @param string $default_on_error      (Optional) Hexadecimal representation of a color to be used in case $color
1406
     *                                      is not recognized as a hexadecimal color.
1407
     *
1408
     *                                      Default is #FFFFFF
1409
     *
1410
     * @return array                       Returns an associative array with the values of (R)ed, (G)reen and (B)lue
1411
     *
1412
     * @access private
1413
     */
1414
    private function _hex2rgb($color, $default_on_error = '#FFFFFF')
1415
    {
1416
        // if color is not formatted correctly
1417
        // use the default color
1418
        if (preg_match('/^#?([a-f]|[0-9]){3}(([a-f]|[0-9]){3})?$/i', $color) == 0) {
1419
            $color = $default_on_error;
1420
        }
1421
1422
        // trim off the "#" prefix from $background_color
1423
        $color = ltrim($color, '#');
1424
1425
        // if color is given using the shorthand (i.e. "FFF" instead of "FFFFFF")
1426
        if (strlen($color) == 3) {
1427
            $tmp = '';
1428
1429
            // take each value
1430
            // and duplicate it
1431
            for ($i = 0; $i < 3; $i++) {
1432
                $tmp .= str_repeat($color[$i], 2);
1433
            }
1434
1435
            // the color in it's full, 6 characters length notation
1436
            $color = $tmp;
1437
        }
1438
1439
        // decimal representation of the color
1440
        $int = hexdec($color);
1441
1442
        // extract and return the RGB values
1443
        return [
1444
1445
            'r' => 0xFF & ($int >> 0x10),
1446
            'g' => 0xFF & ($int >> 0x8),
1447
            'b' => 0xFF & $int,
1448
1449
        ];
1450
    }
1451
1452
    /**
1453
     *  Creates a blank image of given width, height and background color.
1454
     *
1455
     * @param integer $width            Width of the new image.
1456
     *
1457
     * @param integer $height           Height of the new image.
1458
     *
1459
     * @param string  $background_color (Optional) The hexadecimal color of the background.
1460
     *
1461
     *                                          Can also be -1 case in which the script will try to create a transparent
1462
     *                                          image, if possible.
1463
     *
1464
     *                                          Default is #FFFFFF.
1465
     *
1466
     * @return                                 Returns the identifier of the newly created image.
0 ignored issues
show
Bug introduced by
The type Returns was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
1467
     *
1468
     * @access private
1469
     */
1470
    private function _prepare_image($width, $height, $background_color = '#FFFFFF')
1471
    {
1472
        // create a blank image
1473
        $identifier = imagecreatetruecolor((int)$width <= 0 ? 1 : (int)$width, (int)$height <= 0 ? 1 : (int)$height);
1474
1475
        // if we are creating a transparent image, and image type supports transparency
1476
        if ($background_color == -1 && $this->target_type != 'jpg') {
1477
            // disable blending
1478
            imagealphablending($identifier, false);
1479
1480
            // allocate a transparent color
1481
            $background_color = imagecolorallocatealpha($identifier, 0, 0, 0, 127);
1482
1483
            // we also need to set this for saving gifs
1484
            imagecolortransparent($identifier, $background_color);
1485
1486
            // save full alpha channel information
1487
            imagesavealpha($identifier, true);
1488
            // if we are not creating a transparent image
1489
        } else {
1490
            // convert hex color to rgb
1491
            $background_color = $this->_hex2rgb($background_color);
1492
1493
            // prepare the background color
1494
            $background_color = imagecolorallocate($identifier, $background_color['r'], $background_color['g'], $background_color['b']);
1495
        }
1496
1497
        // fill the image with the background color
1498
        imagefill($identifier, 0, 0, $background_color);
1499
1500
        // return the image's identifier
1501
        return $identifier;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $identifier returns the type GdImage|resource which is incompatible with the documented return type Returns.
Loading history...
1502
    }
1503
1504
    /**
1505
     *  Sharpens images. Useful when creating thumbnails.
1506
     *
1507
     *  Code taken from the comments at {@link http://docs.php.net/imageconvolution}.
1508
     *
1509
     *  <i>This function will yield a result only for PHP version 5.1.0+ and will leave the image unaltered for older
1510
     *  versions!</i>
1511
     *
1512
     * @param  $identifier identifier  An image identifier
0 ignored issues
show
Bug introduced by
The type identifier was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
1513
     *
1514
     * @access private
1515
     */
1516
    private function _sharpen_image($image)
1517
    {
1518
        // if the "sharpen_images" is set to true and we're running an appropriate version of PHP
1519
        // (the "imageconvolution" is available only for PHP 5.1.0+)
1520
        if ($this->sharpen_images && version_compare(PHP_VERSION, '5.1.0') >= 0) {
1521
            // the convolution matrix as an array of three arrays of three floats
1522
            $matrix = [
1523
                [-1.2, -1, -1.2],
1524
                [-1, 20, -1],
1525
                [-1.2, -1, -1.2],
1526
            ];
1527
1528
            // the divisor of the matrix
1529
            $divisor = array_sum(array_map('array_sum', $matrix));
1530
1531
            // color offset
1532
            $offset = 0;
1533
1534
            // sharpen image
1535
            imageconvolution($image, $matrix, $divisor, $offset);
1536
        }
1537
1538
        // return the image's identifier
1539
        return $image;
1540
    }
1541
1542
    /**
1543
     *  Creates a new image from given image identifier having the extension as specified by {@link target_path}.
1544
     *
1545
     * @param  $identifier identifier  An image identifier
1546
     *
1547
     * @return boolean                 Returns TRUE on success or FALSE on error.
1548
     *
1549
     *                                  If FALSE is returned, check the {@link error} property to see the error code.
1550
     *
1551
     * @access private
1552
     */
1553
    private function _write_image($identifier)
1554
    {
1555
        // sharpen image if it's required
1556
        $this->_sharpen_image($identifier);
1557
1558
        // image saving process goes according to required extension
1559
        switch ($this->target_type) {
1560
            // if GIF
1561
            case 'gif':
1562
1563
                // if GD support for this file type is not available
1564
                // in version 1.6 of GD the support for GIF files was dropped see
1565
                // http://php.net/manual/en/function.imagegif.php#function.imagegif.notes
1566
                if (!function_exists('imagegif')) {
1567
                    // save the error level and stop the execution of the script
1568
                    $this->error = 6;
1569
1570
                    return false;
1571
                    // if, for some reason, file could not be created
1572
                } elseif (@!imagegif($identifier, $this->target_path)) {
1573
                    // save the error level and stop the execution of the script
1574
                    $this->error = 3;
1575
1576
                    return false;
1577
                }
1578
1579
                break;
1580
1581
            // if JPEG
1582
            case 'jpg':
1583
            case 'jpeg':
1584
1585
                // if GD support for this file type is not available
1586
                if (!function_exists('imagejpeg')) {
1587
                    // save the error level and stop the execution of the script
1588
                    $this->error = 6;
1589
1590
                    return false;
1591
                    // if, for some reason, file could not be created
1592
                } elseif (@!imagejpeg($identifier, $this->target_path, $this->jpeg_quality)) {
1593
                    // save the error level and stop the execution of the script
1594
                    $this->error = 3;
1595
1596
                    return false;
1597
                }
1598
1599
                break;
1600
1601
            // if PNG
1602
            case 'png':
1603
1604
                // if GD support for this file type is not available
1605
                if (!function_exists('imagepng')) {
1606
                    // save the error level and stop the execution of the script
1607
                    $this->error = 6;
1608
1609
                    return false;
1610
                    // if, for some reason, file could not be created
1611
                } elseif (@!imagepng($identifier, $this->target_path, $this->png_compression)) {
1612
                    // save the error level and stop the execution of the script
1613
                    $this->error = 3;
1614
1615
                    return false;
1616
                }
1617
1618
                break;
1619
1620
            // if not a supported file extension
1621
            default:
1622
1623
                // save the error level and stop the execution of the script
1624
                $this->error = 5;
1625
1626
                return false;
1627
        }
1628
1629
        // get a list of functions disabled via configuration
1630
        $disabled_functions = @ini_get('disable_functions');
1631
1632
        // if the 'chmod' function is not disabled via configuration
1633
        if ($disabled_functions == '' || strpos('chmod', $disabled_functions) === false) {
0 ignored issues
show
Bug introduced by
It seems like $disabled_functions can also be of type false; however, parameter $needle of strpos() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1633
        if ($disabled_functions == '' || strpos('chmod', /** @scrutinizer ignore-type */ $disabled_functions) === false) {
Loading history...
1634
            // chmod the file
1635
            chmod($this->target_path, intval($this->chmod_value, 8));
1636
            // save the error level
1637
        } else {
1638
            $this->error = 8;
1639
        }
1640
1641
        // if target file has to have the same timestamp as the source image
1642
        if ($this->preserve_time && isset($this->source_image_time)) {
1643
            // touch the newly created file
1644
            @touch($this->target_path, $this->source_image_time);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for touch(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

1644
            /** @scrutinizer ignore-unhandled */ @touch($this->target_path, $this->source_image_time);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
1645
        }
1646
1647
        // free memory
1648
        imagedestroy($this->source_identifier);
1649
        imagedestroy($identifier);
1650
1651
        // return true
1652
        return true;
1653
    }
1654
}
1655