Completed
Push — master ( 1dd68b...a95690 )
by Oscar
02:07
created

Gd   D

Complexity

Total Complexity 58

Size/Duplication

Total Lines 385
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 3

Importance

Changes 0
Metric Value
wmc 58
lcom 2
cbo 3
dl 0
loc 385
rs 4.5599
c 0
b 0
f 0

23 Methods

Rating   Name   Duplication   Size   Complexity  
A checkCompatibility() 0 4 1
A createFromFile() 0 14 4
A createFromString() 0 8 2
A __construct() 0 8 2
A __destruct() 0 4 1
A flip() 0 4 1
A flop() 0 4 1
A save() 0 9 3
A getImage() 0 4 1
A getString() 0 19 3
A getMimeType() 0 4 1
A getWidth() 0 4 1
A getHeight() 0 4 1
B format() 0 49 10
A resize() 0 11 2
A getCropOffsets() 0 8 2
A crop() 0 16 2
A rotate() 0 13 3
A blur() 0 19 3
A watermark() 0 8 2
B opacity() 0 30 6
A setProgressive() 0 4 1
A createImage() 0 22 5

How to fix   Complexity   

Complex Class

Complex classes like 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. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

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 Gd, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Imagecow\Libs;
4
5
use Imagecow\ImageException;
6
7
/**
8
 * GD library.
9
 */
