Imagick::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 Imagick as BaseImagick;
6
use ImagickPixel as BaseImagickPixel;
7
use Imagecow\ImageException;
8
9
/**
10
 * Imagick library.
11
 */
12
class Imagick extends AbstractLib implements LibInterface
13
{
14
    protected $image;
15
16
    /**
17
     * {@inheritdoc}
18
     */
19
    public static function checkCompatibility()
20
    {
21
        return extension_loaded('imagick') && !empty(BaseImagick::queryformats());
22
    }
23
24
    /**
25
     * {@inheritdoc}
26
     *
27
     * @return Imagick
28
     */
29
    public static function createFromFile($filename)
30
    {
31
        $imagick = new BaseImagick();
32
33
        if ($imagick->readImage($filename) !== true) {
34
            throw new ImageException("The image file '{$filename}' cannot be loaded");
35
        }
36
37
        return new static($imagick);
38
    }
39
40
    /**
41
     * {@inheritdoc}
42
     *
43
     * @return Imagick
44
     */
45
    public static function createFromString($string)
46
    {
47
        $imagick = new BaseImagick();
48
49
        $imagick->readImageBlob($string);
50
51
        return new static($imagick);
52
    }
53
54
    /**
55
     * Constructor of the class.
56
     *
57
     * @param BaseImagick $image The Imagick instance
58
     */
59
    public function __construct(BaseImagick $image)
60
    {
61
        $this->image = $image;
62
63
        //Convert CMYK to RGB
64
        if ($this->image->getImageColorspace() !== BaseImagick::COLORSPACE_CMYK) {
65
            return $this;
0 ignored issues
show
Bug introduced by
Constructors do not have meaningful return values, anything that is returned from here is discarded. Are you sure this is correct?
Loading history...
66
        }
67
68
        $profiles = $this->image->getImageProfiles('*', false);
69
70
        if (array_search('icc', $profiles) === false) {
71
            $this->image->profileImage('icc', file_get_contents(__DIR__.'/icc/us_web_uncoated.icc'));
72
        }
73
74
        $this->image->profileImage('icm', file_get_contents(__DIR__.'/icc/srgb.icm'));
75
        $this->image->transformImageColorspace(BaseImagick::COLORSPACE_SRGB);
76
    }
77
78
    /**
79
     * Destroy the image.
80
     */
81
    public function __destruct()
82
    {
83
        $this->image->destroy();
84
    }
85
86
    /**
87
     * {@inheritdoc}
88
     */
89
    public function flip()
90
    {
91
        if ($this->image->flipImage() !== true) {
92
            throw new ImageException('There was an error on flip the image');
93
        }
94
    }
95
96
    /**
97
     * {@inheritdoc}
98
     */
99
    public function flop()
100
    {
101
        if ($this->image->flopImage() !== true) {
102
            throw new ImageException('There was an error on flop the image');
103
        }
104
    }
105
106
    /**
107
     * {@inheritdoc}
108
     */
109
    public function save($filename)
110
    {
111
        $image = $this->getCompressed();
112
113
        if ($this->animated) {
114
            if (!($fp = fopen($filename, 'w'))) {
115
                throw new ImageException("The image file '{$filename}' cannot be saved");
116
            }
117
118
            $image->writeImagesFile($fp);
119
120
            fclose($fp);
121
        } elseif (!$image->writeImage($filename)) {
122
            throw new ImageException("The image file '{$filename}' cannot be saved");
123
        }
124
    }
125
126
    /**
127
     * Gets the original image object.
128
     *
129
     * @return object
130
     */
131
    public function getImage()
132
    {
133
        return $this->image;
134
    }
135
136
    /**
137
     * {@inheritdoc}
138
     */
139
    public function getString()
140
    {
141
        $image = $this->getCompressed();
142
143
        if (!$this->animated) {
144
            return $image->getImageBlob();
145
        }
146
147
        if (!($fp = fopen($file = tempnam(sys_get_temp_dir(), 'imagick'), 'w'))) {
148
            throw new ImageException('Cannot create a temp file to generate the string data image');
149
        }
150
151
        $image->writeImagesFile($fp);
152
153
        fclose($fp);
154
155
        $string = file_get_contents($file);
156
157
        unlink($file);
158
159
        return $string;
160
    }
161
162
    /**
163
     * {@inheritdoc}
164
     */
165
    public function getMimeType()
166
    {
167
        $format = strtolower($this->image->getImageFormat());
168
169
        if (in_array($format, ['jpeg', 'jpg', 'gif', 'png', 'webp'], true)) {
170
            return "image/$format";
171
        }
172
    }
173
174
    /**
175
     * {@inheritdoc}
176
     */
177
    public function getWidth()
178
    {
179
        if ($this->animated) {
180
            return $this->image->coalesceImages()->getImageWidth();
181
        } else {
182
            return $this->image->getImageWidth();
183
        }
184
    }
185
186
    /**
187
     * {@inheritdoc}
188
     */
189
    public function getHeight()
190
    {
191
        if ($this->animated) {
192
            return $this->image->coalesceImages()->getImageHeight();
193
        } else {
194
            return $this->image->getImageHeight();
195
        }
196
    }
197
198
    /**
199
     * {@inheritdoc}
200
     */
201
    public function format($format)
202
    {
203
        if (preg_match('/jpe?g/i', $format)) {
204
            list($r, $g, $b) = $this->background;
205
206
            $this->image->setImageBackgroundColor("rgb($r,$g,$b)");
207
            $this->image = $this->image->mergeImageLayers(BaseImagick::LAYERMETHOD_FLATTEN);
208
        }
209
210
        if ($this->image->setImageFormat($format) !== true) {
211
            throw new ImageException("The image format '{$format}' is not valid");
212
        }
213
    }
214
215
    /**
216
     * {@inheritdoc}
217
     */
218
    public function resize($width, $height)
219
    {
220
        if ($this->animated) {
221
            $this->image = $this->image->coalesceImages();
222
223
            foreach ($this->image as $frame) {
224
                $frame->scaleImage($width, $height);
225
            }
226
227
            $this->image = $this->image->deconstructImages();
228 View Code Duplication
        } else {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
229
            if ($this->image->scaleImage($width, $height) !== true) {
230
                throw new ImageException('There was an error resizing the image');
231
            }
232
233
            $this->image->setImagePage(0, 0, 0, 0);
234
        }
235
    }
236
237
    /**
238
     * {@inheritdoc}
239
     */
240
    public function getCropOffsets($width, $height, $method)
241
    {
242
        $class = 'Imagecow\\Crops\\'.ucfirst(strtolower($method));
243
244
        if (!class_exists($class)) {
245
            throw new ImageException("The crop method '$method' is not available for Imagick");
246
        }
247
248
        return $class::getOffsets($this->image, $width, $height);
249
    }
250
251
    /**
252
     * {@inheritdoc}
253
     */
254
    public function crop($width, $height, $x, $y)
255
    {
256
        if ($this->animated) {
257
            $this->image = $this->image->coalesceImages();
258
259
            foreach ($this->image as $frame) {
260
                $frame->cropImage($width, $height, $x, $y);
261
                $frame->setImagePage(0, 0, 0, 0);
262
            }
263
264
            $this->image = $this->image->deconstructImages();
265 View Code Duplication
        } else {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
266
            if ($this->image->cropImage($width, $height, $x, $y) !== true) {
267
                throw new ImageException('There was an error cropping the image');
268
            }
269
270
            $this->image->setImagePage(0, 0, 0, 0);
271
        }
272
    }
273
274
    /**
275
     * {@inheritdoc}
276
     */
277
    public function rotate($angle)
278
    {
279
        if ($this->image->rotateImage(new BaseImagickPixel('#FFFFFF'), $angle) !== true) {
280
            throw new ImageException('There was an error rotating the image');
281
        }
282
283
        $this->image->setImagePage(0, 0, 0, 0);
284
    }
285
286
    /**
287
     * {@inheritdoc}
288
     */
289
    public function blur($loops)
290
    {
291
        $width = $this->getWidth();
292
        $height = $this->getHeight();
293
294
        $this->resize($width / 5, $height / 5);
295
296
        for ($i = 0; $i < $loops; $i++) {
297
            $this->image->blurImage(5, 100);
298
        }
299
300
        $this->resize($width, $height);
301
302
        $this->image->blurImage(10, 100);
303
    }
304
305
    /**
306
     * Returns a copy of the image compressed and ready to save or print.
307
     *
308
     * @return BaseImagick The instance of the image
309
     */
310
    private function getCompressed()
311
    {
312
        $image = $this->image;
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->image; of type Imagick|boolean adds the type boolean to the return on line 347 which is incompatible with the return type documented by Imagecow\Libs\Imagick::getCompressed of type Imagick.
Loading history...
313
314
        if ($this->animated) {
315
            $image = $image->coalesceImages();
316
317
            foreach ($image as $frame) {
318
                $frame->stripImage();
319
                $frame->setImageUnits(1);
320
                $frame->setImageCompressionQuality($this->quality);
321
            }
322
323
            return $image->deconstructImages();
324
        }
325
326
        $format = strtolower($image->getImageFormat());
327
328
        $image->stripImage();
329
        $image->setImageUnits(1);
330
        $image->setImageCompressionQuality($this->quality);
331
332
        switch ($format) {
333
            case 'jpeg':
334
                $image->setInterlaceScheme(BaseImagick::INTERLACE_JPEG);
335
                $image->setImageCompression(BaseImagick::COMPRESSION_JPEG);
336
                break;
337
338
            case 'gif':
339
                $image->setInterlaceScheme(BaseImagick::INTERLACE_GIF);
340
                break;
341
342
            case 'png':
343
                $image->setInterlaceScheme(BaseImagick::INTERLACE_PNG);
344
                break;
345
        }
346
347
        return $image;
348
    }
349
350
    /**
351
     * {@inheritdoc}
352
     */
353
    public function watermark(LibInterface $image, $x, $y)
354
    {
355
        if (!($image instanceof self)) {
356
            $image = self::createFromString($image->getString());
357
        }
358
359
        $this->image->compositeImage($image->getImage(), BaseImagick::COMPOSITE_DISSOLVE, $x, $y);
360
    }
361
362
    /**
363
     * {@inheritdoc}
364
     */
365
    public function opacity($opacity)
366
    {
367
        if ($opacity >= 100 || $opacity < 0) {
368
            return;
369
        }
370
371
        if ($this->image->getImageAlphaChannel() !== BaseImagick::ALPHACHANNEL_ACTIVATE) {
372
            $this->image->setImageAlphaChannel(BaseImagick::ALPHACHANNEL_OPAQUE);
373
        }
374
375
        // NOTE: Using setImageOpacity will destroy current alpha channels!
376
        $this->image->evaluateImage(BaseImagick::EVALUATE_MULTIPLY, $opacity / 100, BaseImagick::CHANNEL_ALPHA);
377
    }
378
379
    /**
380
     * {@inheritdoc}
381
     */
382
    public function setProgressive($progressive)
383
    {
384
        if ($progressive) {
385
            $this->image->setInterlaceScheme(BaseImagick::INTERLACE_PLANE);
386
        } else {
387
            $this->image->setInterlaceScheme(BaseImagick::INTERLACE_NO);
388
        }
389
    }
390
}
391