Completed
Push — master ( 561cc0...c28280 )
by Derek Stephen
02:26
created

Image::save()   B

Complexity

Conditions 5
Paths 4

Size

Total Lines 17
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 5

Importance

Changes 0
Metric Value
cc 5
eloc 12
nc 4
nop 3
dl 0
loc 17
ccs 13
cts 13
cp 1
crap 5
rs 8.8571
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
    private $contentType = [
20
        IMAGETYPE_JPEG => 'image/jpeg',
21
        IMAGETYPE_GIF => 'image/gif',
22
        IMAGETYPE_PNG =>'image/png',
23
    ];
24
25
    private $createCommand = [
26
        IMAGETYPE_JPEG => 'imagecreatefromjpeg',
27
        IMAGETYPE_GIF => 'imagecreatefromgif',
28
        IMAGETYPE_PNG =>'imagecreatefrompng',
29
    ];
30
31
    /**
32
     * @param string $filename
33
     */
34 21
    public function __construct($filename = null)
35
    {
36 21
        if ($filename) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $filename of type null|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
37 1
            $this->fileName = $filename;
38 1
            $this->load($filename);
39
        }
40 21
    }
41
42 20
    private function checkFileExists($path)
43
    {
44 20
        if (!file_exists($path)) {
45 1
            throw new NotFoundException("$path does not exist");
46
        }
47 19
    }
48
49
50
    /**
51
     * @param string $filename
52
     * @throws NotFoundException
53
     */
54 20
    public function load($filename)
55
    {
56 20
        $this->checkFileExists($filename);
57 19
        $imageInfo = getimagesize($filename);
58 19
        $this->imageType = $imageInfo[2];
59 19
        $this->image = $this->createCommand[$this->imageType]($filename);
60 19
    }
61
62
63
    /**
64
     *  @param string $filename
65
     *  @param int $compression
66
     *  @param string $permissions
67
     */
68 3
    public function save($filename = null, $compression = 100, $permissions = null)
69
    {
70 3
        $filename = ($filename) ?: $this->fileName;
71
72 3
        switch ($this->imageType) {
73 3
            case IMAGETYPE_JPEG:
74 1
                imagejpeg($this->image, $filename, $compression);
75 1
                break;
76 2
            case IMAGETYPE_GIF:
77 1
                imagegif($this->image, $filename);
78 1
                break;
79 1
            case IMAGETYPE_PNG:
80 1
                imagepng($this->image, $filename);
81 1
                break;
82
        }
83
84 3
        $this->setPermissions($filename, $permissions);
85 3
    }
86
87
    /**
88
     * @param $filename
89
     * @param $permissions
90
     */
91 3
    private function setPermissions($filename, $permissions)
92
    {
93 3
        if( $permissions !== null) {
94 1
            chmod($filename, (int) $permissions);
95
        }
96 3
    }
97
98
99
    /**
100
     * @param bool $return either output directly
101
     * @return void|string image contents
102
     */
103 14
    public function output($return = false)
104
    {
105 14
        if ($return) {
106 14
            ob_start();
107
        }
108
109 14
        $this->renderImage();
110
111 14
        if ($return) {
112 14
            $contents = ob_get_flush();
113 14
            return $contents;
114
        }
115
    }
116
117 14
    private function renderImage()
118
    {
119 14
        switch ($this->imageType) {
120 14
            case IMAGETYPE_JPEG:
121 3
                imagejpeg($this->image);
122 3
                break;
123 11
            case IMAGETYPE_GIF:
124 2
                imagegif($this->image);
125 2
                break;
126 9
            case IMAGETYPE_PNG:
127 9
                imagealphablending($this->image,true);
128 9
                imagesavealpha($this->image,true);
129 9
                imagepng($this->image);
130 9
                break;
131
        }
132 14
    }
133
134
    /**
135
     * @return int
136
     */
137 8
    public function getWidth()
138
    {
139 8
        return imagesx($this->image);
140
    }
141
142
    /**
143
     * @return int
144
     */
145 8
    public function getHeight()
146
    {
147 8
        return imagesy($this->image);
148
    }
149
150
    /**
151
     * @param int $height
152
     */
153 2
    public function resizeToHeight($height)
154
    {
155 2
        $ratio = $height / $this->getHeight();
156 2
        $width = $this->getWidth() * $ratio;
157 2
        $this->resize($width,$height);
158 2
    }
159
160
    /**
161
     * @param int $width
162
     */
163 2
    public function resizeToWidth($width)
164
    {
165 2
        $ratio = $width / $this->getWidth();
166 2
        $height = $this->getHeight() * $ratio;
167 2
        $this->resize($width,$height);
168 2
    }
169
170
    /**
171
     * @param int $scale %
172
     */
173 1
    public function scale($scale)
174
    {
175 1
        $width = $this->getWidth() * $scale / 100;
176 1
        $height = $this->getHeight() * $scale / 100;
177 1
        $this->resize($width,$height);
178 1
    }
