ImageFilter   F
last analyzed

Complexity

Total Complexity 62

Size/Duplication

Total Lines 310
Duplicated Lines 0 %

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
eloc 159
dl 0
loc 310
ccs 0
cts 169
cp 0
rs 3.44
c 0
b 0
f 0
wmc 62

15 Methods

Rating   Name   Duplication   Size   Complexity  
A mapResizeMode() 0 9 3
A resizeWithMaxHeight() 0 7 2
D resize() 0 47 17
A scaleToHeight() 0 4 1
A getType() 0 3 1
B apply() 0 38 7
A scaleToWidth() 0 4 1
B crop() 0 49 8
A backgroundColorHexToByteArray() 0 6 2
A resizeWithMaxHeightAndWidth() 0 11 3
A getSaveOptions() 0 18 4
A resizeWithMaxWidth() 0 7 2
A getFormat() 0 11 3
B resizeWithFixHeightAndWidth() 0 40 7
A isAnimatedGif() 0 4 1

How to fix   Complexity   

Complex Class

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

1
<?php
2
/**
3
 * Created by PhpStorm.
4
 * User: gseidel
5
 * Date: 11.11.17
6
 * Time: 12:09
7
 */
8
9
namespace Enhavo\Bundle\MediaBundle\Filter\Filter;
10
11
use Enhavo\Bundle\MediaBundle\Content\ContentInterface;
12
use Enhavo\Bundle\MediaBundle\Exception\FilterException;
13
use Enhavo\Bundle\MediaBundle\Filter\AbstractFilter;
14
use Enhavo\Bundle\MediaBundle\Model\FilterSetting;
15
use Imagine\Gd\Imagine;
16
use Imagine\Image\Box;
17
use Imagine\Image\ImageInterface;
18
use Imagine\Image\Palette\RGB;
19
use Imagine\Image\Point;
20
use Imagine\Imagick\Imagine as ImagickImagine;
21
22
class ImageFilter extends AbstractFilter
23
{
24
    /**
25
     * @param ContentInterface|\Enhavo\Bundle\MediaBundle\Model\FileInterface|\Enhavo\Bundle\MediaBundle\Model\FormatInterface $file
26
     * @param FilterSetting $setting
27
     * @throws FilterException
28
     */
29
    public function apply($file, FilterSetting $setting)
30
    {
31
        $content = $this->getContent($file);
32
33
        $format = $this->getFormat($content, $setting);
34
35
        if ($format == 'gif' && class_exists('Imagick') && $this->isAnimatedGif($content->getContent())) {
36
            $imagine = new ImagickImagine();
37
            $image = $imagine->load($content->getContent());
38
            $image->layers()->coalesce();
39
            $resultImage = $image->copy();
40
            $resultImage = $this->resize($resultImage, $setting);
41
            $options = $this->getSaveOptions($format, $setting);
42
            $options['animated'] = true;
43
            $resultImage->save($content->getFilePath(), $options);
44
45
        } elseif($format == 'bmp' && class_exists('Imagick') ) {
46
            $imagine = new ImagickImagine();
47
            $image = $imagine->load($content->getContent());
48
            $resultImage = $image->copy();
49
            $resultImage = $this->resize($resultImage, $setting);
50
            $resultImage->save($content->getFilePath(), $this->getSaveOptions($format, $setting));
51
52
        } elseif(class_exists('Imagick') ) {
53
            $imagine = new ImagickImagine();
54
            $imagine = $imagine->load($content->getContent());
55
            $imagine = $this->resize($imagine, $setting);
56
            $imagine->save($content->getFilePath(), $this->getSaveOptions($format, $setting));
57
58
        } else {
59
            $imagine = new Imagine();
60
            $imagine = $imagine->load($content->getContent());
61
            $imagine = $this->resize($imagine, $setting);
62
            $imagine->save($content->getFilePath(), $this->getSaveOptions($format, $setting));
63
        }
64
65
        $this->setMimeType($file, $this->getMimeTypeFromImageFormat($format));
66
        $this->setExtension($file, $format);
67
    }
68
69
    /**
70
     * @param $format
71
     * @param FilterSetting $setting
72
     * @return array
73
     */
74
    private function getSaveOptions($format, FilterSetting $setting)
75
    {
76
        $options = [];
77
78
        $options['format'] = $format;
79
        if($setting->hasSetting('format')) {
80
            $options['format'] = $setting->getSetting('format');
81
        }
82
83
        if($setting->hasSetting('jpeg_quality')) {
84
            $options['jpeg_quality'] = $setting->getSetting('jpeg_quality');
85
        }
86
87
        if($setting->hasSetting('png_compression_level')) {
88
            $options['png_compression_level'] = $setting->getSetting('png_compression_level');
89
        }
90
91
        return $options;
92
    }
93
94
    private function getFormat(ContentInterface $content, FilterSetting $setting)
95
    {
96
        $format = $setting->getSetting('format');
97
        if($format === null) {
98
            $format = $this->getImageFormat($content);
99
            if($format) {
100
                return $format;
101
            }
102
            return 'png';
103
        }
104
        return $format;
105
    }
106
107
    /**
108
     * @param ImageInterface $image
109
     * @param FilterSetting $setting
110
     * @return ImageInterface
111
     * @throws FilterException
112
     */
113
    private function resize(ImageInterface $image, FilterSetting $setting)
114
    {
115
        if($setting->getSetting('cropHeight') !== null &&
116
            $setting->getSetting('cropWidth') !== null  &&
117
            $setting->getSetting('cropY') !== null  &&
118
            $setting->getSetting('cropX') !== null
119
        ) {
120
            $image = $this->crop(
121
                $image,
122
                $setting->getSetting('cropWidth'),
123
                $setting->getSetting('cropHeight'),
124
                $setting->getSetting('cropX'),
125
                $setting->getSetting('cropY'),
126
                $this->backgroundColorHexToByteArray($setting->getSetting('backgroundColor', 'FFFFFF')),
127
                $setting->getSetting('backgroundColorAlpha', 0)
128
            );
129
        }
130
131
        if($setting->getSetting('maxHeight') && $setting->getSetting('maxWidth') == null) {
132
            $image = $this->resizeWithMaxHeight($image, $setting->getSetting('maxHeight'));
133
        }
134
135
        if($setting->getSetting('maxWidth') && $setting->getSetting('maxHeight') == null) {
136
            $image = $this->resizeWithMaxWidth($image, $setting->getSetting('maxWidth'));
137
        }
138
139
        if($setting->getSetting('maxHeight') && $setting->getSetting('maxWidth')) {
140
            $image = $this->resizeWithMaxHeightAndWidth($image, $setting->getSetting('maxWidth'), $setting->getSetting('maxHeight'));
141
        }
142
143
        if($setting->getSetting('height') && $setting->getSetting('width') == null) {
144
            $image = $this->scaleToHeight($image, $setting->getSetting('height'));
145
        }
146
147
        if($setting->getSetting('width') && $setting->getSetting('height') == null) {
148
            $image = $this->scaleToWidth($image, $setting->getSetting('width'));
149
        }
150
151
        if($setting->getSetting('height') && $setting->getSetting('width')) {
152
            $image = $this->resizeWithFixHeightAndWidth(
153
                $image, $setting->getSetting('width'),
154
                $setting->getSetting('height'),
155
                $this->mapResizeMode($setting->getSetting('mode', ImageInterface::THUMBNAIL_OUTBOUND))
156
            );
157
        }
158
159
        return $image;
160
    }
161
162
    private function crop(ImageInterface $image, $width, $height, $x, $y, $backgroundColorValues, $backgroundColorAlpha)
163
    {
164
        $palette = new RGB();
165
        $backgroundColor = $palette->color($backgroundColorValues, $backgroundColorAlpha);
166
167
        if ($image instanceof \Imagine\Imagick\Image) {
168
            $imagine = new ImagickImagine();
169
        } else {
170
            $imagine = new Imagine();
171
        }
172
173
        $size = $image->getSize();
174
        // resize image for x position if necessary
175
        if($x < 0) {
176
            $newImage = $imagine->create(new Box($size->getWidth() + abs($x), $size->getHeight()), $backgroundColor);
0 ignored issues
show
Bug introduced by
$size->getWidth() + abs($x) of type double is incompatible with the type integer expected by parameter $width of Imagine\Image\Box::__construct(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

176
            $newImage = $imagine->create(new Box(/** @scrutinizer ignore-type */ $size->getWidth() + abs($x), $size->getHeight()), $backgroundColor);
Loading history...
177
            $image = $newImage->paste($image, new Point(abs($x), 0));
0 ignored issues
show
Bug introduced by
It seems like abs($x) can also be of type double; however, parameter $x of Imagine\Image\Point::__construct() 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

177
            $image = $newImage->paste($image, new Point(/** @scrutinizer ignore-type */ abs($x), 0));
Loading history...
178
            $x = 0;
179
        } elseif($x > $size->getWidth()) {
180
            $newImage = $imagine->create(new Box($x + 1, $size->getHeight()), $backgroundColor);
181
            $image = $newImage->paste($image, new Point(0, 0));
182
        }
183
184
        $size = $image->getSize();
185
        // resize image for y position if necessary
186
        if($y < 0) {
187
            $newImage = $imagine->create(new Box($size->getWidth(), $size->getHeight() + abs($y)), $backgroundColor);
0 ignored issues
show
Bug introduced by
$size->getHeight() + abs($y) of type double is incompatible with the type integer expected by parameter $height of Imagine\Image\Box::__construct(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

187
            $newImage = $imagine->create(new Box($size->getWidth(), /** @scrutinizer ignore-type */ $size->getHeight() + abs($y)), $backgroundColor);
Loading history...
188
            $image = $newImage->paste($image, new Point(0, abs($y)));
0 ignored issues
show
Bug introduced by
It seems like abs($y) can also be of type double; however, parameter $y of Imagine\Image\Point::__construct() 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

188
            $image = $newImage->paste($image, new Point(0, /** @scrutinizer ignore-type */ abs($y)));
Loading history...
189
            $y = 0;
190
        } elseif($y > $size->getHeight()) {
191
            $newImage = $imagine->create(new Box($size->getWidth(), $y + 1), $backgroundColor);
192
            $image = $newImage->paste($image, new Point(0, 0));
193
        }
194
195
        $size = $image->getSize();
196
        // resize image if width is to big
197
        if(($x + $width) > $size->getWidth()) {
198
            $newImage = $imagine->create(new Box($x + $width, $size->getHeight()), $backgroundColor);
199
            $image = $newImage->paste($image, new Point(0, 0));
200
        }
201
202
        $size = $image->getSize();
203
        // resize image if height is to big
204
        if(($y + $height) > $size->getHeight()) {
205
            $newImage = $imagine->create(new Box($size->getWidth(), $y + $height + 1), $backgroundColor);
206
            $image = $newImage->paste($image, new Point(0, 0));
207
        }
208
209
        $image = $image->crop(new Point($x, $y), new Box($width, $height));
210
        return $image;
211
    }
212
213
    private function resizeWithFixHeightAndWidth(ImageInterface $image, $width, $height, $mode)
214
    {
215
        if($image->getSize()->getHeight() < $height) {
216
            $image = $this->scaleToHeight($image, $height);
217
        }
218
219
        if($image->getSize()->getWidth() < $width) {
220
            $image = $this->scaleToWidth($image, $width);
221
        }
222
223
        $image = $image->thumbnail(new Box($width, $height), $mode);
224
225
        if($mode === ImageInterface::THUMBNAIL_OUTBOUND) {
226
            return $image;
227
        }
228
229
        //inset my not have target size, so we resize
230
        $palette = new RGB();
231
        $backgroundColor = $palette->color(array(255, 255, 255), 0);
232
233
        if ($image instanceof \Imagine\Imagick\Image) {
234
            $imagine = new ImagickImagine();
235
        } else {
236
            $imagine = new Imagine();
237
        }
238
        $newImage = $imagine->create(new Box($width, $height), $backgroundColor);
239
        $newImage->resize(new Box($width, $height));
240
241
        $size = $image->getSize();
242
        if($size->getHeight() !== $height) {
243
            $y =  intval($height / 2) - intval($size->getHeight() / 2);
244
            $newImage->paste($image, new Point(0, $y));
245
        } elseif($size->getWidth() !== $width) {
246
            $x =  intval($width / 2) - intval($size->getWidth() / 2);
247
            $newImage->paste($image, new Point($x,0));
248
        } else {
249
            return $image;
250
        }
251
252
        return $newImage;
253
    }
254
255
    private function resizeWithMaxHeightAndWidth(ImageInterface $image, $width, $height)
256
    {
257
        if($image->getSize()->getHeight() > $height) {
258
            $image = $this->scaleToHeight($image, $height);
259
        }
260
261
        if($image->getSize()->getWidth() > $width) {
262
            $image = $this->scaleToWidth($image, $width);
263
        }
264
265
        return $image;
266
    }
267
268
    private function resizeWithMaxHeight(ImageInterface $image, $height)
269
    {
270
        if($image->getSize()->getHeight() > $height) {
271
            $image = $this->scaleToHeight($image, $height);
272
        }
273
274
        return $image;
275
    }
276
277
    private function resizeWithMaxWidth(ImageInterface $image, $width)
278
    {
279
        if($image->getSize()->getWidth() > $width) {
280
            $image = $this->scaleToWidth($image, $width);
281
        }
282
283
        return $image;
284
    }
285
286
    private function scaleToHeight(ImageInterface $image, $height)
287
    {
288
        $width = $image->getSize()->getWidth() / $image->getSize()->getHeight() * $height;
289
        return $image->resize(new Box($width, $height));
290
    }
291
292
    private function scaleToWidth(ImageInterface $image, $width)
293
    {
294
        $height = $width / ($image->getSize()->getWidth() / $image->getSize()->getHeight());
295
        return $image->resize(new Box($width, $height));
296
    }
297
298
    private function isAnimatedGif($file)
299
    {
300
        $count = preg_match_all('#\x00\x21\xF9\x04.{4}\x00(\x2C|\x21)#s', $file, $m);
301
        return $count >= 2;
302
    }
303
304
    private function mapResizeMode($mode)
305
    {
306
        switch ($mode) {
307
            case 'inset':
308
                return ImageInterface::THUMBNAIL_INSET;
309
            case ImageInterface::THUMBNAIL_INSET:
310
                return ImageInterface::THUMBNAIL_INSET;
311
            default:
312
                return ImageInterface::THUMBNAIL_OUTBOUND;
313
        }
314
    }
315
316
    /**
317
     * @param string $backgroundColorHex
318
     * @return array
319
     * @throws FilterException
320
     */
321
    private function backgroundColorHexToByteArray($backgroundColorHex)
322
    {
323
        if (!preg_match('/^[0-9A-Fa-f]{6}$/', $backgroundColorHex)) {
324
            throw new FilterException('Imagefilter: Parameter "backgroundColor" must be color in hex format (e.g. "FFFFFF")');
325
        }
326
        return sscanf($backgroundColorHex, "%02x%02x%02x");
0 ignored issues
show
Bug Best Practice introduced by
The expression return sscanf($backgroun...lorHex, '%02x%02x%02x') also could return the type integer which is incompatible with the documented return type array.
Loading history...
327
    }
328
329
    public function getType()
330
    {
331
        return 'image';
332
    }
333
}
334