Image_GD   F
last analyzed

Complexity

Total Complexity 62

Size/Duplication

Total Lines 643
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 216
dl 0
loc 643
rs 3.44
c 1
b 0
f 0
wmc 62

17 Methods

Rating   Name   Duplication   Size   Complexity  
B _do_resize() 0 43 7
A _do_flip() 0 27 4
B __construct() 0 31 7
A _do_crop() 0 17 2
A _do_adapt() 0 15 1
A _do_save() 0 25 4
A __destruct() 0 5 2
A _do_rotate() 0 30 3
A _load_image() 0 11 2
A _do_reflection() 0 61 5
B _save_function() 0 36 6
A check() 0 34 5
A _create() 0 12 1
A _do_render() 0 21 4
A _do_sharpen() 0 25 3
A _do_watermark() 0 38 4
A _do_background() 0 25 2

How to fix   Complexity   

Complex Class

Complex classes like Image_GD often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Image_GD, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace image\components\drivers;
4
5
use yii\base\ErrorException;
6
use image\components\Kohana\Image;
7
8
/**
9
 * Support for image manipulation using [GD](http://php.net/GD).
10
 *
11
 * @package    Kohana/Image
12
 * @category   Drivers
13
 * @author     Kohana Team
14
 * @copyright  (c) 2008-2009,2018 Kohana Team
15
 * @license    http://kohanaphp.com/license.html
16
 */
