Passed
Push — 1.11.x ( bce6cd...c146d9 )
by Angel Fernando Quiroz
12:25
created

main/inc/lib/image.lib.php (1 issue)

1
<?php
2
/* For licensing terms, see /license.txt */
3
4
/**
5
 * Image class
6
 * This class provides a layer to manage images.
7
 *
8
 * @author Julio Montoya <[email protected]>
9
 *
10
 * @todo move in a DB configuration setting
11
 */
12
class Image
13
{
14
    public $image_wrapper = null;
15
16
    /**
17
     * Image constructor.
18
     *
19
     * @param string $path
20
     */
21
    public function __construct($path)
22
    {
23
        if (IMAGE_PROCESSOR == 'gd') {
24
            $this->image_wrapper = new GDWrapper($path);
25
        } else {
26
            if (class_exists('Imagick')) {
27
                $this->image_wrapper = new ImagickWrapper($path);
28
            } else {
29
                echo Display::return_message(
30
                    'Class Imagick not found',
31
                    'warning'
32
                );
33
                exit;
34
            }
35
        }
36
    }
37
38
    public function resize($max_size_for_picture)
39
    {
40
        $image_size = $this->get_image_size($this->image_wrapper->path);
41
        $width = $image_size['width'];
42
        $height = $image_size['height'];
43
        if ($width >= $height) {
44
            if ($width >= $max_size_for_picture) {
45
                // scale height
46
                $new_height = round($height * ($max_size_for_picture / $width));
47
                $this->image_wrapper->resize(
48
                    $max_size_for_picture,
49
                    $new_height,
50
                    0
51
                );
52
            }
53
        } else { // height > $width
54
            if ($height >= $max_size_for_picture) {
55
                // scale width
56
                $new_width = round($width * ($max_size_for_picture / $height));
57
                $this->image_wrapper->resize(
58
                    $new_width,
59
                    $max_size_for_picture,
60
                    0
61
                );
62
            }
63
        }
64
    }
65
66
    /**
67
     * @param string $cropParameters
68
     *
69
     * @return bool
70
     */
71
    public function crop($cropParameters)
72
    {
73
        $image_size = $this->get_image_size($this->image_wrapper->path);
74
        $src_width = $image_size['width'];
75
        $src_height = $image_size['height'];
76
        $cropParameters = explode(',', $cropParameters);
77
78
        if (isset($cropParameters[0]) && isset($cropParameters[1])) {
79
            $x = intval($cropParameters[0]);
80
            $y = intval($cropParameters[1]);
81
            $width = intval($cropParameters[2]);
82
            $height = intval($cropParameters[3]);
83
84
            $image = $this->image_wrapper->crop(
85
                $x,
86
                $y,
87
                $width,
88
                $height,
89
                $src_width,
90
                $src_height
91
            );
92
93
            return $image;
94
        }
95
96
        return false;
97
    }
98
99
    /**
100
     * @param string $file
101
     * @param int    $compress
102
     * @param null   $convert_file_to
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $convert_file_to is correct as it would always require null to be passed?
Loading history...
103
     *
104
     * @return bool
105
     */
106
    public function send_image(
107
        $file = '',
108
        $compress = -1,
109
        $convert_file_to = null
110
    ) {
111
        return $this->image_wrapper->send_image(
112
            $file,
113
            $compress,
114
            $convert_file_to
115
        );
116
    }
117
118
    /**
119
     * @return array
120
     */
121
    public function get_image_size()
122
    {
123
        return $this->image_wrapper->get_image_size();
124
    }
125
126
    /**
127
     * @return array
128
     */
129
    public function get_image_info()
130
    {
131
        return $this->image_wrapper->get_image_info();
132
    }
133
134
    public function convert2bw()
135
    {
136
        $this->image_wrapper->convert2bw();
137
    }
138
}
139
140
/**
141
 * Image wrapper class.
142
 */
143
abstract class ImageWrapper
144
{
145
    public $debug = true;
146
    public $path;
147
    public $width;
148
    public $height;
149
    public $type;
150
    public $allowed_extensions = ['jpeg', 'jpg', 'png', 'gif'];
151
    public $image_validated = false;
152
153
    public function __construct($path)
154
    {
155
        if (empty($path)) {
156
            return false;
157
        }
158
        $this->path = $path;
159
        $this->set_image_wrapper(); //Creates image obj
160
    }
161
162
    abstract public function set_image_wrapper();
163
164
    abstract public function fill_image_info();
165
166
    abstract public function get_image_size();
167
168
    abstract public function resize($thumbw, $thumbh, $border, $specific_size = false);
169
170
    abstract public function crop($x, $y, $width, $height, $src_width, $src_height);
171
172
    abstract public function send_image($file = '', $compress = -1, $convert_file_to = null);
173
174
    /**
175
     * @return array
176
     */
177
    public function get_image_info()
178
    {
179
        return [
180
            'width' => $this->width,
181
            'height' => $this->height,
182
            'type' => $this->type,
183
        ];
184
    }
185
}
186
187
/**
188
 * Imagick Chamilo wrapper.
189
 *
190
 * @author jmontoya
191
 */
