Passed
Branch master (068d7d)
by Mathieu
01:51
created

Image   B

Complexity

Total Complexity 51

Size/Duplication

Total Lines 291
Duplicated Lines 0 %

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
eloc 160
dl 0
loc 291
ccs 0
cts 170
cp 0
rs 7.92
c 0
b 0
f 0
wmc 51

17 Methods

Rating   Name   Duplication   Size   Complexity  
A getHeight() 0 3 1
A isLandscape() 0 3 1
A load() 0 17 4
A getWidth() 0 3 1
A isPortrait() 0 3 1
A chain() 0 7 1
A writeText() 0 17 4
A mirror() 0 2 1
A crop() 0 18 2
A rotate() 0 2 1
A line() 0 11 2
A resizeCanvas() 0 21 3
A flip() 0 2 1
B save() 0 24 7
B getCoordinatesFromString() 0 44 10
A resize() 0 29 5
B merge() 0 38 6

How to fix   Complexity   

Complex Class

Complex classes like Image 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, and based on these observations, apply Extract Interface, too.

1
<?php declare(strict_types=1);
2
namespace Suricate;
3
4
class Image
5
{
6
    use Traits\ImageFilter;
7
8
    public $source;
9
    private $destination;
10
11
    private $width;
12
    private $height;
13
    
14
15
    public function load($filename)
16
    {
17
        if (is_file($filename) && $imgString = file_get_contents($filename)) {
18
            $imgString = imagecreatefromstring($imgString);
19
            if ($imgString !== false) {
20
                $this->source = $imgString;
21
                $this->destination = $this->source;
22
                $this->width = imagesx($this->source);
23
                $this->height = imagesy($this->source);
24
25
                return $this;
26
            }
27
28
            throw new \InvalidArgumentException('Cannot load ' . $filename . ', not an image');
29
        }
30
        
31
        throw new \InvalidArgumentException('Cannot load ' . $filename . ', file unreadable');
32
    }
33
34
    public function getWidth()
35
    {
36
        return $this->width;
37
    }
38
39
    public function getHeight()
40
    {
41
        return $this->height;
42
    }
43
44
    public function isPortrait()
45
    {
46
        return $this->width < $this->height;
47
    }
48
49
    public function isLandscape()
50
    {
51
        return $this->height < $this->width;
52
    }
53
54
    public function chain()
55
    {
56
        $this->source   = $this->destination;
57
        $this->width    = imagesx($this->source);
58
        $this->height   = imagesy($this->source);
59
60
        return $this;
61
    }
62
63
    public function resize($width = null, $height = null)
64
    {
65
        if ($this->source) {
66
            if ($width == null) {
67
                $width = intval(round(($height / $this->height) * $this->width, 0));
68
            } elseif ($height == null) {
69
                $height = intval(round(($width / $this->width) * $this->height, 0));
70
            }
71
72
            $this->destination = imagecreatetruecolor($width, $height);
73
            if ($this->destination === false) {
74
                throw new \RuntimeException("Can't create destination image");
75
            }
76
            imagecopyresampled(
77
                $this->destination,
78
                $this->source,
79
                0,
80
                0,
81
                0,
82
                0,
83
                $width,
84
                $height,
85
                $this->width,
86
                $this->height
87
            );
88
89
            return $this->chain();
90
        }
91
        return $this;
92
    }
93
94
    public function crop($width, $height)
95
    {
96
        $centerX = round($this->width / 2);
97
        $centerY = round($this->height / 2);
98
99
        $cropWidthHalf  = round($width / 2);
100
        $cropHeightHalf = round($height / 2);
101
102
        $x1 = intval(max(0, $centerX - $cropWidthHalf));
103
        $y1 = intval(max(0, $centerY - $cropHeightHalf));
104
        
105
        $this->destination = imagecreatetruecolor($width, $height);
106
        if ($this->destination === false) {
107
            throw new \RuntimeException("Can't create destination image");
108
        }
109
        imagecopy($this->destination, $this->source, 0, 0, $x1, $y1, $width, $height);
110
111
        return $this->chain();
112
    }
113
114
    public function resizeCanvas($width, $height, $position = null, $color = [0, 0, 0])
115
    {
116
        $this->destination = imagecreatetruecolor($width, $height);
117
        if ($this->destination === false) {
118
            throw new \RuntimeException("Can't create destination image");
119
        }
120
        $colorRes = imagecolorallocate($this->destination, $color[0], $color[1], $color[2]);
121
        $imageObj = new Image();
122
        $imageObj->width = $width;
123
        $imageObj->height = $height;
124
        imagefill($this->destination, 0, 0, $colorRes);
125
126
        if ($position !== null) {
127
            list($x, $y) = $imageObj->getCoordinatesFromString($position, $this->width, $this->height);
128
        } else {
129
            $x = 0;
130
            $y = 0;
131
        }
132
        imagecopy($this->destination, $this->source, $x, $y, 0, 0, $this->width, $this->height);
0 ignored issues
show
Bug introduced by
It seems like $y can also be of type double; however, parameter $dst_y of imagecopy() does only seem to accept integer, 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

132
        imagecopy($this->destination, $this->source, $x, /** @scrutinizer ignore-type */ $y, 0, 0, $this->width, $this->height);
Loading history...
Bug introduced by
It seems like $x can also be of type double; however, parameter $dst_x of imagecopy() does only seem to accept integer, 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

132
        imagecopy($this->destination, $this->source, /** @scrutinizer ignore-type */ $x, $y, 0, 0, $this->width, $this->height);
Loading history...
133
134
        return $this->chain();
135
    }
136
137
    
138
139
    public function rotate()
140
    {
141
    }
142
143
    public function mirror()
144
    {
145
    }
146
147
    public function flip()
148
    {
149
    }
150
151
    public function merge($source, $position = null, $x = null, $y = null, $percent = 100)
152
    {
153
        if ($source instanceof \Suricate\Image) {
154
        } else {
155
            $source = with(new Image())->load($source);
156
        }
157
158
        if ($position !== null) {
159
            list($x, $y) = $this->getCoordinatesFromString($position, $source->width, $source->height);
160
        }
161
        $x = $x !== null ? $x : 0;
162
        $y = $y !== null ? $y : 0;
163
164
        // Handle transparent image
165
        // creating a cut resource
166
        $cut = imagecreatetruecolor($source->getWidth(), $source->getHeight());
167
        if ($cut === false) {
168
            throw new \RuntimeException("Can't create destination image");
169
        }
170
        // copying relevant section from background to the cut resource
171
        imagecopy($cut, $this->destination, 0, 0, $x, $y, $source->getWidth(), $source->getHeight());
172
        
173
        // copying relevant section from watermark to the cut resource
174
        imagecopy($cut, $source->source, 0, 0, 0, 0, $source->getWidth(), $source->getHeight());
175
176
        imagecopymerge(
177
            $this->destination,
178
            $cut,
179
            $x,
180
            $y,
181
            0,
182
            0,
183
            $source->getWidth(),
184
            $source->getHeight(),
185
            $percent
186
        );
187
188
        return $this->chain();
189
    }
190
191
    public function writeText($text, $x = 0, $y = 0, \Closure $callback = null)
192
    {
193
        if ($x < 0) {
194
            $x = $this->width + $x;
195
        }
196
        if ($y < 0) {
197
            $y = $this->height + $y;
198
        }
199
        $imageFont = new ImageFont($text);
200
201
        if ($callback != null) {
202
            $callback($imageFont);
203
        }
204
205
        $imageFont->apply($this->source, $x, $y);
206
        
207
        return $this;
208
    }
209
210
    public function line($x1, $y1, $x2, $y2, \Closure $callback = null)
211
    {
212
        $imageShape = new ImageShape();
213
        $imageShape->setImage($this->source);
214
        if ($callback != null) {
215
            $callback($imageShape);
216
        }
217
218
        $imageShape->drawLine($x1, $y1, $x2, $y2);
219
220
        return $this;
221
    }
222
223
    
224
225
    public function save($filename, $outputType = null, $quality = 70)
226
    {
227
        $result = false;
228
        if ($outputType === null) {
229
            $extension = pathinfo($filename, PATHINFO_EXTENSION);
230
        } else {
231
            $extension = $outputType;
232
        }
233
        if ($extension !== false) {
234
            switch (strtolower($extension)) {
235
                case 'jpg':
236
                case 'jpeg':
237
                    $result = imagejpeg($this->source, $filename, $quality);
238
                    break;
239
                case 'png':
240
                    $result = imagepng($this->source, $filename);
241
                    break;
242
                case 'gif':
243
                    $result = imagegif($this->source, $filename);
244
                    break;
245
            }
246
        }
247
248
        return $result;
249
    }
250
251
    private function getCoordinatesFromString($position, $offsetWidth = 0, $offsetHeight = 0)
252
    {
253
        switch ($position) {
254
            case 'top':
255
                $x = floor(($this->width / 2) - ($offsetWidth / 2));
256
                $y = 0;
257
                break;
258
            case 'top-right':
259
                $x = $this->width - $offsetWidth;
260
                $y = 0;
261
                break;
262
            case 'left':
263
                $x = 0;
264
                $y = floor(($this->height / 2) - ($offsetHeight / 2));
265
                break;
266
            case 'center':
267
                $x = floor(($this->width / 2) - ($offsetWidth / 2));
268
                $y = floor(($this->height / 2) - ($offsetHeight / 2));
269
                break;
270
            case 'right':
271
                $x = $this->width - $offsetWidth;
272
                $y = floor(($this->height / 2) - ($offsetHeight / 2));
273
                break;
274
            case 'bottom-left':
275
                $x = 0;
276
                $y = $this->height - $offsetHeight;
277
                break;
278
            case 'bottom':
279
                $x = floor(($this->width / 2) - ($offsetWidth / 2));
280
                $y = $this->height - $offsetHeight;
281
                break;
282
            case 'bottom-right':
283
                $x = $this->width - $offsetWidth;
284
                $y = $this->height - $offsetHeight;
285
                break;
286
            
287
            case 'top-left':
288
            default:
289
                $x = 0;
290
                $y = 0;
291
                break;
292
        }
293
294
        return [$x, $y];
295
    }
296
}
297