17
class Image_GD extends Image implements DriverInterface
18
{
19
20
    // Which GD functions are available?
21
    const IMAGEROTATE = 'imagerotate';
22
    const IMAGECONVOLUTION = 'imageconvolution';
23
    const IMAGEFILTER = 'imagefilter';
24
    const IMAGELAYEREFFECT = 'imagelayereffect';
25
    protected static $_available_functions = array();
26
27
    /**
28
     * Checks if GD is enabled and verify that key methods exist, some of which require GD to
29
     * be bundled with PHP.  Exceptions will be thrown from those methods when GD is not
30
     * bundled.
31
     *
32
     * @return  boolean
33
     * @throws ErrorException
34
     */
35
    public static function check()
36
    {
37
        if (!function_exists('gd_info')) {
38
            throw new ErrorException('GD is either not installed or not enabled, check your configuration');
39
        }
40
        $functions = array(
41
            Image_GD::IMAGEROTATE,
42
            Image_GD::IMAGECONVOLUTION,
43
            Image_GD::IMAGEFILTER,
44
            Image_GD::IMAGELAYEREFFECT
45
        );
46
        foreach ($functions as $function) {
47
            Image_GD::$_available_functions[$function] = function_exists($function);
48
        }
49
50
        if (defined('GD_VERSION')) {
51
            // Get the version via a constant, available in PHP 5.2.4+
52
            $version = GD_VERSION;
53
        } else {
54
            // Get the version information
55
            $info = gd_info();
56
57
            // Extract the version number
58
            preg_match('/\d+\.\d+(?:\.\d+)?/', $info['GD Version'], $matches);
59
60
            // Get the major version
61
            $version = $matches[0];
62
        }
63
64
        if (!version_compare($version, '2.0.1', '>=')) {
65
            throw new ErrorException(sprintf('Image_GD requires GD version 2.0.1 or greater, you have %s', $version));
66
        }
67
68
        return Image_GD::$_checked = true;
69
    }
70
71
    /* @var resource Temporary image resource */
72
    protected $_image;
73
74
    /* @var string Function name to open Image */
75
    protected $_create_function;
76
77
    /**
78
     * Runs [Image_GD::check] and loads the image.
79
     *
80
     * @param   string $file image file path
81
     * @return  void
82
     * @throws  ErrorException
83
     */
84
    public function __construct($file)
85
    {
86
        if (!Image_GD::$_checked) {
87
            // Run the install check
88
            Image_GD::check();
89
        }
90
91
        parent::__construct($file);
92
93
        // Set the image creation function name
94
        switch ($this->type) {
95
            case IMAGETYPE_JPEG:
96
                $create = 'imagecreatefromjpeg';
97
                break;
98
            case IMAGETYPE_GIF:
99
                $create = 'imagecreatefromgif';
100
                break;
101
            case IMAGETYPE_PNG:
102
                $create = 'imagecreatefrompng';
103
                break;
104
        }
105
106
        if (!isset($create) || !function_exists($create)) {
107
            throw new ErrorException(sprintf('Installed GD does not support %s images', image_type_to_extension($this->type, false)));
108
        }
109
110
        // Save function for future use
111
        $this->_create_function = $create;
112
113
        // Save filename for lazy loading
114
        $this->_image = $this->file;
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->file of type string is incompatible with the declared type resource of property $_image.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
115
    }
116
117
    /**
118
     * Destroys the loaded image to free up resources.
119
     *
120
     * @return  void
121
     */
122
    public function __destruct()
123
    {
124
        if (is_resource($this->_image)) {
125
            // Free all resources
126
            imagedestroy($this->_image);
127
        }
128
    }
129
130
    /**
131
     * Loads an image into GD.
132
     *
133
     * @return  void
134
     */
135
    protected function _load_image()
136
    {
137
        if (!is_resource($this->_image)) {
138
            // Gets create function
139
            $create = $this->_create_function;
140
141
            // Open the temporary image
142
            $this->_image = $create($this->file);
143
144
            // Preserve transparency when saving
145
            imagesavealpha($this->_image, true);
146
        }
147
    }
148
149
    /**
150
     * Execute a resize.
151
     *
152
     * @param   integer $width new width
153
     * @param   integer $height new height
154
     * @return  void
155
     */
156
    public function _do_resize($width, $height)
157
    {
158
        // Presize width and height
159
        $pre_width = $this->width;
160
        $pre_height = $this->height;
161
162
        // Loads image if not yet loaded
163
        $this->_load_image();
164
165
        // Test if we can do a resize without resampling to speed up the final resize
166
        if ($width > ($this->width / 2) && $height > ($this->height / 2)) {
167
            // The maximum reduction is 10% greater than the final size
168
            $reduction_width = round($width * 1.1);
169
            $reduction_height = round($height * 1.1);
170
171
            while ($pre_width / 2 > $reduction_width && $pre_height / 2 > $reduction_height) {
172
                // Reduce the size using an O(2n) algorithm, until it reaches the maximum reduction
173
                $pre_width /= 2;
174
                $pre_height /= 2;
175
            }
176
177
            // Create the temporary image to copy to
178
            $image = $this->_create($pre_width, $pre_height);
179
180
            if (imagecopyresized($image, $this->_image, 0, 0, 0, 0, $pre_width, $pre_height, $this->width, $this->height)) {
181
                // Swap the new image for the old one
182
                imagedestroy($this->_image);
183
                $this->_image = $image;
184
            }
185
        }
186
187
        // Create the temporary image to copy to
188
        $image = $this->_create($width, $height);
189
190
        // Execute the resize
191
        if (imagecopyresampled($image, $this->_image, 0, 0, 0, 0, $width, $height, $pre_width, $pre_height)) {
192
            // Swap the new image for the old one
193
            imagedestroy($this->_image);
194
            $this->_image = $image;
195
196
            // Reset the width and height
197
            $this->width = imagesx($image);
198
            $this->height = imagesy($image);
199
        }
200
    }
201
202
    /**
203
     * Adaptation the image.
204
     *
205
     * @param   integer $width image width
206
     * @param   integer $height image height
207
     * @param   integer $bg_width background width
208
     * @param   integer $bg_height background height
209
     * @param   integer $offset_x offset from the left
210
     * @param   integer $offset_y offset from the top
211
     */
212
    public function _do_adapt($width, $height, $bg_width, $bg_height, $offset_x, $offset_y)
213
    {
214
        $this->_load_image();
215
        $image = $this->_image;
216
        $this->_image = $this->_create($bg_width, $bg_height);
217
        $this->width = $bg_width;
218
        $this->height = $bg_height;
219
        imagealphablending($this->_image, false);
220
        $col = imagecolorallocatealpha($this->_image, 0, 255, 0, 127);
221
        imagefilledrectangle($this->_image, 0, 0, $bg_width, $bg_height, $col);
222
        imagealphablending($this->_image, true);
223
        imagecopy($this->_image, $image, $offset_x, $offset_y, 0, 0, $width, $height);
224
        imagealphablending($this->_image, false);
225
        imagesavealpha($this->_image, true);
226
        imagedestroy($image);
227
    }
228
229
    /**
230
     * Execute a crop.
231
     *
232
     * @param   integer $width new width
233
     * @param   integer $height new height
234
     * @param   integer $offset_x offset from the left
235
     * @param   integer $offset_y offset from the top
236
     * @return  void
237
     */
238
    public function _do_crop($width, $height, $offset_x, $offset_y)
239
    {
240
        // Create the temporary image to copy to
241
        $image = $this->_create($width, $height);
242
243
        // Loads image if not yet loaded
244
        $this->_load_image();
245
246
        // Execute the crop
247
        if (imagecopyresampled($image, $this->_image, 0, 0, $offset_x, $offset_y, $width, $height, $width, $height)) {
248
            // Swap the new image for the old one
249
            imagedestroy($this->_image);
250
            $this->_image = $image;
251
252
            // Reset the width and height
253
            $this->width = imagesx($image);
254
            $this->height = imagesy($image);
255
        }
256
    }
257
258
    /**
259
     * Execute a rotation.
260
     *
261
     * @param   integer $degrees degrees to rotate
262
     * @return  void
263
     * @throws ErrorException
264
     */
265
    public function _do_rotate($degrees)
266
    {
267
        if (empty(Image_GD::$_available_functions[Image_GD::IMAGEROTATE])) {
268
            throw new ErrorException('This method requires imagerotate, which is only available in the bundled version of GD');
269
        }
270
271
        // Loads image if not yet loaded
272
        $this->_load_image();
273
274
        // Transparent black will be used as the background for the uncovered region
275
        $transparent = imagecolorallocatealpha($this->_image, 0, 0, 0, 127);
276
277
        // Rotate, setting the transparent color
278
        $image = imagerotate($this->_image, 360 - $degrees, $transparent, 1);
279
280
        // Save the alpha of the rotated image
281
        imagesavealpha($image, true);
0 ignored issues
show
Bug introduced by
It seems like $image can also be of type false; however, parameter $image of imagesavealpha() does only seem to accept resource, 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

281
        imagesavealpha(/** @scrutinizer ignore-type */ $image, true);
Loading history...
282
283
        // Get the width and height of the rotated image
284
        $width = imagesx($image);
0 ignored issues
show
Bug introduced by
It seems like $image can also be of type false; however, parameter $image of imagesx() does only seem to accept resource, 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

284
        $width = imagesx(/** @scrutinizer ignore-type */ $image);
Loading history...
285
        $height = imagesy($image);
0 ignored issues
show
Bug introduced by
It seems like $image can also be of type false; however, parameter $image of imagesy() does only seem to accept resource, 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

285
        $height = imagesy(/** @scrutinizer ignore-type */ $image);
Loading history...
286
287
        if (imagecopymerge($this->_image, $image, 0, 0, 0, 0, $width, $height, 100)) {
0 ignored issues
show
Bug introduced by
It seems like $image can also be of type false; however, parameter $src_im of imagecopymerge() does only seem to accept resource, 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

287
        if (imagecopymerge($this->_image, /** @scrutinizer ignore-type */ $image, 0, 0, 0, 0, $width, $height, 100)) {
Loading history...
288
            // Swap the new image for the old one
289
            imagedestroy($this->_image);
290
            $this->_image = $image;
0 ignored issues
show
Documentation Bug introduced by
It seems like $image can also be of type false. However, the property $_image is declared as type resource. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
291
292
            // Reset the width and height
293
            $this->width = $width;
294
            $this->height = $height;
295
        }
296
    }
297
298
    /**
299
     * Execute a flip.
300
     *
301
     * @param   integer $direction direction to flip
302
     * @return  void
303
     */
304
    public function _do_flip($direction)
305
    {
306
        // Create the flipped image
307
        $flipped = $this->_create($this->width, $this->height);
308
309
        // Loads image if not yet loaded
310
        $this->_load_image();
311
312
        if ($direction === Image::HORIZONTAL) {
313
            for ($x = 0; $x < $this->width; $x++) {
314
                // Flip each row from top to bottom
315
                imagecopy($flipped, $this->_image, $x, 0, $this->width - $x - 1, 0, 1, $this->height);
316
            }
317
        } else {
318
            for ($y = 0; $y < $this->height; $y++) {
319
                // Flip each column from left to right
320
                imagecopy($flipped, $this->_image, 0, $y, 0, $this->height - $y - 1, $this->width, 1);
321
            }
322
        }
323
324
        // Swap the new image for the old one
325
        imagedestroy($this->_image);
326
        $this->_image = $flipped;
327
328
        // Reset the width and height
329
        $this->width = imagesx($flipped);
330
        $this->height = imagesy($flipped);
331
    }
332
333
    /**
334
     * Execute a sharpen.
335
     *
336
     * @param   integer $amount amount to sharpen
337
     * @return  void
338
     * @throws ErrorException
339
     */
340
    public function _do_sharpen($amount)
341
    {
342
        if (empty(Image_GD::$_available_functions[Image_GD::IMAGECONVOLUTION])) {
343
            throw new ErrorException('This method requires imageconvolution, which is only available in the bundled version of GD');
344
        }
345
346
        // Loads image if not yet loaded
347
        $this->_load_image();
348
349
        // Amount should be in the range of 18-10
350
        $amount = round(abs(-18 + ($amount * 0.08)), 2);
351
352
        // Gaussian blur matrix
353
        $matrix = array
354
        (
355
            array(-1, -1, -1),
356
            array(-1, $amount, -1),
357
            array(-1, -1, -1),
358
        );
359
360
        // Perform the sharpen
361
        if (imageconvolution($this->_image, $matrix, $amount - 8, 0)) {
362
            // Reset the width and height
363
            $this->width = imagesx($this->_image);
364
            $this->height = imagesy($this->_image);
365
        }
366
    }
367
368
    /**
369
     * Execute a reflection.
370
     *
371
     * @param   integer $height reflection height
372
     * @param   integer $opacity reflection opacity
373
     * @param   boolean $fade_in true to fade out, false to fade in
374
     * @return  void
375
     * @throws ErrorException
376
     */
377
    public function _do_reflection($height, $opacity, $fade_in)
378
    {
379
        if (empty(Image_GD::$_available_functions[Image_GD::IMAGEFILTER])) {
380
            throw new ErrorException('This method requires imagefilter, which is only available in the bundled version of GD');
381
        }
382
383
        // Loads image if not yet loaded
384
        $this->_load_image();
385
386
        // Convert an opacity range of 0-100 to 127-0
387
        $opacity = round(abs(($opacity * 127 / 100) - 127));
388
389
        if ($opacity < 127) {
390
            // Calculate the opacity stepping
391
            $stepping = (127 - $opacity) / $height;
392
        } else {
393
            // Avoid a "divide by zero" error
394
            $stepping = 127 / $height;
395
        }
396
397
        // Create the reflection image
398
        $reflection = $this->_create($this->width, $this->height + $height);
399
400
        // Copy the image to the reflection
401
        imagecopy($reflection, $this->_image, 0, 0, 0, 0, $this->width, $this->height);
402
403
        for ($offset = 0; $height >= $offset; $offset++) {
404
            // Read the next line down
405
            $src_y = $this->height - $offset - 1;
406
407
            // Place the line at the bottom of the reflection
408
            $dst_y = $this->height + $offset;
409
410
            if ($fade_in === true) {
411
                // Start with the most transparent line first
412
                $dst_opacity = round($opacity + ($stepping * ($height - $offset)));
413
            } else {
414
                // Start with the most opaque line first
415
                $dst_opacity = round($opacity + ($stepping * $offset));
416
            }
417
418
            // Create a single line of the image
419
            $line = $this->_create($this->width, 1);
420
421
            // Copy a single line from the current image into the line
422
            imagecopy($line, $this->_image, 0, 0, 0, $src_y, $this->width, 1);
423
424
            // Colorize the line to add the correct alpha level
425
            imagefilter($line, IMG_FILTER_COLORIZE, 0, 0, 0, $dst_opacity);
0 ignored issues
show
Bug introduced by
$dst_opacity of type double is incompatible with the type integer expected by parameter $arg4 of imagefilter(). ( Ignorable by Annotation )

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

425
            imagefilter($line, IMG_FILTER_COLORIZE, 0, 0, 0, /** @scrutinizer ignore-type */ $dst_opacity);
Loading history...
426
427
            // Copy a the line into the reflection
428
            imagecopy($reflection, $line, 0, $dst_y, 0, 0, $this->width, 1);
429
        }
430
431
        // Swap the new image for the old one
432
        imagedestroy($this->_image);
433
        $this->_image = $reflection;
434
435
        // Reset the width and height
436
        $this->width = imagesx($reflection);
437
        $this->height = imagesy($reflection);
438
    }
439
440
    /**
441
     * Execute a watermarking.
442
     *
443
     * @param   \image\components\Kohana\Image $watermark watermarking Kohana_Image
444
     * @param   integer $offset_x offset from the left
445
     * @param   integer $offset_y offset from the top
446
     * @param   integer $opacity opacity of watermark
447
     * @return  void
448
     * @throws ErrorException
449
     */
450
    public function _do_watermark(\image\components\Kohana\Image $watermark, $offset_x, $offset_y, $opacity)
451
    {
452
        if (empty(Image_GD::$_available_functions[Image_GD::IMAGELAYEREFFECT])) {
453
            throw new ErrorException('This method requires imagelayereffect, which is only available in the bundled version of GD');
454
        }
455
456
        // Loads image if not yet loaded
457
        $this->_load_image();
458
459
        // Create the watermark image resource
460
        $overlay = imagecreatefromstring($watermark->render());
461
462
        imagesavealpha($overlay, true);
0 ignored issues
show
Bug introduced by
It seems like $overlay can also be of type false; however, parameter $image of imagesavealpha() does only seem to accept resource, 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

462
        imagesavealpha(/** @scrutinizer ignore-type */ $overlay, true);
Loading history...
463
464
        // Get the width and height of the watermark
465
        $width = imagesx($overlay);
0 ignored issues
show
Bug introduced by
It seems like $overlay can also be of type false; however, parameter $image of imagesx() does only seem to accept resource, 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

465
        $width = imagesx(/** @scrutinizer ignore-type */ $overlay);
Loading history...
466
        $height = imagesy($overlay);
0 ignored issues
show
Bug introduced by
It seems like $overlay can also be of type false; however, parameter $image of imagesy() does only seem to accept resource, 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

466
        $height = imagesy(/** @scrutinizer ignore-type */ $overlay);
Loading history...
467
468
        if ($opacity < 100) {
469
            // Convert an opacity range of 0-100 to 127-0
470
            $opacity = round(abs(($opacity * 127 / 100) - 127));
471
472
            // Allocate transparent gray
473
            $color = imagecolorallocatealpha($overlay, 127, 127, 127, $opacity);
0 ignored issues
show
Bug introduced by
$opacity of type double is incompatible with the type integer expected by parameter $alpha of imagecolorallocatealpha(). ( Ignorable by Annotation )

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

473
            $color = imagecolorallocatealpha($overlay, 127, 127, 127, /** @scrutinizer ignore-type */ $opacity);
Loading history...
Bug introduced by
It seems like $overlay can also be of type false; however, parameter $image of imagecolorallocatealpha() does only seem to accept resource, 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

473
            $color = imagecolorallocatealpha(/** @scrutinizer ignore-type */ $overlay, 127, 127, 127, $opacity);
Loading history...
474
475
            // The transparent image will overlay the watermark
476
            imagelayereffect($overlay, IMG_EFFECT_OVERLAY);
0 ignored issues
show
Bug introduced by
It seems like $overlay can also be of type false; however, parameter $image of imagelayereffect() does only seem to accept resource, 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

476
            imagelayereffect(/** @scrutinizer ignore-type */ $overlay, IMG_EFFECT_OVERLAY);
Loading history...
477
478
            // Fill the background with the transparent color
479
            imagefilledrectangle($overlay, 0, 0, $width, $height, $color);
0 ignored issues
show
Bug introduced by
It seems like $overlay can also be of type false; however, parameter $image of imagefilledrectangle() does only seem to accept resource, 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

479
            imagefilledrectangle(/** @scrutinizer ignore-type */ $overlay, 0, 0, $width, $height, $color);
Loading history...
480
        }
481
482
        // Alpha blending must be enabled on the background!
483
        imagealphablending($this->_image, true);
484
485
        if (imagecopy($this->_image, $overlay, $offset_x, $offset_y, 0, 0, $width, $height)) {
0 ignored issues
show
Bug introduced by
It seems like $overlay can also be of type false; however, parameter $src_im of imagecopy() does only seem to accept resource, 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

485
        if (imagecopy($this->_image, /** @scrutinizer ignore-type */ $overlay, $offset_x, $offset_y, 0, 0, $width, $height)) {
Loading history...
486
            // Destroy the overlay image
487
            imagedestroy($overlay);
0 ignored issues
show
Bug introduced by
It seems like $overlay can also be of type false; however, parameter $image of imagedestroy() does only seem to accept resource, 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

487
            imagedestroy(/** @scrutinizer ignore-type */ $overlay);
Loading history...
488
        }
489
    }
490
491
    /**
492
     * Execute a background.
493
     *
494
     * @param   integer $r red
495
     * @param   integer $g green
496
     * @param   integer $b blue
497
     * @param   integer $opacity opacity
498
     * @return void
499
     */
500
    public function _do_background($r, $g, $b, $opacity)
501
    {
502
        // Loads image if not yet loaded
503
        $this->_load_image();
504
505
        // Convert an opacity range of 0-100 to 127-0
506
        $opacity = round(abs(($opacity * 127 / 100) - 127));
507
508
        // Create a new background
509
        $background = $this->_create($this->width, $this->height);
510
511
        // Allocate the color
512
        $color = imagecolorallocatealpha($background, $r, $g, $b, $opacity);
0 ignored issues
show
Bug introduced by
$opacity of type double is incompatible with the type integer expected by parameter $alpha of imagecolorallocatealpha(). ( Ignorable by Annotation )

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

512
        $color = imagecolorallocatealpha($background, $r, $g, $b, /** @scrutinizer ignore-type */ $opacity);
Loading history...
513
514
        // Fill the image with white
515
        imagefilledrectangle($background, 0, 0, $this->width, $this->height, $color);
516
517
        // Alpha blending must be enabled on the background!
518
        imagealphablending($background, true);
519
520
        // Copy the image onto a white background to remove all transparency
521
        if (imagecopy($background, $this->_image, 0, 0, 0, 0, $this->width, $this->height)) {
522
            // Swap the new image for the old one
523
            imagedestroy($this->_image);
524
            $this->_image = $background;
525
        }
526
    }
527
528
    /**
529
     * Execute a save.
530
     *
531
     * @param   string $file new image filename
532
     * @param   integer $quality quality
533
     * @return  boolean
534
     * @throws ErrorException
535
     */
536
    public function _do_save($file, $quality)
537
    {
538
        // Loads image if not yet loaded
539
        $this->_load_image();
540
541
        // Get the extension of the file
542
        $extension = pathinfo($file, PATHINFO_EXTENSION);
543
544
        // Get the save function and IMAGETYPE
545
        list($save, $type) = $this->_save_function($extension, $quality);
546
547
        // Enable interlace
548
        // progressive jpeg
549
        imageinterlace($this->_image, true);
0 ignored issues
show
Bug introduced by
true of type true is incompatible with the type integer expected by parameter $interlace of imageinterlace(). ( Ignorable by Annotation )

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

549
        imageinterlace($this->_image, /** @scrutinizer ignore-type */ true);
Loading history...
550
551
        // Save the image to a file
552
        $status = isset($quality) ? $save($this->_image, $file, $quality) : $save($this->_image, $file);
553
554
        if ($status === true && $type !== $this->type) {
555
            // Reset the image type and mime type
556
            $this->type = $type;
557
            $this->mime = image_type_to_mime_type($type);
558
        }
559
560
        return true;
561
    }
562
563
    /**
564
     * Execute a render.
565
     *
566
     * @param   string $type image type: png, jpg, gif, etc
567
     * @param   integer $quality quality
568
     * @return  string
569
     * @throws ErrorException
570
     */
571
    public function _do_render($type, $quality)
572
    {
573
        // Loads image if not yet loaded
574
        $this->_load_image();
575
576
        // Get the save function and IMAGETYPE
577
        list($save, $type) = $this->_save_function($type, $quality);
578
579
        // Capture the output
580
        ob_start();
581
582
        // Render the image
583
        $status = isset($quality) ? $save($this->_image, null, $quality) : $save($this->_image, null);
584
585
        if ($status === true && $type !== $this->type) {
586
            // Reset the image type and mime type
587
            $this->type = $type;
588
            $this->mime = image_type_to_mime_type($type);
589
        }
590
591
        return ob_get_clean();
592
    }
593
594
    /**
595
     * Get the GD saving function and image type for this extension.
596
     * Also normalizes the quality setting
597
     *
598
     * @param   string $extension image type: png, jpg, etc
599
     * @param   integer $quality image quality
600
     * @return  array    save function, IMAGETYPE_* constant
601
     * @throws  ErrorException
602
     */
603
    protected function _save_function($extension, & $quality)
604
    {
605
        if (!$extension) {
606
            // Use the current image type
607
            $extension = image_type_to_extension($this->type, false);
608
        }
609
610
        switch (strtolower($extension)) {
611
            case 'jpg':
612
            case 'jpeg':
613
                // Save a JPG file
614
                $save = 'imagejpeg';
615
                $type = IMAGETYPE_JPEG;
616
                break;
617
            case 'gif':
618
                // Save a GIF file
619
                $save = 'imagegif';
620
                $type = IMAGETYPE_GIF;
621
622
                // GIFs do not a quality setting
623
                $quality = null;
624
                break;
625
            case 'png':
626
                // Save a PNG file
627
                $save = 'imagepng';
628
                $type = IMAGETYPE_PNG;
629
630
                // Use a compression level of 9 (does not affect quality!)
631
                $quality = 9;
632
                break;
633
            default:
634
                throw new ErrorException(sprintf('Installed GD does not support %s images', $extension));
635
                break;
636
        }
637
638
        return array($save, $type);
639
    }
640
641
    /**
642
     * Create an empty image with the given width and height.
643
     *
644
     * @param   integer $width image width
645
     * @param   integer $height image height
646
     * @return  resource
647
     */
648
    protected function _create($width, $height)
649
    {
650
        // Create an empty image
651
        $image = imagecreatetruecolor($width, $height);
652
653
        // Do not apply alpha blending
654
        imagealphablending($image, false);
0 ignored issues
show
Bug introduced by
It seems like $image can also be of type false; however, parameter $image of imagealphablending() does only seem to accept resource, 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

654
        imagealphablending(/** @scrutinizer ignore-type */ $image, false);
Loading history...
655
656
        // Save alpha levels
657
        imagesavealpha($image, true);
0 ignored issues
show
Bug introduced by
It seems like $image can also be of type false; however, parameter $image of imagesavealpha() does only seem to accept resource, 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

657
        imagesavealpha(/** @scrutinizer ignore-type */ $image, true);
Loading history...
658
659
        return $image;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $image could also return false which is incompatible with the documented return type resource. Did you maybe forget to handle an error condition?

If the returned type also contains false, it is an indicator that maybe an error condition leading to the specific return statement remains unhandled.

Loading history...
660
    }
661
662
} // End Image_GD
663