Completed
Push — master ( b2f7ca...a3ce9b )
by Derek Stephen
02:09
created

Image::savePng()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

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