imageLib   F
last analyzed

Complexity

Total Complexity 396

Size/Duplication

Total Lines 2879
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 1324
c 1
b 0
f 0
dl 0
loc 2879
rs 0.8
wmc 396

87 Methods

Rating   Name   Duplication   Size   Complexity  
A getOptimalCrop() 0 23 5
A greyScale() 0 4 2
A addText() 0 28 2
A addTextToCaptionBox() 0 38 4
A checkStringStartsWith() 0 3 1
A hex2dec() 0 16 2
A getHeight() 0 3 1
A borderPreset() 0 11 2
A getErrors() 0 3 1
F resizeImage() 0 71 20
A GetPixelColor() 0 7 2
A getTextFont() 0 25 5
A greyScaleDramatic() 0 3 1
B displayImage() 0 38 8
A gd_apply_overlay() 0 25 1
B rotate() 0 49 8
A checkInterlaceImage() 0 4 2
A sepia() 0 7 2
A testFunct() 0 3 1
B getDimensions() 0 35 11
A writeIPTCwriter() 0 2 1
A getTextSize() 0 12 1
A getFileName() 0 3 1
A getIsImage() 0 3 1
C calculatePosition() 0 60 12
A setCropFromTop() 0 3 1
A blackAndWhite() 0 5 2
A cropImage() 0 12 3
A formatColor() 0 26 5
A writeIPTC() 0 7 1
A iptc_maketag() 0 8 2
A getWidth() 0 3 1
A findUnusedBlue() 0 17 4
A addWatermark() 0 26 3
A initialise() 0 7 1
A setFillColor() 0 4 1
A __destruct() 0 4 2
A sharpen() 0 29 4
A getSizeByFixedWidth() 0 16 3
A keepTransparancy() 0 11 3
A setTransparency() 0 3 1
A reset() 0 10 2
A findUnusedGreen() 0 17 4
B getSizeByAuto() 0 45 8
A addCaptionBox() 0 18 1
B transparentImage() 0 24 7
A sharpen2() 0 6 1
A testIsImage() 0 22 6
B roundCorners() 0 66 6
A gd_filter_monopin() 0 7 2
B addReflection() 0 74 7
A getOriginalWidth() 0 3 1
F ImageCreateFromBMP() 0 122 22
A LittleEndian2String() 0 9 2
A testGDInstalled() 0 9 3
A setFile() 0 5 2
C getCropPlacing() 0 68 13
A createImageColor() 0 7 1
B filterOpacity() 0 52 10
A testEXIFInstalled() 0 9 2
A testColorExists() 0 11 2
D resolveFlash() 0 74 23
A getOriginalHeight() 0 3 1
A greyScaleEnhanced() 0 10 3
B openImage() 0 38 10
A calculateCaptionBoxPosition() 0 34 5
C saveImage() 0 72 14
A setForceStretch() 0 3 1
A negative() 0 4 2
B addShadow() 0 128 8
A addBorder() 0 17 3
A imagecreatefrompsd() 0 15 3
B resolveMeteringMode() 0 32 9
B resolveExposureProgram() 0 35 10
A sepia2() 0 10 3
A image_colorize() 0 21 2
A findSharp() 0 10 1
A GD2BMPstring() 0 40 4
A writeIPTCcaption() 0 3 1
A getSizeByFixedHeight() 0 16 3
A vintage() 0 3 1
A gd_filter_vintage() 0 9 2
F getExif() 0 171 35
A invertTransparency() 0 17 4
A prepOption() 0 16 6
A __construct() 0 46 5
A crop() 0 17 1

How to fix   Complexity   

Complex Class

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

1
<?php
2
# ========================================================================#
3
#
4
#  This work is licensed under the Creative Commons Attribution 3.0 Unported
5
#  License. To view a copy of this license,
6
#  visit http://creativecommons.org/licenses/by/3.0/ or send a letter to
7
#  Creative Commons, 444 Castro Street, Suite 900, Mountain View, California,
8
#  94041, USA.
9
#
10
#  All rights reserved.
11
#
12
#  Author:    Jarrod Oberto
13
#  Version:   1.5.2
14
#  Purpose:   Provide tools for image manipulation using GD
15
#  Param In:  See functions.
16
#  Param Out: Produces a resized image
17
#  Requires : Requires PHP GD library.
18
#  Usage Example:
19
#                     include("lib/php_image_magician.php");
20
#                     $magicianObj = new imageLib('images/car.jpg');
21
#                     $magicianObj -> resizeImage(150, 100, 0);
22
#                     $magicianObj -> saveImage('images/car_small.jpg', 100);
23
#
24
#        - See end of doc for more examples -
25
#
26
#  Supported file types include: jpg, png, gif, bmp, psd (read)
27
#
28
#
29
#
30
#  The following functions are taken from phpThumb() [available from
31
#    http://phpthumb.sourceforge.net], and are used with written permission
32
#  from James Heinrich.
33
#    - GD2BMPstring
34
#      - GetPixelColor
35
#      - LittleEndian2String
36
#
37
#  The following functions are from Marc Hibbins and are used with written
38
#  permission (are also under the Attribution-ShareAlike
39
#  [http://creativecommons.org/licenses/by-sa/3.0/] license.
40
#    -
41
#
42
#  PhpPsdReader is used with written permission from Tim de Koning.
43
#  [http://www.kingsquare.nl/phppsdreader]
44
#
45
#
46
#
47
#  Known issues & Limitations:
48
# -------------------------------
49
#  Not so much an issue, the image is destroyed on the deconstruct rather than
50
#  when we have finished with it. The reason for this is that we don't know
51
#  when we're finished with it as you can both save the image and display
52
#  it directly to the screen (imagedestroy($this->imageResized))
53
#
54
#  Opening BMP files is slow. A test with 884 bmp files processed in a loop
55
#  takes forever - over 5 min. This test inlcuded opening the file, then
56
#  getting and displaying its width and height.
57
#
58
#  $forceStretch:
59
# -------------------------------
60
#  On by default.
61
#  $forceStretch can be disabled by calling method setForceStretch with false
62
#  parameter. If disabled, if an images original size is smaller than the size
63
#  specified by the user, the original size will be used. This is useful when
64
#  dealing with small images.
65
#
66
#  If enabled, images smaller than the size specified will be stretched to
67
#  that size.
68
#
69
#  Tips:
70
# -------------------------------
71
#  * If you're resizing a transparent png and saving it as a jpg, set
72
#  $keepTransparency to false with: $magicianObj->setTransparency(false);
73
#
74
#  FEATURES:
75
#    * EASY TO USE
76
#    * BMP SUPPORT (read & write)
77
#    * PSD (photoshop) support (read)
78
#    * RESIZE IMAGES
79
#      - Preserve transparency (png, gif)
80
#      - Apply sharpening (jpg) (requires PHP >= 5.1.0)
81
#      - Set image quality (jpg, png)
82
#      - Resize modes:
83
#        - exact size
84
#        - resize by width (auto height)
85
#        - resize by height (auto width)
86
#        - auto (automatically determine the best of the above modes to use)
87
#        - crop - resize as best as it can then crop the rest
88
#      - Force stretching of smaller images (upscale)
89
#    * APPLY FILTERS
90
#      - Convert to grey scale
91
#      - Convert to black and white
92
#      - Convert to sepia
93
#      - Convert to negative
94
#    * ROTATE IMAGES
95
#      - Rotate using predefined "left", "right", or "180"; or any custom degree amount
96
#    * EXTRACT EXIF DATA (requires exif module)
97
#      - make
98
#      - model
99
#      - date
100
#      - exposure
101
#      - aperture
102
#      - f-stop
103
#      - iso
104
#      - focal length
105
#      - exposure program
106
#      - metering mode
107
#      - flash status
108
#      - creator
109
#      - copyright
110
#    * ADD WATERMARK
111
#      - Specify exact x, y placement
112
#      - Or, specify using one of the 9 pre-defined placements such as "tl"
113
#        (for top left), "m" (for middle), "br" (for bottom right)
114
#        - also specify padding from edge amount (optional).
115
#      - Set opacity of watermark (png).
116
#    * ADD BORDER
117
#    * USE HEX WHEN SPECIFYING COLORS (eg: #ffffff)
118
#    * SAVE IMAGE OR OUTPUT TO SCREEN
119
#
120
#
121
# ========================================================================#
122
123
/**
124
 * Class imageLib
125
 */
