Passed
Pull Request — master (#1)
by compolom
02:43
created

GD::blur()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
dl 0
loc 11
ccs 0
cts 6
cp 0
rs 9.9
c 0
b 0
f 0
cc 3
nc 3
nop 0
crap 12
1
<?php declare(strict_types=1);
2
3
namespace Compolomus\Compomage;
4
5
use Compolomus\Compomage\Interfaces\ImageInterface;
6
use Exception;
7
use InvalidArgumentException;
8
use LogicException;
9
use RangeException;
10
use SplFileObject;
11
12
class GD extends AbstractImage
13
{
14
    /**
15
     * GD constructor.
16
     * @param string $image
17
     * @throws Exception
18
     */
19 9
    public function __construct(string $image)
20
    {
21 9
        $this->init($image);
22 7
    }
23
24
    /**
25
     * @param int $width
26
     * @param int $height
27
     * @return ImageInterface
28
     * @throws Exception
29
     */
30
    public function resizeByTransparentBackground(int $width, int $height): ImageInterface
31
    {
32
        $temp = new SplFileObject(tempnam(sys_get_temp_dir(), 'image' . mt_rand()), 'w+');
33
        imagepng($this->newImage($width, $height), $temp->getRealPath(), 9, PNG_ALL_FILTERS);
34
35
        return $this->setBackground($width, $height, new Image($temp->getRealPath(), Image::GD));
36
    }
37
38
    /**
39
     * @param int $width
40
     * @param int $height
41
     * @return ImageInterface
42
     * @throws Exception
43
     */
44 View Code Duplication
    public function resizeByBlurBackground(int $width, int $height): ImageInterface
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
45
    {
46
        $background = new Image(base64_encode((string) $this), Image::GD);
47
        $background->blur()->resize($width, $height);
48
49
        return $this->setBackground($width, $height, $background);
50
    }
51
52
    /**
53
     * @param int $level
54
     * @return ImageInterface
55
     */
56 View Code Duplication
    public function brightness(int $level): ImageInterface
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
57
    {
58
        if (!$this->compareRangeValue($level, 255))
59
        {
60
            throw new InvalidArgumentException('Wrong brightness level, range -255 - 255, ' . $level . ' given');
61
        }
62
        imagefilter($this->getImage(), IMG_FILTER_BRIGHTNESS, $level);
63
64
        return $this;
65
    }
66
67
    /**
68
     * @param int $level
69
     * @return ImageInterface
70
     */
71 View Code Duplication
    public function contrast(int $level): ImageInterface
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
72
    {
73
        if (!$this->compareRangeValue($level, 100))
74
        {
75
            throw new InvalidArgumentException('Wrong contrast level, range -100 - 100, ' . $level . ' given');
76
        }
77
        imagefilter($this->getImage(), IMG_FILTER_CONTRAST, $level);
78
79
        return $this;
80
    }
81
82
    /**
83
     * @return ImageInterface
84
     */
85
    public function negate(): ImageInterface
86
    {
87
        imagefilter($this->getImage(), IMG_FILTER_NEGATE);
88
89
        return $this;
90
    }
91
92
    /**
93
     * @return ImageInterface
94
     */
95
    public function blur(): ImageInterface
96
    {
97
        for ($i = 0; $i < 30; $i++) {
98
            if ($i % 5 === 0) {
99
                imagefilter($this->getImage(), IMG_FILTER_SMOOTH, -7);
100
            }
101
            imagefilter($this->getImage(), IMG_FILTER_GAUSSIAN_BLUR);
102
        }
103
104
        return $this;
105
    }
106
107
    /**
108
     * @return ImageInterface
109
     */
110
    public function flip(): ImageInterface
111
    {
112
        imageflip($this->getImage(), IMG_FLIP_VERTICAL);
113
114
        return $this;
115
    }
116
117
    /**
118
     * @return ImageInterface
119
     */
120
    public function flop(): ImageInterface
121
    {
122
        imageflip($this->getImage(), IMG_FLIP_HORIZONTAL);
123
124
        return $this;
125
    }
126
127
    /**
128
     * @return ImageInterface
129
     */
130
    public function grayscale(): ImageInterface
131
    {
132
        imagefilter($this->getImage(), IMG_FILTER_GRAYSCALE);
133
134
        return $this;
135
    }
136
137
    /**
138
     * @param string $text
139
     * @param string $font
140
     * @param string $position
141
     * @return $this
142
     * @throws InvalidArgumentException
143
     * @throws Exception
144
     */
145 1
    public function copyright(string $text, string $font, string $position = 'SouthWest'): ImageInterface
146
    {
147 1
        if (!array_key_exists(strtoupper($position), self::POSITIONS)) {
148 1
            throw new InvalidArgumentException('Wrong position');
149
        }
150
151
        imagecopymerge(
152
            $this->getImage(),
153
            $image = $this->prepareImage($text, $font),
154
            (int) ((($this->getWidth() - imagesx($image)) / 2) * self::POSITIONS[strtoupper($position)]['x']) + self::POSITIONS[strtoupper($position)]['padX'],
155
            (int) ((($this->getHeight() - imagesy($image)) / 2) * self::POSITIONS[strtoupper($position)]['y']) + self::POSITIONS[strtoupper($position)]['padY'],
156
            0,
157
            0,
158
            $this->getWidth(),
159
            $this->getHeight(),
160
            80
161
        );
162
163
        return $this;
164
    }
165
166
    /**
167
     * @param string $text
168
     * @param string $font
169
     * @return resource
170
     * @throws InvalidArgumentException
171
     * @throws Exception
172
     */
173
    private function prepareImage(string $text, string $font)
174
    {
175
        if (!$coordinates = imagettfbbox($fontSize = 15, 0, $font, $text)) {
176
            throw new InvalidArgumentException('Does not support font');
177
        }
178
179
        $minX = min([$coordinates[0], $coordinates[2], $coordinates[4], $coordinates[6]]);
180
        $maxX = max([$coordinates[0], $coordinates[2], $coordinates[4], $coordinates[6]]);
181
        $minY = min([$coordinates[1], $coordinates[3], $coordinates[5], $coordinates[7]]);
182
        $maxY = max([$coordinates[1], $coordinates[3], $coordinates[5], $coordinates[7]]);
183
        $textX = (int) abs($minX) + 1;
184
        $textY = (int) abs($minY) + 1;
185
        $image = $this->newImage($maxX - $minX + 2, $maxY - $minY + 2);
186
        imagecolortransparent($image, $white = imagecolorallocate($image, 0, 0, 0));
187
        imagefilledrectangle($image, 0, 0, $this->getWidth(), 20, $white);
188
        imagettftext($image, $fontSize, 0, $textX, $textY, $white, $font, $text);
189
        imagettftext($image, $fontSize, 0, $textX + 1, $textY + 1, $blue = imagecolorallocate($image, 0, 128, 128),
190
            $font, $text);
191
        imagettftext($image, $fontSize, 0, $textX + 1, $textY + 1, $red = imagecolorallocate($image, 255, 0, 0), $font,
192
            $text);
193
        imagettftext($image, $fontSize, 0, $textX + 2, $textY + 2, $black = imagecolorallocate($image, 255, 255, 255),
194
            $font, $text);
195
        imagettftext($image, $fontSize, 0, $textX + 2, $textY + 2, $gray = imagecolorallocate($image, 128, 128, 128),
196
            $font, $text);
197
198
        return $image;
199
    }
200
201
    /**
202
     * @param int $width
203
     * @param int $height
204
     * @return resource
205
     */
206 3
    protected function newImage(int $width, int $height)
207
    {
208 3
        $newimage = imagecreatetruecolor($width, $height);
209 3
        $transparent = imagecolorallocatealpha($newimage, 255, 255, 255, 127);
210 3
        imagefill($newimage, 0, 0, $transparent);
211 3
        imagealphablending($newimage, true);
212 3
        imagesavealpha($newimage, true);
213
214 3
        return $newimage;
215
    }
216
217
    /**
218
     * @param int $width
219
     * @param int $height
220
     * @return ImageInterface
221
     */
222 2
    public function resize(int $width, int $height): ImageInterface
223
    {
224 2
        $newimage = $this->newImage($width, $height);
225 2
        imagecopyresampled($newimage, $this->getImage(), 0, 0, 0, 0, $width, $height, $this->getWidth(),
226 2
            $this->getHeight());
227 2
        $this->setImage($newimage);
228 2
        $this->setSizes();
229
230 2
        return $this;
231
    }
232
233 8
    protected function setSizes(): void
234
    {
235 8
        $this->setWidth(imagesx($this->getImage()));
236 8
        $this->setHeight(imagesy($this->getImage()));
237 8
        $this->setOrientation();
238 8
    }
239
240
    /**
241
     * @param int $width
242
     * @param int $height
243
     * @param int $x
244
     * @param int $y
245
     * @return ImageInterface
246
     */
247
    public function crop(int $width, int $height, int $x, int $y): ImageInterface
248
    {
249
        $width -= $x;
250
        $height -= $y;
251
        $newimage = $this->newImage($width, $height);
252
        imagecopyresampled($newimage, $this->getImage(), 0, 0, $x, $y, $width, $height, $width, $height);
253
        $this->setImage($newimage);
254
        $this->setSizes();
255
256
        return $this;
257
    }
258
259
    /**
260
     * @param int $angle
261
     * @return ImageInterface
262
     */
263 1
    public function rotate(int $angle = 90): ImageInterface
264
    {
265 1
        $angle *= -1;
266 1
        $transparent = imagecolorallocatealpha($this->image, 0, 0, 0, 127);
267 1
        $rotate = imagerotate($this->getImage(), $angle, $transparent);
268 1
        imagealphablending($rotate, true);
269 1
        imagesavealpha($rotate, true);
270 1
        $this->setImage($rotate);
271 1
        $this->setSizes();
272
273 1
        return $this;
274
    }
275
276
    /**
277
     * @throws LogicException
278
     * @throws RangeException
279
     * @return string
280
     */
281 1
    public function __toString(): string
282
    {
283 1
        $temp = new SplFileObject(tempnam(sys_get_temp_dir(), 'image' . mt_rand()), 'w+');
284 1
        imagepng($this->getImage(), $temp->getRealPath(), 9, PNG_ALL_FILTERS);
285
286 1
        return trim(file_get_contents($temp->getRealPath()));
287
    }
288
289
    /**
290
     * @param string $filename
291
     * @param int $quality
292
     * @return bool
293
     */
294 1
    public function save(string $filename, $quality = 100): bool
295
    {
296 1
        return imagepng($this->getImage(), $filename . '.png', (int) ($quality / 11), PNG_ALL_FILTERS);
297
    }
298
299
    /**
300
     * @param int $width
301
     * @param int $height
302
     * @param int $newWidth
303
     * @param int $newHeight
304
     * @return ImageInterface
305
     */
306 1
    protected function prepareThumbnail(
307
        int $width,
308
        int $height,
309
        int $newWidth = 0,
310
        int $newHeight = 0
311
    ): ImageInterface {
312 1
        imagecopyresampled(
313 1
            $newimage = $this->newImage($width, $height),
314 1
            $this->getImage(),
315 1
            0 - (int) (($newWidth - $width) / 2),
316 1
            0 - (int) (($newHeight - $height) / 2),
317 1
            0,
318 1
            0,
319 1
            $newWidth,
320 1
            $newHeight,
321 1
            $this->getWidth(),
322 1
            $this->getHeight()
323
        );
324 1
        $this->setImage($newimage);
325 1
        $this->setSizes();
326
327 1
        return $this;
328
    }
329
330
    /**
331
     * @param string $source
332
     * @return ImageInterface
333
     * @throws InvalidArgumentException
334
     * @throws Exception
335
     */
336 8
    protected function tmp(string $source): ImageInterface
337
    {
338 8
        if (@!is_resource($image = @imagecreatefromstring($source))) {
339 1
            throw new InvalidArgumentException('Image create failed');
340
        }
341 7
        $this->setImage($image);
342 7
        $this->setSizes();
343
        // save transparent
344 7
        imagesavealpha($this->getImage(), true);
345 7
        imagealphablending($this->getImage(), false);
346
347 7
        return $this;
348
    }
349
350
    /**
351
     * @param ImageInterface $watermark
352
     * @param int $x
353
     * @param int $y
354
     * @return ImageInterface
355
     */
356
    protected function prepareWatermark($watermark, int $x, int $y): ImageInterface
357
    {
358
        imagealphablending($this->getImage(), true);
359
        imagesavealpha($this->getImage(), true);
360
        imagecopyresampled(
361
            $this->getImage(),
362
            $watermark->getImage(),
363
            $x,
364
            $y,
365
            0,
366
            0,
367
            $width = $watermark->getWidth(),
368
            $height = $watermark->getHeight(),
369
            $width,
370
            $height
371
        );
372
373
        return $this;
374
    }
375
}
376