Completed
Push — master ( 03b0a7...6358f1 )
by Oscar
01:25
created

Gd   C

Complexity

Total Complexity 57

Size/Duplication

Total Lines 380
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 3

Importance

Changes 2
Bugs 1 Features 0
Metric Value
wmc 57
c 2
b 1
f 0
lcom 2
cbo 3
dl 0
loc 380
rs 6.433

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