126
class imageLib
127
{
128
    private $fileName;
129
    private $image;
130
    protected $imageResized;
131
    private $widthOriginal;     # Always be the original width
132
    private $heightOriginal;
133
    private $width;         # Current width (width after resize)
134
    private $height;
135
    private $imageSize;
136
    private $fileExtension;
137
138
    private $isImage = false;
139
140
    private $debug = true;
141
    private $errorArray = [];
142
143
    private $forceStretch = true;
144
    private $aggresiveSharpening = false;
145
146
    private $transparentArray = ['.png', '.gif'];
147
    private $keepTransparency = true;
148
    private $fillColorArray = ['r' => 255, 'g' => 255, 'b' => 255];
149
150
    private $sharpenArray = ['jpg'];
151
152
    private $psdReaderPath;
153
    private $filterOverlayPath;
154
155
    private $isInterlace;
156
157
    private $captionBoxPositionArray = [];
158
159
    private $fontDir = 'fonts';
160
161
    private $cropFromTopPercent = 10;
162
163
    ## --------------------------------------------------------
164
165
    /**
166
     * imageLib constructor.
167
     * @param $fileName
168
     */
169
    public function __construct($fileName)
170
    {
171
        if (!$this->testGDInstalled()) {
172
            if ($this->debug) {
173
                die('The GD Library is not installed.');
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
174
            }
175
            die();
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
176
        }
177
178
        $this->initialise();
179
180
        // *** Save the image file name. Only store this incase you want to display it
181
        $this->fileName = $fileName;
182
        $this->fileExtension = mb_strtolower(mb_strrchr($fileName, '.'));
183
184
        // *** Open up the file
185
        try {
186
            $this->image = $this->openImage($fileName);
187
        } catch (Exception $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
188
        }
189
190
        // *** Assign here so we don't modify the original
191
        $this->imageResized = $this->image;
192
193
        // *** If file is an image
194
        $this->isImage = $this->testIsImage();
195
196
        if ($this->isImage) {
197
            // *** Get width and height
198
            $this->width = imagesx($this->image);
199
            $this->widthOriginal = imagesx($this->image);
200
            $this->height = imagesy($this->image);
201
            $this->heightOriginal = imagesy($this->image);
202
203
            /*  Added 15-09-08
204
             *  Get the filesize using this build in method.
205
             *  Stores an array of size
206
             *
207
             *  $this->imageSize[1] = width
208
             *  $this->imageSize[2] = height
209
             *  $this->imageSize[3] = width x height
210
             *
211
             */
212
            $this->imageSize = getimagesize($this->fileName);
213
        } else {
214
            $this->errorArray[] = 'File is not an image';
215
        }
216
    }
217
218
    ## --------------------------------------------------------
219
220
    private function initialise()
221
    {
222
        $this->psdReaderPath = __DIR__ . '/classPhpPsdReader.php';
223
        $this->filterOverlayPath = __DIR__ . '/filters';
224
225
        // *** Set if image should be interlaced or not.
226
        $this->isInterlace = false;
227
    }
228
229
    /*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*-
230
        Resize
231
    *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*/
232
233
    /**
234
     * @param      $newWidth
235
     * @param      $newHeight
236
     * @param int  $option
237
     * @param bool $sharpen
238
     * @param bool $autoRotate
239
     */
240
    public function resizeImage($newWidth, $newHeight, $option = 0, $sharpen = false, $autoRotate = true)
241
    {
242
        // *** We can pass in an array of options to change the crop position
243
        $cropPos = 'm';
244
        if (is_array($option) && 'crop' === mb_strtolower($option[0])) {
0 ignored issues
show
introduced by
The condition is_array($option) is always false.
Loading history...
245
            $cropPos = $option[1];         # get the crop option
246
        } elseif (false !== mb_strpos($option, '-')) {
247
            // *** Or pass in a hyphen seperated option
248
            $optionPiecesArray = explode('-', $option);
249
            $cropPos = end($optionPiecesArray);
250
        }
251
252
        // *** Check the option is valid
253
        try {
254
            $option = $this->prepOption($option);
255
        } catch (Exception $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
256
        }
257
258
        // *** Make sure the file passed in is valid
259
        if (!$this->image) {
260
            if ($this->debug) {
261
                die('file ' . $this->getFileName() . ' is missing or invalid');
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
262
            }
263
            die();
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
264
        }
265
266
        // *** Get optimal width and height - based on $option
267
        $dimensionsArray = $this->getDimensions($newWidth, $newHeight, $option);
268
269
        $optimalWidth = $dimensionsArray['optimalWidth'];
270
        $optimalHeight = $dimensionsArray['optimalHeight'];
271
272
        // *** Resample - create image canvas of x, y size
273
        $this->imageResized = imagecreatetruecolor($optimalWidth, $optimalHeight);
274
        $this->keepTransparancy($optimalWidth, $optimalHeight, $this->imageResized);
275
        imagecopyresampled($this->imageResized, $this->image, 0, 0, 0, 0, $optimalWidth, $optimalHeight, $this->width, $this->height);
276
277
        // *** If '4', then crop too
278
        if (4 == $option || 'crop' === $option) {
279
            if (($optimalWidth >= $newWidth && $optimalHeight >= $newHeight)) {
280
                $this->crop($optimalWidth, $optimalHeight, $newWidth, $newHeight, $cropPos);
281
            }
282
        }
283
284
        // *** If Rotate.
285
        if ($autoRotate) {
286
            try {
287
                $exifData = $this->getExif(false);
288
            } catch (Exception $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
289
            }
290
            if (count($exifData) > 0) {
291
                switch ($exifData['orientation']) {
292
                    case 8:
293
                        $this->imageResized = imagerotate($this->imageResized, 90, 0);
294
                        break;
295
                    case 3:
296
                        $this->imageResized = imagerotate($this->imageResized, 180, 0);
297
                        break;
298
                    case 6:
299
                        $this->imageResized = imagerotate($this->imageResized, -90, 0);
300
                        break;
301
                }
302
            }
303
        }
304
305
        // *** Sharpen image (if jpg and the user wishes to do so)
306
        if ($sharpen && in_array($this->fileExtension, $this->sharpenArray)) {
307
            // *** Sharpen
308
            try {
309
                $this->sharpen();
310
            } catch (Exception $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
311
            }
312
        }
313
    }
314
315
    ## --------------------------------------------------------
316
317
    /**
318
     * @param        $newWidth
319
     * @param        $newHeight
320
     * @param string $cropPos
321
     */
322
    public function cropImage($newWidth, $newHeight, $cropPos = 'm')
323
    {
324
        // *** Make sure the file passed in is valid
325
        if (!$this->image) {
326
            if ($this->debug) {
327
                die('file ' . $this->getFileName() . ' is missing or invalid');
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
328
            }
329
            die();
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
330
        }
331
332
        $this->imageResized = $this->image;
333
        $this->crop($this->width, $this->height, $newWidth, $newHeight, $cropPos);
334
    }
335
336
    ## --------------------------------------------------------
337
338
    /**
339
     * @param $width
340
     * @param $height
341
     * @param $im
342
     */
343
    private function keepTransparancy($width, $height, $im)
344
    {
345
        // *** If PNG, perform some transparency retention actions (gif untested)
346
        if (in_array($this->fileExtension, $this->transparentArray) && $this->keepTransparency) {
347
            imagealphablending($im, false);
348
            imagesavealpha($im, true);
349
            $transparent = imagecolorallocatealpha($im, 255, 255, 255, 127);
350
            imagefilledrectangle($im, 0, 0, $width, $height, $transparent);
351
        } else {
352
            $color = imagecolorallocate($im, $this->fillColorArray['r'], $this->fillColorArray['g'], $this->fillColorArray['b']);
353
            imagefilledrectangle($im, 0, 0, $width, $height, $color);
354
        }
355
    }
356
357
    ## --------------------------------------------------------
358
359
    /**
360
     * @param $optimalWidth
361
     * @param $optimalHeight
362
     * @param $newWidth
363
     * @param $newHeight
364
     * @param $cropPos
365
     */
366
    private function crop($optimalWidth, $optimalHeight, $newWidth, $newHeight, $cropPos)
367
    {
368
        // *** Get cropping co-ordinates
369
        $cropArray = $this->getCropPlacing($optimalWidth, $optimalHeight, $newWidth, $newHeight, $cropPos);
370
        $cropStartX = $cropArray['x'];
371
        $cropStartY = $cropArray['y'];
372
373
        // *** Crop this bad boy
374
        $crop = imagecreatetruecolor($newWidth, $newHeight);
375
        $this->keepTransparancy($optimalWidth, $optimalHeight, $crop);
376
        imagecopyresampled($crop, $this->imageResized, 0, 0, $cropStartX, $cropStartY, $newWidth, $newHeight, $newWidth, $newHeight);
0 ignored issues
show
Bug introduced by
It seems like $cropStartX can also be of type string; however, parameter $src_x of imagecopyresampled() 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

376
        imagecopyresampled($crop, $this->imageResized, 0, 0, /** @scrutinizer ignore-type */ $cropStartX, $cropStartY, $newWidth, $newHeight, $newWidth, $newHeight);
Loading history...
Bug introduced by
It seems like $cropStartY can also be of type string; however, parameter $src_y of imagecopyresampled() 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

376
        imagecopyresampled($crop, $this->imageResized, 0, 0, $cropStartX, /** @scrutinizer ignore-type */ $cropStartY, $newWidth, $newHeight, $newWidth, $newHeight);
Loading history...
377
378
        $this->imageResized = $crop;
379
380
        // *** Set new width and height to our variables
381
        $this->width = $newWidth;
382
        $this->height = $newHeight;
383
    }
384
385
    ## --------------------------------------------------------
386
387
    /**
388
     * @param        $optimalWidth
389
     * @param        $optimalHeight
390
     * @param        $newWidth
391
     * @param        $newHeight
392
     * @param string $pos
393
     * @return float[]|int[]
394
     */
395
    private function getCropPlacing($optimalWidth, $optimalHeight, $newWidth, $newHeight, $pos = 'm')
396
    {
397
        $pos = mb_strtolower($pos);
398
399
        // *** If co-ords have been entered
400
        if (mb_strstr($pos, 'x')) {
401
            $pos = str_replace(' ', '', $pos);
402
403
            $xyArray = explode('x', $pos);
404
            list($cropStartX, $cropStartY) = $xyArray;
405
        } else {
406
            switch ($pos) {
407
                case 'tl':
408
                    $cropStartX = 0;
409
                    $cropStartY = 0;
410
                    break;
411
                case 't':
412
                    $cropStartX = ($optimalWidth / 2) - ($newWidth / 2);
413
                    $cropStartY = 0;
414
                    break;
415
                case 'tr':
416
                    $cropStartX = $optimalWidth - $newWidth;
417
                    $cropStartY = 0;
418
                    break;
419
                case 'l':
420
                    $cropStartX = 0;
421
                    $cropStartY = ($optimalHeight / 2) - ($newHeight / 2);
422
                    break;
423
                case 'm':
424
                    $cropStartX = ($optimalWidth / 2) - ($newWidth / 2);
425
                    $cropStartY = ($optimalHeight / 2) - ($newHeight / 2);
426
                    break;
427
                case 'r':
428
                    $cropStartX = $optimalWidth - $newWidth;
429
                    $cropStartY = ($optimalHeight / 2) - ($newHeight / 2);
430
                    break;
431
                case 'bl':
432
                    $cropStartX = 0;
433
                    $cropStartY = $optimalHeight - $newHeight;
434
                    break;
435
                case 'b':
436
                    $cropStartX = ($optimalWidth / 2) - ($newWidth / 2);
437
                    $cropStartY = $optimalHeight - $newHeight;
438
                    break;
439
                case 'br':
440
                    $cropStartX = $optimalWidth - $newWidth;
441
                    $cropStartY = $optimalHeight - $newHeight;
442
                    break;
443
                case 'auto':
444
                    // *** If image is a portrait crop from top, not center. v1.5
445
                    if ($optimalHeight > $optimalWidth) {
446
                        $cropStartX = ($optimalWidth / 2) - ($newWidth / 2);
447
                        $cropStartY = ($this->cropFromTopPercent / 100) * $optimalHeight;
448
                    } else {
449
                        // *** Else crop from the center
450
                        $cropStartX = ($optimalWidth / 2) - ($newWidth / 2);
451
                        $cropStartY = ($optimalHeight / 2) - ($newHeight / 2);
452
                    }
453
                    break;
454
                default:
455
                    // *** Default to center
456
                    $cropStartX = ($optimalWidth / 2) - ($newWidth / 2);
457
                    $cropStartY = ($optimalHeight / 2) - ($newHeight / 2);
458
                    break;
459
            }
460
        }
461
462
        return ['x' => $cropStartX, 'y' => $cropStartY];
0 ignored issues
show
Bug Best Practice introduced by
The expression return array('x' => $cro...tX, 'y' => $cropStartY) returns an array which contains values of type string which are incompatible with the documented value type double|integer.
Loading history...
463
    }
464
465
    ## --------------------------------------------------------
466
467
    /**
468
     * @param $newWidth
469
     * @param $newHeight
470
     * @param $option
471
     * @return array
472
     */
473
    private function getDimensions($newWidth, $newHeight, $option)
474
    {
475
        switch ((string)$option) {
476
            case '0':
477
            case 'exact':
478
                $optimalWidth = $newWidth;
479
                $optimalHeight = $newHeight;
480
                break;
481
            case '1':
482
            case 'portrait':
483
                $dimensionsArray = $this->getSizeByFixedHeight($newWidth, $newHeight);
484
                $optimalWidth = $dimensionsArray['optimalWidth'];
485
                $optimalHeight = $dimensionsArray['optimalHeight'];
486
                break;
487
            case '2':
488
            case 'landscape':
489
                $dimensionsArray = $this->getSizeByFixedWidth($newWidth, $newHeight);
490
                $optimalWidth = $dimensionsArray['optimalWidth'];
491
                $optimalHeight = $dimensionsArray['optimalHeight'];
492
                break;
493
            case '3':
494
            case 'auto':
495
                $dimensionsArray = $this->getSizeByAuto($newWidth, $newHeight);
496
                $optimalWidth = $dimensionsArray['optimalWidth'];
497
                $optimalHeight = $dimensionsArray['optimalHeight'];
498
                break;
499
            case '4':
500
            case 'crop':
501
                $dimensionsArray = $this->getOptimalCrop($newWidth, $newHeight);
502
                $optimalWidth = $dimensionsArray['optimalWidth'];
503
                $optimalHeight = $dimensionsArray['optimalHeight'];
504
                break;
505
        }
506
507
        return ['optimalWidth' => $optimalWidth, 'optimalHeight' => $optimalHeight];
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $optimalWidth does not seem to be defined for all execution paths leading up to this point.
Loading history...
Comprehensibility Best Practice introduced by
The variable $optimalHeight does not seem to be defined for all execution paths leading up to this point.
Loading history...
508
    }
509
510
    ## --------------------------------------------------------
511
512
    /**
513
     * @param $newWidth
514
     * @param $newHeight
515
     * @return array
516
     */
517
    private function getSizeByFixedHeight($newWidth, $newHeight)
0 ignored issues
show
Unused Code introduced by
The parameter $newWidth is not used and could be removed. ( Ignorable by Annotation )

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

517
    private function getSizeByFixedHeight(/** @scrutinizer ignore-unused */ $newWidth, $newHeight)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
518
    {
519
        // *** If forcing is off...
520
        if (!$this->forceStretch) {
521
            // *** ...check if actual height is less than target height
522
            if ($this->height < $newHeight) {
523
                return ['optimalWidth' => $this->width, 'optimalHeight' => $this->height];
524
            }
525
        }
526
527
        $ratio = $this->width / $this->height;
528
529
        $newWidth = $newHeight * $ratio;
530
531
        //return $newWidth;
532
        return ['optimalWidth' => $newWidth, 'optimalHeight' => $newHeight];
533
    }
534
535
    ## --------------------------------------------------------
536
537
    /**
538
     * @param $newWidth
539
     * @param $newHeight
540
     * @return array
541
     */
542
    private function getSizeByFixedWidth($newWidth, $newHeight)
0 ignored issues
show
Unused Code introduced by
The parameter $newHeight is not used and could be removed. ( Ignorable by Annotation )

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

542
    private function getSizeByFixedWidth($newWidth, /** @scrutinizer ignore-unused */ $newHeight)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
543
    {
544
        // *** If forcing is off...
545
        if (!$this->forceStretch) {
546
            // *** ...check if actual width is less than target width
547
            if ($this->width < $newWidth) {
548
                return ['optimalWidth' => $this->width, 'optimalHeight' => $this->height];
549
            }
550
        }
551
552
        $ratio = $this->height / $this->width;
553
554
        $newHeight = $newWidth * $ratio;
555
556
        //return $newHeight;
557
        return ['optimalWidth' => $newWidth, 'optimalHeight' => $newHeight];
558
    }
559
560
    ## --------------------------------------------------------
561
562
    /**
563
     * @param $newWidth
564
     * @param $newHeight
565
     * @return array
566
     */
567
    private function getSizeByAuto($newWidth, $newHeight)
568
    {
569
        // *** If forcing is off...
570
        if (!$this->forceStretch) {
571
            // *** ...check if actual size is less than target size
572
            if ($this->width < $newWidth && $this->height < $newHeight) {
573
                return ['optimalWidth' => $this->width, 'optimalHeight' => $this->height];
574
            }
575
        }
576
577
        if ($this->height < $this->width) { // *** Image to be resized is wider (landscape)
578
            //$optimalWidth = $newWidth;
579
            //$optimalHeight= $this->getSizeByFixedWidth($newWidth);
580
581
            $dimensionsArray = $this->getSizeByFixedWidth($newWidth, $newHeight);
582
            $optimalWidth = $dimensionsArray['optimalWidth'];
583
            $optimalHeight = $dimensionsArray['optimalHeight'];
584
        } elseif ($this->height > $this->width) { // *** Image to be resized is taller (portrait)
585
            //$optimalWidth = $this->getSizeByFixedHeight($newHeight);
586
            //$optimalHeight= $newHeight;
587
588
            $dimensionsArray = $this->getSizeByFixedHeight($newWidth, $newHeight);
589
            $optimalWidth = $dimensionsArray['optimalWidth'];
590
            $optimalHeight = $dimensionsArray['optimalHeight'];
591
        } else { // *** Image to be resizerd is a square
592
            if ($newHeight < $newWidth) {
593
                //$optimalWidth = $newWidth;
594
                //$optimalHeight= $this->getSizeByFixedWidth($newWidth);
595
                $dimensionsArray = $this->getSizeByFixedWidth($newWidth, $newHeight);
596
                $optimalWidth = $dimensionsArray['optimalWidth'];
597
                $optimalHeight = $dimensionsArray['optimalHeight'];
598
            } elseif ($newHeight > $newWidth) {
599
                //$optimalWidth = $this->getSizeByFixedHeight($newHeight);
600
                //$optimalHeight= $newHeight;
601
                $dimensionsArray = $this->getSizeByFixedHeight($newWidth, $newHeight);
602
                $optimalWidth = $dimensionsArray['optimalWidth'];
603
                $optimalHeight = $dimensionsArray['optimalHeight'];
604
            } else {
605
                // *** Sqaure being resized to a square
606
                $optimalWidth = $newWidth;
607
                $optimalHeight = $newHeight;
608
            }
609
        }
610
611
        return ['optimalWidth' => $optimalWidth, 'optimalHeight' => $optimalHeight];
612
    }
613
614
    ## --------------------------------------------------------
615
616
    /**
617
     * @param $newWidth
618
     * @param $newHeight
619
     * @return array
620
     */
621
    private function getOptimalCrop($newWidth, $newHeight)
622
    {
623
        // *** If forcing is off...
624
        if (!$this->forceStretch) {
625
            // *** ...check if actual size is less than target size
626
            if ($this->width < $newWidth && $this->height < $newHeight) {
627
                return ['optimalWidth' => $this->width, 'optimalHeight' => $this->height];
628
            }
629
        }
630
631
        $heightRatio = $this->height / $newHeight;
632
        $widthRatio = $this->width / $newWidth;
633
634
        if ($heightRatio < $widthRatio) {
635
            $optimalRatio = $heightRatio;
636
        } else {
637
            $optimalRatio = $widthRatio;
638
        }
639
640
        $optimalHeight = round($this->height / $optimalRatio);
641
        $optimalWidth = round($this->width / $optimalRatio);
642
643
        return ['optimalWidth' => $optimalWidth, 'optimalHeight' => $optimalHeight];
644
    }
645
646
    ## --------------------------------------------------------
647
648
    private function sharpen()
649
    {
650
        if (version_compare(PHP_VERSION, '5.1.0') >= 0) {
651
            // ***
652
            if ($this->aggresiveSharpening) { # A more aggressive sharpening solution
653
                $sharpenMatrix = [
654
                    [-1, -1, -1],
655
                    [-1, 16, -1],
656
                    [-1, -1, -1],
657
                ];
658
                $divisor = 8;
659
                $offset = 0;
660
661
                imageconvolution($this->imageResized, $sharpenMatrix, $divisor, $offset);
662
            } else { # More subtle and personally more desirable
663
                $sharpness = $this->findSharp($this->widthOriginal, $this->width);
664
665
                $sharpenMatrix = [
666
                    [-1, -2, -1],
667
                    [-2, $sharpness + 12, -2], //Lessen the effect of a filter by increasing the value in the center cell
668
                    [-1, -2, -1],
669
                ];
670
                $divisor = $sharpness; // adjusts brightness
671
                $offset = 0;
672
                imageconvolution($this->imageResized, $sharpenMatrix, $divisor, $offset);
673
            }
674
        } else {
675
            if ($this->debug) {
676
                die('Sharpening required PHP 5.1.0 or greater.');
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
677
            }
678
        }
679
    }
680
681
    ## --------------------------------------------------------
682
683
    /**
684
     * @param $level
685
     */
686
    private function sharpen2($level)
0 ignored issues
show
Unused Code introduced by
The method sharpen2() is not used, and could be removed.

This check looks for private methods that have been defined, but are not used inside the class.

Loading history...
687
    {
688
        $sharpenMatrix = [
0 ignored issues
show
Unused Code introduced by
The assignment to $sharpenMatrix is dead and can be removed.
Loading history...
689
            [$level, $level, $level],
690
            [$level, (8 * $level) + 1, $level], //Lessen the effect of a filter by increasing the value in the center cell
691
            [$level, $level, $level],
692
        ];
693
    }
694
695
    ## --------------------------------------------------------
696
697
    /**
698
     * @param $orig
699
     * @param $final
700
     * @return mixed
701
     */
702
    private function findSharp($orig, $final)
703
    {
704
        $final = $final * (750.0 / $orig);
705
        $a = 52;
706
        $b = -0.27810650887573124;
707
        $c = .00047337278106508946;
708
709
        $result = $a + $b * $final + $c * $final * $final;
710
711
        return max(round($result), 0);
712
    }
713
714
    ## --------------------------------------------------------
715
716
    /**
717
     * @param $option
718
     * @return bool|false|string|string[]|null
719
     */
720
    private function prepOption($option)
721
    {
722
        if (is_array($option)) {
723
            if ('crop' === mb_strtolower($option[0]) && 2 == count($option)) {
724
                return 'crop';
725
            }
726
            die('Crop resize option array is badly formatted.');
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
727
        } elseif (false !== mb_strpos($option, 'crop')) {
728
            return 'crop';
729
        }
730
731
        if (is_string($option)) {
732
            return mb_strtolower($option);
733
        }
734
735
        return $option;
736
    }
737
738
    /*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*-
739
        Presets
740
    *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*/
741
742
    #
743
    # Preset are pre-defined templates you can apply to your image.
744
    #
745
    # These are inteded to be applied to thumbnail images.
746
    #
747
748
    /**
749
     * @param $preset
750
     */
751
    public function borderPreset($preset)
752
    {
753
        switch ($preset) {
754
            case 'simple':
755
                $this->addBorder(7, '#fff');
756
                $this->addBorder(6, '#f2f1f0');
757
                $this->addBorder(2, '#fff');
758
                $this->addBorder(1, '#ccc');
759
                break;
760
            default:
761
                break;
762
        }
763
    }
764
765
    /*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*-
766
        Draw border
767
    *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*/
768
769
    /**
770
     * @param int   $thickness
771
     * @param int[] $rgbArray
772
     */
773
    public function addBorder($thickness = 1, $rgbArray = [255, 255, 255])
774
    {
775
        if ($this->imageResized) {
776
            $rgbArray = $this->formatColor($rgbArray);
777
            $r = $rgbArray['r'];
778
            $g = $rgbArray['g'];
779
            $b = $rgbArray['b'];
780
781
            $x1 = 0;
782
            $y1 = 0;
783
            $x2 = imagesx($this->imageResized) - 1;
784
            $y2 = imagesy($this->imageResized) - 1;
785
786
            $rgbArray = imagecolorallocate($this->imageResized, $r, $g, $b);
787
788
            for ($i = 0; $i < $thickness; ++$i) {
789
                imagerectangle($this->imageResized, ++$x1, ++$y1, $x2--, $y2--, $rgbArray);
790
            }
791
        }
792
    }
793
794
    /*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*-
795
        Gray Scale
796
    *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*/
797
798
    public function greyScale()
799
    {
800
        if ($this->imageResized) {
801
            imagefilter($this->imageResized, IMG_FILTER_GRAYSCALE);
802
        }
803
    }
804
805
    ## --------------------------------------------------------
806
807
    public function greyScaleEnhanced()
808
    {
809
        if ($this->imageResized) {
810
            imagefilter($this->imageResized, IMG_FILTER_GRAYSCALE);
811
            imagefilter($this->imageResized, IMG_FILTER_CONTRAST, -15);
812
            imagefilter($this->imageResized, IMG_FILTER_BRIGHTNESS, 2);
813
814
            try {
815
                $this->sharpen($this->width);
0 ignored issues
show
Unused Code introduced by
The call to imageLib::sharpen() has too many arguments starting with $this->width. ( Ignorable by Annotation )

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

815
                $this->/** @scrutinizer ignore-call */ 
816
                       sharpen($this->width);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
816
            } catch (Exception $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
817
            }
818
        }
819
    }
820
821
    ## --------------------------------------------------------
822
823
    public function greyScaleDramatic()
824
    {
825
        $this->gd_filter_monopin();
826
    }
827
828
    /*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*-
829
        Black 'n White
830
    *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*/
831
832
    public function blackAndWhite()
833
    {
834
        if ($this->imageResized) {
835
            imagefilter($this->imageResized, IMG_FILTER_GRAYSCALE);
836
            imagefilter($this->imageResized, IMG_FILTER_CONTRAST, -1000);
837
        }
838
    }
839
840
    /*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*-
841
        Negative
842
    *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*/
843
844
    public function negative()
845
    {
846
        if ($this->imageResized) {
847
            imagefilter($this->imageResized, IMG_FILTER_NEGATE);
848
        }
849
    }
850
851
    /*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*-
852
        Sepia
853
    *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*/
854
855
    public function sepia()
856
    {
857
        if ($this->imageResized) {
858
            imagefilter($this->imageResized, IMG_FILTER_GRAYSCALE);
859
            imagefilter($this->imageResized, IMG_FILTER_BRIGHTNESS, -10);
860
            imagefilter($this->imageResized, IMG_FILTER_CONTRAST, -20);
861
            imagefilter($this->imageResized, IMG_FILTER_COLORIZE, 60, 30, -15);
862
        }
863
    }
864
865
    ## --------------------------------------------------------
866
867
    public function sepia2()
868
    {
869
        if ($this->imageResized) {
870
            $total = imagecolorstotal($this->imageResized);
871
            for ($i = 0; $i < $total; ++$i) {
872
                $index = imagecolorsforindex($this->imageResized, $i);
873
                $red = ($index['red'] * 0.393 + $index['green'] * 0.769 + $index['blue'] * 0.189) / 1.351;
874
                $green = ($index['red'] * 0.349 + $index['green'] * 0.686 + $index['blue'] * 0.168) / 1.203;
875
                $blue = ($index['red'] * 0.272 + $index['green'] * 0.534 + $index['blue'] * 0.131) / 2.140;
876
                imagecolorset($this->imageResized, $i, $red, $green, $blue);
0 ignored issues
show
Bug introduced by
$blue of type double is incompatible with the type integer expected by parameter $blue of imagecolorset(). ( Ignorable by Annotation )

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

876
                imagecolorset($this->imageResized, $i, $red, $green, /** @scrutinizer ignore-type */ $blue);
Loading history...
Bug introduced by
$red of type double is incompatible with the type integer expected by parameter $red of imagecolorset(). ( Ignorable by Annotation )

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

876
                imagecolorset($this->imageResized, $i, /** @scrutinizer ignore-type */ $red, $green, $blue);
Loading history...
Bug introduced by
$green of type double is incompatible with the type integer expected by parameter $green of imagecolorset(). ( Ignorable by Annotation )

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

876
                imagecolorset($this->imageResized, $i, $red, /** @scrutinizer ignore-type */ $green, $blue);
Loading history...
877
            }
878
        }
879
    }
880
881
    /*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*-
882
        Vintage
883
    *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*/
884
885
    public function vintage()
886
    {
887
        $this->gd_filter_vintage();
888
    }
889
890
    /*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*-
891
        Presets By Marc Hibbins
892
    *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*/
893
894
    /** Apply 'Monopin' preset */
895
    public function gd_filter_monopin()
896
    {
897
        if ($this->imageResized) {
898
            imagefilter($this->imageResized, IMG_FILTER_GRAYSCALE);
899
            imagefilter($this->imageResized, IMG_FILTER_BRIGHTNESS, -15);
900
            imagefilter($this->imageResized, IMG_FILTER_CONTRAST, -15);
901
            $this->imageResized = $this->gd_apply_overlay($this->imageResized, 'vignette', 100);
902
        }
903
    }
904
905
    ## --------------------------------------------------------
906
907
    public function gd_filter_vintage()
908
    {
909
        if ($this->imageResized) {
910
            $this->imageResized = $this->gd_apply_overlay($this->imageResized, 'vignette', 45);
911
            imagefilter($this->imageResized, IMG_FILTER_BRIGHTNESS, 20);
912
            imagefilter($this->imageResized, IMG_FILTER_CONTRAST, -35);
913
            imagefilter($this->imageResized, IMG_FILTER_COLORIZE, 60, -10, 35);
914
            imagefilter($this->imageResized, IMG_FILTER_SMOOTH, 7);
915
            $this->imageResized = $this->gd_apply_overlay($this->imageResized, 'scratch', 10);
916
        }
917
    }
918
919
    ## --------------------------------------------------------
920
921
    /** Apply a PNG overlay
922
     * @param $im
923
     * @param $type
924
     * @param $amount
925
     * @return mixed
926
     */
927
    private function gd_apply_overlay($im, $type, $amount)
928
    {
929
        $width = imagesx($im);
930
        $height = imagesy($im);
931
        $filter = imagecreatetruecolor($width, $height);
932
933
        imagealphablending($filter, false);
934
        imagesavealpha($filter, true);
935
936
        $transparent = imagecolorallocatealpha($filter, 255, 255, 255, 127);
937
        imagefilledrectangle($filter, 0, 0, $width, $height, $transparent);
938
939
        // *** Resize overlay
940
        $overlay = $this->filterOverlayPath . '/' . $type . '.png';
941
        $png = imagecreatefrompng($overlay);
942
        imagecopyresampled($filter, $png, 0, 0, 0, 0, $width, $height, imagesx($png), imagesy($png));
943
944
        $comp = imagecreatetruecolor($width, $height);
945
        imagecopy($comp, $im, 0, 0, 0, 0, $width, $height);
946
        imagecopy($comp, $filter, 0, 0, 0, 0, $width, $height);
947
        imagecopymerge($im, $comp, 0, 0, 0, 0, $width, $height, $amount);
948
949
        imagedestroy($comp);
950
951
        return $im;
952
    }
953
954
    /*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*-
955
        Colorise
956
    *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*/
957
958
    /**
959
     * @param $rgb
960
     * @return bool
961
     */
962
    public function image_colorize($rgb)
963
    {
964
        imagetruecolortopalette($this->imageResized, true, 256);
965
        $numColors = imagecolorstotal($this->imageResized);
966
967
        for ($x = 0; $x < $numColors; ++$x) {
968
            list($r, $g, $b) = array_values(imagecolorsforindex($this->imageResized, $x));
969
970
            // calculate grayscale in percent
971
            $grayscale = ($r + $g + $b) / 3 / 0xff;
972
973
            imagecolorset(
974
                $this->imageResized,
975
                $x,
976
                $grayscale * $rgb[0],
977
                $grayscale * $rgb[1],
978
                $grayscale * $rgb[2]
979
            );
980
        }
981
982
        return true;
983
    }
984
985
    /*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*-
986
        Reflection
987
    *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*/
988
989
    /**
990
     * @param int    $reflectionHeight
991
     * @param int    $startingTransparency
992
     * @param bool   $inside
993
     * @param string $bgColor
994
     * @param bool   $stretch
995
     * @param int    $divider
996
     */
997
    public function addReflection($reflectionHeight = 50, $startingTransparency = 30, $inside = false, $bgColor = '#fff', $stretch = false, $divider = 0)
998
    {
999
        // *** Convert color
1000
        $rgbArray = $this->formatColor($bgColor);
1001
        $r = $rgbArray['r'];
1002
        $g = $rgbArray['g'];
1003
        $b = $rgbArray['b'];
1004
1005
        $im = $this->imageResized;
1006
        $li = imagecreatetruecolor($this->width, 1);
1007
1008
        $bgc = imagecolorallocate($li, $r, $g, $b);
1009
        imagefilledrectangle($li, 0, 0, $this->width, 1, $bgc);
1010
1011
        $bg = imagecreatetruecolor($this->width, $reflectionHeight);
1012
        $wh = imagecolorallocate($im, 255, 255, 255);
1013
1014
        $im = imagerotate($im, -180, $wh);
1015
        imagecopyresampled($bg, $im, 0, 0, 0, 0, $this->width, $this->height, $this->width, $this->height);
1016
1017
        $im = $bg;
1018
1019
        $bg = imagecreatetruecolor($this->width, $reflectionHeight);
1020
1021
        for ($x = 0; $x < $this->width; ++$x) {
1022
            imagecopy($bg, $im, $x, 0, $this->width - $x - 1, 0, 1, $reflectionHeight);
1023
        }
1024
        $im = $bg;
1025
1026
        $transaprencyAmount = $this->invertTransparency($startingTransparency, 100);
0 ignored issues
show
Unused Code introduced by
The assignment to $transaprencyAmount is dead and can be removed.
Loading history...
1027
1028
        // *** Fade
1029
        if ($stretch) {
1030
            $step = 100 / ($reflectionHeight + $startingTransparency);
1031
        } else {
1032
            $step = 100 / $reflectionHeight;
1033
        }
1034
        for ($i = 0; $i <= $reflectionHeight; ++$i) {
1035
            if ($startingTransparency > 100) {
1036
                $startingTransparency = 100;
1037
            }
1038
            if ($startingTransparency < 1) {
1039
                $startingTransparency = 1;
1040
            }
1041
            imagecopymerge($bg, $li, 0, $i, 0, 0, $this->width, 1, $startingTransparency);
1042
            $startingTransparency += $step;
1043
        }
1044
1045
        // *** Apply fade
1046
        imagecopymerge($im, $li, 0, 0, 0, 0, $this->width, $divider, 100); // Divider
1047
1048
        // *** width, height of reflection.
1049
        $x = imagesx($im);
1050
        $y = imagesy($im);
1051
1052
        // *** Determines if the reflection should be displayed inside or outside the image
1053
        if ($inside) {
1054
            // Create new blank image with sizes.
1055
            $final = imagecreatetruecolor($this->width, $this->height);
1056
1057
            imagecopymerge($final, $this->imageResized, 0, 0, 0, $reflectionHeight, $this->width, $this->height - $reflectionHeight, 100);
1058
            imagecopymerge($final, $im, 0, $this->height - $reflectionHeight, 0, 0, $x, $y, 100);
1059
        } else {
1060
            // Create new blank image with sizes.
1061
            $final = imagecreatetruecolor($this->width, $this->height + $y);
1062
1063
            imagecopymerge($final, $this->imageResized, 0, 0, 0, 0, $this->width, $this->height, 100);
1064
            imagecopymerge($final, $im, 0, $this->height, 0, 0, $x, $y, 100);
1065
        }
1066
1067
        $this->imageResized = $final;
1068
1069
        imagedestroy($li);
1070
        imagedestroy($im);
1071
    }
1072
1073
    /*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*-
1074
        Rotate
1075
    *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*/
1076
1077
    /**
1078
     * @param int    $value
1079
     * @param string $bgColor
1080
     */
1081
    public function rotate($value = 90, $bgColor = 'transparent')
1082
    {
1083
        if ($this->imageResized) {
1084
            if (is_int($value)) {
0 ignored issues
show
introduced by
The condition is_int($value) is always true.
Loading history...
1085
                $degrees = $value;
1086
            }
1087
1088
            // *** Convert color
1089
            $rgbArray = $this->formatColor($bgColor);
1090
            $r = $rgbArray['r'];
1091
            $g = $rgbArray['g'];
1092
            $b = $rgbArray['b'];
1093
            if (isset($rgbArray['a'])) {
1094
                $a = $rgbArray['a'];
1095
            }
1096
1097
            if (is_string($value)) {
0 ignored issues
show
introduced by
The condition is_string($value) is always false.
Loading history...
1098
                $value = mb_strtolower($value);
1099
1100
                switch ($value) {
1101
                    case 'left':
1102
                        $degrees = 90;
1103
                        break;
1104
                    case 'right':
1105
                        $degrees = 270;
1106
                        break;
1107
                    case 'upside':
1108
                        $degrees = 180;
1109
                        break;
1110
                    default:
1111
                        break;
1112
                }
1113
            }
1114
1115
            // *** The default direction of imageRotate() is counter clockwise
1116
            //   * This makes it clockwise
1117
            $degrees = 360 - $degrees;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $degrees does not seem to be defined for all execution paths leading up to this point.
Loading history...
1118
1119
            // *** Create background color
1120
            $bg = imagecolorallocatealpha($this->imageResized, $r, $g, $b, $a);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $a does not seem to be defined for all execution paths leading up to this point.
Loading history...
1121
1122
            // *** Fill with background
1123
            imagefill($this->imageResized, 0, 0, $bg);
1124
1125
            // *** Rotate
1126
            $this->imageResized = imagerotate($this->imageResized, $degrees, $bg); // Rotate 45 degrees and allocated the transparent colour as the one to make transparent (obviously)
1127
1128
            // Ensure alpha transparency
1129
            imagesavealpha($this->imageResized, true);
1130
        }
1131
    }
1132
1133
    /*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*-
1134
        Round corners
1135
    *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*/
1136
1137
    /**
1138
     * @param int    $radius
1139
     * @param string $bgColor
1140
     */
1141
    public function roundCorners($radius = 5, $bgColor = 'transparent')
1142
    {
1143
        // *** Check if the user wants transparency
1144
        $isTransparent = false;
1145
        if (!is_array($bgColor)) {
0 ignored issues
show
introduced by
The condition is_array($bgColor) is always false.
Loading history...
1146
            if ('transparent' === mb_strtolower($bgColor)) {
1147
                $isTransparent = true;
1148
            }
1149
        }
1150
1151
        // *** If we use transparency, we need to color our curved mask with a unique color
1152
        if ($isTransparent) {
1153
            $bgColor = $this->findUnusedGreen();
1154
        }
1155
1156
        // *** Convert color
1157
        $rgbArray = $this->formatColor($bgColor);
1158
        $r = $rgbArray['r'];
1159
        $g = $rgbArray['g'];
1160
        $b = $rgbArray['b'];
1161
        if (isset($rgbArray['a'])) {
1162
            $a = $rgbArray['a'];
0 ignored issues
show
Unused Code introduced by
The assignment to $a is dead and can be removed.
Loading history...
1163
        }
1164
1165
        // *** Create top-left corner mask (square)
1166
        $cornerImg = imagecreatetruecolor($radius, $radius);
1167
        //$cornerImg = imagecreate($radius, $radius);
1168
1169
        //imagealphablending($cornerImg, true);
1170
        //imagesavealpha($cornerImg, true);
1171
1172
        //imagealphablending($this->imageResized, false);
1173
        //imagesavealpha($this->imageResized, true);
1174
1175
        // *** Give it a color
1176
        $maskColor = imagecolorallocate($cornerImg, 0, 0, 0);
1177
1178
        // *** Replace the mask color (black) to transparent
1179
        imagecolortransparent($cornerImg, $maskColor);
1180
1181
        // *** Create the image background color
1182
        $imagebgColor = imagecolorallocate($cornerImg, $r, $g, $b);
1183
1184
        // *** Fill the corner area to the user defined color
1185
        imagefill($cornerImg, 0, 0, $imagebgColor);
1186
1187
        imagefilledellipse($cornerImg, $radius, $radius, $radius * 2, $radius * 2, $maskColor);
1188
1189
        // *** Map to top left corner
1190
        imagecopymerge($this->imageResized, $cornerImg, 0, 0, 0, 0, $radius, $radius, 100); #tl
1191
1192
        // *** Map rounded corner to other corners by rotating and applying the mask
1193
        $cornerImg = imagerotate($cornerImg, 90, 0);
1194
        imagecopymerge($this->imageResized, $cornerImg, 0, $this->height - $radius, 0, 0, $radius, $radius, 100); #bl
1195
1196
        $cornerImg = imagerotate($cornerImg, 90, 0);
1197
        imagecopymerge($this->imageResized, $cornerImg, $this->width - $radius, $this->height - $radius, 0, 0, $radius, $radius, 100); #br
1198
1199
        $cornerImg = imagerotate($cornerImg, 90, 0);
1200
        imagecopymerge($this->imageResized, $cornerImg, $this->width - $radius, 0, 0, 0, $radius, $radius, 100); #tr
1201
1202
        // *** If corners are to be transparent, we fill our chromakey color as transparent.
1203
        if ($isTransparent) {
1204
            //imagecolortransparent($this->imageResized, $imagebgColor);
1205
            $this->imageResized = $this->transparentImage($this->imageResized);
1206
            imagesavealpha($this->imageResized, true);
1207
        }
1208
    }
1209
1210
    /*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*-
1211
        Shadow
1212
    *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*/
1213
1214
    /**
1215
     * @param int    $shadowAngle
1216
     * @param int    $blur
1217
     * @param string $bgColor
1218
     */
1219
    public function addShadow($shadowAngle = 45, $blur = 15, $bgColor = 'transparent')
1220
    {
1221
        // *** A higher number results in a smoother shadow
1222
        define('STEPS', $blur * 2);
1223
1224
        // *** Set the shadow distance
1225
        $shadowDistance = $blur * 0.25;
1226
1227
        // *** Set blur width and height
1228
        $blurWidth = $blurHeight = $blur;
1229
1230
        if (0 == $shadowAngle) {
1231
            $distWidth = 0;
1232
            $distHeight = 0;
1233
        } else {
1234
            $distWidth = $shadowDistance * cos(deg2rad($shadowAngle));
1235
            $distHeight = $shadowDistance * sin(deg2rad($shadowAngle));
1236
        }
1237
1238
        // *** Convert color
1239
        if ('transparent' !== mb_strtolower($bgColor)) {
1240
            $rgbArray = $this->formatColor($bgColor);
1241
            $r0 = $rgbArray['r'];
1242
            $g0 = $rgbArray['g'];
1243
            $b0 = $rgbArray['b'];
1244
        }
1245
1246
        $image = $this->imageResized;
1247
        $width = $this->width;
1248
        $height = $this->height;
1249
1250
        $newImage = imagecreatetruecolor($width, $height);
1251
        imagecopyresampled($newImage, $image, 0, 0, 0, 0, $width, $height, $width, $height);
1252
1253
        // *** RGB
1254
        $rgb = imagecreatetruecolor($width + $blurWidth, $height + $blurHeight);
1255
        $colour = imagecolorallocate($rgb, 0, 0, 0);
1256
        imagefilledrectangle($rgb, 0, 0, $width + $blurWidth, $height + $blurHeight, $colour);
1257
        $colour = imagecolorallocate($rgb, 255, 255, 255);
1258
        //imagefilledrectangle($rgb, $blurWidth*0.5-$distWidth, $blurHeight*0.5-$distHeight, $width+$blurWidth*0.5-$distWidth, $height+$blurWidth*0.5-$distHeight, $colour);
1259
        imagefilledrectangle($rgb, $blurWidth * 0.5 - $distWidth, $blurHeight * 0.5 - $distHeight, $width + $blurWidth * 0.5 - $distWidth, $height + $blurWidth * 0.5 - $distHeight, $colour);
0 ignored issues
show
Bug introduced by
$width + $blurWidth * 0.5 - $distWidth of type double is incompatible with the type integer expected by parameter $x2 of imagefilledrectangle(). ( Ignorable by Annotation )

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

1259
        imagefilledrectangle($rgb, $blurWidth * 0.5 - $distWidth, $blurHeight * 0.5 - $distHeight, /** @scrutinizer ignore-type */ $width + $blurWidth * 0.5 - $distWidth, $height + $blurWidth * 0.5 - $distHeight, $colour);
Loading history...
Bug introduced by
$blurHeight * 0.5 - $distHeight of type double is incompatible with the type integer expected by parameter $y1 of imagefilledrectangle(). ( Ignorable by Annotation )

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

1259
        imagefilledrectangle($rgb, $blurWidth * 0.5 - $distWidth, /** @scrutinizer ignore-type */ $blurHeight * 0.5 - $distHeight, $width + $blurWidth * 0.5 - $distWidth, $height + $blurWidth * 0.5 - $distHeight, $colour);
Loading history...
Bug introduced by
$blurWidth * 0.5 - $distWidth of type double is incompatible with the type integer expected by parameter $x1 of imagefilledrectangle(). ( Ignorable by Annotation )

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

1259
        imagefilledrectangle($rgb, /** @scrutinizer ignore-type */ $blurWidth * 0.5 - $distWidth, $blurHeight * 0.5 - $distHeight, $width + $blurWidth * 0.5 - $distWidth, $height + $blurWidth * 0.5 - $distHeight, $colour);
Loading history...
Bug introduced by
$height + $blurWidth * 0.5 - $distHeight of type double is incompatible with the type integer expected by parameter $y2 of imagefilledrectangle(). ( Ignorable by Annotation )

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

1259
        imagefilledrectangle($rgb, $blurWidth * 0.5 - $distWidth, $blurHeight * 0.5 - $distHeight, $width + $blurWidth * 0.5 - $distWidth, /** @scrutinizer ignore-type */ $height + $blurWidth * 0.5 - $distHeight, $colour);
Loading history...
1260
        //imagecopymerge($rgb, $newImage, 1+$blurWidth*0.5-$distWidth, 1+$blurHeight*0.5-$distHeight, 0,0, $width, $height, 100);
1261
        imagecopymerge($rgb, $newImage, $blurWidth * 0.5 - $distWidth, $blurHeight * 0.5 - $distHeight, 0, 0, $width + $blurWidth, $height + $blurHeight, 100);
0 ignored issues
show
Bug introduced by
$blurWidth * 0.5 - $distWidth of type double is incompatible with the type integer expected by parameter $dst_x of imagecopymerge(). ( Ignorable by Annotation )

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

1261
        imagecopymerge($rgb, $newImage, /** @scrutinizer ignore-type */ $blurWidth * 0.5 - $distWidth, $blurHeight * 0.5 - $distHeight, 0, 0, $width + $blurWidth, $height + $blurHeight, 100);
Loading history...
Bug introduced by
$blurHeight * 0.5 - $distHeight of type double is incompatible with the type integer expected by parameter $dst_y of imagecopymerge(). ( Ignorable by Annotation )

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

1261
        imagecopymerge($rgb, $newImage, $blurWidth * 0.5 - $distWidth, /** @scrutinizer ignore-type */ $blurHeight * 0.5 - $distHeight, 0, 0, $width + $blurWidth, $height + $blurHeight, 100);
Loading history...
1262
1263
        // *** Shadow (alpha)
1264
        $shadow = imagecreatetruecolor($width + $blurWidth, $height + $blurHeight);
1265
        imagealphablending($shadow, false);
1266
        $colour = imagecolorallocate($shadow, 0, 0, 0);
1267
        imagefilledrectangle($shadow, 0, 0, $width + $blurWidth, $height + $blurHeight, $colour);
1268
1269
        for ($i = 0; $i <= STEPS; ++$i) {
1270
            $t = ((1.0 * $i) / STEPS);
1271
            $intensity = 255 * $t * $t;
1272
1273
            $colour = imagecolorallocate($shadow, $intensity, $intensity, $intensity);
1274
            $points = [
1275
                $blurWidth * $t,
1276
                $blurHeight,     // Point 1 (x, y)
1277
                $blurWidth,
1278
                $blurHeight * $t,  // Point 2 (x, y)
1279
                $width,
1280
                $blurHeight * $t,  // Point 3 (x, y)
1281
                $width + $blurWidth * (1 - $t),
1282
                $blurHeight,     // Point 4 (x, y)
1283
                $width + $blurWidth * (1 - $t),
1284
                $height,     // Point 5 (x, y)
1285
                $width,
1286
                $height + $blurHeight * (1 - $t),  // Point 6 (x, y)
1287
                $blurWidth,
1288
                $height + $blurHeight * (1 - $t),  // Point 7 (x, y)
1289
                $blurWidth * $t,
1290
                $height,      // Point 8 (x, y)
1291
            ];
1292
            imagepolygon($shadow, $points, 8, $colour);
1293
        }
1294
1295
        for ($i = 0; $i <= STEPS; ++$i) {
1296
            $t = ((1.0 * $i) / STEPS);
1297
            $intensity = 255 * $t * $t;
1298
1299
            $colour = imagecolorallocate($shadow, $intensity, $intensity, $intensity);
1300
            imagefilledarc($shadow, $blurWidth - 1, $blurHeight - 1, 2 * (1 - $t) * $blurWidth, 2 * (1 - $t) * $blurHeight, 180, 268, $colour, IMG_ARC_PIE);
1301
            imagefilledarc($shadow, $width, $blurHeight - 1, 2 * (1 - $t) * $blurWidth, 2 * (1 - $t) * $blurHeight, 270, 358, $colour, IMG_ARC_PIE);
1302
            imagefilledarc($shadow, $width, $height, 2 * (1 - $t) * $blurWidth, 2 * (1 - $t) * $blurHeight, 0, 90, $colour, IMG_ARC_PIE);
1303
            imagefilledarc($shadow, $blurWidth - 1, $height, 2 * (1 - $t) * $blurWidth, 2 * (1 - $t) * $blurHeight, 90, 180, $colour, IMG_ARC_PIE);
1304
        }
1305
1306
        $colour = imagecolorallocate($shadow, 255, 255, 255);
1307
        imagefilledrectangle($shadow, $blurWidth, $blurHeight, $width, $height, $colour);
1308
        imagefilledrectangle($shadow, $blurWidth * 0.5 - $distWidth, $blurHeight * 0.5 - $distHeight, $width + $blurWidth * 0.5 - 1 - $distWidth, $height + $blurHeight * 0.5 - 1 - $distHeight, $colour);
1309
1310
        // *** The magic
1311
        imagealphablending($rgb, false);
1312
1313
        for ($theX = 0, $theXMax = imagesx($rgb); $theX < $theXMax; $theX++) {
1314
            for ($theY = 0, $theYMax = imagesy($rgb); $theY < $theYMax; $theY++) {
1315
                // *** Get the RGB values for every pixel of the RGB image
1316
                $colArray = imagecolorat($rgb, $theX, $theY);
1317
                $r = ($colArray >> 16) & 0xFF;
1318
                $g = ($colArray >> 8) & 0xFF;
1319
                $b = $colArray & 0xFF;
1320
1321
                // *** Get the alpha value for every pixel of the shadow image
1322
                $colArray = imagecolorat($shadow, $theX, $theY);
1323
                $a = $colArray & 0xFF;
1324
                $a = 127 - floor($a / 2);
1325
                $t = $a / 128.0;
1326
1327
                // *** Create color
1328
                if ('transparent' === mb_strtolower($bgColor)) {
1329
                    $myColour = imagecolorallocatealpha($rgb, $r, $g, $b, $a);
0 ignored issues
show
Bug introduced by
$a of type double is incompatible with the type integer expected by parameter $alpha of imagecolorallocatealpha(). ( Ignorable by Annotation )

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

1329
                    $myColour = imagecolorallocatealpha($rgb, $r, $g, $b, /** @scrutinizer ignore-type */ $a);
Loading history...
1330
                } else {
1331
                    $myColour = imagecolorallocate($rgb, $r * (1.0 - $t) + $r0 * $t, $g * (1.0 - $t) + $g0 * $t, $b * (1.0 - $t) + $b0 * $t);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $r0 does not seem to be defined for all execution paths leading up to this point.
Loading history...
Bug introduced by
$g * 1.0 - $t + $g0 * $t of type double is incompatible with the type integer expected by parameter $green of imagecolorallocate(). ( Ignorable by Annotation )

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

1331
                    $myColour = imagecolorallocate($rgb, $r * (1.0 - $t) + $r0 * $t, /** @scrutinizer ignore-type */ $g * (1.0 - $t) + $g0 * $t, $b * (1.0 - $t) + $b0 * $t);
Loading history...
Bug introduced by
$b * 1.0 - $t + $b0 * $t of type double is incompatible with the type integer expected by parameter $blue of imagecolorallocate(). ( Ignorable by Annotation )

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

1331
                    $myColour = imagecolorallocate($rgb, $r * (1.0 - $t) + $r0 * $t, $g * (1.0 - $t) + $g0 * $t, /** @scrutinizer ignore-type */ $b * (1.0 - $t) + $b0 * $t);
Loading history...
Comprehensibility Best Practice introduced by
The variable $g0 does not seem to be defined for all execution paths leading up to this point.
Loading history...
Bug introduced by
$r * 1.0 - $t + $r0 * $t of type double is incompatible with the type integer expected by parameter $red of imagecolorallocate(). ( Ignorable by Annotation )

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

1331
                    $myColour = imagecolorallocate($rgb, /** @scrutinizer ignore-type */ $r * (1.0 - $t) + $r0 * $t, $g * (1.0 - $t) + $g0 * $t, $b * (1.0 - $t) + $b0 * $t);
Loading history...
Comprehensibility Best Practice introduced by
The variable $b0 does not seem to be defined for all execution paths leading up to this point.
Loading history...
1332
                }
1333
1334
                // *** Add color to new rgb image
1335
                imagesetpixel($rgb, $theX, $theY, $myColour);
1336
            }
1337
        }
1338
1339
        imagealphablending($rgb, true);
1340
        imagesavealpha($rgb, true);
1341
1342
        $this->imageResized = $rgb;
1343
1344
        imagedestroy($image);
1345
        imagedestroy($newImage);
1346
        imagedestroy($shadow);
1347
    }
1348
1349
    /*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*-
1350
        Add Caption Box
1351
    *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*/
1352
1353
    /**
1354
     * @param string $side
1355
     * @param int    $thickness
1356
     * @param int    $padding
1357
     * @param string $bgColor
1358
     * @param int    $transaprencyAmount
1359
     */
1360
    public function addCaptionBox($side = 'b', $thickness = 50, $padding = 0, $bgColor = '#000', $transaprencyAmount = 30)
1361
    {
1362
        $side = mb_strtolower($side);
1363
1364
        // *** Convert color
1365
        $rgbArray = $this->formatColor($bgColor);
1366
        $r = $rgbArray['r'];
1367
        $g = $rgbArray['g'];
1368
        $b = $rgbArray['b'];
1369
1370
        $positionArray = $this->calculateCaptionBoxPosition($side, $thickness, $padding);
1371
1372
        // *** Store incase we want to use method addTextToCaptionBox()
1373
        $this->captionBoxPositionArray = $positionArray;
1374
1375
        $transaprencyAmount = $this->invertTransparency($transaprencyAmount, 127, false);
1376
        $transparent = imagecolorallocatealpha($this->imageResized, $r, $g, $b, $transaprencyAmount);
1377
        imagefilledrectangle($this->imageResized, $positionArray['x1'], $positionArray['y1'], $positionArray['x2'], $positionArray['y2'], $transparent);
1378
    }
1379
1380
    ## --------------------------------------------------------
1381
1382
    /**
1383
     * @param        $text
1384
     * @param string $fontColor
1385
     * @param int    $fontSize
1386
     * @param int    $angle
1387
     * @param null   $font
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $font is correct as it would always require null to be passed?
Loading history...
1388
     * @return bool
1389
     */
1390
    public function addTextToCaptionBox($text, $fontColor = '#fff', $fontSize = 12, $angle = 0, $font = null)
1391
    {
1392
        // *** Get the caption box measurements
1393
        if (4 == count($this->captionBoxPositionArray)) {
1394
            $x1 = $this->captionBoxPositionArray['x1'];
1395
            $x2 = $this->captionBoxPositionArray['x2'];
1396
            $y1 = $this->captionBoxPositionArray['y1'];
1397
            $y2 = $this->captionBoxPositionArray['y2'];
1398
        } else {
1399
            if ($this->debug) {
1400
                die('No caption box found.');
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
1401
            }
1402
1403
            return false;
1404
        }
1405
1406
        // *** Get text font
1407
        try {
1408
            $font = $this->getTextFont($font);
1409
        } catch (Exception $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
1410
        }
1411
1412
        // *** Get text size
1413
        $textSizeArray = $this->getTextSize($fontSize, $angle, $font, $text);
1414
        $textWidth = $textSizeArray['width'];
1415
        $textHeight = $textSizeArray['height'];
1416
1417
        // *** Find the width/height middle points
1418
        $boxXMiddle = (($x2 - $x1) / 2);
1419
        $boxYMiddle = (($y2 - $y1) / 2);
1420
1421
        // *** Box middle - half the text width/height
1422
        $xPos = ($x1 + $boxXMiddle) - ($textWidth / 2);
1423
        $yPos = ($y1 + $boxYMiddle) - ($textHeight / 2);
1424
1425
        $pos = $xPos . 'x' . $yPos;
1426
1427
        $this->addText($text, $pos, $padding = 0, $fontColor, $fontSize, $angle, $font);
1428
    }
1429
1430
    ## --------------------------------------------------------
1431
1432
    /**
1433
     * @param $side
1434
     * @param $thickness
1435
     * @param $padding
1436
     * @return array
1437
     */
1438
    private function calculateCaptionBoxPosition($side, $thickness, $padding)
1439
    {
1440
        $positionArray = [];
1441
1442
        switch ($side) {
1443
            case 't':
1444
                $positionArray['x1'] = 0;
1445
                $positionArray['y1'] = $padding;
1446
                $positionArray['x2'] = $this->width;
1447
                $positionArray['y2'] = $thickness + $padding;
1448
                break;
1449
            case 'r':
1450
                $positionArray['x1'] = $this->width - $thickness - $padding;
1451
                $positionArray['y1'] = 0;
1452
                $positionArray['x2'] = $this->width - $padding;
1453
                $positionArray['y2'] = $this->height;
1454
                break;
1455
            case 'b':
1456
                $positionArray['x1'] = 0;
1457
                $positionArray['y1'] = $this->height - $thickness - $padding;
1458
                $positionArray['x2'] = $this->width;
1459
                $positionArray['y2'] = $this->height - $padding;
1460
                break;
1461
            case 'l':
1462
                $positionArray['x1'] = $padding;
1463
                $positionArray['y1'] = 0;
1464
                $positionArray['x2'] = $thickness + $padding;
1465
                $positionArray['y2'] = $this->height;
1466
                break;
1467
            default:
1468
                break;
1469
        }
1470
1471
        return $positionArray;
1472
    }
1473
1474
    /*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*-
1475
        Get EXIF Data
1476
    *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*/
1477
1478
    /**
1479
     * @param bool $debug
1480
     * @return array
1481
     */
1482
    public function getExif($debug = true)
1483
    {
1484
        if (!$this->debug || !$debug) {
1485
            $debug = false;
1486
        }
1487
1488
        // *** Check all is good - check the EXIF library exists and the file exists, too.
1489
        if (!$this->testEXIFInstalled()) {
1490
            if ($debug) {
1491
                die('The EXIF Library is not installed.');
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
1492
            }
1493
1494
            return [];
1495
        }
1496
        if (!file_exists($this->fileName)) {
1497
            if ($debug) {
1498
                die('Image not found.');
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
1499
            }
1500
1501
            return [];
1502
        }
1503
        if ('.jpg' !== $this->fileExtension) {
1504
            if ($debug) {
1505
                die('Metadata not supported for this image type.');
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
1506
            }
1507
1508
            return [];
1509
        }
1510
        $exifData = exif_read_data($this->fileName, 'IFD0');
1511
1512
        // *** Format the apperture value
1513
        $ev = isset($exifData['ApertureValue']) ? $exifData['ApertureValue'] : '';
1514
        $apPeicesArray = explode('/', $ev);
1515
        if (2 == count($apPeicesArray)) {
1516
            $apertureValue = round($apPeicesArray[0] / $apPeicesArray[1], 2, PHP_ROUND_HALF_DOWN) . ' EV';
1517
        } else {
1518
            $apertureValue = '';
1519
        }
1520
1521
        // *** Format the focal length
1522
        $focalLength = isset($exifData['FocalLength']) ? $exifData['FocalLength'] : '';
1523
        $flPeicesArray = explode('/', $focalLength);
1524
        if (2 == count($flPeicesArray)) {
1525
            $focalLength = $flPeicesArray[0] / $flPeicesArray[1] . '.0 mm';
1526
        } else {
1527
            $focalLength = '';
1528
        }
1529
1530
        // *** Format fNumber
1531
        $fNumber = isset($exifData['FNumber']) ? $exifData['FNumber'] : '';
1532
        $fnPeicesArray = explode('/', $fNumber);
1533
        if (2 == count($fnPeicesArray)) {
1534
            $fNumber = $fnPeicesArray[0] / $fnPeicesArray[1];
1535
        } else {
1536
            $fNumber = '';
1537
        }
1538
1539
        // *** Resolve ExposureProgram
1540
        if (isset($exifData['ExposureProgram'])) {
1541
            $ep = $exifData['ExposureProgram'];
1542
        }
1543
        if (isset($ep)) {
1544
            $ep = $this->resolveExposureProgram($ep);
1545
        }
1546
1547
        // *** Resolve MeteringMode
1548
        $mm = isset($exifData['MeteringMode']) ? $exifData['MeteringMode'] : '';
1549
        $mm = $this->resolveMeteringMode($mm);
1550
1551
        // *** Resolve Flash
1552
        $flash = isset($exifData['Flash']) ? $exifData['Flash'] : '';
1553
        $flash = $this->resolveFlash($flash);
1554
1555
        if (isset($exifData['Make'])) {
1556
            $exifDataArray['make'] = $exifData['Make'];
0 ignored issues
show
Comprehensibility Best Practice introduced by
$exifDataArray was never initialized. Although not strictly required by PHP, it is generally a good practice to add $exifDataArray = array(); before regardless.
Loading history...
1557
        } else {
1558
            $exifDataArray['make'] = '';
1559
        }
1560
1561
        if (isset($exifData['Model'])) {
1562
            $exifDataArray['model'] = $exifData['Model'];
1563
        } else {
1564
            $exifDataArray['model'] = '';
1565
        }
1566
1567
        if (isset($exifData['DateTime'])) {
1568
            $exifDataArray['date'] = $exifData['DateTime'];
1569
        } else {
1570
            $exifDataArray['date'] = '';
1571
        }
1572
1573
        if (isset($exifData['ExposureTime'])) {
1574
            $exifDataArray['exposure time'] = $exifData['ExposureTime'] . ' sec.';
1575
        } else {
1576
            $exifDataArray['exposure time'] = '';
1577
        }
1578
1579
        if ('' != $apertureValue) {
1580
            $exifDataArray['aperture value'] = $apertureValue;
1581
        } else {
1582
            $exifDataArray['aperture value'] = '';
1583
        }
1584
1585
        if (isset($exifData['COMPUTED']['ApertureFNumber'])) {
1586
            $exifDataArray['f-stop'] = $exifData['COMPUTED']['ApertureFNumber'];
1587
        } else {
1588
            $exifDataArray['f-stop'] = '';
1589
        }
1590
1591
        if (isset($exifData['FNumber'])) {
1592
            $exifDataArray['fnumber'] = $exifData['FNumber'];
1593
        } else {
1594
            $exifDataArray['fnumber'] = '';
1595
        }
1596
1597
        if ('' != $fNumber) {
1598
            $exifDataArray['fnumber value'] = $fNumber;
1599
        } else {
1600
            $exifDataArray['fnumber value'] = '';
1601
        }
1602
1603
        if (isset($exifData['ISOSpeedRatings'])) {
1604
            $exifDataArray['iso'] = $exifData['ISOSpeedRatings'];
1605
        } else {
1606
            $exifDataArray['iso'] = '';
1607
        }
1608
1609
        if ('' != $focalLength) {
1610
            $exifDataArray['focal length'] = $focalLength;
1611
        } else {
1612
            $exifDataArray['focal length'] = '';
1613
        }
1614
1615
        if (isset($ep)) {
1616
            $exifDataArray['exposure program'] = $ep;
1617
        } else {
1618
            $exifDataArray['exposure program'] = '';
1619
        }
1620
1621
        if ('' != $mm) {
1622
            $exifDataArray['metering mode'] = $mm;
1623
        } else {
1624
            $exifDataArray['metering mode'] = '';
1625
        }
1626
1627
        if ('' != $flash) {
1628
            $exifDataArray['flash status'] = $flash;
1629
        } else {
1630
            $exifDataArray['flash status'] = '';
1631
        }
1632
1633
        if (isset($exifData['Artist'])) {
1634
            $exifDataArray['creator'] = $exifData['Artist'];
1635
        } else {
1636
            $exifDataArray['creator'] = '';
1637
        }
1638
1639
        if (isset($exifData['Copyright'])) {
1640
            $exifDataArray['copyright'] = $exifData['Copyright'];
1641
        } else {
1642
            $exifDataArray['copyright'] = '';
1643
        }
1644
1645
        // *** Orientation
1646
        if (isset($exifData['Orientation'])) {
1647
            $exifDataArray['orientation'] = $exifData['Orientation'];
1648
        } else {
1649
            $exifDataArray['orientation'] = '';
1650
        }
1651
1652
        return $exifDataArray;
1653
    }
1654
1655
    ## --------------------------------------------------------
1656
1657
    /**
1658
     * @param $ep
1659
     * @return string
1660
     */
1661
    private function resolveExposureProgram($ep)
1662
    {
1663
        switch ($ep) {
1664
            case 0:
1665
                $ep = '';
1666
                break;
1667
            case 1:
1668
                $ep = 'manual';
1669
                break;
1670
            case 2:
1671
                $ep = 'normal program';
1672
                break;
1673
            case 3:
1674
                $ep = 'aperture priority';
1675
                break;
1676
            case 4:
1677
                $ep = 'shutter priority';
1678
                break;
1679
            case 5:
1680
                $ep = 'creative program';
1681
                break;
1682
            case 6:
1683
                $ep = 'action program';
1684
                break;
1685
            case 7:
1686
                $ep = 'portrait mode';
1687
                break;
1688
            case 8:
1689
                $ep = 'landscape mode';
1690
                break;
1691
            default:
1692
                break;
1693
        }
1694
1695
        return $ep;
1696
    }
1697
1698
    ## --------------------------------------------------------
1699
1700
    /**
1701
     * @param $mm
1702
     * @return string
1703
     */
1704
    private function resolveMeteringMode($mm)
1705
    {
1706
        switch ($mm) {
1707
            case 0:
1708
                $mm = 'unknown';
1709
                break;
1710
            case 1:
1711
                $mm = 'average';
1712
                break;
1713
            case 2:
1714
                $mm = 'center weighted average';
1715
                break;
1716
            case 3:
1717
                $mm = 'spot';
1718
                break;
1719
            case 4:
1720
                $mm = 'multi spot';
1721
                break;
1722
            case 5:
1723
                $mm = 'pattern';
1724
                break;
1725
            case 6:
1726
                $mm = 'partial';
1727
                break;
1728
            case 255:
1729
                $mm = 'other';
1730
                break;
1731
            default:
1732
                break;
1733
        }
1734
1735
        return $mm;
1736
    }
1737
1738
    ## --------------------------------------------------------
1739
1740
    /**
1741
     * @param $flash
1742
     * @return string
1743
     */
1744
    private function resolveFlash($flash)
1745
    {
1746
        switch ($flash) {
1747
            case 0:
1748
                $flash = 'flash did not fire';
1749
                break;
1750
            case 1:
1751
                $flash = 'flash fired';
1752
                break;
1753
            case 5:
1754
                $flash = 'strobe return light not detected';
1755
                break;
1756
            case 7:
1757
                $flash = 'strobe return light detected';
1758
                break;
1759
            case 9:
1760
                $flash = 'flash fired, compulsory flash mode';
1761
                break;
1762
            case 13:
1763
                $flash = 'flash fired, compulsory flash mode, return light not detected';
1764
                break;
1765
            case 15:
1766
                $flash = 'flash fired, compulsory flash mode, return light detected';
1767
                break;
1768
            case 16:
1769
                $flash = 'flash did not fire, compulsory flash mode';
1770
                break;
1771
            case 24:
1772
                $flash = 'flash did not fire, auto mode';
1773
                break;
1774
            case 25:
1775
                $flash = 'flash fired, auto mode';
1776
                break;
1777
            case 29:
1778
                $flash = 'flash fired, auto mode, return light not detected';
1779
                break;
1780
            case 31:
1781
                $flash = 'flash fired, auto mode, return light detected';
1782
                break;
1783
            case 32:
1784
                $flash = 'no flash function';
1785
                break;
1786
            case 65:
1787
                $flash = 'flash fired, red-eye reduction mode';
1788
                break;
1789
            case 69:
1790
                $flash = 'flash fired, red-eye reduction mode, return light not detected';
1791
                break;
1792
            case 71:
1793
                $flash = 'flash fired, red-eye reduction mode, return light detected';
1794
                break;
1795
            case 73:
1796
                $flash = 'flash fired, compulsory flash mode, red-eye reduction mode';
1797
                break;
1798
            case 77:
1799
                $flash = 'flash fired, compulsory flash mode, red-eye reduction mode, return light not detected';
1800
                break;
1801
            case 79:
1802
                $flash = 'flash fired, compulsory flash mode, red-eye reduction mode, return light detected';
1803
                break;
1804
            case 89:
1805
                $flash = 'flash fired, auto mode, red-eye reduction mode';
1806
                break;
1807
            case 93:
1808
                $flash = 'flash fired, auto mode, return light not detected, red-eye reduction mode';
1809
                break;
1810
            case 95:
1811
                $flash = 'flash fired, auto mode, return light detected, red-eye reduction mode';
1812
                break;
1813
            default:
1814
                break;
1815
        }
1816
1817
        return $flash;
1818
    }
1819
1820
    /*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*-
1821
        Get IPTC Data
1822
    *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*/
1823
1824
    /*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*-
1825
        Write IPTC Data
1826
    *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*/
1827
1828
    /**
1829
     * @param $value
1830
     */
1831
    public function writeIPTCcaption($value)
1832
    {
1833
        $this->writeIPTC(120, $value);
1834
    }
1835
1836
    ## --------------------------------------------------------
1837
1838
    /**
1839
     * @param $value
1840
     */
1841
    public function writeIPTCwriter($value)
0 ignored issues
show
Unused Code introduced by
The parameter $value is not used and could be removed. ( Ignorable by Annotation )

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

1841
    public function writeIPTCwriter(/** @scrutinizer ignore-unused */ $value)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1842
    {
1843
        //$this->writeIPTC(65, $value);
1844
    }
1845
1846
    ## --------------------------------------------------------
1847
1848
    /**
1849
     * @param $dat
1850
     * @param $value
1851
     */
1852
    private function writeIPTC($dat, $value)
1853
    {
1854
        # LIMIT TO JPG
1855
1856
        $caption_block = $this->iptc_maketag(2, $dat, $value);
1857
        $image_string = iptcembed($caption_block, $this->fileName);
1858
        file_put_contents('iptc.jpg', $image_string);
1859
    }
1860
1861
    ## --------------------------------------------------------
1862
1863
    /**
1864
     * @param $rec
1865
     * @param $dat
1866
     * @param $val
1867
     * @return string
1868
     */
1869
    private function iptc_maketag($rec, $dat, $val)
1870
    {
1871
        $len = mb_strlen($val);
1872
        if ($len < 0x8000) {
1873
            return chr(0x1c) . chr($rec) . chr($dat) . chr($len >> 8) . chr($len & 0xff) . $val;
1874
        }
1875
1876
        return chr(0x1c) . chr($rec) . chr($dat) . chr(0x80) . chr(0x04) . chr(($len >> 24) & 0xff) . chr(($len >> 16) & 0xff) . chr(($len >> 8) & 0xff) . chr(($len) & 0xff) . $val;
1877
    }
1878
1879
    /*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*-
1880
        Write XMP Data
1881
    *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*/
1882
1883
    //http://xmpphptoolkit.sourceforge.net/
1884
1885
    /*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*-
1886
        Add Text
1887
    *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*/
1888
1889
    /**
1890
     * @param        $text
1891
     * @param string $pos
1892
     * @param int    $padding
1893
     * @param string $fontColor
1894
     * @param int    $fontSize
1895
     * @param int    $angle
1896
     * @param null   $font
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $font is correct as it would always require null to be passed?
Loading history...
1897
     */
1898
    public function addText($text, $pos = '20x20', $padding = 0, $fontColor = '#fff', $fontSize = 12, $angle = 0, $font = null)
1899
    {
1900
        // *** Convert color
1901
        $rgbArray = $this->formatColor($fontColor);
1902
        $r = $rgbArray['r'];
1903
        $g = $rgbArray['g'];
1904
        $b = $rgbArray['b'];
1905
1906
        // *** Get text font
1907
        try {
1908
            $font = $this->getTextFont($font);
1909
        } catch (Exception $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
1910
        }
1911
1912
        // *** Get text size
1913
        $textSizeArray = $this->getTextSize($fontSize, $angle, $font, $text);
1914
        $textWidth = $textSizeArray['width'];
1915
        $textHeight = $textSizeArray['height'];
1916
1917
        // *** Find co-ords to place text
1918
        $posArray = $this->calculatePosition($pos, $padding, $textWidth, $textHeight, false);
1919
        $x = $posArray['width'];
1920
        $y = $posArray['height'];
1921
1922
        $fontColor = imagecolorallocate($this->imageResized, $r, $g, $b);
1923
1924
        // *** Add text
1925
        imagettftext($this->imageResized, $fontSize, $angle, $x, $y, $fontColor, $font, $text);
1926
    }
1927
1928
    ## --------------------------------------------------------
1929
1930
    /**
1931
     * @param $font
1932
     * @return bool|string
1933
     */
1934
    private function getTextFont($font)
1935
    {
1936
        // *** Font path (shou
1937
        $fontPath = __DIR__ . '/' . $this->fontDir;
1938
1939
        // *** The below is/may be needed depending on your version (see ref)
1940
        putenv('GDFONTPATH=' . realpath('.'));
1941
1942
        // *** Check if the passed in font exsits...
1943
        if (null == $font || !file_exists($font)) {
1944
            // *** ...If not, default to this font.
1945
            $font = $fontPath . '/arimo.ttf';
1946
1947
            // *** Check our default font exists...
1948
            if (!file_exists($font)) {
1949
                // *** If not, return false
1950
                if ($this->debug) {
1951
                    die('Font not found');
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
1952
                }
1953
1954
                return false;
1955
            }
1956
        }
1957
1958
        return $font;
1959
    }
1960
1961
    ## --------------------------------------------------------
1962
1963
    /**
1964
     * @param $fontSize
1965
     * @param $angle
1966
     * @param $font
1967
     * @param $text
1968
     * @return array
1969
     */
1970
    private function getTextSize($fontSize, $angle, $font, $text)
1971
    {
1972
        // *** Define box (so we can get the width)
1973
        $box = @imagettfbbox($fontSize, $angle, $font, $text);
1974
1975
        // ***  Get width of text from dimensions
1976
        $textWidth = abs($box[4] - $box[0]);
1977
1978
        // ***  Get height of text from dimensions (should also be same as $fontSize)
1979
        $textHeight = abs($box[5] - $box[1]);
1980
1981
        return ['height' => $textHeight, 'width' => $textWidth];
1982
    }
1983
1984
    /*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*-
1985
        Add Watermark
1986
    *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*/
1987
1988
    /**
1989
     * @param     $watermarkImage
1990
     * @param     $pos
1991
     * @param int $padding
1992
     * @param int $opacity
1993
     */
1994
    public function addWatermark($watermarkImage, $pos, $padding = 0, $opacity = 0)
1995
    {
1996
        // Load the stamp and the photo to apply the watermark to
1997
        try {
1998
            $stamp = $this->openImage($watermarkImage);
1999
        } catch (Exception $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
2000
        }    # stamp
2001
        $im = $this->imageResized;            # photo
2002
2003
        // *** Get stamps width and height
2004
        $sx = imagesx($stamp);
2005
        $sy = imagesy($stamp);
2006
2007
        // *** Find co-ords to place image
2008
        $posArray = $this->calculatePosition($pos, $padding, $sx, $sy);
2009
        $x = $posArray['width'];
2010
        $y = $posArray['height'];
2011
2012
        // *** Set watermark opacity
2013
        if ('.png' === mb_strtolower(mb_strrchr($watermarkImage, '.'))) {
2014
            $opacity = $this->invertTransparency($opacity, 100);
2015
            $this->filterOpacity($stamp, $opacity);
2016
        }
2017
2018
        // Copy the watermark image onto our photo
2019
        imagecopy($im, $stamp, $x, $y, 0, 0, imagesx($stamp), imagesy($stamp));
2020
    }
2021
2022
    ## --------------------------------------------------------
2023
2024
    /**
2025
     * @param      $pos
2026
     * @param      $padding
2027
     * @param      $assetWidth
2028
     * @param      $assetHeight
2029
     * @param bool $upperLeft
2030
     * @return array
2031
     */
2032
    private function calculatePosition($pos, $padding, $assetWidth, $assetHeight, $upperLeft = true)
2033
    {
2034
        $pos = mb_strtolower($pos);
2035
2036
        // *** If co-ords have been entered
2037
        if (mb_strstr($pos, 'x')) {
2038
            $pos = str_replace(' ', '', $pos);
2039
2040
            $xyArray = explode('x', $pos);
2041
            list($width, $height) = $xyArray;
2042
        } else {
2043
            switch ($pos) {
2044
                case 'tl':
2045
                    $width = 0 + $padding;
2046
                    $height = 0 + $padding;
2047
                    break;
2048
                case 't':
2049
                    $width = ($this->width / 2) - ($assetWidth / 2);
2050
                    $height = 0 + $padding;
2051
                    break;
2052
                case 'tr':
2053
                    $width = $this->width - $assetWidth - $padding;
2054
                    $height = 0 + $padding;
2055
                    break;
2056
                case 'l':
2057
                    $width = 0 + $padding;
2058
                    $height = ($this->height / 2) - ($assetHeight / 2);
2059
                    break;
2060
                case 'm':
2061
                    $width = ($this->width / 2) - ($assetWidth / 2);
2062
                    $height = ($this->height / 2) - ($assetHeight / 2);
2063
                    break;
2064
                case 'r':
2065
                    $width = $this->width - $assetWidth - $padding;
2066
                    $height = ($this->height / 2) - ($assetHeight / 2);
2067
                    break;
2068
                case 'bl':
2069
                    $width = 0 + $padding;
2070
                    $height = $this->height - $assetHeight - $padding;
2071
                    break;
2072
                case 'b':
2073
                    $width = ($this->width / 2) - ($assetWidth / 2);
2074
                    $height = $this->height - $assetHeight - $padding;
2075
                    break;
2076
                case 'br':
2077
                    $width = $this->width - $assetWidth - $padding;
2078
                    $height = $this->height - $assetHeight - $padding;
2079
                    break;
2080
                default:
2081
                    $width = 0;
2082
                    $height = 0;
2083
                    break;
2084
            }
2085
        }
2086
2087
        if (!$upperLeft) {
2088
            $height = $height + $assetHeight;
2089
        }
2090
2091
        return ['width' => $width, 'height' => $height];
2092
    }
2093
2094
    ## --------------------------------------------------------
2095
2096
    /**
2097
     * @param     $img
2098
     * @param int $opacity
2099
     * @return bool
2100
     */
2101
    private function filterOpacity(&$img, $opacity = 75)
2102
    {
2103
        if (!isset($opacity)) {
2104
            return false;
2105
        }
2106
2107
        if (100 == $opacity) {
2108
            return true;
2109
        }
2110
2111
        $opacity /= 100;
2112
2113
        //get image width and height
2114
        $w = imagesx($img);
2115
        $h = imagesy($img);
2116
2117
        //turn alpha blending off
2118
        imagealphablending($img, false);
2119
2120
        //find the most opaque pixel in the image (the one with the smallest alpha value)
2121
        $minalpha = 127;
2122
        for ($x = 0; $x < $w; ++$x) {
2123
            for ($y = 0; $y < $h; ++$y) {
2124
                $alpha = (imagecolorat($img, $x, $y) >> 24) & 0xFF;
2125
                if ($alpha < $minalpha) {
2126
                    $minalpha = $alpha;
2127
                }
2128
            }
2129
        }
2130
2131
        //loop through image pixels and modify alpha for each
2132
        for ($x = 0; $x < $w; ++$x) {
2133
            for ($y = 0; $y < $h; ++$y) {
2134
                //get current alpha value (represents the TANSPARENCY!)
2135
                $colorxy = imagecolorat($img, $x, $y);
2136
                $alpha = ($colorxy >> 24) & 0xFF;
2137
                //calculate new alpha
2138
                if (127 !== $minalpha) {
2139
                    $alpha = 127 + 127 * $opacity * ($alpha - 127) / (127 - $minalpha);
2140
                } else {
2141
                    $alpha += 127 * $opacity;
2142
                }
2143
                //get the color index with new alpha
2144
                $alphacolorxy = imagecolorallocatealpha($img, ($colorxy >> 16) & 0xFF, ($colorxy >> 8) & 0xFF, $colorxy & 0xFF, $alpha);
2145
                //set pixel with the new color + opacity
2146
                if (!imagesetpixel($img, $x, $y, $alphacolorxy)) {
2147
                    return false;
2148
                }
2149
            }
2150
        }
2151
2152
        return true;
2153
    }
2154
2155
    ## --------------------------------------------------------
2156
2157
    /**
2158
     * @param $file
2159
     * @return bool|false|resource|string
2160
     */
2161
    private function openImage($file)
2162
    {
2163
        if (!file_exists($file) && !$this->checkStringStartsWith('http://', $file)) {
2164
            if ($this->debug) {
2165
                die('Image not found.');
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
2166
            }
2167
            die();
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
2168
        }
2169
2170
        // *** Get extension
2171
        $extension = mb_strrchr($file, '.');
2172
        $extension = mb_strtolower($extension);
2173
2174
        switch ($extension) {
2175
            case '.jpg':
2176
            case '.jpeg':
2177
                $img = @imagecreatefromjpeg($file);
2178
                break;
2179
            case '.gif':
2180
                $img = @imagecreatefromgif($file);
2181
                break;
2182
            case '.png':
2183
                $img = @imagecreatefrompng($file);
2184
                break;
2185
            case '.bmp':
2186
                $img = @$this->ImageCreateFromBMP($file);
2187
                break;
2188
            case '.psd':
2189
                $img = @$this->imagecreatefrompsd($file);
2190
                break;
2191
            // ... etc
2192
2193
            default:
2194
                $img = false;
2195
                break;
2196
        }
2197
2198
        return $img;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $img also could return the type GdImage which is incompatible with the documented return type boolean|resource|string.
Loading history...
2199
    }
2200
2201
    ## --------------------------------------------------------
2202
2203
    public function reset()
2204
    {
2205
        $this->__destruct();
2206
        $this->image = null;
2207
        $this->imageResized = null;
2208
        gc_collect_cycles();
2209
2210
        try {
2211
            $this->__construct($this->fileName);
2212
        } catch (Exception $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
2213
        }
2214
    }
2215
2216
    ## --------------------------------------------------------
2217
2218
    /**
2219
     * @param        $savePath
2220
     * @param string $imageQuality
2221
     */
2222
    public function saveImage($savePath, $imageQuality = '100')
2223
    {
2224
        // *** Perform a check or two.
2225
        if (!is_resource($this->imageResized)) {
2226
            if ($this->debug) {
2227
                die('saveImage: This is not a resource.');
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
2228
            }
2229
            die();
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
2230
        }
2231
        $fileInfoArray = pathinfo($savePath);
2232
        clearstatcache();
2233
        if (!is_writable($fileInfoArray['dirname'])) {
2234
            if ($this->debug) {
2235
                die('The path is not writable. Please check your permissions.');
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
2236
            }
2237
            die();
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
2238
        }
2239
2240
        // *** Get extension
2241
        $extension = mb_strrchr($savePath, '.');
2242
        $extension = mb_strtolower($extension);
2243
2244
        $error = '';
2245
2246
        switch ($extension) {
2247
            case '.jpg':
2248
            case '.jpeg':
2249
                $this->checkInterlaceImage($this->isInterlace);
2250
                if (imagetypes() & IMG_JPG) {
2251
                    imagejpeg($this->imageResized, $savePath, $imageQuality);
0 ignored issues
show
Bug introduced by
$imageQuality of type string is incompatible with the type integer expected by parameter $quality of imagejpeg(). ( Ignorable by Annotation )

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

2251
                    imagejpeg($this->imageResized, $savePath, /** @scrutinizer ignore-type */ $imageQuality);
Loading history...
2252
                } else {
2253
                    $error = 'jpg';
2254
                }
2255
                break;
2256
            case '.gif':
2257
                $this->checkInterlaceImage($this->isInterlace);
2258
                if (imagetypes() & IMG_GIF) {
2259
                    imagegif($this->imageResized, $savePath);
2260
                } else {
2261
                    $error = 'gif';
2262
                }
2263
                break;
2264
            case '.png':
2265
                // *** Scale quality from 0-100 to 0-9
2266
                $scaleQuality = round(($imageQuality / 100) * 9);
2267
2268
                // *** Invert qualit setting as 0 is best, not 9
2269
                $invertScaleQuality = 9 - $scaleQuality;
2270
2271
                $this->checkInterlaceImage($this->isInterlace);
2272
                if (imagetypes() & IMG_PNG) {
2273
                    imagepng($this->imageResized, $savePath, $invertScaleQuality);
0 ignored issues
show
Bug introduced by
$invertScaleQuality of type double is incompatible with the type integer expected by parameter $quality of imagepng(). ( Ignorable by Annotation )

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

2273
                    imagepng($this->imageResized, $savePath, /** @scrutinizer ignore-type */ $invertScaleQuality);
Loading history...
2274
                } else {
2275
                    $error = 'png';
2276
                }
2277
                break;
2278
            case '.bmp':
2279
                file_put_contents($savePath, $this->GD2BMPstring($this->imageResized));
2280
                break;
2281
            // ... etc
2282
2283
            default:
2284
                // *** No extension - No save.
2285
                $this->errorArray[] = 'This file type (' . $extension . ') is not supported. File not saved.';
2286
                break;
2287
        }
2288
2289
        //imagedestroy($this->imageResized);
2290
2291
        // *** Display error if a file type is not supported.
2292
        if ('' != $error) {
2293
            $this->errorArray[] = $error . ' support is NOT enabled. File not saved.';
2294
        }
2295
    }
2296
2297
    ## --------------------------------------------------------
2298
2299
    /**
2300
     * @param string $fileType
2301
     * @param string $imageQuality
2302
     */
2303
    public function displayImage($fileType = 'jpg', $imageQuality = '100')
2304
    {
2305
        if (!is_resource($this->imageResized)) {
2306
            if ($this->debug) {
2307
                die('saveImage: This is not a resource.');
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
2308
            }
2309
            die();
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
2310
        }
2311
2312
        switch ($fileType) {
2313
            case 'jpg':
2314
            case 'jpeg':
2315
                header('Content-type: image/jpeg');
2316
                imagejpeg($this->imageResized, '', $imageQuality);
0 ignored issues
show
Bug introduced by
$imageQuality of type string is incompatible with the type integer expected by parameter $quality of imagejpeg(). ( Ignorable by Annotation )

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

2316
                imagejpeg($this->imageResized, '', /** @scrutinizer ignore-type */ $imageQuality);
Loading history...
2317
                break;
2318
            case 'gif':
2319
                header('Content-type: image/gif');
2320
                imagegif($this->imageResized);
2321
                break;
2322
            case 'png':
2323
                header('Content-type: image/png');
2324
2325
                // *** Scale quality from 0-100 to 0-9
2326
                $scaleQuality = round(($imageQuality / 100) * 9);
2327
2328
                // *** Invert qualit setting as 0 is best, not 9
2329
                $invertScaleQuality = 9 - $scaleQuality;
2330
2331
                imagepng($this->imageResized, '', $invertScaleQuality);
0 ignored issues
show
Bug introduced by
$invertScaleQuality of type double is incompatible with the type integer expected by parameter $quality of imagepng(). ( Ignorable by Annotation )

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

2331
                imagepng($this->imageResized, '', /** @scrutinizer ignore-type */ $invertScaleQuality);
Loading history...
2332
                break;
2333
            case 'bmp':
2334
                echo 'bmp file format is not supported.';
2335
                break;
2336
            // ... etc
2337
2338
            default:
2339
                // *** No extension - No save.
2340
                break;
2341
        }
2342
        //imagedestroy($this->imageResized);
2343
    }
2344
2345
    ## --------------------------------------------------------
2346
2347
    /**
2348
     * @param $bool
2349
     */
2350
    public function setTransparency($bool)
2351
    {
2352
        $this->keepTransparency = $bool;
2353
    }
2354
2355
    ## --------------------------------------------------------
2356
2357
    /**
2358
     * @param $value
2359
     */
2360
    public function setFillColor($value)
2361
    {
2362
        $colorArray = $this->formatColor($value);
2363
        $this->fillColorArray = $colorArray;
2364
    }
2365
2366
    ## --------------------------------------------------------
2367
2368
    /**
2369
     * @param $value
2370
     */
2371
    public function setCropFromTop($value)
2372
    {
2373
        $this->cropFromTopPercent = $value;
2374
    }
2375
2376
    ## --------------------------------------------------------
2377
2378
    /**
2379
     * @return bool
2380
     */
2381
    public function testGDInstalled()
2382
    {
2383
        if (extension_loaded('gd') && function_exists('gd_info')) {
2384
            $gdInstalled = true;
2385
        } else {
2386
            $gdInstalled = false;
2387
        }
2388
2389
        return $gdInstalled;
2390
    }
2391
2392
    ## --------------------------------------------------------
2393
2394
    /**
2395
     * @return bool
2396
     */
2397
    public function testEXIFInstalled()
2398
    {
2399
        if (extension_loaded('exif')) {
2400
            $exifInstalled = true;
2401
        } else {
2402
            $exifInstalled = false;
2403
        }
2404
2405
        return $exifInstalled;
2406
    }
2407
2408
    ## --------------------------------------------------------
2409
2410
    /**
2411
     * @return bool
2412
     */
2413
    public function testIsImage()
2414
    {
2415
        $file = $this->fileName;
2416
        $isImage = false;
2417
2418
        $finfo = finfo_open(FILEINFO_MIME_TYPE);
2419
        $mimeType = finfo_file($finfo, $file);
2420
        finfo_close($finfo);
2421
2422
        switch ($mimeType) {
2423
            case 'image/jpeg':
2424
            case 'image/gif':
2425
            case 'image/png':
2426
            case 'image/bmp':
2427
            case 'image/x-windows-bmp':
2428
                $isImage = true;
2429
                break;
2430
            default:
2431
                $isImage = false;
2432
        }
2433
2434
        return $isImage;
2435
    }
2436
2437
    ## --------------------------------------------------------
2438
2439
    /**
2440
     * @return bool
2441
     */
2442
    public function getIsImage()
2443
    {
2444
        return $this->isImage;
2445
    }
2446
2447
    ## --------------------------------------------------------
2448
2449
    public function testFunct()
2450
    {
2451
        echo $this->height;
2452
    }
2453
2454
    ## --------------------------------------------------------
2455
2456
    /**
2457
     * @param $value
2458
     */
2459
    public function setForceStretch($value)
2460
    {
2461
        $this->forceStretch = $value;
2462
    }
2463
2464
    ## --------------------------------------------------------
2465
2466
    /**
2467
     * @param $fileName
2468
     */
2469
    public function setFile($fileName)
2470
    {
2471
        try {
2472
            $this->__construct($fileName);
2473
        } catch (Exception $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
2474
        }
2475
    }
2476
2477
    ## --------------------------------------------------------
2478
2479
    public function getFileName()
2480
    {
2481
        return $this->fileName;
2482
    }
2483
2484
    ## --------------------------------------------------------
2485
2486
    /**
2487
     * @return false|int
2488
     */
2489
    public function getHeight()
2490
    {
2491
        return $this->height;
2492
    }
2493
2494
    ## --------------------------------------------------------
2495
2496
    /**
2497
     * @return false|int
2498
     */
2499
    public function getWidth()
2500
    {
2501
        return $this->width;
2502
    }
2503
2504
    ## --------------------------------------------------------
2505
2506
    /**
2507
     * @return false|int
2508
     */
2509
    public function getOriginalHeight()
2510
    {
2511
        return $this->heightOriginal;
2512
    }
2513
2514
    ## --------------------------------------------------------
2515
2516
    /**
2517
     * @return false|int
2518
     */
2519
    public function getOriginalWidth()
2520
    {
2521
        return $this->widthOriginal;
2522
    }
2523
2524
    ## --------------------------------------------------------
2525
2526
    /**
2527
     * @return array
2528
     */
2529
    public function getErrors()
2530
    {
2531
        return $this->errorArray;
2532
    }
2533
2534
    ## --------------------------------------------------------
2535
2536
    /**
2537
     * @param $isEnabled
2538
     */
2539
    private function checkInterlaceImage($isEnabled)
2540
    {
2541
        if ($isEnabled) {
2542
            imageinterlace($this->imageResized, $isEnabled);
2543
        }
2544
    }
2545
2546
    ## --------------------------------------------------------
2547
2548
    /**
2549
     * @param $value
2550
     * @return array|int[]
2551
     */
2552
    protected function formatColor($value)
2553
    {
2554
        $rgbArray = [];
2555
2556
        // *** If it's an array it should be R, G, B
2557
        if (is_array($value)) {
2558
            if (0 == key($value) && 3 == count($value)) {
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing key($value) of type integer|null|string to 0; this is ambiguous as not only 0 == 0 is true, but null == 0 is true, too. Consider using a strict comparison ===.
Loading history...
2559
                $rgbArray['r'] = $value[0];
2560
                $rgbArray['g'] = $value[1];
2561
                $rgbArray['b'] = $value[2];
2562
            } else {
2563
                $rgbArray = $value;
2564
            }
2565
        } elseif ('transparent' === mb_strtolower($value)) {
2566
            $rgbArray = [
2567
                'r' => 255,
2568
                'g' => 255,
2569
                'b' => 255,
2570
                'a' => 127,
2571
            ];
2572
        } else {
2573
            // *** ...Else it should be hex. Let's make it RGB
2574
            $rgbArray = $this->hex2dec($value);
2575
        }
2576
2577
        return $rgbArray;
2578
    }
2579
2580
    ## --------------------------------------------------------
2581
2582
    /**
2583
     * @param $hex
2584
     * @return array
2585
     */
2586
    public function hex2dec($hex)
2587
    {
2588
        $color = str_replace('#', '', $hex);
2589
2590
        if (3 == mb_strlen($color)) {
2591
            $color = $color . $color;
2592
        }
2593
2594
        $rgb = [
2595
            'r' => hexdec(mb_substr($color, 0, 2)),
2596
            'g' => hexdec(mb_substr($color, 2, 2)),
2597
            'b' => hexdec(mb_substr($color, 4, 2)),
2598
            'a' => 0,
2599
        ];
2600
2601
        return $rgb;
2602
    }
2603
2604
    ## --------------------------------------------------------
2605
2606
    /**
2607
     * @param $colorArray
2608
     * @return false|int
2609
     */
2610
    private function createImageColor($colorArray)
0 ignored issues
show
Unused Code introduced by
The method createImageColor() is not used, and could be removed.

This check looks for private methods that have been defined, but are not used inside the class.

Loading history...
2611
    {
2612
        $r = $colorArray['r'];
2613
        $g = $colorArray['g'];
2614
        $b = $colorArray['b'];
2615
2616
        return imagecolorallocate($this->imageResized, $r, $g, $b);
2617
    }
2618
2619
    ## --------------------------------------------------------
2620
2621
    /**
2622
     * @param $colorArray
2623
     * @return bool
2624
     */
2625
    private function testColorExists($colorArray)
2626
    {
2627
        $r = $colorArray['r'];
2628
        $g = $colorArray['g'];
2629
        $b = $colorArray['b'];
2630
2631
        if (-1 == imagecolorexact($this->imageResized, $r, $g, $b)) {
2632
            return false;
2633
        }
2634
2635
        return true;
2636
    }
2637
2638
    ## --------------------------------------------------------
2639
2640
    /**
2641
     * @return int[]
2642
     */
2643
    private function findUnusedGreen()
2644
    {
2645
        $green = 255;
2646
2647
        do {
2648
            $greenChroma = [0, $green, 0];
2649
            $colorArray = $this->formatColor($greenChroma);
2650
            $match = $this->testColorExists($colorArray);
2651
            $green--;
2652
        } while (false == $match && $green > 0);
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
2653
2654
        // *** If no match, just bite the bullet and use green value of 255
2655
        if (!$match) {
2656
            $greenChroma = [0, $green, 0];
2657
        }
2658
2659
        return $greenChroma;
2660
    }
2661
2662
    ## --------------------------------------------------------
2663
2664
    /**
2665
     * @return int[]
2666
     */
2667
    private function findUnusedBlue()
0 ignored issues
show
Unused Code introduced by
The method findUnusedBlue() is not used, and could be removed.

This check looks for private methods that have been defined, but are not used inside the class.

Loading history...
2668
    {
2669
        $blue = 255;
2670
2671
        do {
2672
            $blueChroma = [0, 0, $blue];
2673
            $colorArray = $this->formatColor($blueChroma);
2674
            $match = $this->testColorExists($colorArray);
2675
            $blue--;
2676
        } while (false == $match && $blue > 0);
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
2677
2678
        // *** If no match, just bite the bullet and use blue value of 255
2679
        if (!$match) {
2680
            $blueChroma = [0, 0, $blue];
2681
        }
2682
2683
        return $blueChroma;
2684
    }
2685
2686
    ## --------------------------------------------------------
2687
2688
    /**
2689
     * @param      $value
2690
     * @param      $originalMax
2691
     * @param bool $invert
2692
     * @return float|int
2693
     */
2694
    private function invertTransparency($value, $originalMax, $invert = true)
2695
    {
2696
        // *** Test max range
2697
        if ($value > $originalMax) {
2698
            $value = $originalMax;
2699
        }
2700
2701
        // *** Test min range
2702
        if ($value < 0) {
2703
            $value = 0;
2704
        }
2705
2706
        if ($invert) {
2707
            return $originalMax - (($value / 100) * $originalMax);
2708
        }
2709
2710
        return ($value / 100) * $originalMax;
2711
    }
2712
2713
    ## --------------------------------------------------------
2714
2715
    /**
2716
     * @param $src
2717
     * @return mixed
2718
     */
2719
    private function transparentImage($src)
2720
    {
2721
        // *** making images with white bg transparent
2722
        $r1 = 0;
0 ignored issues
show
Unused Code introduced by
The assignment to $r1 is dead and can be removed.
Loading history...
2723
        $g1 = 255;
0 ignored issues
show
Unused Code introduced by
The assignment to $g1 is dead and can be removed.
Loading history...
2724
        $b1 = 0;
0 ignored issues
show
Unused Code introduced by
The assignment to $b1 is dead and can be removed.
Loading history...
2725
        for ($x = 0, $xMax = imagesx($src); $x < $xMax; ++$x) {
2726
            for ($y = 0, $yMax = imagesy($src); $y < $yMax; ++$y) {
2727
                $color = imagecolorat($src, $x, $y);
2728
                $r = ($color >> 16) & 0xFF;
2729
                $g = ($color >> 8) & 0xFF;
2730
                $b = $color & 0xFF;
2731
                for ($i = 0; $i < 270; ++$i) {
2732
                    //if ($r . $g . $b == ($r1 + $i) . ($g1 + $i) . ($b1 + $i)) {
2733
                    if (0 == $r && 255 == $g && 0 == $b) {
2734
                        //if ($g == 255) {
2735
                        $trans_colour = imagecolorallocatealpha($src, 0, 0, 0, 127);
2736
                        imagefill($src, $x, $y, $trans_colour);
2737
                    }
2738
                }
2739
            }
2740
        }
2741
2742
        return $src;
2743
    }
2744
2745
    ## --------------------------------------------------------
2746
2747
    /**
2748
     * @param $needle
2749
     * @param $haystack
2750
     * @return bool
2751
     */
2752
    public function checkStringStartsWith($needle, $haystack)
2753
    {
2754
        return (mb_substr($haystack, 0, mb_strlen($needle)) == $needle);
2755
    }
2756
2757
    /*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*-
2758
        BMP SUPPORT (SAVING) - James Heinrich
2759
    *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*/
2760
2761
    /**
2762
     * @param $gd_image
2763
     * @return string
2764
     */
2765
    private function GD2BMPstring($gd_image)
2766
    {
2767
        $imageX = imagesx($gd_image);
2768
        $imageY = imagesy($gd_image);
2769
2770
        $BMP = '';
2771
        for ($y = ($imageY - 1); $y >= 0; $y--) {
2772
            $thisline = '';
2773
            for ($x = 0; $x < $imageX; ++$x) {
2774
                $argb = $this->GetPixelColor($gd_image, $x, $y);
2775
                $thisline .= chr($argb['blue']) . chr($argb['green']) . chr($argb['red']);
2776
            }
2777
            while (mb_strlen($thisline) % 4) {
2778
                $thisline .= "\x00";
2779
            }
2780
            $BMP .= $thisline;
2781
        }
2782
2783
        $bmpSize = mb_strlen($BMP) + 14 + 40;
2784
        // BITMAPFILEHEADER [14 bytes] - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_62uq.asp
2785
        $BITMAPFILEHEADER = 'BM';                                    // WORD    bfType;
2786
        $BITMAPFILEHEADER .= $this->LittleEndian2String($bmpSize, 4); // DWORD   bfSize;
2787
        $BITMAPFILEHEADER .= $this->LittleEndian2String(0, 2); // WORD    bfReserved1;
2788
        $BITMAPFILEHEADER .= $this->LittleEndian2String(0, 2); // WORD    bfReserved2;
2789
        $BITMAPFILEHEADER .= $this->LittleEndian2String(54, 4); // DWORD   bfOffBits;
2790
2791
        // BITMAPINFOHEADER - [40 bytes] http://msdn.microsoft.com/library/en-us/gdi/bitmaps_1rw2.asp
2792
        $BITMAPINFOHEADER = $this->LittleEndian2String(40, 4); // DWORD  biSize;
2793
        $BITMAPINFOHEADER .= $this->LittleEndian2String($imageX, 4); // LONG   biWidth;
2794
        $BITMAPINFOHEADER .= $this->LittleEndian2String($imageY, 4); // LONG   biHeight;
2795
        $BITMAPINFOHEADER .= $this->LittleEndian2String(1, 2); // WORD   biPlanes;
2796
        $BITMAPINFOHEADER .= $this->LittleEndian2String(24, 2); // WORD   biBitCount;
2797
        $BITMAPINFOHEADER .= $this->LittleEndian2String(0, 4); // DWORD  biCompression;
2798
        $BITMAPINFOHEADER .= $this->LittleEndian2String(0, 4); // DWORD  biSizeImage;
2799
        $BITMAPINFOHEADER .= $this->LittleEndian2String(2835, 4); // LONG   biXPelsPerMeter;
2800
        $BITMAPINFOHEADER .= $this->LittleEndian2String(2835, 4); // LONG   biYPelsPerMeter;
2801
        $BITMAPINFOHEADER .= $this->LittleEndian2String(0, 4); // DWORD  biClrUsed;
2802
        $BITMAPINFOHEADER .= $this->LittleEndian2String(0, 4); // DWORD  biClrImportant;
2803
2804
        return $BITMAPFILEHEADER . $BITMAPINFOHEADER . $BMP;
2805
    }
2806
2807
    ## --------------------------------------------------------
2808
2809
    /**
2810
     * @param $img
2811
     * @param $x
2812
     * @param $y
2813
     * @return array|bool|false
2814
     */
2815
    private function GetPixelColor($img, $x, $y)
2816
    {
2817
        if (!is_resource($img)) {
2818
            return false;
2819
        }
2820
2821
        return @imagecolorsforindex($img, @imagecolorat($img, $x, $y));
2822
    }
2823
2824
    ## --------------------------------------------------------
2825
2826
    /**
2827
     * @param     $number
2828
     * @param int $minbytes
2829
     * @return string
2830
     */
2831
    private function LittleEndian2String($number, $minbytes = 1)
2832
    {
2833
        $intstring = '';
2834
        while ($number > 0) {
2835
            $intstring = $intstring . chr($number & 255);
2836
            $number >>= 8;
2837
        }
2838
2839
        return str_pad($intstring, $minbytes, "\x00", STR_PAD_RIGHT);
2840
    }
2841
2842
    /*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*-
2843
        BMP SUPPORT (READING)
2844
    *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*/
2845
2846
    /**
2847
     * @param $filename
2848
     * @return bool|false|resource
2849
     */
2850
    private function ImageCreateFromBMP($filename)
2851
    {
2852
        //Ouverture du fichier en mode binaire
2853
        if (!$f1 = fopen($filename, 'rb')) {
2854
            return false;
2855
        }
2856
2857
        //1 : Chargement des ent�tes FICHIER
2858
        $FILE = unpack('vfile_type/Vfile_size/Vreserved/Vbitmap_offset', fread($f1, 14));
2859
        if (19778 != $FILE['file_type']) {
2860
            return false;
2861
        }
2862
2863
        //2 : Chargement des ent�tes BMP
2864
        $BMP = unpack(
2865
            'Vheader_size/Vwidth/Vheight/vplanes/vbits_per_pixel' . '/Vcompression/Vsize_bitmap/Vhoriz_resolution' . '/Vvert_resolution/Vcolors_used/Vcolors_important',
2866
            fread($f1, 40)
2867
        );
2868
        $BMP['colors'] = pow(2, $BMP['bits_per_pixel']);
2869
2870
        if (0 == $BMP['size_bitmap']) {
2871
            $BMP['size_bitmap'] = $FILE['file_size'] - $FILE['bitmap_offset'];
2872
        }
2873
2874
        $BMP['bytes_per_pixel'] = $BMP['bits_per_pixel'] / 8;
2875
        $BMP['bytes_per_pixel2'] = ceil($BMP['bytes_per_pixel']);
2876
        $BMP['decal'] = ($BMP['width'] * $BMP['bytes_per_pixel'] / 4);
2877
        $BMP['decal'] -= floor($BMP['width'] * $BMP['bytes_per_pixel'] / 4);
2878
        $BMP['decal'] = 4 - (4 * $BMP['decal']);
2879
2880
        if (4 == $BMP['decal']) {
2881
            $BMP['decal'] = 0;
2882
        }
2883
2884
        //3 : Chargement des couleurs de la palette
2885
        $PALETTE = [];
2886
        if ($BMP['colors'] < 16777216) {
2887
            $PALETTE = unpack('V' . $BMP['colors'], fread($f1, $BMP['colors'] * 4));
2888
        }
2889
2890
        //4 : Cr�ation de l'image
2891
        $IMG = fread($f1, $BMP['size_bitmap']);
2892
        $VIDE = chr(0);
2893
2894
        $res = imagecreatetruecolor($BMP['width'], $BMP['height']);
2895
        $P = 0;
2896
        $Y = $BMP['height'] - 1;
2897
        while ($Y >= 0) {
2898
            $X = 0;
2899
            while ($X < $BMP['width']) {
2900
                if (24 == $BMP['bits_per_pixel']) {
2901
                    $COLOR = unpack('V', mb_substr($IMG, $P, 3) . $VIDE);
2902
                } elseif (16 == $BMP['bits_per_pixel']) {
2903
                    /*
2904
                     * BMP 16bit fix
2905
                     * =================
2906
                     *
2907
                     * Ref: http://us3.php.net/manual/en/function.imagecreate.php#81604
2908
                     *
2909
                     * Notes:
2910
                     * "don't work with bmp 16 bits_per_pixel. change pixel
2911
                     * generator for this."
2912
                     *
2913
                     */
2914
2915
                    // *** Original code (don't work)
2916
                    //$COLOR = unpack("n",substr($IMG,$P,2));
2917
                    //$COLOR[1] = $PALETTE[$COLOR[1]+1];
2918
2919
                    $COLOR = unpack('v', mb_substr($IMG, $P, 2));
2920
                    $blue = ($COLOR[1] & 0x001f) << 3;
2921
                    $green = ($COLOR[1] & 0x07e0) >> 3;
2922
                    $red = ($COLOR[1] & 0xf800) >> 8;
2923
                    $COLOR[1] = $red * 65536 + $green * 256 + $blue;
2924
                } elseif (8 == $BMP['bits_per_pixel']) {
2925
                    $COLOR = unpack('n', $VIDE . mb_substr($IMG, $P, 1));
2926
                    $COLOR[1] = $PALETTE[$COLOR[1] + 1];
2927
                } elseif (4 == $BMP['bits_per_pixel']) {
2928
                    $COLOR = unpack('n', $VIDE . mb_substr($IMG, floor($P), 1));
0 ignored issues
show
Bug introduced by
floor($P) of type double is incompatible with the type integer expected by parameter $start of mb_substr(). ( Ignorable by Annotation )

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

2928
                    $COLOR = unpack('n', $VIDE . mb_substr($IMG, /** @scrutinizer ignore-type */ floor($P), 1));
Loading history...
2929
                    if (0 == ($P * 2) % 2) {
2930
                        $COLOR[1] = ($COLOR[1] >> 4);
2931
                    } else {
2932
                        $COLOR[1] = ($COLOR[1] & 0x0F);
2933
                    }
2934
                    $COLOR[1] = $PALETTE[$COLOR[1] + 1];
2935
                } elseif (1 == $BMP['bits_per_pixel']) {
2936
                    $COLOR = unpack('n', $VIDE . mb_substr($IMG, floor($P), 1));
2937
                    if (0 == ($P * 8) % 8) {
2938
                        $COLOR[1] = $COLOR[1] >> 7;
2939
                    } elseif (1 == ($P * 8) % 8) {
2940
                        $COLOR[1] = ($COLOR[1] & 0x40) >> 6;
2941
                    } elseif (2 == ($P * 8) % 8) {
2942
                        $COLOR[1] = ($COLOR[1] & 0x20) >> 5;
2943
                    } elseif (3 == ($P * 8) % 8) {
2944
                        $COLOR[1] = ($COLOR[1] & 0x10) >> 4;
2945
                    } elseif (4 == ($P * 8) % 8) {
2946
                        $COLOR[1] = ($COLOR[1] & 0x8) >> 3;
2947
                    } elseif (5 == ($P * 8) % 8) {
2948
                        $COLOR[1] = ($COLOR[1] & 0x4) >> 2;
2949
                    } elseif (6 == ($P * 8) % 8) {
2950
                        $COLOR[1] = ($COLOR[1] & 0x2) >> 1;
2951
                    } elseif (7 == ($P * 8) % 8) {
2952
                        $COLOR[1] = ($COLOR[1] & 0x1);
2953
                    }
2954
                    $COLOR[1] = $PALETTE[$COLOR[1] + 1];
2955
                } else {
2956
                    return false;
2957
                }
2958
2959
                imagesetpixel($res, $X, $Y, $COLOR[1]);
2960
                ++$X;
2961
                $P += $BMP['bytes_per_pixel'];
2962
            }
2963
2964
            $Y--;
2965
            $P += $BMP['decal'];
2966
        }
2967
2968
        //Fermeture du fichier
2969
        fclose($f1);
2970
2971
        return $res;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $res also could return the type GdImage which is incompatible with the documented return type boolean|resource.
Loading history...
2972
    }
2973
2974
    /*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*-
2975
        PSD SUPPORT (READING)
2976
    *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*/
2977
2978
    /**
2979
     * @param $fileName
2980
     * @return bool|false|resource|string
2981
     */
2982
    private function imagecreatefrompsd($fileName)
2983
    {
2984
        if (file_exists($this->psdReaderPath)) {
2985
            include_once($this->psdReaderPath);
2986
2987
            $psdReader = new PhpPsdReader($fileName);
2988
2989
            if (isset($psdReader->infoArray['error'])) {
2990
                return '';
2991
            }
2992
2993
            return $psdReader->getImage();
0 ignored issues
show
Bug Best Practice introduced by
The expression return $psdReader->getImage() also could return the type GdImage which is incompatible with the documented return type boolean|resource|string.
Loading history...
2994
        }
2995
2996
        return false;
2997
    }
2998
2999
    ## --------------------------------------------------------
3000
3001
    public function __destruct()
3002
    {
3003
        if (is_resource($this->imageResized)) {
3004
            imagedestroy($this->imageResized);
3005
        }
3006
    }
3007
3008
    ## --------------------------------------------------------
3009
}
3010
3011
/*
3012
 *    Example with some API calls (outdated):
3013
 *
3014
 *
3015
 *      ===============================
3016
 *      Compulsary
3017
 *      ===============================
3018
 *
3019
 *      include("classes/resize_class.php");
3020
 *
3021
 *      // *** Initialise object
3022
 *      $magicianObj = new resize('images/cars/large/a.jpg');
3023
 *
3024
 *      // *** Turn off stretching (optional)
3025
 *      $magicianObj -> setForceStretch(false);
3026
 *
3027
 *      // *** Resize object
3028
 *      $magicianObj -> resizeImage(150, 100, 0);
3029
 *
3030
 *      ===============================
3031
 *      Image options - can run none, one, or all.
3032
 *      ===============================
3033
 *
3034
 *      //  *** Add watermark
3035
 *        $magicianObj -> addWatermark('stamp.png');
3036
 *
3037
 *          // *** Add text
3038
 *      $magicianObj -> addText('testing...');
3039
 *
3040
 *      ===============================
3041
 *      Output options - can run one, or the other, or both.
3042
 *      ===============================
3043
 *
3044
 *      // *** Save image to disk
3045
 *      $magicianObj -> saveImage('images/cars/large/b.jpg', 100);
3046
 *
3047
 *          // *** Or output to screen (params in can be jpg, gif, png)
3048
 *      $magicianObj -> displayImage('png');
3049
 *
3050
 *      ===============================
3051
 *      Return options - return errors. nice for debuggin.
3052
 *      ===============================
3053
 *
3054
 *      // *** Return error array
3055
 *      $errorArray = $magicianObj -> getErrors();
3056
 *
3057
 *
3058
 *      ===============================
3059
 *      Cleanup options - not really neccessary, but good practice
3060
 *      ===============================
3061
 *
3062
 *      // *** Free used memory
3063
 *      $magicianObj -> __destruct();
3064
 */
3065