Passed
Branch master (465698)
by Michael
05:38
created

Zebra_Image::resize()   F

Complexity

Conditions 55
Paths 265

Size

Total Lines 273
Code Lines 90

Duplication

Lines 54
Ratio 19.78 %

Importance

Changes 0
Metric Value
cc 55
eloc 90
nc 265
nop 4
dl 54
loc 273
rs 3.3333
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);
20
21
/**
22
 *  A compact, lightweight, object-oriented image manipulation library written in and for PHP, that provides methods
23
 *  for performing several types of image manipulation operations. It doesn't require any external libraries other than
24
 *  the GD2 extension (with which PHP usually comes precompiled with).
25
 *
26
 *  The code is heavily commented and generates no warnings/errors/notices when PHP's error reporting level is set to
27
 *  E_ALL.
28
 *
29
 *  With this library you can rescale, flip, rotate and crop images. It supports loading and saving images in the GIF,
30
 *  JPEG and PNG formats and preserves transparency for GIF, PNG and PNG24.
31
 *
32
 *  The cool thing about it is that it can resize images to exact given width and height and still maintain aspect
33
 *  ratio.
34
 *
35
 *  Visit {@link http://stefangabos.ro/php-libraries/zebra-image/} for more information.
36
 *
37
 *  For more resources visit {@link http://stefangabos.ro/}
38
 *
39
 * @author         Stefan Gabos <[email protected]>
40
 * @version        2.2.3 (last revision: July 14, 2013)
41
 * @copyright  (c) 2006 - 2013 Stefan Gabos
42
 * @license        http://www.gnu.org/licenses/lgpl-3.0.txt GNU LESSER GENERAL PUBLIC LICENSE
43
 * @package        Zebra_Image
44
 */