192
class ImagickWrapper extends ImageWrapper
193
{
194
    public $image;
195
    public $filter = Imagick::FILTER_LANCZOS;
196
197
    /**
198
     * ImagickWrapper constructor.
199
     *
200
     * @param $path
201
     */
202
    public function __construct($path)
203
    {
204
        parent::__construct($path);
205
    }
206
207
    public function set_image_wrapper()
208
    {
209
        if ($this->debug) {
210
            error_log('Image::set_image_wrapper loaded');
211
        }
212
        try {
213
            if (file_exists($this->path)) {
214
                $this->image = new Imagick($this->path);
215
216
                if ($this->image) {
217
                    $this->fill_image_info(); //Fills height, width and type
218
                }
219
            } else {
220
                if ($this->debug) {
221
                    error_log('Image::image does not exist');
222
                }
223
            }
224
        } catch (ImagickException $e) {
225
            if ($this->debug) {
226
                error_log($e->getMessage());
227
            }
228
        }
229
    }
230
231
    public function fill_image_info()
232
    {
233
        $image_info = $this->image->identifyImage();
234
        $this->width = $image_info['geometry']['width'];
235
        $this->height = $image_info['geometry']['height'];
236
        $this->type = strtolower($this->image->getImageFormat());
237
238
        if (in_array($this->type, $this->allowed_extensions)) {
239
            $this->image_validated = true;
240
            if ($this->debug) {
241
                error_log('image_validated true');
242
            }
243
        }
244
    }
245
246
    public function get_image_size()
247
    {
248
        $imagesize = ['width' => 0, 'height' => 0];
249
        if ($this->image_validated) {
250
            $imagesize = $this->image->getImageGeometry();
251
        }
252
253
        return $imagesize;
254
    }
255
256
    //@todo implement border logic case for Imagick
257
    public function resize($thumbw, $thumbh, $border, $specific_size = false)
258
    {
259
        if (!$this->image_validated) {
260
            return false;
261
        }
262
263
        if ($specific_size) {
264
            $width = $thumbw;
265
            $height = $thumbh;
266
        } else {
267
            $scale = ($this->width > 0 && $this->height > 0) ? min($thumbw / $this->width, $thumbh / $this->height) : 0;
268
            $width = (int) ($this->width * $scale);
269
            $height = (int) ($this->height * $scale);
270
        }
271
        $result = $this->image->resizeImage($width, $height, $this->filter, 1);
272
        $this->width = $thumbw;
273
        $this->height = $thumbh;
274
    }
275
276
    /**
277
     * @author José Loguercio <[email protected]>
278
     *
279
     * @param int $x          coordinate of the cropped region top left corner
280
     * @param int $y          coordinate of the cropped region top left corner
281
     * @param int $width      the width of the crop
282
     * @param int $height     the height of the crop
283
     * @param int $src_width  the source width of the original image
284
     * @param int $src_height the source height of the original image
285
     *
286
     * @return bool
287
     */
288
    public function crop($x, $y, $width, $height, $src_width, $src_height)
289
    {
290
        if (!$this->image_validated) {
291
            return false;
292
        }
293
        $this->image->cropimage($width, $height, $x, $y);
294
        $this->width = $width;
295
        $this->height = $height;
296
297
        return true;
298
    }
299
300
    public function send_image(
301
        $file = '',
302
        $compress = -1,
303
        $convert_file_to = null
304
    ) {
305
        if (!$this->image_validated) {
306
            return false;
307
        }
308
        $type = $this->type;
309
        if (!empty($convert_file_to) && in_array($convert_file_to, $this->allowed_extensions)) {
310
            $type = $convert_file_to;
311
        }
312
        switch ($type) {
313
            case 'jpeg':
314
            case 'jpg':
315
                if (!$file) {
316
                    header("Content-type: image/jpeg");
317
                }
318
                break;
319
            case 'png':
320
                if (!$file) {
321
                    header("Content-type: image/png");
322
                }
323
                break;
324
            case 'gif':
325
                if (!$file) {
326
                    header("Content-type: image/gif");
327
                }
328
                break;
329
        }
330
        $result = false;
331
        try {
332
            $result = $this->image->writeImage($file);
333
        } catch (ImagickException $e) {
334
            if ($this->debug) {
335
                error_log($e->getMessage());
336
            }
337
        }
338
339
        if (!$file) {
340
            echo $this->image;
341
            $this->image->clear();
342
            $this->image->destroy();
343
        } else {
344
            $this->image->clear();
345
            $this->image->destroy();
346
347
            return $result;
348
        }
349
    }
350
}
351
352
/**
353
 * php-gd wrapper.
354
 */
