Passed
Push — master ( c140e3...b2f7ca )
by Derek Stephen
01:45
created

Image::crop()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 15
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 10
nc 2
nop 3
dl 0
loc 15
ccs 12
cts 12
cp 1
crap 2
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
namespace Del;
4
5
use Del\Image\Exception\NotFoundException;
6
use Del\Image\Exception\NothingLoadedException;
7
8
class Image 
9
{
10
    /** @var resource $image */
11
    private $image;
12
13
    /** @var int $imageType */
14
    private $imageType;
15
16
    /** @var string $fileName */
17
    private $fileName;
18
19
    /** @var array  */
20
    private $contentType = [
21
        IMAGETYPE_JPEG => 'image/jpeg',
22
        IMAGETYPE_GIF => 'image/gif',
23
        IMAGETYPE_PNG =>'image/png',
24
    ];
25
26
    /** @var array  */
27
    private $createCommand = [
28
        IMAGETYPE_JPEG => 'imagecreatefromjpeg',
29
        IMAGETYPE_GIF => 'imagecreatefromgif',
30
        IMAGETYPE_PNG =>'imagecreatefrompng',
31
    ];
32
33
    /** @var array  */
34
    private $saveMethod = [
35
        IMAGETYPE_JPEG => 'saveJpg',
36
        IMAGETYPE_GIF => 'saveGif',
37
        IMAGETYPE_PNG => 'savePng',
38
    ];
39
40
    /**
41
     * @param string $filename
42
     */
43 21
    public function __construct($filename = null)
44
    {
45 21
        if ($filename !== null) {
46 1
            $this->fileName = $filename;
47 1
            $this->load($filename);
48 1
        }
49 21
    }
50
51
    /**
52
     * @param $path
53
     * @throws NotFoundException
54
     */
55 20
    private function checkFileExists($path)
56
    {
57 20
        if (!file_exists($path)) {
58 1
            throw new NotFoundException("$path does not exist");
59
        }
60 19
    }
61
62
63
    /**
64
     * @param string $filename
65
     * @throws NotFoundException
66
     */
67 20
    public function load($filename)
68
    {
69 20
        $this->checkFileExists($filename);
70 19
        $imageInfo = getimagesize($filename);
71 19
        $this->imageType = $imageInfo[2];
72 19
        $this->image = $this->createCommand[$this->imageType]($filename);
73 19
    }
74
75
76
    /**
77
     *  @param string $filename
78
     *  @param int $compression
79
     *  @param string $permissions
80
     */
81 3
    public function save($filename = null, $compression = 100, $permissions = null)
82
    {
83 3
        $filename = ($filename) ?: $this->fileName;
84 3
        $method = $this->saveMethod[$this->imageType];
85 3
        $this->$method($filename, $compression);
86 3
        $this->setPermissions($filename, $permissions);
87 3
    }
88
89
    /**
90
     * @param $filename
91
     * @param $compression
92
     */
93 1
    private function saveJpg($filename, $compression)
94
    {
95 1
        imagejpeg($this->image, $filename, $compression);
96 1
    }
97
98
    /**
99
     * @param $filename
100
     * @param $compression
101
     */
102 1
    private function saveGif($filename, $compression)
103
    {
104 1
        unset($compression);
105 1
        imagegif($this->image, $filename);
106 1
    }
107
108
    /**
109
     * @param $filename
110
     * @param $compression
111
     */
112 1
    private function savePng($filename, $compression)
113
    {
114 1
        unset($compression);
115 1
        imagepng($this->image, $filename);
116 1
    }
117
118
    /**
119
     * @param $filename
120
     * @param $permissions
121
     */
122 3
    private function setPermissions($filename, $permissions)
123
    {
124 3
        if ($permissions !== null) {
125 1
            chmod($filename, (int) $permissions);
126 1
        }
127 3
    }
128
129
130
    /**
131
     * @param bool $return either output directly
132
     * @return void|string image contents
133
     */
134 14
    public function output($return = false)
135
    {
136 14
        if ($return) {
137 14
            ob_start();
138 14
        }
139
140 14
        $this->renderImage();
141
142 14
        if ($return) {
143 14
            $contents = ob_get_flush();
144 14
            return $contents;
145
        }
146
    }
147
148 14
    private function renderImage()
149
    {
150 14
        switch ($this->imageType) {
151 14
            case IMAGETYPE_JPEG:
152 3
                imagejpeg($this->image);
153 3
                break;
154 11
            case IMAGETYPE_GIF:
155 2
                imagegif($this->image);
156 2
                break;
157 9
            case IMAGETYPE_PNG:
158 9
                imagealphablending($this->image, true);
159 9
                imagesavealpha($this->image, true);
160 9
                imagepng($this->image);
161 9
                break;
162 14
        }
163 14
    }
164
165
    /**
166
     * @return int
167
     */
168 8
    public function getWidth()
169
    {
170 8
        return imagesx($this->image);
171
    }
172
173
    /**
174
     * @return int
175
     */
176 8
    public function getHeight()
177
    {
178 8
        return imagesy($this->image);
179
    }
180
181
    /**
182
     * @param int $height
183
     */
184 2
    public function resizeToHeight($height)
185
    {
186 2
        $ratio = $height / $this->getHeight();
187 2
        $width = $this->getWidth() * $ratio;
188 2
        $this->resize($width, $height);
189 2
    }
190
191
    /**
192
     * @param int $width
193
     */
194 2
    public function resizeToWidth($width)
195
    {
196 2
        $ratio = $width / $this->getWidth();
197 2
        $height = $this->getHeight() * $ratio;
198 2
        $this->resize($width, $height);
199 2
    }
200
201
    /**
202
     * @param int $scale %
203
     */
204 1
    public function scale($scale)
205
    {
206 1
        $width = $this->getWidth() * $scale / 100;
207 1
        $height = $this->getHeight() * $scale / 100;
208 1
        $this->resize($width, $height);
209 1
    }
210
211
    /**
212
     * @param int $width
213
     * @param int $height
214
     */
215 3
    public function resizeAndCrop($width, $height)
216
    {
217 3
        $targetRatio = $width / $height;
218 3
        $actualRatio = $this->getWidth() / $this->getHeight();
219
220 3
        if ($targetRatio == $actualRatio) {
221
            // Scale to size
222 1
            $this->resize($width, $height);
223 3
        } elseif ($targetRatio > $actualRatio) {
224
            // Resize to width, crop extra height
225 1
            $this->resizeToWidth($width);
226 1
            $this->crop($width, $height);
227 1
        } else {
228
            // Resize to height, crop additional width
229 1
            $this->resizeToHeight($height);
230 1
            $this->crop($width, $height);
231
        }
232 3
    }
233
234
235
    /**
236
     *  Now with added Transparency resizing feature
237
     *  @param int $width
238
     *  @param int $height
239
     */
240 6
    public function resize($width, $height)
241
    {
242 6
        $newImage = imagecreatetruecolor($width, $height);
243
244 6
        $this->handleTransparency($newImage);
245
246
        // Now resample the image
247 6
        imagecopyresampled($newImage, $this->image, 0, 0, 0, 0, $width, $height, $this->getWidth(), $this->getHeight());
248
249
        // And allocate to $this
250 6
        $this->image = $newImage;
251 6
    }
252
253 6
    private function handleTransparency($resource)
254
    {
255 6
        if ($this->getImageType() == IMAGETYPE_JPEG) {
256
            return;
257
        }
258
259
        // Get transparency color's index number
260 6
        $transparency = imagecolortransparent($this->image);
261
262
        // Is a strange index other than -1 set?
263 6
        if ($transparency >= 0) {
264
265
            // deal with alpha channels
266
            $this->prepWithExistingIndex($resource, $transparency);
267
268 6
        } elseif ($this->getImageType() == IMAGETYPE_PNG) {
269
270
            // deal with alpha channels
271 6
            $this->prepTransparentPng($resource);
272 6
        }
273
274 6
    }
275
276
    /**
277
     * @param $resource
278
     * @param $index
279
     */
280
    private function prepWithExistingIndex($resource, $index)
281
    {
282
        // Get the array of RGB vals for the transparency index
283
        $transparentColor = imagecolorsforindex($this->image, $index);
284
285
        // Now allocate the color
286
        $transparency = imagecolorallocate($resource, $transparentColor['red'], $transparentColor['green'], $transparentColor['blue']);
287
288
        // Fill the background with the color
289
        imagefill($resource, 0, 0, $transparency);
290
291
        // And set that color as the transparent one
292
        imagecolortransparent($resource, $transparency);
293
    }
294
295
    /**
296
     * @param $resource
297
     */
298 6
    private function prepTransparentPng($resource)
299
    {
300
        // Set blending mode as false
301 6
        imagealphablending($resource, false);
302
303
        // Tell it we want to save alpha channel info
304 6
        imagesavealpha($resource, true);
305
306
        // Set the transparent color
307 6
        $color = imagecolorallocatealpha($resource, 0, 0, 0, 127);
308
309
        // Fill the image with nothingness
310 6
        imagefill($resource, 0, 0, $color);
311 6
    }
312
313
314
    /**
315
     * @param int $width
316
     * @param int $height
317
     * @param string $trim
318
     */
319 3
    public function crop($width, $height, $trim = 'center')
320
    {
321 3
        $offsetX = 0;
322 3
        $offsetY = 0;
323 3
        $currentWidth = $this->getWidth();
324 3
        $currentHeight = $this->getHeight();
325
326 3
        if ($trim != 'left') {
327 3
            $offsetX = $this->getOffsetX($currentWidth, $width, $trim);
328 3
            $offsetY = $this->getOffsetY($currentHeight, $height, $trim);
329 3
        }
330
331 3
        $newImage = imagecreatetruecolor($width, $height);
332 3
        imagecopyresampled($newImage, $this->image, 0, 0, $offsetX, $offsetY, $width, $height, $width, $height);
333 3
        $this->image = $newImage;
334 3
    }
335
336
    /**
337
     * @param $currentWidth
338
     * @param $width
339
     * @param $trim
340
     * @return float|int
341
     */
342 3
    private function getOffsetX($currentWidth, $width, $trim)
343
    {
344 3
        $offsetX = 0;
345 3
        if ($currentWidth > $width) {
346 2
            $diff = $currentWidth - $width;
347 2
            $offsetX = ($trim == 'center') ? $diff / 2 : $diff; //full diff for trim right
348 2
        }
349 3
        return $offsetX;
350
    }
351
352
    /**
353
     * @param $currentHeight
354
     * @param $height
355
     * @param $trim
356
     * @return float|int
357
     */
358 3
    private function getOffsetY($currentHeight, $height, $trim)
359
    {
360 3
        $offsetY = 0;
361 3
        if ($currentHeight > $height) {
362 2
            $diff = $currentHeight - $height;
363 2
            $offsetY = ($trim == 'center') ? $diff / 2 : $diff;
364 2
        }
365 3
        return $offsetY;
366
    }
367
368
    /**
369
     * @return mixed
370
     */
371 6
    public function getImageType()
372
    {
373 6
        return $this->imageType;
374
    }
375
376
    /**
377
     * @return mixed
378
     * @throws NothingLoadedException
379
     */
380 4
    public function getHeader()
381
    {
382 4
        if (!$this->imageType) {
383 1
            throw new NothingLoadedException();
384
        }
385 3
        return $this->contentType[$this->imageType];
386
    }
387
388
    /**
389
     *  Frees up memory
390
     */
391 1
    public function destroy()
392
    {
393 1
        imagedestroy($this->image);
394 1
    }
395
}
396