45
class Zebra_Image
46
{
47
48
    /**
49
     *  Indicates the file system permissions to be set for newly created images.
50
     *
51
     *  Better is to leave this setting as it is.
52
     *
53
     *  If you know what you are doing, here is how you can calculate the permission levels:
54
     *
55
     *  - 400 Owner Read
56
     *  - 200 Owner Write
57
     *  - 100 Owner Execute
58
     *  - 40 Group Read
59
     *  - 20 Group Write
60
     *  - 10 Group Execute
61
     *  - 4 Global Read
62
     *  - 2 Global Write
63
     *  - 1 Global Execute
64
     *
65
     *  Default is 0755
66
     *
67
     * @var integer
68
     */
69
    public $chmod_value;
70
71
    /**
72
     *  If set to FALSE, images having both width and height smaller than the required width and height, will be left
73
     *  untouched ({@link jpeg_quality} and {@link png_compression} will still apply).
74
     *
75
     *  Available only for the {@link resize()} method
76
     *
77
     *  Default is TRUE
78
     *
79
     * @var boolean
80
     */
81
    public $enlarge_smaller_images;
82
83
    /**
84
     *  In case of an error read this property's value to see the error's code.
85
     *
86
     *  Possible error codes are:
87
     *
88
     *  - 1:  source file could not be found
89
     *  - 2:  source file is not readable
90
     *  - 3:  could not write target file
91
     *  - 4:  unsupported source file format
92
     *  - 5:  unsupported target file format
93
     *  - 6:  GD library version does not support target file format
94
     *  - 7:  GD library is not installed!
95
     *  - 8:  "chmod" command is disabled via configuration
96
     *
97
     *  Default is 0 (no error).
98
     *
99
     * @var integer
100
     */
101
    public $error;
102
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
    /**
117
     *  Indicates the compression level of the output image (lower compression means bigger file size).
118
     *
119
     *  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
120
     *  ignored otherwise.
121
     *
122
     *  Range is 0 - 9
123
     *
124
     *  Default is 9
125
     *
126
     * @since 2.2
127
     *
128
     * @var integer
129
     */
130
    public $png_compression;
131
132
    /**
133
     *  Specifies whether, upon resizing, images should preserve their aspect ratio.
134
     *
135
     *  Available only for the {@link resize()} method
136
     *
137
     *  Default is TRUE
138
     *
139
     * @var boolean
140
     */
141
    public $preserve_aspect_ratio;
142
143
    /**
144
     *  Indicates whether a target files should preserve the source file's date/time.
145
     *
146
     *  Default is TRUE
147
     *
148
     * @since 1.0.4
149
     *
150
     * @var boolean
151
     */
152
    public $preserve_time;
153
154
    /**
155
     *  Indicates whether the target image should have a "sharpen" filter applied to it.
156
     *
157
     *  Can be very useful when creating thumbnails and should be used only when creating thumbnails.
158
     *
159
     *  <i>The sharpen filter relies on the "imageconvolution" PHP function which is available only for PHP version
160
     *  5.1.0+, and will leave the images unaltered for older versions!</i>
161
     *
162
     *  Default is FALSE
163
     *
164
     * @since 2.2
165
     *
166
     * @var boolean
167
     */
168
    public $sharpen_images;
169
170
    /**
171
     *  Path to an image file to apply the transformations to.
172
     *
173
     *  Supported file types are <b>GIF</b>, <b>PNG</b> and <b>JPEG</b>.
174
     *
175
     * @var string
176
     */
177
    public $source_path;
178
179
    /**
180
     *  Path (including file name) to where to save the transformed image.
181
     *
182
     *  <i>Can be a different than {@link source_path} - the type of the transformed image will be as indicated by the
183
     *  file's extension (supported file types are GIF, PNG and JPEG)</i>.
184
     *
185
     * @var string
186
     */
187
    public $target_path;
188
189
    /**
190
     *  Constructor of the class.
191
     *
192
     *  Initializes the class and the default properties
193
     *
194
     */
195
    public function __construct()
196
    {
197
198
        // set default values for properties
199
        $this->chmod_value = 0755;
200
201
        $this->error = 0;
202
203
        $this->jpeg_quality = 85;
204
205
        $this->png_compression = 9;
206
207
        $this->preserve_aspect_ratio = $this->preserve_time = $this->enlarge_smaller_images = true;
208
209
        $this->sharpen_images = false;
210
211
        $this->source_path = $this->target_path = '';
212
    }
213
214
    /**
215
     *  Applies one or more filters to the image given as {@link source_path} and outputs it as the file specified as
216
     *  {@link target_path}.
217
     *
218
     *  <samp>This method is available only if the {@link http://php.net/manual/en/function.imagefilter.php imagefilter}
219
     *  function is available (available from PHP 5+), and will leave images unaltered otherwise.</samp>
220
     *
221
     *  <code>
222
     *  // include the Zebra_Image library
223
     *  require 'path/to/Zebra_Image.php';
224
     *
225
     *  // instantiate the class
226
     *  $img = new Zebra_Image();
227
     *
228
     *  // a source image
229
     *  $img->source_path = 'path/to/source.ext';
230
     *
231
     *  // path to where should the resulting image be saved
232
     *  // note that by simply setting a different extension to the file will
233
     *  // instruct the script to create an image of that particular type
234
     *  $img->target_path = 'path/to/target.ext';
235
     *
236
     *  // apply the "grayscale" filter
237
     *  $img->apply_filter('grayscale');
238
     *
239
     *  // apply the "contrast" filter
240
     *  $img->apply_filter('contrast', -20);
241
     *  </code>
242
     *
243
     *  You can also apply multiple filters at once. In this case, the method requires a single argument, an array of
244
     *  arrays, containing the filters and associated arguments, where applicable:
245
     *
246
     *  <code>
247
     *  // create a sepia effect
248
     *  // note how we're applying multiple filters at once
249
     *  // each filter is in its own array
250
     *  $img->apply_filter(array(
251
     *
252
     *      // first we apply the "grayscale" filter
253
     *      array('grayscale'),
254
     *
255
     *      // then we apply the "colorize" filter with 90, 60, 40 as
256
     *      // the values for red, green and blue
257
     *      array('colorize', 90, 60, 40),
258
     *
259
     *  ));
260
     *  </code>
261
     *
262
     * @param string $filter The (case-insensitive) name of the filter to apply. Can be one of the following:
263
     *
264
     *                              -   <b>brightness</b>       -   changes the brightness of the image; use <b>arg1</b>
265
     *                                                              to set the level of brightness; the range of brightness
266
     *                                                              is -255 to 255;
267
     *                              -   <b>colorize</b>         -   adds (subtracts) specified RGB values to each pixel;
268
     *                                                              use <b>arg1</b>, <b>arg2</b> and <b>arg3</b> in the
269
     *                                                              form of red, green, blue and <b>arg4</b> for the alpha
270
     *                                                              channel. the range for each color is -255 to 255 and
271
     *                                                              0 to 127 for alpha; <i>alpha support is available only
272
     *                                                              for PHP 5.2.5+</i>;
273
     *                              -   <b>contrast</b>         -   changes the contrast of the image; use <b>arg1</b>
274
     *                                                              to set the level of contrast; the range of contrast
275
     *                                                              is -100 to 100;
276
     *                              -   <b>gausian_blur</b>     -   blurs the image using the Gaussian method;
277
     *                              -   <b>grayscale</b>        -   converts the image into grayscale;
278
     *                              -   <b>edgedetect</b>       -   uses edge detection to highlight the edges in the image;
279
     *                              -   <b>emboss</b>           -   embosses the image;
280
     *                              -   <b>mean_removal</b>     -   uses mean removal to achieve a "sketchy" effect;
281
     *                              -   <b>negate</b>           -   reverses all the colors of the image;
282
     *                              -   <b>pixelate</b>         -   applies pixelation effect to the image, use <b>arg1</b>
283
     *                                                              to set the block size and <b>arg2</b> to set the
284
     *                                                              pixelation effect mode; <i>this filter is available
285
     *                                                              only for PHP 5.3.0+</i>;
286
     *                              -   <b>selective_blur</b>   -   blurs the image;
287
     *                              -   <b>smooth</b>           -   makes the image smoother. Use <b>arg1</b> to set the
288
     *                                                              level of smoothness. applies a 9-cell convolution matrix
289
     *                                                              where center pixel has the weight of <b>arg1</b> and
290
     *                                                              others weight of 1.0. the result is normalized by dividing
291
     *                                                              the sum with <b>arg1</b> + 8.0 (sum of the matrix).
292
     *                                                              any float is accepted;
293
     *
294
     * @param mixed  $arg1   Used by the following filters:
295
     *                       -   <b>brightness</b>       -   sets the brightness level (-255 to 255)
296
     *                       -   <b>contrast</b>         -   sets the contrast level (-100 to 100)
297
     *                       -   <b>colorize</b>         -   sets the value of the red component (-255 to 255)
298
     *                       -   <b>smooth</b>           -   sets the smoothness level
299
     *                       -   <b>pixelate</b>         -   sets the block size, in pixels
300
     *
301
     * @param mixed  $arg2   Used by the following filters:
302
     *                       -   <b>colorize</b>         -   sets the value of the green component (-255 to 255)
303
     *                       -   <b>pixelate</b>         -   whether to use advanced pixelation effect or not (defaults to FALSE).
304
     *
305
     * @param mixed  $arg3   Used by the following filters:
306
     *                       -   <b>colorize</b>         -   sets the value of the blue component (-255 to 255)
307
     *
308
     * @param mixed  $arg4   Used by the following filters:
309
     *                       -   <b>colorize</b>         -   alpha channel; a value between 0 and 127. 0 indicates
310
     *                       completely opaque while 127 indicates completely
311
     *                       transparent.
312
     *
313
     * @since 2.2.2
314
     *
315
     * @return boolean Returns TRUE on success or FALSE on error.
316
     *
317
     *                              If {@link http://php.net/manual/en/function.imagefilter.php imagefilter} is not
318
     *                              available the method will return FALSE without setting an {@link error} code.
319
     *
320
     *                              If the requested filter doesn't exist, or invalid arguments are passed, the method
321
     *                              will trigger a warning.
322
     *
323
     *                              If FALSE is returned and you are sure that
324
     *                              {@link http://php.net/manual/en/function.imagefilter.php imagefilter} exists and that
325
     *                              the requested filter is valid, check the {@link error} property to see the error code.
326
     */
327
    public function apply_filter($filter, $arg1 = '', $arg2 = '', $arg3 = '', $arg4 = '')
328
    {
329
330
        // if "imagefilter" function exists and the requested filter exists
331
        if (function_exists('imagefilter')) { // if image resource was successfully created
332
            if ($this->_create_from_source()) {
333
334
                // prepare the target image
335
                $target_identifier = $this->_prepare_image($this->source_width, $this->source_height, -1);
336
337
                // copy the original image
338
                imagecopyresampled(
339
340
                    $target_identifier, $this->source_identifier, 0, 0, 0, 0, $this->source_width, $this->source_height, $this->source_width, $this->source_height
341
342
                );
343
344
                // if multiple filters are to be applied at once
345
                if (is_array($filter)) {
346
347
                    // iterate through the filters
348
                    foreach ($filter as $arguments) { // if filter exists
349
                        if (defined('IMG_FILTER_' . strtoupper($arguments[0]))) {
350
351
                            // try to apply the filter...
352
                            if (!@call_user_func_array('imagefilter', array_merge(array($target_identifier, constant('IMG_FILTER_' . strtoupper($arguments[0]))), array_slice($arguments, 1)))) {
353
                                // ...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
358
                            // if filter doesn't exists, trigger an error
359
                        } else {
360
                            trigger_error('Filter "' . strtoupper($arguments[0]) . '" is not available', E_USER_WARNING);
361
                        }
362
                    }
363
364
                    // if a single filter is to be applied and it is available
365
                } elseif (defined('IMG_FILTER_' . strtoupper($filter))) {
366
367
                    // get all the arguments passed to the method
368
                    $arguments = func_get_args();
369
370
                    // try to apply the filter...
371
                    if (!@call_user_func_array('imagefilter', array_merge(array($target_identifier, constant('IMG_FILTER_' . strtoupper($filter))), array_slice($arguments, 1)))) {
372
                        // ...and trigger an error if the filter could not be applied
373
374
                        trigger_error('Invalid arguments used for "' . strtoupper($arguments[0]) . '" filter', E_USER_WARNING);
375
                    }
376
377
                    // if filter doesn't exists, trigger an error
378
                } else {
379
                    trigger_error('Filter "' . strtoupper($arguments[0]) . '" is not available', E_USER_WARNING);
380
                }
381
382
                // write image
383
                return $this->_write_image($target_identifier);
384
            }
385
        }
386
387
        // if script gets this far, return false
388
        // note that we do not set the error level as it has been already set
389
        // by the _create_from_source() method earlier, if the case
390
        return false;
391
    }
392
393
    /**
394
     *  Crops a portion of the image given as {@link source_path} and outputs it as the file specified as {@link target_path}.
395
     *
396
     *  <code>
397
     *  // include the Zebra_Image library
398
     *  require 'path/to/Zebra_Image.php';
399
     *
400
     *  // instantiate the class
401
     *  $img = new Zebra_Image();
402
     *
403
     *  // a source image
404
     *  $img->source_path = 'path/to/source.ext';
405
     *
406
     *  // path to where should the resulting image be saved
407
     *  // note that by simply setting a different extension to the file will
408
     *  // instruct the script to create an image of that particular type
409
     *  $img->target_path = 'path/to/target.ext';
410
     *
411
     *  // crop a rectangle of 100x100 pixels, starting from the top-left corner
412
     *  $img->crop(0, 0, 100, 100);
413
     *  </code>
414
     *
415
     * @param integer $start_x x coordinate to start cropping from
416
     *
417
     * @param integer $start_y y coordinate to start cropping from
418
     *
419
     * @param integer $end_x   x coordinate where to end the cropping
420
     *
421
     * @param integer $end_y   y coordinate where to end the cropping
422
     *
423
     * @since  1.0.4
424
     *
425
     * @return boolean Returns TRUE on success or FALSE on error.
426
     *
427
     *                      If FALSE is returned, check the {@link error} property to see the error code.
428
     */
429
    public function crop($start_x, $start_y, $end_x, $end_y)
430
    {
431
432
        // this method might be also called internally
433
        // in this case, there's a fifth argument that points to an already existing image identifier
434
        $args = func_get_args();
435
436
        // if fifth argument exists
437
        if (isset($args[4]) && is_resource($args[4])) {
438
439
            // that it is the image identifier that we'll be using further on
440
            $this->source_identifier = $args[4];
441
442
            // set this to true so that the script will continue to execute at the next IF
443
            $result = true;
444
445
            // if method is called as usually
446
            // try to create an image resource from source path
447
        } else {
448
            $result = $this->_create_from_source();
449
        }
450
451
        // if image resource was successfully created
452
        if ($result !== false) {
453
454
            // prepare the target image
455
            $target_identifier = $this->_prepare_image($end_x - $start_x, $end_y - $start_y, -1);
456
457
            // crop the image
458
            imagecopyresampled(
459
460
                $target_identifier, $this->source_identifier, 0, 0, $start_x, $start_y, $end_x - $start_x, $end_y - $start_y, $end_x - $start_x, $end_y - $start_y
461
462
            );
463
464
            // write image
465
            return $this->_write_image($target_identifier);
466
        }
467
468
        // if script gets this far, return false
469
        // note that we do not set the error level as it has been already set
470
        // by the _create_from_source() method earlier
471
        return false;
472
    }
473
474
    /**
475
     *  Flips both horizontally and vertically the image given as {@link source_path} and outputs the resulted image as
476
     *  {@link target_path}
477
     *
478
     *  <code>
479
     *  // include the Zebra_Image library
480
     *  require 'path/to/Zebra_Image.php';
481
     *
482
     *  // instantiate the class
483
     *  $img = new Zebra_Image();
484
     *
485
     *  // a source image
486
     *  $img->source_path = 'path/to/source.ext';
487
     *
488
     *  // path to where should the resulting image be saved
489
     *  // note that by simply setting a different extension to the file will
490
     *  // instruct the script to create an image of that particular type
491
     *  $img->target_path = 'path/to/target.ext';
492
     *
493
     *  // flip the image both horizontally and vertically
494
     *  $img->flip_both();
495
     *  </code>
496
     *
497
     * @since 2.1
498
     *
499
     * @return boolean Returns TRUE on success or FALSE on error.
500
     *
501
     *                      If FALSE is returned, check the {@link error} property to see the error code.
502
     */
503
    public function flip_both()
504
    {
505
        return $this->_flip('both');
506
    }
507
508
    /**
509
     *  Flips horizontally the image given as {@link source_path} and outputs the resulted image as {@link target_path}
510
     *
511
     *  <code>
512
     *  // include the Zebra_Image library
513
     *  require 'path/to/Zebra_Image.php';
514
     *
515
     *  // instantiate the class
516
     *  $img = new Zebra_Image();
517
     *
518
     *  // a source image
519
     *  $img->source_path = 'path/to/source.ext';
520
     *
521
     *  // path to where should the resulting image be saved
522
     *  // note that by simply setting a different extension to the file will
523
     *  // instruct the script to create an image of that particular type
524
     *  $img->target_path = 'path/to/target.ext';
525
     *
526
     *  // flip the image horizontally
527
     *  $img->flip_horizontal();
528
     *  </code>
529
     *
530
     * @return boolean Returns TRUE on success or FALSE on error.
531
     *
532
     *                      If FALSE is returned, check the {@link error} property to see the error code.
533
     */
534
    public function flip_horizontal()
535
    {
536
        return $this->_flip('horizontal');
537
    }
538
539
    /**
540
     *  Flips vertically the image given as {@link source_path} and outputs the resulted image as {@link target_path}
541
     *
542
     *  <code>
543
     *  // include the Zebra_Image library
544
     *  require 'path/to/Zebra_Image.php';
545
     *
546
     *  // instantiate the class
547
     *  $img = new Zebra_Image();
548
     *
549
     *  // a source image
550
     *  $img->source_path = 'path/to/source.ext';
551
     *
552
     *  // path to where should the resulting image be saved
553
     *  // note that by simply setting a different extension to the file will
554
     *  // instruct the script to create an image of that particular type
555
     *  $img->target_path = 'path/to/target.ext';
556
     *
557
     *  // flip the image vertically
558
     *  $img->flip_vertical();
559
     *  </code>
560
     *
561
     * @return boolean Returns TRUE on success or FALSE on error.
562
     *
563
     *                      If FALSE is returned, check the {@link error} property to see the error code.
564
     */
565
    public function flip_vertical()
566
    {
567
        return $this->_flip('vertical');
568
    }
569
570
    /**
571
     *  Resizes the image given as {@link source_path} and outputs the resulted image as {@link target_path}.
572
     *
573
     *  <code>
574
     *  // include the Zebra_Image library
575
     *  require 'path/to/Zebra_Image.php';
576
     *
577
     *  // instantiate the class
578
     *  $img = new Zebra_Image();
579
     *
580
     *  // a source image
581
     *  $img->source_path = 'path/to/source.ext';
582
     *
583
     *  // path to where should the resulting image be saved
584
     *  // note that by simply setting a different extension to the file will
585
     *  // instruct the script to create an image of that particular type
586
     *  $img->target_path = 'path/to/target.ext';
587
     *
588
     *  // apply a "sharpen" filter to the resulting images
589
     *  $img->sharpen_images = true;
590
     *
591
     *  // resize the image to exactly 150x150 pixels, without altering aspect ratio, by using the CROP_CENTER method
592
     *  $img->resize(150, 150, ZEBRA_IMAGE_CROP_CENTER);
593
     *  </code>
594
     *
595
     * @param integer             $width            The width to resize the image to.
596
     *
597
     *                                          If set to <b>0</b>, the width will be automatically adjusted, depending
598
     *                                          on the value of the <b>height</b> argument so that the image preserves
599
     *                                          its aspect ratio.
600
     *
601
     *                                          If {@link preserve_aspect_ratio} is set to TRUE and both this and the
602
     *                                          <b>height</b> arguments are values greater than <b>0</b>, the image will
603
     *                                          be resized to the exact required width and height and the aspect ratio
604
     *                                          will be preserved - (also see the description for the <b>method</b>
605
     *                                          argument below on how can this be done).
606
     *
607
     *                                          If {@link preserve_aspect_ratio} is set to FALSE, the image will be
608
     *                                          resized to the required width and the aspect ratio will be ignored.
609
     *
610
     *                                          If both <b>width</b> and <b>height</b> are set to <b>0</b>, a copy of
611
     *                                          the source image will be created ({@link jpeg_quality} and
612
     *                                          {@link png_compression} will still apply).
613
     *
614
     *                                          If either <b>width</b> or <b>height</b> are set to <b>0</b>, the script
615
     *                                          will consider the value of the {@link preserve_aspect_ratio} to bet set
616
     *                                          to TRUE regardless of its actual value!
617
     *
618
     * @param integer             $height           The height to resize the image to.
619
     *
620
     *                                          If set to <b>0</b>, the height will be automatically adjusted, depending
621
     *                                          on the value of the <b>width</b> argument so that the image preserves
622
     *                                          its aspect ratio.
623
     *
624
     *                                          If {@link preserve_aspect_ratio} is set to TRUE and both this and the
625
     *                                          <b>width</b> arguments are values greater than <b>0</b>, the image will
626
     *                                          be resized to the exact required width and height and the aspect ratio
627
     *                                          will be preserved - (also see the description for the <b>method</b>
628
     *                                          argument below on how can this be done).
629
     *
630
     *                                          If {@link preserve_aspect_ratio} is set to FALSE, the image will be
631
     *                                          resized to the required height and the aspect ratio will be ignored.
632
     *
633
     *                                          If both <b>width</b> and <b>height</b> are set to <b>0</b>, a copy of
634
     *                                          the source image will be created ({@link jpeg_quality} and
635
     *                                          {@link png_compression} will still apply).
636
     *
637
     *                                          If either <b>height</b> or <b>width</b> are set to <b>0</b>, the script
638
     *                                          will consider the value of the {@link preserve_aspect_ratio} to bet set
639
     *                                          to TRUE regardless of its actual value!
640
     *
641
     * @param int                 $method           (Optional) Method to use when resizing images to exact width and height
642
     *                                              while preserving aspect ratio.
643
     *
644
     *                                          If the {@link preserve_aspect_ratio} property is set to TRUE and both the
645
     *                                          <b>width</b> and <b>height</b> arguments are values greater than <b>0</b>,
646
     *                                          the image will be resized to the exact given width and height and the
647
     *                                          aspect ratio will be preserved by using on of the following methods:
648
     *
649
     *                                          -   <b>ZEBRA_IMAGE_BOXED</b> - the image will be scalled so that it will
650
     *                                              fit in a box with the given width and height (both width/height will
651
     *                                              be smaller or equal to the required width/height) and then it will
652
     *                                              be centered both horizontally and vertically. The blank area will be
653
     *                                              filled with the color specified by the <b>bgcolor</b> argument. (the
654
     *                                              blank area will be filled only if the image is not transparent!)
655
     *
656
     *                                          -   <b>ZEBRA_IMAGE_NOT_BOXED</b> - the image will be scalled so that it
657
     *                                              <i>could</i> fit in a box with the given width and height but will
658
     *                                              not be enclosed in a box with given width and height. The new width/
659
     *                                              height will be both smaller or equal to the required width/height
660
     *
661
     *                                          -   <b>ZEBRA_IMAGE_CROP_TOPLEFT</b>
662
     *                                          -   <b>ZEBRA_IMAGE_CROP_TOPCENTER</b>
663
     *                                          -   <b>ZEBRA_IMAGE_CROP_TOPRIGHT</b>
664
     *                                          -   <b>ZEBRA_IMAGE_CROP_MIDDLELEFT</b>
665
     *                                          -   <b>ZEBRA_IMAGE_CROP_CENTER</b>
666
     *                                          -   <b>ZEBRA_IMAGE_CROP_MIDDLERIGHT</b>
667
     *                                          -   <b>ZEBRA_IMAGE_CROP_BOTTOMLEFT</b>
668
     *                                          -   <b>ZEBRA_IMAGE_CROP_BOTTOMCENTER</b>
669
     *                                          -   <b>ZEBRA_IMAGE_CROP_BOTTOMRIGHT</b>
670
     *
671
     *                                          For the methods involving crop, first the image is scaled so that both
672
     *                                          its sides are equal or greater than the respective sizes of the bounding
673
     *                                          box; next, a region of required width and height will be cropped from
674
     *                                          indicated region of the resulted image.
675
     *
676
     *                                          Default is ZEBRA_IMAGE_CROP_CENTER
677
     *
678
     * @param \hexadecimal|string $background_color (Optional) The hexadecimal color (like "#FFFFFF" or "#FFF") of the
679
     *                                              blank area. See the <b>method</b> argument.
680
     *
681
     *                                          When set to -1 the script will preserve transparency for transparent GIF
682
     *                                          and PNG images. For non-transparent images the background will be white
683
     *                                          in this case.
684
     *
685
     *                                          Default is #FFFFFF.
686
     *
687
     * @return boolean Returns TRUE on success or FALSE on error.
688
     *
689
     *                                          If FALSE is returned, check the {@link error} property to see what went
690
     *                                          wrong
691
     */
692
    public function resize($width = 0, $height = 0, $method = ZEBRA_IMAGE_CROP_CENTER, $background_color = '#FFFFFF')
693
    {
694
695
        // if image resource was successfully created
696
        if ($this->_create_from_source()) {
697
698
            // if either width or height is to be adjusted automatically
699
            // set a flag telling the script that, even if $preserve_aspect_ratio is set to false
700
            // treat everything as if it was set to true
701
            if ($width == 0 || $height == 0) {
702
                $auto_preserve_aspect_ratio = true;
703
            }
704
705
            // if aspect ratio needs to be preserved
706
            if ($this->preserve_aspect_ratio || isset($auto_preserve_aspect_ratio)) {
707
708
                // if height is given and width is to be computed accordingly
709
                if ($width == 0 && $height > 0) {
710
711
                    // get the original image's aspect ratio
712
                    $aspect_ratio = $this->source_width / $this->source_height;
713
714
                    // the target image's height is as given as argument to the method
715
                    $target_height = $height;
716
717
                    // compute the target image's width, preserving the aspect ratio
718
                    $target_width = round($height * $aspect_ratio);
719
720
                    // if width is given and height is to be computed accordingly
721
                } elseif ($width > 0 && $height == 0) {
722
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
732
                    // if both width and height are given and ZEBRA_IMAGE_BOXED or ZEBRA_IMAGE_NOT_BOXED methods are to be used
733
                } elseif ($width > 0 && $height > 0 && ($method == 0 || $method == 1)) {
734
735
                    // compute the horizontal and vertical aspect ratios
736
                    $vertical_aspect_ratio   = $height / $this->source_height;
737
                    $horizontal_aspect_ratio = $width / $this->source_width;
738
739
                    // if the image's newly computed height would be inside the bounding box
740
                    if (round($horizontal_aspect_ratio * $this->source_height < $height)) {
741
742
                        // the target image's width is as given as argument to the method
743
                        $target_width = $width;
744
745
                        // compute the target image's height so that the image will stay inside the bounding box
746
                        $target_height = round($horizontal_aspect_ratio * $this->source_height);
747
748
                        // otherwise
749
                    } else {
750
751
                        // the target image's height is as given as argument to the method
752
                        $target_height = $height;
753
754
                        // compute the target image's width so that the image will stay inside the bounding box
755
                        $target_width = round($vertical_aspect_ratio * $this->source_width);
756
                    }
757
758
                    // if both width and height are given and image is to be cropped in order to get to the required size
759
                } elseif ($width > 0 && $height > 0 && $method > 1 && $method < 11) {
760
761
                    // compute the horizontal and vertical aspect ratios
762
                    $vertical_aspect_ratio   = $this->source_height / $height;
763
                    $horizontal_aspect_ratio = $this->source_width / $width;
764
765
                    // we'll use one of the two
766
                    $aspect_ratio = $vertical_aspect_ratio < $horizontal_aspect_ratio ?
767
768
                        $vertical_aspect_ratio :
769
770
                        $horizontal_aspect_ratio;
771
772
                    // compute the target image's width, preserving the aspect ratio
773
                    $target_width = round($this->source_width / $aspect_ratio);
774
775
                    // compute the target image's height, preserving the aspect ratio
776
                    $target_height = round($this->source_height / $aspect_ratio);
777
778
                    // for any other case
779
                } else {
780
781
                    // we will create a copy of the source image
782
                    $target_width  = $this->source_width;
783
                    $target_height = $this->source_height;
784
                }
785
786
                // if aspect ratio does not need to be preserved
787
            } else {
788
789
                // compute the target image's width
790
                $target_width = ($width > 0 ? $width : $this->source_width);
791
792
                // compute the target image's height
793
                $target_height = ($height > 0 ? $height : $this->source_height);
794
            }
795
796
            // if
797
            if (
798
799
                // all images are to be resized - including images that are smaller than the given width/height
800
                $this->enlarge_smaller_images || // smaller images than the given width/height are to be left untouched
801
                // but current image has at leas one side that is larger than the required width/height
802
                ($width > 0 && $height > 0 ?
803
804
                    ($this->source_width > $width || $this->source_height > $height) :
805
806
                    ($this->source_width > $target_width || $this->source_height > $target_height)
807
808
                )
809
810
            ) {
811
812
                // if
813
                if (
814
815
                    // aspect ratio needs to be preserved AND
816
                    ($this->preserve_aspect_ratio || isset($auto_preserve_aspect_ratio)) && // both width and height are given
817
                    ($width > 0 && $height > 0) && // images are to be cropped
818
                    ($method > 1 && $method < 11)
819
820
                ) {
821
822
                    // prepare the target image
823
                    $target_identifier = $this->_prepare_image($target_width, $target_height, $background_color);
824
825
                    imagecopyresampled(
826
827
                        $target_identifier, $this->source_identifier, 0, 0, 0, 0, $target_width, $target_height, $this->source_width, $this->source_height
828
829
                    );
830
831
                    // do the crop according to the required method
832
                    switch ($method) {
833
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(0, 0, $width, $height, $target_identifier // crop this resource instead
839
                            );
840
841
                            break;
842
843
                        // if image needs to be cropped from the top-center
844
                        case ZEBRA_IMAGE_CROP_TOPCENTER:
845
846
                            // crop accordingly
847
                            return $this->crop(floor(($target_width - $width) / 2), 0, floor(($target_width - $width) / 2) + $width, $height, $target_identifier // crop this resource instead
848
                            );
849
850
                            break;
851
852
                        // if image needs to be cropped from the top-right corner
853
                        case ZEBRA_IMAGE_CROP_TOPRIGHT:
854
855
                            // crop accordingly
856
                            return $this->crop($target_width - $width, 0, $target_width, $height, $target_identifier // crop this resource instead
857
                            );
858
859
                            break;
860
861
                        // if image needs to be cropped from the middle-left
862
                        case ZEBRA_IMAGE_CROP_MIDDLELEFT:
863
864
                            // crop accordingly
865
                            return $this->crop(
866
867
                                0, floor(($target_height - $height) / 2), $width, floor(($target_height - $height) / 2) + $height, $target_identifier // crop this resource instead
868
869
                            );
870
871
                            break;
872
873
                        // if image needs to be cropped from the center of the image
874
                        case ZEBRA_IMAGE_CROP_CENTER:
875
876
                            // crop accordingly
877
                            return $this->crop(
878
879
                                floor(($target_width - $width) / 2), floor(($target_height - $height) / 2), floor(($target_width - $width) / 2) + $width, floor(($target_height - $height) / 2) + $height, $target_identifier // crop this resource instead
880
881
                            );
882
883
                            break;
884
885
                        // if image needs to be cropped from the middle-right
886
                        case ZEBRA_IMAGE_CROP_MIDDLERIGHT:
887
888
                            // crop accordingly
889
                            return $this->crop(
890
891
                                $target_width - $width, floor(($target_height - $height) / 2), $target_width, floor(($target_height - $height) / 2) + $height, $target_identifier // crop this resource instead
892
893
                            );
894
895
                            break;
896
897
                        // if image needs to be cropped from the bottom-left corner
898
                        case ZEBRA_IMAGE_CROP_BOTTOMLEFT:
899
900
                            // crop accordingly
901
                            return $this->crop(
902
903
                                0, $target_height - $height, $width, $target_height, $target_identifier // crop this resource instead
904
905
                            );
906
907
                            break;
908
909
                        // if image needs to be cropped from the bottom-center
910
                        case ZEBRA_IMAGE_CROP_BOTTOMCENTER:
911
912
                            // crop accordingly
913
                            return $this->crop(
914
915
                                floor(($target_width - $width) / 2), $target_height - $height, floor(($target_width - $width) / 2) + $width, $target_height, $target_identifier // crop this resource instead
916
917
                            );
918
919
                            break;
920
921
                        // if image needs to be cropped from the bottom-right corner
922
                        case ZEBRA_IMAGE_CROP_BOTTOMRIGHT:
923
924
                            // crop accordingly
925
                            return $this->crop(
926
927
                                $target_width - $width, $target_height - $height, $target_width, $target_height, $target_identifier // crop this resource instead
928
929
                            );
930
931
                            break;
932
933
                    }
934
935
                    // if aspect ratio doesn't need to be preserved or
936
                    // it needs to be preserved and method is ZEBRA_IMAGE_BOXED or ZEBRA_IMAGE_NOT_BOXED
937
                } else {
938
939
                    // prepare the target image
940
                    $target_identifier = $this->_prepare_image(($width > 0 && $height > 0 && $method != ZEBRA_IMAGE_NOT_BOXED ? $width : $target_width), ($width > 0 && $height > 0 && $method != ZEBRA_IMAGE_NOT_BOXED ? $height : $target_height), $background_color);
941
942
                    imagecopyresampled(
943
944
                        $target_identifier, $this->source_identifier, ($width > 0 && $height > 0 && $method != ZEBRA_IMAGE_NOT_BOXED ? ($width - $target_width) / 2 : 0), ($width > 0 && $height > 0 && $method != ZEBRA_IMAGE_NOT_BOXED ? ($height - $target_height) / 2 : 0), 0, 0, $target_width, $target_height, $this->source_width, $this->source_height
945
946
                    );
947
948
                    // if script gets this far, write the image to disk
949
                    return $this->_write_image($target_identifier);
950
                }
951
952
                // if we get here it means that
953
                // smaller images than the given width/height are to be left untouched
954
                // therefore, we save the image as it is
955
            } else {
956
                return $this->_write_image($this->source_identifier);
957
            }
958
        }
959
960
        // if script gets this far return false
961
        // note that we do not set the error level as it has been already set
962
        // by the _create_from_source() method earlier
963
        return false;
964
    }
965
966
    /**
967
     *  Rotates the image given as {@link source_path} and outputs the resulted image as {@link target_path}.
968
     *
969
     *  <code>
970
     *  // include the Zebra_Image library
971
     *  require 'path/to/Zebra_Image.php';
972
     *
973
     *  // instantiate the class
974
     *  $img = new Zebra_Image();
975
     *
976
     *  // a source image
977
     *  $img->source_path = 'path/to/source.ext';
978
     *
979
     *  // path to where should the resulting image be saved
980
     *  // note that by simply setting a different extension to the file will
981
     *  // instruct the script to create an image of that particular type
982
     *  $img->target_path = 'path/to/target.ext';
983
     *
984
     *  // rotate the image 45 degrees, clockwise
985
     *  $img->rotate(45);
986
     *  </code>
987
     *
988
     * @param double $angle            Angle by which to rotate the image clockwise.
989
     *
990
     *                                          Between 0 and 360.
991
     *
992
     * @param mixed  $background_color (Optional) The hexadecimal color (like "#FFFFFF" or "#FFF") of the
993
     *                                 uncovered zone after the rotation.
994
     *
995
     *                                          When set to -1 the script will preserve transparency for transparent GIF
996
     *                                          and PNG images. For non-transparent images the background will be white
997
     *                                          in this case.
998
     *
999
     *                                          Default is -1.
1000
     *
1001
     * @return boolean Returns TRUE on success or FALSE on error.
1002
     *
1003
     *                                          If FALSE is returned, check the {@link error} property to see the error
1004
     *                                          code.
1005
     */
1006
    public function rotate($angle, $background_color = -1)
1007
    {
1008
1009
        // if image resource was successfully created
1010
        if ($this->_create_from_source()) {
1011
1012
            // angles are given clockwise but imagerotate works counterclockwise so we need to negate our value
1013
            $angle = -$angle;
1014
1015
            // if source image is PNG
1016
            if ($this->source_type == IMAGETYPE_PNG && $background_color == -1) {
1017
1018
                // rotate the image
1019
                // but if using -1 as background color didn't work (as is the case for PNG8)
1020
                if (!($target_identifier = imagerotate($this->source_identifier, $angle, -1))) {
1021
1022
                    // we will be using #FFF as the color to fill the uncovered zone after the rotation
1023
                    $background_color = imagefilledarc($this->source_identifier, 255, 255, 255);
1024
1025
                    // rotate the image
1026
                    $target_identifier = imagerotate($this->source_identifier, $angle, $background_color);
1027
                }
1028
1029
                // if source image is a transparent GIF
1030
            } elseif ($this->source_type == IMAGETYPE_GIF && $this->source_transparent_color_index >= 0) {
1031
1032
                // convert the background color to RGB values
1033
                $background_color = $this->_hex2rgb($background_color);
1034
1035
                // allocate the color to the image identifier
1036
                $background_color = imagefilledarc(
1037
1038
                    $this->source_identifier, $background_color['r'], $background_color['g'], $background_color['b']
1039
1040
                );
1041
1042
                // rotate the image
1043
                $this->source_identifier = imagerotate($this->source_identifier, $angle, $background_color);
1044
1045
                // get the width of rotated image
1046
                $width = imagesx($this->source_identifier);
1047
1048
                // get the height of rotated image
1049
                $height = imagesy($this->source_identifier);
1050
1051
                // create a blank image with the new width and height
1052
                // (this intermediary step is for preserving transparency)
1053
                $target_identifier = $this->_prepare_image($width, $height, -1);
1054
1055
                // copy the rotated image on to the new one
1056
                imagecopyresampled($target_identifier, $this->source_identifier, 0, 0, 0, 0, $width, $height, $width, $height);
1057
1058
                // for the other cases
1059
            } else {
1060
1061
                // convert the color to RGB values
1062
                $background_color = $this->_hex2rgb($background_color);
1063
1064
                // allocate the color to the image identifier
1065
                $background_color = imagefilledarc(
1066
1067
                    $this->source_identifier, $background_color['r'], $background_color['g'], $background_color['b']
1068
1069
                );
1070
1071
                // rotate the image
1072
                $target_identifier = imagerotate($this->source_identifier, $angle, $background_color);
1073
            }
1074
1075
            // write image
1076
            $this->_write_image($target_identifier);
1077
        }
1078
1079
        // if script gets this far return false
1080
        // note that we do not set the error level as it has been already set
1081
        // by the _create_from_source() method earlier
1082
        return false;
1083
    }
1084
1085
    /**
1086
     *  Returns an array containing the image identifier representing the image obtained from {@link $source_path}, the
1087
     *  image's width and height and the image's type
1088
     *
1089
     * @access private
1090
     */
1091
    public function _create_from_source()
1092
    {
1093
1094
        // perform some error checking first
1095
        // if the GD library is not installed
1096
        if (!function_exists('gd_info')) {
1097
1098
            // save the error level and stop the execution of the script
1099
            $this->error = 7;
1100
1101
            return false;
1102
1103
            // if source file does not exist
1104
        } elseif (!is_file($this->source_path)) {
1105
1106
            // save the error level and stop the execution of the script
1107
            $this->error = 1;
1108
1109
            return false;
1110
1111
            // if source file is not readable
1112
        } elseif (!is_readable($this->source_path)) {
1113
1114
            // save the error level and stop the execution of the script
1115
            $this->error = 2;
1116
1117
            return false;
1118
1119
            // if target file is same as source file and source file is not writable
1120
        } elseif ($this->target_path == $this->source_path && !is_writable($this->source_path)) {
1121
1122
            // save the error level and stop the execution of the script
1123
            $this->error = 3;
1124
1125
            return false;
1126
1127
            // try to get source file width, height and type
1128
            // and if it founds an unsupported file type
1129
        } elseif (!list($this->source_width, $this->source_height, $this->source_type) = @getimagesize($this->source_path)) {
1130
1131
            // save the error level and stop the execution of the script
1132
            $this->error = 4;
1133
1134
            return false;
1135
1136
            // if no errors so far
1137
        } else {
1138
1139
            // get target file's type based on the file extension
1140
            $this->target_type = strtolower(substr($this->target_path, strrpos($this->target_path, '.') + 1));
1141
1142
            // create an image from file using extension dependant function
1143
            // checks for file extension
1144
            switch ($this->source_type) {
1145
1146
                // if GIF
1147
                case IMAGETYPE_GIF:
1148
1149
                    // create an image from file
1150
                    $identifier = imagecreatefromgif($this->source_path);
1151
1152
                    // get the index of the transparent color (if any)
1153
                    if (($this->source_transparent_color_index = imagecolortransparent($identifier)) >= 0) {
1154
                        // get the transparent color's RGB values
1155
                        // we have to mute errors because there are GIF images which *are* transparent and everything
1156
                        // works as expected, but imagecolortransparent() returns a color that is outside the range of
1157
                        // colors in the image's pallette...
1158
1159
                        $this->source_transparent_color = @imagecolorsforindex($identifier, $this->source_transparent_color_index);
1160
                    }
1161
1162
                    break;
1163
1164
                // if JPEG
1165
                case IMAGETYPE_JPEG:
1166
1167
                    // create an image from file
1168
                    $identifier = imagecreatefromjpeg($this->source_path);
1169
1170
                    break;
1171
1172
                // if PNG
1173
                case IMAGETYPE_PNG:
1174
1175
                    // create an image from file
1176
                    $identifier = imagecreatefrompng($this->source_path);
1177
1178
                    // disable blending
1179
                    imagealphablending($identifier, false);
1180
1181
                    break;
1182
1183
                default:
1184
1185
                    // if unsupported file type
1186
                    // note that we call this if the file is not GIF, JPG or PNG even though the getimagesize function
1187
                    // handles more image types
1188
                    $this->error = 4;
1189
1190
                    return false;
1191
1192
            }
1193
        }
1194
1195
        // if target file has to have the same timestamp as the source image
1196
        // save it as a global property of the class
1197
        if ($this->preserve_time) {
1198
            $this->source_image_time = filemtime($this->source_path);
1199
        }
1200
1201
        // make available the source image's identifier
1202
        $this->source_identifier = $identifier;
1203
1204
        return true;
1205
    }
1206
1207
    /**
1208
     *  Converts a hexadecimal representation of a color (i.e. #123456 or #AAA) to a RGB representation.
1209
     *
1210
     *  The RGB values will be a value between 0 and 255 each.
1211
     *
1212
     * @param string $color            Hexadecimal representation of a color (i.e. #123456 or #AAA).
1213
     *
1214
     * @param string $default_on_error Hexadecimal representation of a color to be used in case $color is not
1215
     *                                 recognized as a hexadecimal color.
1216
     *
1217
     * @return array Returns an associative array with the values of (R)ed, (G)reen and (B)lue
1218
     *
1219
     * @access private
1220
     */
1221
    public function _hex2rgb($color, $default_on_error = '#FFFFFF')
1222
    {
1223
1224
        // if color is not formatted correctly
1225
        // use the default color
1226
        if (preg_match('/^#?([a-f]|[0-9]){3}(([a-f]|[0-9]){3})?$/i', $color) == 0) {
1227
            $color = $default_on_error;
1228
        }
1229
1230
        // trim off the "#" prefix from $background_color
1231
        $color = ltrim($color, '#');
1232
1233
        // if color is given using the shorthand (i.e. "FFF" instead of "FFFFFF")
1234
        if (strlen($color) == 3) {
1235
            $tmp = '';
1236
1237
            // take each value
1238
            // and duplicate it
1239
            for ($i = 0; $i < 3; ++$i) {
1240
                $tmp .= str_repeat($color[$i], 2);
1241
            }
1242
1243
            // the color in it's full, 6 characters length notation
1244
            $color = $tmp;
1245
        }
1246
1247
        // decimal representation of the color
1248
        $int = hexdec($color);
1249
1250
        // extract and return the RGB values
1251
        return array(
1252
1253
            'r' => 0xFF & ($int >> 0x10),
1254
            'g' => 0xFF & ($int >> 0x8),
1255
            'b' => 0xFF & $int
1256
1257
        );
1258
    }
1259
1260
    /**
1261
     *  Flips horizontally or vertically or both ways the image given as {@link source_path}.
1262
     *
1263
     * @since  2.1
1264
     *
1265
     * @access private
1266
     *
1267
     * @param $orientation
1268
     *
1269
     * @return boolean Returns TRUE on success or FALSE on error.
1270
     *
1271
     *                      If FALSE is returned, check the {@link error} property to see the error code.
1272
     */
1273
    public function _flip($orientation)
1274
    {
1275
1276
        // if image resource was successfully created
1277
        if ($this->_create_from_source()) {
1278
1279
            // prepare the target image
1280
            $target_identifier = $this->_prepare_image($this->source_width, $this->source_height, -1);
1281
1282
            // flip according to $orientation
1283
            switch ($orientation) {
1284
1285
                case 'horizontal':
1286
1287
                    imagecopyresampled(
1288
1289
                        $target_identifier, $this->source_identifier, 0, 0, $this->source_width - 1, 0, $this->source_width, $this->source_height, -$this->source_width, $this->source_height
1290
1291
                    );
1292
1293
                    break;
1294
1295
                case 'vertical':
1296
1297
                    imagecopyresampled(
1298
1299
                        $target_identifier, $this->source_identifier, 0, 0, 0, $this->source_height - 1, $this->source_width, $this->source_height, $this->source_width, -$this->source_height
1300
1301
                    );
1302
1303
                    break;
1304
1305
                case 'both':
1306
1307
                    imagecopyresampled(
1308
1309
                        $target_identifier, $this->source_identifier, 0, 0, $this->source_width - 1, $this->source_height - 1, $this->source_width, $this->source_height, -$this->source_width, -$this->source_height
1310
1311
                    );
1312
1313
                    break;
1314
1315
            }
1316
1317
            // write image
1318
            return $this->_write_image($target_identifier);
1319
        }
1320
1321
        // if script gets this far, return false
1322
        // note that we do not set the error level as it has been already set
1323
        // by the _create_from_source() method earlier
1324
        return false;
1325
    }
1326
1327
    /**
1328
     *  Creates a blank image of given width, height and background color.
1329
     *
1330
     * @param integer $width            Width of the new image.
1331
     *
1332
     * @param integer $height           Height of the new image.
1333
     *
1334
     * @param string  $background_color (Optional) The hexadecimal color of the background.
1335
     *
1336
     *                                          Can also be -1 case in which the script will try to create a transparent
1337
     *                                          image, if possible.
1338
     *
1339
     *                                          Default is "#FFFFFF".
1340
     *
1341
     * @return Returns the identifier of the newly created image.
1342
     *
1343
     * @access private
1344
     */
1345
    public function _prepare_image($width, $height, $background_color = '#FFFFFF')
1346
    {
1347
1348
        // create a blank image
1349
        $identifier = imagecreatetruecolor((int)$width <= 0 ? 1 : (int)$width, (int)$height <= 0 ? 1 : (int)$height);
1350
1351
        // if we are creating a PNG image
1352
        if ($this->target_type === 'png' && $background_color == -1) {
1353
1354
            // disable blending
1355
            imagealphablending($identifier, false);
1356
1357
            // allocate a transparent color
1358
            $transparent_color = imagecolorallocatealpha($identifier, 0, 0, 0, 127);
1359
1360
            // fill the image with the transparent color
1361
            imagefill($identifier, 0, 0, $transparent_color);
1362
1363
            //save full alpha channel information
1364
            imagesavealpha($identifier, true);
1365
1366
            // if source image is a transparent GIF
1367
        } elseif ($this->target_type === 'gif' && $background_color == -1 && $this->source_transparent_color_index >= 0) {
1368
1369
            // allocate the source image's transparent color also to the new image resource
1370
            $transparent_color = imagefilledarc($identifier, $this->source_transparent_color['red'], $this->source_transparent_color['green'], $this->source_transparent_color['blue']);
1371
1372
            // fill the background of the new image with transparent color
1373
            imagefill($identifier, 0, 0, $transparent_color);
1374
1375
            // from now on, every pixel having the same RGB as the transparent color will be transparent
1376
            imagecolortransparent($identifier, $transparent_color);
1377
1378
            // for other image types
1379
        } else {
1380
1381
            // if transparent background color specified, revert to white
1382
            if ($background_color == -1) {
1383
                $background_color = '#FFFFFF';
1384
            }
1385
1386
            // convert hex color to rgb
1387
            $background_color = $this->_hex2rgb($background_color);
1388
1389
            // prepare the background color
1390
            $background_color = imagefilledarc($identifier, $background_color['r'], $background_color['g'], $background_color['b']);
1391
1392
            // fill the image with the background color
1393
            imagefill($identifier, 0, 0, $background_color);
1394
        }
1395
1396
        // return the image's identifier
1397
        return $identifier;
1398
    }
1399
1400
    /**
1401
     *  Sharpens images. Useful when creating thumbnails.
1402
     *
1403
     *  Code taken from the comments at {@link http://docs.php.net/imageconvolution}.
1404
     *
1405
     *  <i>This function will yield a result only for PHP version 5.1.0+ and will leave the image unaltered for older
1406
     *  versions!</i>
1407
     *
1408
     * @param $image
1409
     * @return mixed
1410
     * @internal param \identifier $identifier An image identifier
1411
     *
1412
     * @access   private
1413
     */
1414
    public function _sharpen_image($image)
1415
    {
1416
1417
        // if the "sharpen_images" is set to true and we're running an appropriate version of PHP
1418
        // (the "imageconvolution" is available only for PHP 5.1.0+)
1419
        if ($this->sharpen_images && version_compare(PHP_VERSION, '5.1.0') >= 0) {
1420
1421
            // the convolution matrix as an array of three arrays of three floats
1422
            $matrix = array(
1423
                array(-1.2, -1, -1.2),
1424
                array(-1, 20, -1),
1425
                array(-1.2, -1, -1.2)
1426
            );
1427
1428
            // the divisor of the matrix
1429
            $divisor = array_sum(array_map('array_sum', $matrix));
1430
1431
            // color offset
1432
            $offset = 0;
1433
1434
            // sharpen image
1435
            imageconvolution($image, $matrix, $divisor, $offset);
1436
        }
1437
1438
        // return the image's identifier
1439
        return $image;
1440
    }
1441
1442
    /**
1443
     *  Creates a new image from given image identifier having the extension as specified by {@link target_path}.
1444
     *
1445
     * @param  $identifier identifier  An image identifier
1446
     *
1447
     * @return boolean Returns TRUE on success or FALSE on error.
1448
     *
1449
     *                                  If FALSE is returned, check the {@link error} property to see the error code.
1450
     *
1451
     * @access private
1452
     */
1453
    public function _write_image($identifier)
1454
    {
1455
1456
        // sharpen image if it's required
1457
        $this->_sharpen_image($identifier);
1458
1459
        // image saving process goes according to required extension
1460
        switch ($this->target_type) {
1461
1462
            // if GIF
1463
            case 'gif':
1464
1465
                // if GD support for this file type is not available
1466
                // in version 1.6 of GD the support for GIF files was dropped see
1467
                // http://php.net/manual/en/function.imagegif.php#function.imagegif.notes
1468
                if (!function_exists('imagegif')) {
1469
1470
                    // save the error level and stop the execution of the script
1471
                    $this->error = 6;
1472
1473
                    return false;
1474
1475
                    // if, for some reason, file could not be created
1476
                } elseif (@!imagegif($identifier, $this->target_path)) {
1477
1478
                    // save the error level and stop the execution of the script
1479
                    $this->error = 3;
1480
1481
                    return false;
1482
                }
1483
1484
                break;
1485
1486
            // if JPEG
1487
            case 'jpg':
1488
            case 'jpeg':
1489
1490
                // if GD support for this file type is not available
1491
                if (!function_exists('imagejpeg')) {
1492
1493
                    // save the error level and stop the execution of the script
1494
                    $this->error = 6;
1495
1496
                    return false;
1497
1498
                    // if, for some reason, file could not be created
1499
                } elseif (@!imagejpeg($identifier, $this->target_path, $this->jpeg_quality)) {
1500
1501
                    // save the error level and stop the execution of the script
1502
                    $this->error = 3;
1503
1504
                    return false;
1505
                }
1506
1507
                break;
1508
1509
            // if PNG
1510
            case 'png':
1511
1512
                // save full alpha channel information
1513
                imagesavealpha($identifier, true);
1514
1515
                // if GD support for this file type is not available
1516
                if (!function_exists('imagepng')) {
1517
1518
                    // save the error level and stop the execution of the script
1519
                    $this->error = 6;
1520
1521
                    return false;
1522
1523
                    // if, for some reason, file could not be created
1524
                } elseif (@!imagepng($identifier, $this->target_path, $this->png_compression)) {
1525
1526
                    // save the error level and stop the execution of the script
1527
                    $this->error = 3;
1528
1529
                    return false;
1530
                }
1531
1532
                break;
1533
1534
            // if not a supported file extension
1535
            default:
1536
1537
                // save the error level and stop the execution of the script
1538
                $this->error = 5;
1539
1540
                return false;
1541
1542
        }
1543
1544
        // get a list of functions disabled via configuration
1545
        $disabled_functions = @ini_get('disable_functions');
1546
1547
        // if the 'chmod' function is not disabled via configuration
1548
        if ($disabled_functions == '' || strpos('chmod', $disabled_functions) === false) {
1549
1550
            // chmod the file
1551
            chmod($this->target_path, (int)$this->chmod_value, 8);
1552
1553
            // save the error level
1554
        } else {
1555
            $this->error = 8;
1556
        }
1557
1558
        // if target file has to have the same timestamp as the source image
1559
        if ($this->preserve_time && isset($this->source_image_time)) {
1560
1561
            // touch the newly created file
1562
            @touch($this->target_path, $this->source_image_time);
1563
        }
1564
1565
        // return true
1566
        return true;
1567
    }
1568
}
1569