355
class GDWrapper extends ImageWrapper
356
{
357
    public $bg;
358
359
    /**
360
     * GDWrapper constructor.
361
     *
362
     * @param $path
363
     */
364
    public function __construct($path)
365
    {
366
        parent::__construct($path);
367
    }
368
369
    public function set_image_wrapper()
370
    {
371
        $handler = null;
372
        $this->fill_image_info();
373
374
        switch ($this->type) {
375
            case 0:
376
                $handler = false;
377
                break;
378
            case 1:
379
                $handler = @imagecreatefromgif($this->path);
380
                $this->type = 'gif';
381
                break;
382
            case 2:
383
                $handler = @imagecreatefromjpeg($this->path);
384
                $this->type = 'jpg';
385
                break;
386
            case 3:
387
                $handler = @imagecreatefrompng($this->path);
388
                $this->type = 'png';
389
                break;
390
        }
391
        if ($handler) {
392
            $this->image_validated = true;
393
            $this->bg = $handler;
394
            @imagealphablending($this->bg, false);
395
            @imagesavealpha($this->bg, true);
396
        }
397
    }
398
399
    /**
400
     * @return array
401
     */
402
    public function get_image_size()
403
    {
404
        $return_array = ['width' => 0, 'height' => 0];
405
        if ($this->image_validated) {
406
            $return_array = ['width' => $this->width, 'height' => $this->height];
407
        }
408
409
        return $return_array;
410
    }
411
412
    public function fill_image_info()
413
    {
414
        if (file_exists($this->path)) {
415
            $image_info = getimagesize($this->path);
416
            $this->width = $image_info[0];
417
            $this->height = $image_info[1];
418
            $this->type = $image_info[2];
419
        } else {
420
            $this->width = 0;
421
            $this->height = 0;
422
            $this->type = 0;
423
        }
424
    }
425
426
    public function resize($thumbw, $thumbh, $border, $specific_size = false)
427
    {
428
        if (!$this->image_validated) {
429
            return false;
430
        }
431
        if (1 == $border) {
432
            if ($specific_size) {
433
                $width = $thumbw;
434
                $height = $thumbh;
435
            } else {
436
                $scale = min($thumbw / $this->width, $thumbh / $this->height);
437
                $width = (int) ($this->width * $scale);
438
                $height = (int) ($this->height * $scale);
439
            }
440
            $deltaw = (int) (($thumbw - $width) / 2);
441
            $deltah = (int) (($thumbh - $height) / 2);
442
            $dst_img = @ImageCreateTrueColor($thumbw, $thumbh);
443
            @imagealphablending($dst_img, false);
444
            @imagesavealpha($dst_img, true);
445
446
            if (!empty($this->color)) {
447
                @imagefill($dst_img, 0, 0, $this->color);
448
            }
449
            $this->width = $thumbw;
450
            $this->height = $thumbh;
451
        } elseif (0 == $border) {
452
            if ($specific_size) {
453
                $width = $thumbw;
454
                $height = $thumbh;
455
            } else {
456
                $scale = ($this->width > 0 && $this->height > 0) ? min($thumbw / $this->width, $thumbh / $this->height) : 0;
457
                $width = (int) ($this->width * $scale);
458
                $height = (int) ($this->height * $scale);
459
            }
460
            $deltaw = 0;
461
            $deltah = 0;
462
            $dst_img = @ImageCreateTrueColor($width, $height);
463
            @imagealphablending($dst_img, false);
464
            @imagesavealpha($dst_img, true);
465
            $this->width = $width;
466
            $this->height = $height;
467
        }
468
        $src_img = $this->bg;
469
        @ImageCopyResampled(
470
            $dst_img,
471
            $src_img,
472
            $deltaw,
473
            $deltah,
474
            0,
475
            0,
476
            $width,
477
            $height,
478
            ImageSX($src_img),
479
            ImageSY($src_img)
480
        );
481
        $this->bg = $dst_img;
482
        @imagedestroy($src_img);
483
    }
484
485
    /**
486
     * @author José Loguercio <[email protected]>
487
     *
488
     * @param int $x          coordinate of the cropped region top left corner
489
     * @param int $y          coordinate of the cropped region top left corner
490
     * @param int $width      the width of the crop
491
     * @param int $height     the height of the crop
492
     * @param int $src_width  the source width of the original image
493
     * @param int $src_height the source height of the original image
494
     */
495
    public function crop($x, $y, $width, $height, $src_width, $src_height)
496
    {
497
        if (!$this->image_validated) {
498
            return false;
499
        }
500
        $this->width = $width;
501
        $this->height = $height;
502
        $src = null;
503
        $dest = @imagecreatetruecolor($width, $height);
504
        $type = $this->type;
505
        switch ($type) {
506
            case 'jpeg':
507
            case 'jpg':
508
                $src = @imagecreatefromjpeg($this->path);
509
                @imagecopy($dest, $src, 0, 0, $x, $y, $src_width, $src_height);
510
                @imagejpeg($dest, $this->path);
511
                break;
512
            case 'png':
513
                $src = @imagecreatefrompng($this->path);
514
                @imagealphablending($dest, false);
515
                @imagesavealpha($dest, true);
516
                @imagecopy($dest, $src, 0, 0, $x, $y, $src_width, $src_height);
517
                @imagepng($dest, $this->path);
518
                break;
519
            case 'gif':
520
                $src = @imagecreatefromgif($this->path);
521
                @imagecopy($dest, $src, 0, 0, $x, $y, $src_width, $src_height);
522
                @imagegif($dest, $this->path);
523
                break;
524
            default:
525
                return 0;
526
        }
527
        @imagedestroy($dest);
528
        @imagedestroy($src);
529
    }
530
531
    /**
532
     * @param string $file
533
     * @param int    $compress
534
     * @param null   $convert_file_to
535
     *
536
     * @return bool|int
537
     */
538
    public function send_image($file = '', $compress = -1, $convert_file_to = null)
539
    {
540
        if (!$this->image_validated) {
541
            return false;
542
        }
543
        $compress = (int) $compress;
544
        $type = $this->type;
545
        if (!empty($convert_file_to) && in_array($convert_file_to, $this->allowed_extensions)) {
546
            $type = $convert_file_to;
547
        }
548
        switch ($type) {
549
            case 'jpeg':
550
            case 'jpg':
551
                if (!$file) {
552
                    header("Content-type: image/jpeg");
553
                }
554
                if (-1 == $compress) {
555
                    $compress = 100;
556
                }
557
558
                return imagejpeg($this->bg, $file, $compress);
559
                break;
560
            case 'png':
561
                if (!$file) {
562
                    header("Content-type: image/png");
563
                }
564
                if (-1 != $compress) {
565
                    @imagetruecolortopalette($this->bg, true, $compress);
566
                }
567
568
                return imagepng($this->bg, $file, $compress);
569
                break;
570
            case 'gif':
571
                if (!$file) {
572
                    header("Content-type: image/gif");
573
                }
574
                if (-1 != $compress) {
575
                    @imagetruecolortopalette($this->bg, true, $compress);
576
                }
577
578
                return imagegif($this->bg, $file, $compress);
579
                break;
580
            default:
581
                return 0;
582
        }
583
        // TODO: Occupied memory is not released, because the following fragment of code is actually dead.
584
        @imagedestroy($this->bg);
585
    }
586
587
    /**
588
     * Convert image to black & white.
589
     */
590
    public function convert2bw()
591
    {
592
        if (!$this->image_validated) {
593
            return false;
594
        }
595
596
        $dest_img = imagecreatetruecolor(imagesx($this->bg), imagesy($this->bg));
597
        /* copy ignore the transparent color
598
         * so that we can use black (0,0,0) as transparent, which is what
599
         * the image is filled with when created.
600
         */
601
        $transparent = imagecolorallocate($dest_img, 0, 0, 0);
602
        imagealphablending($dest_img, false);
603
        imagesavealpha($dest_img, true);
604
        imagecolortransparent($dest_img, $transparent);
605
        imagecopy(
606
            $dest_img,
607
            $this->bg,
608
            0,
609
            0,
610
            0,
611
            0,
612
            imagesx($this->bg),
613
            imagesx($this->bg)
614
        );
615
        imagefilter($dest_img, IMG_FILTER_GRAYSCALE);
616
        $this->bg = $dest_img;
617
618
        return true;
619
    }
620
}
621