Gd::watermark()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

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