179
180
    /**
181
     * @param int $width
182
     * @param int $height
183
     */
184 3
    public function resizeAndCrop($width,$height)
185
    {
186 3
        $targetRatio = $width / $height;
187 3
        $actualRatio = $this->getWidth() / $this->getHeight();
188
189 3
        if ($targetRatio == $actualRatio) {
190
            // Scale to size
191 1
            $this->resize($width,$height);
192 2
        } elseif ($targetRatio > $actualRatio) {
193
            // Resize to width, crop extra height
194 1
            $this->resizeToWidth($width);
195 1
            $this->crop($width,$height);
196
        } else {
197
            // Resize to height, crop additional width
198 1
            $this->resizeToHeight($height);
199 1
            $this->crop($width,$height);
200
        }
201 3
    }
202
203
204
    /**
205
     *  Now with added Transparency resizing feature
206
     *  @param int $width
207
     *  @param int $height
208
     */
209 6
    public function resize($width,$height)
210
    {
211 6
        $newImage = imagecreatetruecolor($width, $height);
212
213 6
        if ( ($this->getImageType() == IMAGETYPE_GIF) || ($this->getImageType()  == IMAGETYPE_PNG) ) {
214
215
            // Get transparency color's index number
216 6
            $transparency = imagecolortransparent($this->image);
217
218
            // Is a strange index other than -1 set?
219 6
            if ($transparency >= 0) {
220
221
                // deal with alpha channels
222
                $this->prepWithExistingIndex($newImage, $transparency);
223
224 6
            }  elseif ($this->getImageType() == IMAGETYPE_PNG) {
225
226
                // deal with alpha channels
227 6
                $this->prepTransparentPng($newImage);
228
            }
229
        }
230
231
        // Now resample the image
232 6
        imagecopyresampled($newImage, $this->image, 0, 0, 0, 0, $width, $height, $this->getWidth(), $this->getHeight());
233
234
        // And allocate to $this
235 6
        $this->image = $newImage;
236 6
    }
237
238
    /**
239
     * @param $resource
240
     * @param $index
241
     */
242
    private function prepWithExistingIndex($resource, $index)
243
    {
244
        // Get the array of RGB vals for the transparency index
245
        $transparentColor = imagecolorsforindex($this->image, $index);
246
247
        // Now allocate the color
248
        $transparency = imagecolorallocate($resource, $transparentColor['red'], $transparentColor['green'], $transparentColor['blue']);
249
250
        // Fill the background with the color
251
        imagefill($resource, 0, 0, $transparency);
252
253
        // And set that color as the transparent one
254
        imagecolortransparent($resource, $transparency);
255
    }
256
257
    /**
258
     * @param $resource
259
     */
260 6
    private function prepTransparentPng($resource)
261
    {
262
        // Set blending mode as false
263 6
        imagealphablending($resource, false);
264
265
        // Tell it we want to save alpha channel info
266 6
        imagesavealpha($resource, true);
267
268
        // Set the transparent color
269 6
        $color = imagecolorallocatealpha($resource, 0, 0, 0, 127);
270
271
        // Fill the image with nothingness
272 6
        imagefill($resource, 0, 0, $color);
273 6
    }
274
275
276
    /**
277
     * @param int $width
278
     * @param int $height
279
     * @param string $trim
280
     */
281 3
    public function crop($width,$height, $trim = 'center')
282
    {
283 3
        $offsetX = 0;
284 3
        $offsetY = 0;
285 3
        $currentWidth = $this->getWidth();
286 3
        $currentHeight = $this->getHeight();
287
288 3
        if ($trim != 'left') {
289 3
            if ($currentWidth > $width) {
290 2
                $diff = $currentWidth - $width;
291 2
                $offsetX = ($trim == 'center') ? $diff / 2 : $diff; //full diff for trim right
292
            }
293 3
            if ($currentHeight > $height) {
294 2
                $diff = $currentHeight - $height;
295 2
                $offsetY = ($trim == 'center') ? $diff / 2 : $diff;
296
            }
297
        }
298
299 3
        $newImage = imagecreatetruecolor($width,$height);
300 3
        imagecopyresampled($newImage, $this->image, 0, 0, $offsetX, $offsetY, $width, $height, $width, $height);
301 3
        $this->image = $newImage;
302 3
    }
303
304
    /**
305
     * @return mixed
306
     */
307 6
    public function getImageType()
308
    {
309 6
        return $this->imageType;
310
    }
311
312
    /**
313
     * @return mixed
314
     * @throws NothingLoadedException
315
     */
316 4
    public function getHeader()
317
    {
318 4
        if (!$this->imageType) {
319 1
            throw new NothingLoadedException();
320
        }
321 3
        return $this->contentType[$this->imageType];
322
    }
323
324
    /**
325
     *  Frees up memory
326
     */
327 1
    public function destroy()
328
    {
329 1
        imagedestroy($this->image);
330 1
    }
331
}
332