10
class Gd extends AbstractLib implements LibInterface
11
{
12
    public static $fallbackCropMethods = [
13
        'Entropy' => ['center', 'middle'],
14
        'Balanced' => ['center', 'middle'],
15
    ];
16
17
    protected $image;
18
    protected $type;
19
20
    /**
21
     * {@inheritdoc}
22
     */
23
    public static function checkCompatibility()
24
    {
25
        return extension_loaded('gd');
26
    }
27
28
    /**
29
     * {@inheritdoc}
30
     */
31
    public static function createFromFile($filename)
32
    {
33
        $data = getImageSize($filename);
34
35
        if (empty($data) || !is_array($data)) {
36
            throw new ImageException("The image file '{$filename}' cannot be loaded");
37
        }
38
39
        $function = 'imagecreatefrom'.image_type_to_extension($data[2], false);
40
41
        if (function_exists($function)) {
42
            return new static($function($filename), $data[2]);
43
        }
44
    }
45
46
    /**
47
     * {@inheritdoc}
48
     */
49
    public static function createFromString($string)
50
    {
51
        if (($image = imagecreatefromstring($string))) {
52
            return new static($image);
53
        }
54
55
        throw new ImageException('Error creating the image from string');
56
    }
57
58
    /**
59
     * Constructor of the class.
60
     *
61
     * @param resource $image The Gd resource.
62
     */
63
    public function __construct($image, $type = null)
64
    {
65
        $this->image = $image;
66
        $this->type = isset($type) ? $type : IMAGETYPE_PNG;
67
68
        imagealphablending($this->image, true);
69
        imagesavealpha($this->image, true);
70
    }
71
72
    /**
73
     * Destroy the image.
74
     */
75
    public function __destruct()
76
    {
77
        imagedestroy($this->image);
78
    }
79
80
    /**
81
     * {@inheritdoc}
82
     */
83
    public function flip()
84
    {
85
        imageflip($this->image, IMG_FLIP_VERTICAL);
86
    }
87
88
    /**
89
     * {@inheritdoc}
90
     */
91
    public function flop()
92
    {
93
        imageflip($this->image, IMG_FLIP_HORIZONTAL);
94
    }
95
96
    /**
97
     * {@inheritdoc}
98
     */
99
    public function save($filename)
100
    {
101
        $extension = image_type_to_extension($this->type, false);
102
        $function = 'image'.$extension;
103
104
        if (!function_exists($function) || ($function($this->image, $filename) === false)) {
105
            throw new ImageException("The image format '{$extension}' cannot be saved to '{$filename}'");
106
        }
107
    }
108
109
    /**
110
     * Gets the original image object.
111
     *
112
     * @return resource
113
     */
114
    public function getImage()
115
    {
116
        return $this->image;
117
    }
118
119
    /**
120
     * {@inheritdoc}
121
     */
122
    public function getString()
123
    {
124
        $extension = image_type_to_extension($this->type, false);
125
        $function = 'image'.$extension;
126
127
        if (!function_exists($function)) {
128
            throw new ImageException("The image format '{$extension}' cannot be exported");
129
        }
130
131
        ob_start();
132
133
        if ($extension === 'jpeg') {
134
            $function($this->image, null, $this->quality);
135
        } else {
136
            $function($this->image);
137
        }
138
139
        return ob_get_clean();
140
    }
141
142
    /**
143
     * {@inheritdoc}
144
     */
145
    public function getMimeType()
146
    {
147
        return image_type_to_mime_type($this->type);
148
    }
149
150
    /**
151
     * {@inheritdoc}
152
     */
153
    public function getWidth()
154
    {
155
        return imagesx($this->image);
156
    }
157
158
    /**
159
     * {@inheritdoc}
160
     */
161
    public function getHeight()
162
    {
163
        return imagesy($this->image);
164
    }
165
166
    /**
167
     * {@inheritdoc}
168
     */
169
    public function format($format)
170
    {
171
        switch (strtolower($format)) {
172
            case 'jpg':
173
            case 'jpeg':
174
                $width = $this->getWidth();
175
                $height = $this->getHeight();
176
177
                if (($image = imagecreatetruecolor($width, $height)) === false) {
178
                    throw new ImageException('Error creating a image');
179
                }
180
181
                if (imagesavealpha($image, true) === false) {
182
                    throw new ImageException('Error saving the alpha chanel of the image');
183
                }
184
185
                if (isset($this->background[3])) {
186
                    $background = imagecolorallocatealpha($image, $this->background[0], $this->background[1], $this->background[2], $this->background[3]);
187
                } else {
188
                    $background = imagecolorallocate($image, $this->background[0], $this->background[1], $this->background[2]);
189
                }
190
191
                if (imagefill($image, 0, 0, $background) === false) {
192
                    throw new ImageException('Error filling the image');
193
                }
194
195
                imagecopy($image, $this->image, 0, 0, 0, 0, $width, $height);
196
197
                imagedestroy($this->image);
198
                $this->image = $image;
199
                $this->type = IMAGETYPE_JPEG;
200
                break;
201
202
            case 'gif':
203
                $this->type = IMAGETYPE_GIF;
204
                break;
205
206
            case 'png':
207
                $this->type = IMAGETYPE_PNG;
208
                break;
209
210
            case 'webp':
211
                $this->type = IMAGETYPE_WEBP;
212
                break;
213
214
            default:
215
                throw new ImageException("The image format '{$format}' is not valid");
216
        }
217
    }
218
219
    /**
220
     * imagescale() is not used due a weird black border:
221
     * https://bugs.php.net/bug.php?id=73281&thanks=6
222
     * 
223
     * {@inheritdoc}
224
     */
225
    public function resize($width, $height)
226
    {
227
        $image = $this->createImage($width, $height, array(0, 0, 0, 127));
228
229
        if (imagecopyresampled($image, $this->image, 0, 0, 0, 0, $width, $height, $this->getWidth(), $this->getHeight()) === false) {
230
            throw new ImageException('Error resizing the image');
231
        }
232
233
        imagedestroy($this->image);
234
        $this->image = $image;
235
    }
236
237
    /**
238
     * {@inheritdoc}
239
     */
240
    public function getCropOffsets($width, $height, $method)
241
    {
242
        if (empty(static::$fallbackCropMethods[$method])) {
243
            throw new ImageException("The crop method '$method' is not available for Gd");
244
        }
245
246
        return static::$fallbackCropMethods[$method];
247
    }
248
249
    /**
250
     * {@inheritdoc}
251
     */
252
    public function crop($width, $height, $x, $y)
253
    {
254
        $crop = [
255
            'width' => $width,
256
            'height' => $height,
257
            'x' => $x,
258
            'y' => $y,
259
        ];
260
261
        if (($image = imagecrop($this->image, $crop)) === false) {
262
            throw new ImageException('Error cropping the image');
263
        }
264
265
        imagedestroy($this->image);
266
        $this->image = $image;
267
    }
268
269
    /**
270
     * {@inheritdoc}
271
     */
272
    public function rotate($angle)
273
    {
274
        $background = imagecolorallocatealpha($this->image, 0, 0, 0, 127);
275
276
        if ($background === false || ($image = imagerotate($this->image, -$angle, $background)) === false) {
277
            throw new ImageException('Error rotating the image');
278
        }
279
280
        imagecolortransparent($image, imagecolorallocatealpha($image, 0, 0, 0, 127));
281
282
        imagedestroy($this->image);
283
        $this->image = $image;
284
    }
285
286
    /**
287
     * {@inheritdoc}
288
     */
289
    public function blur($loops)
290
    {
291
        $width = $this->getWidth();
292
        $height = $this->getHeight();
293
        $loops *= 10;
294
295
        $this->resize($width / 5, $height / 5);
296
297
        for ($x = 0; $x < $loops; $x++) {
298
            if (($x % 4) === 0) {
299
                imagefilter($this->image, IMG_FILTER_SMOOTH, -4);
300
                imagefilter($this->image, IMG_FILTER_BRIGHTNESS, 2);
301
            }
302
303
            imagefilter($this->image, IMG_FILTER_GAUSSIAN_BLUR);
304
        }
305
306
        $this->resize($width, $height);
307
    }
308
309
    /**
310
     * {@inheritdoc}
311
     */
312
    public function watermark(LibInterface $image, $x, $y)
313
    {
314
        if (!($image instanceof self)) {
315
            $image = self::createFromString($image->getString());
316
        }
317
318
        imagecopy($this->image, $image->getImage(), $x, $y, 0, 0, $image->getWidth(), $image->getHeight());
319
    }
320
321
    /**
322
     * {@inheritdoc}
323
     */
324
    public function opacity($opacity)
325
    {
326
        if ($opacity >= 100 || $opacity < 0) {
327
            return;
328
        }
329
330
        $this->format('png');
331
332
        $opacity = $opacity / 100;
333
334
        $width = $this->getWidth();
335
        $height = $this->getHeight();
336
337
        imagealphablending($this->image, false);
338
339
        for ($x = 0; $x < $width; ++$x) {
340
            for ($y = 0; $y < $height; ++$y) {
341
                $color = imagecolorat($this->image, $x, $y);
342
                $alpha = 127 - (($color >> 24) & 0xFF);
343
344
                if ($alpha <= 0) {
345
                    continue;
346
                }
347
348
                $color = ($color & 0xFFFFFF) | ((int) round(127 - $alpha * $opacity) << 24);
349
350
                imagesetpixel($this->image, $x, $y, $color);
351
            }
352
        }
353
    }
354
355
    /**
356
     * {@inheritdoc}
357
     */
358
    public function setProgressive($progressive)
359
    {
360
        imageinterlace($this->image, $progressive);
361
    }
362
363
    /**
364
     * Creates a new truecolor image
365
     *
366
     * @param integer $width
367
     * @param integer $height
368
     * @param array   $background
369
     *
370
     * @return resource
371
     */
372
     private function createImage($width, $height, array $background = [0, 0, 0])
373
    {
374
        if (($image = imagecreatetruecolor($width, $height)) === false) {
375
            throw new ImageException('Error creating a image');
376
        }
377
378
        if (imagesavealpha($image, true) === false) {
379
            throw new ImageException('Error saving the alpha chanel of the image');
380
        }
381
382
        if (isset($background[3])) {
383
            $background = imagecolorallocatealpha($image, $background[0], $background[1], $background[2], $background[3]);
384
        } else {
385
            $background = imagecolorallocate($image, $background[0], $background[1], $background[2]);
386
        }
387
388
        if (imagefill($image, 0, 0, $background) === false) {
389
            throw new ImageException('Error filling the image');
390
        }
391
392
        return $image;
393
    }
394
}
395