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.1
14   #  Date:      10-05-11
15   #  Purpose:   Provide tools for image manipulation using GD
16   #  Param In:  See functions.
17   #  Param Out: Produces a resized image
18   #  Requires : Requires PHP GD library.
19   #  Usage Example:
20   #                     include("lib/php_image_magician.php");
21   #                     $magicianObj = new resize('images/car.jpg');
22   #                     $magicianObj -> resizeImage(150, 100, 0);
23   #                     $magicianObj -> saveImage('images/car_small.jpg', 100);
24   #
25   #        - See end of doc for more examples -
26   #
27   #  Supported file types include: jpg, png, gif, bmp, psd (read)
28   #
29   #
30   #
31   #  The following functions are taken from phpThumb() [available from
32   #    http://phpthumb.sourceforge.net], and are used with written permission
33   #  from James Heinrich.
34   #    - GD2BMPstring
35   #      - GetPixelColor
36   #      - LittleEndian2String
37   #
38   #  The following functions are from Marc Hibbins and are used with written
39   #  permission (are also under the Attribution-ShareAlike
40   #  [http://creativecommons.org/licenses/by-sa/3.0/] license.
41   #    -
42   #
43   #  PhpPsdReader is used with written permission from Tim de Koning.
44   #  [http://www.kingsquare.nl/phppsdreader]
45   #
46   #
47   #
48   #  Modificatoin history
49   #  Date      Initials  Ver Description
50   #  10-05-11  J.C.O   0.0 Initial build
51   #  01-06-11  J.C.O   0.1.1   * Added reflections
52   #              * Added Rounded corners
53   #              * You can now use PNG interlacing
54   #              * Added shadow
55   #              * Added caption box
56   #              * Added vintage filter
57   #              * Added dynamic image resizing (resize on the fly)
58   #              * minor bug fixes
59   #  05-06-11  J.C.O   0.1.1.1 * Fixed undefined variables
60   #  17-06-11  J.C.O   0.1.2   * Added image_batch_class.php class
61   #              * Minor bug fixes
62   #  26-07-11  J.C.O   0.1.4 * Added support for external images
63   #              * Can now set the crop poisition
64   #  03-08-11  J.C.O   0.1.5 * Added reset() method to reset resource to
65   #                original input file.
66   #              * Added method addTextToCaptionBox() to
67   #                simplify adding text to a caption box.
68   #              * Added experimental writeIPTC. (not finished)
69   #              * Added experimental readIPTC. (not finished)
70   #  11-08-11  J.C.O     * Added initial border presets.
71   #  30-08-11  J.C.O     * Added 'auto' crop option to crop portrait
72   #                images near the top.
73   #  08-09-11  J.C.O     * Added cropImage() method to allow standalone
74   #                cropping.
75   #  17-09-11  J.C.O     * Added setCropFromTop() set method - set the
76   #                percentage to crop from the top when using
77   #                crop 'auto' option.
78   #              * Added setTransparency() set method - allows you
79   #                to turn transparency off (like when saving
80   #                as a jpg).
81   #              * Added setFillColor() set method - set the
82   #                background color to use instead of transparency.
83   #  05-11-11  J.C.O   0.1.5.1 * Fixed interlacing option
84   #  0-07-12  J.C.O   1.0
85   #
86   #  Known issues & Limitations:
87   # -------------------------------
88   #  Not so much an issue, the image is destroyed on the deconstruct rather than
89   #  when we have finished with it. The reason for this is that we don't know
90   #  when we're finished with it as you can both save the image and display
91   #  it directly to the screen (imagedestroy($this->imageResized))
92   #
93   #  Opening BMP files is slow. A test with 884 bmp files processed in a loop
94   #  takes forever - over 5 min. This test inlcuded opening the file, then
95   #  getting and displaying its width and height.
96   #
97   #  $forceStretch:
98   # -------------------------------
99   #  On by default.
100   #  $forceStretch can be disabled by calling method setForceStretch with false
101   #  parameter. If disabled, if an images original size is smaller than the size
102   #  specified by the user, the original size will be used. This is useful when
103   #  dealing with small images.
104   #
105   #  If enabled, images smaller than the size specified will be stretched to
106   #  that size.
107   #
108   #  Tips:
109   # -------------------------------
110   #  * If you're resizing a transparent png and saving it as a jpg, set
111   #  $keepTransparency to false with: $magicianObj->setTransparency(false);
112   #
113   #  FEATURES:
114   #    * EASY TO USE
115   #    * BMP SUPPORT (read & write)
116   #    * PSD (photoshop) support (read)
117   #    * RESIZE IMAGES
118   #      - Preserve transparency (png, gif)
119   #      - Apply sharpening (jpg) (requires PHP >= 5.1.0)
120   #      - Set image quality (jpg, png)
121   #      - Resize modes:
122   #        - exact size
123   #        - resize by width (auto height)
124   #        - resize by height (auto width)
125   #        - auto (automatically determine the best of the above modes to use)
126   #        - crop - resize as best as it can then crop the rest
127   #      - Force stretching of smaller images (upscale)
128   #    * APPLY FILTERS
129   #      - Convert to grey scale
130   #      - Convert to black and white
131   #      - Convert to sepia
132   #      - Convert to negative
133   #    * ROTATE IMAGES
134   #      - Rotate using predefined "left", "right", or "180"; or any custom degree amount
135   #    * EXTRACT EXIF DATA (requires exif module)
136   #      - make
137   #      - model
138   #      - date
139   #      - exposure
140   #      - aperture
141   #      - f-stop
142   #      - iso
143   #      - focal length
144   #      - exposure program
145   #      - metering mode
146   #      - flash status
147   #      - creator
148   #      - copyright
149   #    * ADD WATERMARK
150   #      - Specify exact x, y placement
151   #      - Or, specify using one of the 9 pre-defined placements such as "tl"
152   #        (for top left), "m" (for middle), "br" (for bottom right)
153   #        - also specify padding from edge amount (optional).
154   #      - Set opacity of watermark (png).
155   #    * ADD BORDER
156   #    * USE HEX WHEN SPECIFYING COLORS (eg: #ffffff)
157   #    * SAVE IMAGE OR OUTPUT TO SCREEN
158   #
159   #
160   # ========================================================================#
161
162class imageLib
163{
164    private $fileName;
165    private $image;
166    protected $imageResized;
167    private $widthOriginal;     # Always be the original width
168    private $heightOriginal;
169    private $width;         # Current width (width after resize)
170    private $height;
171    private $imageSize;
172    private $fileExtension;
173
174    private $debug = true;
175    private $errorArray = array();
176
177    private $forceStretch = true;
178    private $aggresiveSharpening = false;
179
180    private $transparentArray = array('.png', '.gif');
181    private $keepTransparency = true;
182    private $fillColorArray = array('r'=>255, 'g'=>255, 'b'=>255);
183
184    private $sharpenArray = array('jpg');
185
186    private $psdReaderPath;
187    private $filterOverlayPath;
188
189    private $isInterlace;
190
191    private $captionBoxPositionArray = array();
192
193    private $fontDir = 'fonts';
194
195    private $cropFromTopPercent = 10;
196
197## --------------------------------------------------------
198
199    public function __construct($fileName)
200    # Author:     Jarrod Oberto
201  # Date:     27-02-08
202    # Purpose:    Constructor
203    # Param in:   $fileName: File name and path.
204    # Param out:  n/a
205    # Reference:
206    # Notes:
207    #
208    {
209        if (!$this->testGDInstalled()) {
210            if ($this->debug) {
211                throw new Exception('The GD Library is not installed.');
212            } else {
213                throw new Exception();
214            }
215        }
216
217        $this->initialise();
218
219        // *** Save the image file name. Only store this incase you want to display it
220        $this->fileName = $fileName;
221        $this->fileExtension = fix_strtolower(strrchr($fileName, '.'));
222
223        // *** Open up the file
224        $this->image = $this->openImage($fileName);
225
226    // *** Assign here so we don't modify the original
227    $this->imageResized = $this->image;
228
229        // *** If file is an image
230        if ($this->testIsImage($this->image)) {
231            // *** Get width and height
232            $this->width  = imagesx($this->image);
233            $this->widthOriginal = imagesx($this->image);
234            $this->height = imagesy($this->image);
235            $this->heightOriginal = imagesy($this->image);
236
237        /*  Added 15-09-08
238         *  Get the filesize using this build in method.
239         *  Stores an array of size
240         *
241         *  $this->imageSize[1] = width
242         *  $this->imageSize[2] = height
243         *  $this->imageSize[3] = width x height
244         *
245         */
246            $this->imageSize = getimagesize($this->fileName);
247        } else {
248            $this->errorArray[] = 'File is not an image';
249        }
250    }
251
252## --------------------------------------------------------
253
254  private function initialise()
255  {
256      $this->psdReaderPath = dirname(__FILE__) . '/classPhpPsdReader.php';
257      $this->filterOverlayPath = dirname(__FILE__) . '/filters';
258
259    // *** Set if image should be interlaced or not.
260    $this->isInterlace = false;
261  }
262
263/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*-
264  Resize
265*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*/
266
267    public function resizeImage($newWidth, $newHeight, $option = 0, $sharpen = false, $autoRotate = false)
268    # Author:     Jarrod Oberto
269    # Date:       27-02-08
270    # Purpose:    Resizes the image
271    # Param in:   $newWidth:
272    #             $newHeight:
273    #             $option:     0 / exact = defined size;
274    #                          1 / portrait = keep aspect set height;
275    #                          2 / landscape = keep aspect set width;
276    #                          3 / auto = auto;
277  #                          4 / crop= resize and crop;
278  #
279  #         $option can also be an array containing options for
280  #         cropping. E.G., array('crop', 'r')
281  #
282  #         This array only applies to 'crop' and the 'r' refers to
283  #         "crop right". Other value include; tl, t, tr, l, m (default),
284  #         r, bl, b, br, or you can specify your own co-ords (which
285  #         isn't recommended.
286  #
287  #       $sharpen:    true: sharpen (jpg only);
288  #                false: don't sharpen
289    # Param out:  n/a
290    # Reference:
291    # Notes:      To clarify the $option input:
292    #               0 = The exact height and width dimensions you set.
293    #               1 = Whatever height is passed in will be the height that
294    #                   is set. The width will be calculated and set automatically
295    #                   to a the value that keeps the original aspect ratio.
296    #               2 = The same but based on the width. We try make the image the
297  #                  biggest size we can while stil fitting inside the box size
298    #               3 = Depending whether the image is landscape or portrait, this
299    #                   will automatically determine whether to resize via
300    #                   dimension 1,2 or 0
301  #               4 = Will resize and then crop the image for best fit
302  #
303  #       forceStretch can be applied to options 1,2,3 and 4
304    #
305    {
306
307    // *** We can pass in an array of options to change the crop position
308    $cropPos = 'm';
309        if (is_array($option) && fix_strtolower($option[0]) == 'crop') {
310            $cropPos = $option[1];         # get the crop option
311        } elseif (strpos($option, '-') !== false) {
312            // *** Or pass in a hyphen seperated option
313      $optionPiecesArray = explode('-', $option);
314            $cropPos = end($optionPiecesArray);
315        }
316
317    // *** Check the option is valid
318    $option = $this->prepOption($option);
319
320    // *** Make sure the file passed in is valid
321    if (!$this->image) {
322        if ($this->debug) {
323            throw new Exception('file ' . $this->getFileName() .' is missing or invalid');
324        } else {
325            throw new Exception();
326        }
327    }
328
329    // *** Get optimal width and height - based on $option
330    $dimensionsArray = $this->getDimensions($newWidth, $newHeight, $option);
331
332        $optimalWidth  = $dimensionsArray['optimalWidth'];
333        $optimalHeight = $dimensionsArray['optimalHeight'];
334
335    // *** Resample - create image canvas of x, y size
336    $this->imageResized = imagecreatetruecolor($optimalWidth, $optimalHeight);
337        $this->keepTransparancy($optimalWidth, $optimalHeight, $this->imageResized);
338        imagecopyresampled($this->imageResized, $this->image, 0, 0, 0, 0, $optimalWidth, $optimalHeight, $this->width, $this->height);
339
340    // *** If '4', then crop too
341    if ($option == 4 || $option == 'crop') {
342        if (($optimalWidth >= $newWidth && $optimalHeight >= $newHeight)) {
343            $this->crop($optimalWidth, $optimalHeight, $newWidth, $newHeight, $cropPos);
344        }
345    }
346
347    // *** If Rotate.
348    if ($autoRotate) {
349        $exifData = $this->getExif(false);
350        if (count($exifData) > 0) {
351            switch ($exifData['orientation']) {
352            case 8:
353                $this->imageResized = imagerotate($this->imageResized, 90, 0);
354
355                break;
356            case 3:
357                $this->imageResized = imagerotate($this->imageResized, 180, 0);
358
359                break;
360            case 6:
361                $this->imageResized = imagerotate($this->imageResized, -90, 0);
362
363                break;
364        }
365        }
366    }
367
368    // *** Sharpen image (if jpg and the user wishes to do so)
369    if ($sharpen && in_array($this->fileExtension, $this->sharpenArray)) {
370
371      // *** Sharpen
372      $this->sharpen();
373    }
374    }
375
376## --------------------------------------------------------
377
378  public function cropImage($newWidth, $newHeight, $cropPos = 'm')
379    # Author:     Jarrod Oberto
380    # Date:       08-09-11
381    # Purpose:    Crops the image
382    # Param in:   $newWidth: crop with
383    #             $newHeight: crop height
384  #       $cropPos: Can be any of the following:
385  #             tl, t, tr, l, m, r, bl, b, br, auto
386  #           Or:
387  #             a custom position such as '30x50'
388    # Param out:  n/a
389    # Reference:
390    # Notes:
391    #
392  {
393
394    // *** Make sure the file passed in is valid
395    if (!$this->image) {
396        if ($this->debug) {
397            throw new Exception('file ' . $this->getFileName() .' is missing or invalid');
398        } else {
399            throw new Exception();
400        }
401    }
402
403      $this->imageResized = $this->image;
404      $this->crop($this->width, $this->height, $newWidth, $newHeight, $cropPos);
405  }
406
407## --------------------------------------------------------
408
409  private function keepTransparancy($width, $height, $im)
410    # Author:     Jarrod Oberto
411    # Date:       08-04-11
412    # Purpose:    Keep transparency for png and gif image
413    # Param in:
414    # Param out:  n/a
415    # Reference:
416    # Notes:
417    #
418  {
419      // *** If PNG, perform some transparency retention actions (gif untested)
420    if (in_array($this->fileExtension, $this->transparentArray) && $this->keepTransparency) {
421        imagealphablending($im, false);
422        imagesavealpha($im, true);
423        $transparent = imagecolorallocatealpha($im, 255, 255, 255, 127);
424        imagefilledrectangle($im, 0, 0, $width, $height, $transparent);
425    } else {
426        $color = imagecolorallocate($im, $this->fillColorArray['r'], $this->fillColorArray['g'], $this->fillColorArray['b']);
427        imagefilledrectangle($im, 0, 0, $width, $height, $color);
428    }
429  }
430
431## --------------------------------------------------------
432
433    private function crop($optimalWidth, $optimalHeight, $newWidth, $newHeight, $cropPos)
434    # Author:     Jarrod Oberto
435    # Date:       15-09-08
436    # Purpose:    Crops the image
437    # Param in:   $newWidth:
438    #             $newHeight:
439    # Param out:  n/a
440    # Reference:
441    # Notes:
442    #
443    {
444
445    // *** Get cropping co-ordinates
446    $cropArray = $this->getCropPlacing($optimalWidth, $optimalHeight, $newWidth, $newHeight, $cropPos);
447        $cropStartX = $cropArray['x'];
448        $cropStartY = $cropArray['y'];
449
450    // *** Crop this bad boy
451    $crop = imagecreatetruecolor($newWidth, $newHeight);
452        $this->keepTransparancy($optimalWidth, $optimalHeight, $crop);
453        imagecopyresampled($crop, $this->imageResized, 0, 0, $cropStartX, $cropStartY, $newWidth, $newHeight, $newWidth, $newHeight);
454
455        $this->imageResized = $crop;
456
457    // *** Set new width and height to our variables
458    $this->width = $newWidth;
459        $this->height = $newHeight;
460    }
461
462## --------------------------------------------------------
463
464  private function getCropPlacing($optimalWidth, $optimalHeight, $newWidth, $newHeight, $pos='m')
465  #
466  # Author:   Jarrod Oberto
467  # Date:   July 11
468  # Purpose:  Set the cropping area.
469  # Params in:
470  # Params out: (array) the crop x and y co-ordinates.
471  # Notes:    When specifying the exact pixel crop position (eg 10x15), be
472  #       very careful as it's easy to crop out of the image leaving
473  #       black borders.
474  #
475  {
476      $pos = fix_strtolower($pos);
477
478    // *** If co-ords have been entered
479    if (strstr($pos, 'x')) {
480        $pos = str_replace(' ', '', $pos);
481
482        $xyArray = explode('x', $pos);
483        list($cropStartX, $cropStartY) = $xyArray;
484    } else {
485        switch ($pos) {
486        case 'tl':
487          $cropStartX = 0;
488          $cropStartY = 0;
489
490          break;
491
492        case 't':
493          $cropStartX = ($optimalWidth / 2) - ($newWidth /2);
494          $cropStartY = 0;
495
496          break;
497
498        case 'tr':
499          $cropStartX = $optimalWidth - $newWidth;
500          $cropStartY = 0;
501
502          break;
503
504        case 'l':
505          $cropStartX = 0;
506          $cropStartY = ($optimalHeight/ 2) - ($newHeight/2);
507
508          break;
509
510        case 'm':
511          $cropStartX = ($optimalWidth / 2) - ($newWidth /2);
512          $cropStartY = ($optimalHeight/ 2) - ($newHeight/2);
513
514          break;
515
516        case 'r':
517          $cropStartX = $optimalWidth - $newWidth;
518          $cropStartY = ($optimalHeight/ 2) - ($newHeight/2);
519
520          break;
521
522        case 'bl':
523          $cropStartX = 0;
524          $cropStartY = $optimalHeight - $newHeight;
525
526          break;
527
528        case 'b':
529          $cropStartX = ($optimalWidth / 2) - ($newWidth /2);
530          $cropStartY = $optimalHeight - $newHeight;
531
532          break;
533
534        case 'br':
535          $cropStartX = $optimalWidth - $newWidth;
536          $cropStartY = $optimalHeight - $newHeight;
537
538          break;
539
540        case 'auto':
541          // *** If image is a portrait crop from top, not center. v1.5
542          if ($optimalHeight > $optimalWidth) {
543              $cropStartX = ($optimalWidth / 2) - ($newWidth /2);
544              $cropStartY = ($this->cropFromTopPercent /100) * $optimalHeight;
545          } else {
546
547            // *** Else crop from the center
548            $cropStartX = ($optimalWidth / 2) - ($newWidth /2);
549              $cropStartY = ($optimalHeight/ 2) - ($newHeight/2);
550          }
551
552          break;
553
554        default:
555          // *** Default to center
556          $cropStartX = ($optimalWidth / 2) - ($newWidth /2);
557          $cropStartY = ($optimalHeight/ 2) - ($newHeight/2);
558
559          break;
560      }
561    }
562
563      return array('x' => $cropStartX, 'y' => $cropStartY);
564  }
565
566## --------------------------------------------------------
567
568  private function getDimensions($newWidth, $newHeight, $option)
569    # Author:     Jarrod Oberto
570    # Date:       17-11-09
571    # Purpose:    Get new image dimensions based on user specificaions
572    # Param in:   $newWidth:
573    #             $newHeight:
574    # Param out:  Array of new width and height values
575    # Reference:
576    # Notes:    If $option = 3 then this function is call recursivly
577  #
578  #       To clarify the $option input:
579    #               0 = The exact height and width dimensions you set.
580    #               1 = Whatever height is passed in will be the height that
581    #                   is set. The width will be calculated and set automatically
582    #                   to a the value that keeps the original aspect ratio.
583    #               2 = The same but based on the width.
584    #               3 = Depending whether the image is landscape or portrait, this
585    #                   will automatically determine whether to resize via
586    #                   dimension 1,2 or 0.
587  #               4 = Resize the image as much as possible, then crop the
588  #         remainder.
589  {
590      switch ((string) $option) {
591            case '0':
592      case 'exact':
593                $optimalWidth = $newWidth;
594                $optimalHeight= $newHeight;
595
596                break;
597            case '1':
598      case 'portrait':
599                $dimensionsArray = $this->getSizeByFixedHeight($newWidth, $newHeight);
600        $optimalWidth = $dimensionsArray['optimalWidth'];
601        $optimalHeight = $dimensionsArray['optimalHeight'];
602
603                break;
604            case '2':
605      case 'landscape':
606                $dimensionsArray = $this->getSizeByFixedWidth($newWidth, $newHeight);
607        $optimalWidth = $dimensionsArray['optimalWidth'];
608        $optimalHeight = $dimensionsArray['optimalHeight'];
609
610                break;
611            case '3':
612      case 'auto':
613                $dimensionsArray = $this->getSizeByAuto($newWidth, $newHeight);
614        $optimalWidth = $dimensionsArray['optimalWidth'];
615        $optimalHeight = $dimensionsArray['optimalHeight'];
616
617                break;
618      case '4':
619      case 'crop':
620                $dimensionsArray = $this->getOptimalCrop($newWidth, $newHeight);
621        $optimalWidth = $dimensionsArray['optimalWidth'];
622        $optimalHeight = $dimensionsArray['optimalHeight'];
623
624                break;
625        }
626
627      return array('optimalWidth' => $optimalWidth, 'optimalHeight' => $optimalHeight);
628  }
629
630## --------------------------------------------------------
631
632    private function getSizeByFixedHeight($newWidth, $newHeight)
633    {
634        // *** If forcing is off...
635    if (!$this->forceStretch) {
636
637      // *** ...check if actual height is less than target height
638      if ($this->height < $newHeight) {
639          return array('optimalWidth' => $this->width, 'optimalHeight' => $this->height);
640      }
641    }
642
643        $ratio = $this->width / $this->height;
644
645        $newWidth = $newHeight * $ratio;
646
647        //return $newWidth;
648    return array('optimalWidth' => $newWidth, 'optimalHeight' => $newHeight);
649    }
650
651## --------------------------------------------------------
652
653    private function getSizeByFixedWidth($newWidth, $newHeight)
654    {
655        // *** If forcing is off...
656    if (!$this->forceStretch) {
657
658      // *** ...check if actual width is less than target width
659      if ($this->width < $newWidth) {
660          return array('optimalWidth' => $this->width, 'optimalHeight' => $this->height);
661      }
662    }
663
664        $ratio = $this->height / $this->width;
665
666        $newHeight = $newWidth * $ratio;
667
668        //return $newHeight;
669    return array('optimalWidth' => $newWidth, 'optimalHeight' => $newHeight);
670    }
671
672## --------------------------------------------------------
673
674    private function getSizeByAuto($newWidth, $newHeight)
675    # Author:     Jarrod Oberto
676    # Date:       19-08-08
677    # Purpose:    Depending on the height, choose to resize by 0, 1, or 2
678    # Param in:   The new height and new width
679    # Notes:
680    #
681    {
682        // *** If forcing is off...
683    if (!$this->forceStretch) {
684
685      // *** ...check if actual size is less than target size
686      if ($this->width < $newWidth && $this->height < $newHeight) {
687          return array('optimalWidth' => $this->width, 'optimalHeight' => $this->height);
688      }
689    }
690
691        if ($this->height < $this->width) {
692            // *** Image to be resized is wider (landscape)
693
694            //$optimalWidth = $newWidth;
695            //$optimalHeight= $this->getSizeByFixedWidth($newWidth);
696
697            $dimensionsArray = $this->getSizeByFixedWidth($newWidth, $newHeight);
698            $optimalWidth = $dimensionsArray['optimalWidth'];
699            $optimalHeight = $dimensionsArray['optimalHeight'];
700        } elseif ($this->height > $this->width) {
701            // *** Image to be resized is taller (portrait)
702
703            //$optimalWidth = $this->getSizeByFixedHeight($newHeight);
704            //$optimalHeight= $newHeight;
705
706            $dimensionsArray = $this->getSizeByFixedHeight($newWidth, $newHeight);
707            $optimalWidth = $dimensionsArray['optimalWidth'];
708            $optimalHeight = $dimensionsArray['optimalHeight'];
709        } else {
710            // *** Image to be resizerd is a square
711
712      if ($newHeight < $newWidth) {
713          //$optimalWidth = $newWidth;
714        //$optimalHeight= $this->getSizeByFixedWidth($newWidth);
715                $dimensionsArray = $this->getSizeByFixedWidth($newWidth, $newHeight);
716          $optimalWidth = $dimensionsArray['optimalWidth'];
717          $optimalHeight = $dimensionsArray['optimalHeight'];
718      } elseif ($newHeight > $newWidth) {
719          //$optimalWidth = $this->getSizeByFixedHeight($newHeight);
720            //$optimalHeight= $newHeight;
721                $dimensionsArray = $this->getSizeByFixedHeight($newWidth, $newHeight);
722          $optimalWidth = $dimensionsArray['optimalWidth'];
723          $optimalHeight = $dimensionsArray['optimalHeight'];
724      } else {
725          // *** Sqaure being resized to a square
726        $optimalWidth = $newWidth;
727          $optimalHeight= $newHeight;
728      }
729        }
730
731        return array('optimalWidth' => $optimalWidth, 'optimalHeight' => $optimalHeight);
732    }
733
734## --------------------------------------------------------
735
736    private function getOptimalCrop($newWidth, $newHeight)
737  # Author:     Jarrod Oberto
738    # Date:       17-11-09
739    # Purpose:    Get optimal crop dimensions
740    # Param in:   width and height as requested by user (fig 3)
741    # Param out:  Array of optimal width and height (fig 2)
742    # Reference:
743    # Notes:      The optimal width and height return are not the same as the
744  #       same as the width and height passed in. For example:
745  #
746  #
747  #   |-----------------|     |------------|       |-------|
748  #   |             |   =>  |**|      |**|   =>  |       |
749  #   |             |     |**|      |**|       |       |
750    #   |           |       |------------|       |-------|
751  #   |-----------------|
752  #        original                optimal             crop
753  #              size                   size               size
754  #  Fig          1                      2                  3
755  #
756  #       300 x 250           150 x 125          150 x 100
757  #
758  #    The optimal size is the smallest size (that is closest to the crop size)
759  #    while retaining proportion/ratio.
760  #
761  #  The crop size is the optimal size that has been cropped on one axis to
762  #  make the image the exact size specified by the user.
763  #
764  #               * represent cropped area
765  #
766    {
767
768    // *** If forcing is off...
769    if (!$this->forceStretch) {
770
771      // *** ...check if actual size is less than target size
772      if ($this->width < $newWidth && $this->height < $newHeight) {
773          return array('optimalWidth' => $this->width, 'optimalHeight' => $this->height);
774      }
775    }
776
777        $heightRatio = $this->height / $newHeight;
778        $widthRatio  = $this->width /  $newWidth;
779
780        if ($heightRatio < $widthRatio) {
781            $optimalRatio = $heightRatio;
782        } else {
783            $optimalRatio = $widthRatio;
784        }
785
786        $optimalHeight = round($this->height / $optimalRatio);
787        $optimalWidth  = round($this->width  / $optimalRatio);
788
789        return array('optimalWidth' => $optimalWidth, 'optimalHeight' => $optimalHeight);
790    }
791
792## --------------------------------------------------------
793
794  private function sharpen()
795    # Author:     Jarrod Oberto
796    # Date:       08 04 2011
797    # Purpose:    Sharpen image
798    # Param in:   n/a
799    # Param out:  n/a
800    # Reference:
801    # Notes:
802    # Credit:   Incorporates Joe Lencioni (August 6, 2008) code
803  {
804      if (version_compare(PHP_VERSION, '5.1.0') >= 0) {
805
806      // ***
807      if ($this->aggresiveSharpening) { # A more aggressive sharpening solution
808
809        $sharpenMatrix = array(array(-1, -1, -1),
810            array(-1, 16, -1),
811            array(-1, -1, -1),
812        );
813          $divisor = 8;
814          $offset = 0;
815
816          imageconvolution($this->imageResized, $sharpenMatrix, $divisor, $offset);
817      } else {
818          # More subtle and personally more desirable
819
820        $sharpness  = $this->findSharp($this->widthOriginal, $this->width);
821
822          $sharpenMatrix  = array(
823              array(-1, -2, -1),
824              array(-2, $sharpness + 12, -2), //Lessen the effect of a filter by increasing the value in the center cell
825              array(-1, -2, -1),
826          );
827          $divisor    = $sharpness; // adjusts brightness
828        $offset     = 0;
829          imageconvolution($this->imageResized, $sharpenMatrix, $divisor, $offset);
830      }
831      } else {
832          if ($this->debug) {
833              throw new Exception('Sharpening required PHP 5.1.0 or greater.');
834          }
835      }
836  }
837
838  ## --------------------------------------------------------
839
840  private function sharpen2($level)
841  {
842      $sharpenMatrix  = array(
843          array($level, $level, $level),
844          array($level, (8*$level)+1, $level), //Lessen the effect of a filter by increasing the value in the center cell
845          array($level, $level, $level),
846      );
847  }
848
849## --------------------------------------------------------
850
851  private function findSharp($orig, $final)
852    # Author:     Ryan Rud (http://adryrun.com)
853    # Purpose:    Find optimal sharpness
854    # Param in:   n/a
855    # Param out:  n/a
856    # Reference:
857    # Notes:
858    #
859  {
860      $final  = $final * (750.0 / $orig);
861      $a    = 52;
862      $b    = -0.27810650887573124;
863      $c    = .00047337278106508946;
864
865      $result = $a + $b * $final + $c * $final * $final;
866
867      return max(round($result), 0);
868  }
869
870## --------------------------------------------------------
871
872  private function prepOption($option)
873    # Author:     Jarrod Oberto
874    # Purpose:    Prep option like change the passed in option to lowercase
875    # Param in:   (str/int) $option: eg. 'exact', 'crop'. 0, 4
876    # Param out:  lowercase string
877    # Reference:
878    # Notes:
879    #
880  {
881      if (is_array($option)) {
882          if (fix_strtolower($option[0]) == 'crop' && count($option) == 2) {
883              return 'crop';
884          } else {
885              throw new Exception('Crop resize option array is badly formatted.');
886          }
887      } elseif (strpos($option, 'crop') !== false) {
888          return 'crop';
889      }
890
891      if (is_string($option)) {
892          return fix_strtolower($option);
893      }
894
895      return $option;
896  }
897
898/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*-
899  Presets
900*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*/
901
902#
903# Preset are pre-defined templates you can apply to your image.
904#
905# These are inteded to be applied to thumbnail images.
906#
907
908  public function borderPreset($preset)
909  {
910      switch ($preset) {
911
912      case 'simple':
913        $this->addBorder(7, '#fff');
914        $this->addBorder(6, '#f2f1f0');
915        $this->addBorder(2, '#fff');
916        $this->addBorder(1, '#ccc');
917
918        break;
919      default:
920        break;
921    }
922  }
923
924/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*-
925  Draw border
926*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*/
927
928  public function addBorder($thickness = 1, $rgbArray = array(255, 255, 255))
929    # Author:     Jarrod Oberto
930    # Date:       05-05-11
931    # Purpose:    Add a border to the image
932    # Param in:
933    # Param out:
934    # Reference:
935    # Notes:    This border is added to the INSIDE of the image
936    #
937  {
938      if ($this->imageResized) {
939          $rgbArray = $this->formatColor($rgbArray);
940          $r = $rgbArray['r'];
941          $g = $rgbArray['g'];
942          $b = $rgbArray['b'];
943
944          $x1 = 0;
945          $y1 = 0;
946          $x2 = imagesx($this->imageResized) - 1;
947          $y2 = imagesy($this->imageResized) - 1;
948
949          $rgbArray = imagecolorallocate($this->imageResized, $r, $g, $b);
950
951          for ($i = 0; $i < $thickness; $i++) {
952              imagerectangle($this->imageResized, $x1++, $y1++, $x2--, $y2--, $rgbArray);
953          }
954      }
955  }
956
957/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*-
958  Gray Scale
959*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*/
960
961  public function greyScale()
962    # Author:     Jarrod Oberto
963    # Date:       07-05-2011
964    # Purpose:    Make image greyscale
965    # Param in:   n/a
966    # Param out:
967    # Reference:
968    # Notes:
969    #
970  {
971      if ($this->imageResized) {
972          imagefilter($this->imageResized, IMG_FILTER_GRAYSCALE);
973      }
974  }
975
976  ## --------------------------------------------------------
977
978  public function greyScaleEnhanced()
979    # Author:     Jarrod Oberto
980    # Date:       07-05-2011
981    # Purpose:    Make image greyscale
982    # Param in:   n/a
983    # Param out:
984    # Reference:
985    # Notes:
986    #
987  {
988      if ($this->imageResized) {
989          imagefilter($this->imageResized, IMG_FILTER_GRAYSCALE);
990          imagefilter($this->imageResized, IMG_FILTER_CONTRAST, -15);
991          imagefilter($this->imageResized, IMG_FILTER_BRIGHTNESS, 2);
992          $this->sharpen($this->width);
993      }
994  }
995
996  ## --------------------------------------------------------
997
998  public function greyScaleDramatic()
999  # Alias of gd_filter_monopin
1000  {
1001      $this->gd_filter_monopin();
1002  }
1003
1004/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*-
1005  Black 'n White
1006*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*/
1007
1008  public function blackAndWhite()
1009    # Author:     Jarrod Oberto
1010    # Date:       07-05-2011
1011    # Purpose:    Make image black and white
1012    # Param in:   n/a
1013    # Param out:
1014    # Reference:
1015    # Notes:
1016    #
1017  {
1018      if ($this->imageResized) {
1019          imagefilter($this->imageResized, IMG_FILTER_GRAYSCALE);
1020          imagefilter($this->imageResized, IMG_FILTER_CONTRAST, -1000);
1021      }
1022  }
1023
1024/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*-
1025  Negative
1026*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*/
1027
1028  public function negative()
1029    # Author:     Jarrod Oberto
1030    # Date:       07-05-2011
1031    # Purpose:    Make image negative
1032    # Param in:   n/a
1033    # Param out:
1034    # Reference:
1035    # Notes:
1036    #
1037  {
1038      if ($this->imageResized) {
1039          imagefilter($this->imageResized, IMG_FILTER_NEGATE);
1040      }
1041  }
1042
1043/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*-
1044  Sepia
1045*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*/
1046
1047  public function sepia()
1048    # Author:     Jarrod Oberto
1049    # Date:       07-05-2011
1050    # Purpose:    Make image sepia
1051    # Param in:   n/a
1052    # Param out:
1053    # Reference:
1054    # Notes:
1055    #
1056  {
1057      if ($this->imageResized) {
1058          imagefilter($this->imageResized, IMG_FILTER_GRAYSCALE);
1059          imagefilter($this->imageResized, IMG_FILTER_BRIGHTNESS, -10);
1060          imagefilter($this->imageResized, IMG_FILTER_CONTRAST, -20);
1061          imagefilter($this->imageResized, IMG_FILTER_COLORIZE, 60, 30, -15);
1062      }
1063  }
1064
1065  ## --------------------------------------------------------
1066
1067  public function sepia2()
1068  {
1069      if ($this->imageResized) {
1070          $total = imagecolorstotal($this->imageResized);
1071          for ($i = 0; $i < $total; $i++) {
1072              $index = imagecolorsforindex($this->imageResized, $i);
1073              $red = ($index["red"] * 0.393 + $index["green"] * 0.769 + $index["blue"] * 0.189) / 1.351;
1074              $green = ($index["red"] * 0.349 + $index["green"] * 0.686 + $index["blue"] * 0.168) / 1.203;
1075              $blue = ($index["red"] * 0.272 + $index["green"] * 0.534 + $index["blue"] * 0.131) / 2.140;
1076              imagecolorset($this->imageResized, $i, $red, $green, $blue);
1077          }
1078      }
1079  }
1080
1081/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*-
1082  Vintage
1083*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*/
1084
1085  public function vintage()
1086  # Alias of gd_filter_monopin
1087  {
1088      $this->gd_filter_vintage();
1089  }
1090
1091/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*-
1092  Presets By Marc Hibbins
1093*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*/
1094
1095  /** Apply 'Monopin' preset */
1096  public function gd_filter_monopin()
1097  {
1098      if ($this->imageResized) {
1099          imagefilter($this->imageResized, IMG_FILTER_GRAYSCALE);
1100          imagefilter($this->imageResized, IMG_FILTER_BRIGHTNESS, -15);
1101          imagefilter($this->imageResized, IMG_FILTER_CONTRAST, -15);
1102          $this->imageResized = $this->gd_apply_overlay($this->imageResized, 'vignette', 100);
1103      }
1104  }
1105
1106  ## --------------------------------------------------------
1107
1108  public function gd_filter_vintage()
1109  {
1110      if ($this->imageResized) {
1111          $this->imageResized = $this->gd_apply_overlay($this->imageResized, 'vignette', 45);
1112          imagefilter($this->imageResized, IMG_FILTER_BRIGHTNESS, 20);
1113          imagefilter($this->imageResized, IMG_FILTER_CONTRAST, -35);
1114          imagefilter($this->imageResized, IMG_FILTER_COLORIZE, 60, -10, 35);
1115          imagefilter($this->imageResized, IMG_FILTER_SMOOTH, 7);
1116          $this->imageResized = $this->gd_apply_overlay($this->imageResized, 'scratch', 10);
1117      }
1118  }
1119
1120  ## --------------------------------------------------------
1121
1122  /** Apply a PNG overlay */
1123  private function gd_apply_overlay($im, $type, $amount)
1124  #
1125  # Original Author:    Marc Hibbins
1126  # License:  Attribution-ShareAlike 3.0
1127  # Purpose:
1128  # Params in:
1129  # Params out:
1130  # Notes:
1131  #
1132  {
1133      $width = imagesx($im);
1134      $height = imagesy($im);
1135      $filter = imagecreatetruecolor($width, $height);
1136
1137      imagealphablending($filter, false);
1138      imagesavealpha($filter, true);
1139
1140      $transparent = imagecolorallocatealpha($filter, 255, 255, 255, 127);
1141      imagefilledrectangle($filter, 0, 0, $width, $height, $transparent);
1142
1143    // *** Resize overlay
1144    $overlay = $this->filterOverlayPath . '/' . $type . '.png';
1145      $png = imagecreatefrompng($overlay);
1146      imagecopyresampled($filter, $png, 0, 0, 0, 0, $width, $height, imagesx($png), imagesy($png));
1147
1148      $comp = imagecreatetruecolor($width, $height);
1149      imagecopy($comp, $im, 0, 0, 0, 0, $width, $height);
1150      imagecopy($comp, $filter, 0, 0, 0, 0, $width, $height);
1151      imagecopymerge($im, $comp, 0, 0, 0, 0, $width, $height, $amount);
1152
1153      imagedestroy($comp);
1154
1155      return $im;
1156  }
1157
1158/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*-
1159  Colorise
1160*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*/
1161
1162  public function image_colorize($rgb)
1163  {
1164      imagetruecolortopalette($this->imageResized, true, 256);
1165      $numColors = imagecolorstotal($this->imageResized);
1166
1167      for ($x = 0; $x < $numColors; $x++) {
1168          list($r, $g, $b) = array_values(imagecolorsforindex($this->imageResized, $x));
1169
1170    // calculate grayscale in percent
1171    $grayscale = ($r + $g + $b) / 3 / 0xff;
1172
1173          imagecolorset(
1174              $this->imageResized,
1175              $x,
1176      $grayscale * $rgb[0],
1177      $grayscale * $rgb[1],
1178      $grayscale * $rgb[2]
1179    );
1180      }
1181
1182      return true;
1183  }
1184
1185/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*-
1186  Reflection
1187*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*/
1188
1189  public function addReflection($reflectionHeight = 50, $startingTransparency = 30, $inside = false, $bgColor = '#fff', $stretch=false, $divider = 0)
1190  {
1191
1192    // *** Convert color
1193    $rgbArray = $this->formatColor($bgColor);
1194      $r = $rgbArray['r'];
1195      $g = $rgbArray['g'];
1196      $b = $rgbArray['b'];
1197
1198      $im = $this->imageResized;
1199      $li = imagecreatetruecolor($this->width, 1);
1200
1201      $bgc = imagecolorallocate($li, $r, $g, $b);
1202      imagefilledrectangle($li, 0, 0, $this->width, 1, $bgc);
1203
1204      $bg = imagecreatetruecolor($this->width, $reflectionHeight);
1205      $wh = imagecolorallocate($im, 255, 255, 255);
1206
1207      $im = imagerotate($im, -180, $wh);
1208      imagecopyresampled($bg, $im, 0, 0, 0, 0, $this->width, $this->height, $this->width, $this->height);
1209
1210      $im = $bg;
1211
1212      $bg = imagecreatetruecolor($this->width, $reflectionHeight);
1213
1214      for ($x = 0; $x < $this->width; $x++) {
1215          imagecopy($bg, $im, $x, 0, $this->width-$x -1, 0, 1, $reflectionHeight);
1216      }
1217      $im = $bg;
1218
1219      $transparencyAmount = $this->invertTransparency($startingTransparency, 100);
1220
1221    // *** Fade
1222    if ($stretch) {
1223        $step = 100/($reflectionHeight + $startingTransparency);
1224    } else {
1225        $step = 100/$reflectionHeight;
1226    }
1227      for ($i=0; $i<=$reflectionHeight; $i++) {
1228          if ($startingTransparency>100) {
1229              $startingTransparency = 100;
1230          }
1231          if ($startingTransparency< 1) {
1232              $startingTransparency = 1;
1233          }
1234          imagecopymerge($bg, $li, 0, $i, 0, 0, $this->width, 1, $startingTransparency);
1235          $startingTransparency+=$step;
1236      }
1237
1238    // *** Apply fade
1239    imagecopymerge($im, $li, 0, 0, 0, 0, $this->width, $divider, 100); // Divider
1240
1241    // *** width, height of reflection.
1242    $x = imagesx($im);
1243      $y = imagesy($im);
1244
1245    // *** Determines if the reflection should be displayed inside or outside the image
1246    if ($inside) {
1247
1248      // Create new blank image with sizes.
1249      $final = imagecreatetruecolor($this->width, $this->height);
1250
1251        imagecopymerge($final, $this->imageResized, 0, 0, 0, $reflectionHeight, $this->width, $this->height - $reflectionHeight, 100);
1252        imagecopymerge($final, $im, 0, $this->height - $reflectionHeight, 0, 0, $x, $y, 100);
1253    } else {
1254
1255      // Create new blank image with sizes.
1256      $final = imagecreatetruecolor($this->width, $this->height + $y);
1257
1258        imagecopymerge($final, $this->imageResized, 0, 0, 0, 0, $this->width, $this->height, 100);
1259        imagecopymerge($final, $im, 0, $this->height, 0, 0, $x, $y, 100);
1260    }
1261
1262      $this->imageResized = $final;
1263
1264      imagedestroy($li);
1265      imagedestroy($im);
1266  }
1267
1268/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*-
1269  Rotate
1270*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*/
1271
1272  public function rotate($value = 90, $bgColor = 'transparent')
1273    # Author:     Jarrod Oberto
1274    # Date:       07-05-2011
1275    # Purpose:    Rotate image
1276    # Param in:   (mixed) $degrees: (int) number of degress to rotate image
1277  #               (str) param "left": rotate left
1278  #               (str) param "right": rotate right
1279  #               (str) param "upside": upside-down image
1280    # Param out:
1281    # Reference:
1282    # Notes:    The default direction of imageRotate() is counter clockwise.
1283    #
1284  {
1285      if ($this->imageResized) {
1286          if (is_int($value)) {
1287              $degrees = $value;
1288          }
1289
1290      // *** Convert color
1291      $rgbArray = $this->formatColor($bgColor);
1292          $r = $rgbArray['r'];
1293          $g = $rgbArray['g'];
1294          $b = $rgbArray['b'];
1295          if (isset($rgbArray['a'])) {
1296              $a = $rgbArray['a'];
1297          }
1298
1299          if (is_string($value)) {
1300              $value = fix_strtolower($value);
1301
1302              switch ($value) {
1303          case 'left':
1304            $degrees = 90;
1305
1306            break;
1307          case 'right':
1308            $degrees = 270;
1309
1310            break;
1311          case 'upside':
1312            $degrees = 180;
1313
1314            break;
1315          default:
1316            break;
1317        }
1318          }
1319
1320      // *** The default direction of imageRotate() is counter clockwise
1321      //   * This makes it clockwise
1322      $degrees = 360 - $degrees;
1323
1324      // *** Create background color
1325      $bg = imagecolorallocatealpha($this->imageResized, $r, $g, $b, $a);
1326
1327      // *** Fill with background
1328      imagefill($this->imageResized, 0, 0, $bg);
1329
1330      // *** Rotate
1331      $this->imageResized = imagerotate($this->imageResized, $degrees, $bg); // Rotate 45 degrees and allocated the transparent colour as the one to make transparent (obviously)
1332
1333      // Ensure alpha transparency
1334      imagesavealpha($this->imageResized, true);
1335      }
1336  }
1337
1338/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*-
1339  Round corners
1340*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*/
1341
1342  public function roundCorners($radius = 5,  $bgColor = 'transparent')
1343    # Author:     Jarrod Oberto
1344    # Date:       19-05-2011
1345    # Purpose:    Create rounded corners on your image
1346    # Param in:   (int) radius = the amount of curvature
1347  #       (mixed) $bgColor = the corner background color
1348    # Param out:  n/a
1349    # Reference:
1350    # Notes:
1351    #
1352  {
1353
1354    // *** Check if the user wants transparency
1355    $isTransparent = false;
1356      if (!is_array($bgColor)) {
1357          if (fix_strtolower($bgColor) == 'transparent') {
1358              $isTransparent = true;
1359          }
1360      }
1361
1362    // *** If we use transparency, we need to color our curved mask with a unique color
1363    if ($isTransparent) {
1364        $bgColor = $this->findUnusedGreen();
1365    }
1366
1367    // *** Convert color
1368    $rgbArray = $this->formatColor($bgColor);
1369      $r = $rgbArray['r'];
1370      $g = $rgbArray['g'];
1371      $b = $rgbArray['b'];
1372      if (isset($rgbArray['a'])) {
1373          $a = $rgbArray['a'];
1374      }
1375
1376    // *** Create top-left corner mask (square)
1377    $cornerImg = imagecreatetruecolor($radius, $radius);
1378    //$cornerImg = imagecreate($radius, $radius);
1379
1380      //imagealphablending($cornerImg, true);
1381      //imagesavealpha($cornerImg, true);
1382
1383      //imagealphablending($this->imageResized, false);
1384      //imagesavealpha($this->imageResized, true);
1385
1386    // *** Give it a color
1387    $maskColor = imagecolorallocate($cornerImg, 0, 0, 0);
1388
1389    // *** Replace the mask color (black) to transparent
1390    imagecolortransparent($cornerImg, $maskColor);
1391
1392    // *** Create the image background color
1393    $imagebgColor = imagecolorallocate($cornerImg, $r, $g, $b);
1394
1395    // *** Fill the corner area to the user defined color
1396    imagefill($cornerImg, 0, 0, $imagebgColor);
1397
1398      imagefilledellipse($cornerImg, $radius, $radius, $radius * 2, $radius * 2, $maskColor);
1399
1400    // *** Map to top left corner
1401    imagecopymerge($this->imageResized, $cornerImg, 0, 0, 0, 0, $radius, $radius, 100); #tl
1402
1403    // *** Map rounded corner to other corners by rotating and applying the mask
1404    $cornerImg = imagerotate($cornerImg, 90, 0);
1405      imagecopymerge($this->imageResized, $cornerImg, 0, $this->height - $radius, 0, 0, $radius, $radius, 100); #bl
1406
1407    $cornerImg = imagerotate($cornerImg, 90, 0);
1408      imagecopymerge($this->imageResized, $cornerImg, $this->width - $radius, $this->height - $radius, 0, 0, $radius, $radius, 100); #br
1409
1410    $cornerImg = imagerotate($cornerImg, 90, 0);
1411      imagecopymerge($this->imageResized, $cornerImg, $this->width - $radius, 0, 0, 0, $radius, $radius, 100); #tr
1412
1413    // *** If corners are to be transparent, we fill our chromakey color as transparent.
1414    if ($isTransparent) {
1415        //imagecolortransparent($this->imageResized, $imagebgColor);
1416      $this->imageResized = $this->transparentImage($this->imageResized);
1417        imagesavealpha($this->imageResized, true);
1418    }
1419  }
1420
1421/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*-
1422  Shadow
1423*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*/
1424
1425  public function addShadow($shadowAngle=45, $blur=15, $bgColor='transparent')
1426  #
1427  # Author:   Jarrod Oberto (Adapted from Pascal Naidon)
1428  # Ref:    http://www.les-stooges.org/pascal/webdesign/vignettes/index.php?la=en
1429  # Purpose:  Add a drop shadow to your image
1430  # Params in:  (int) $angle: the angle of the shadow
1431  #       (int) $blur: the blur distance
1432  #       (mixed) $bgColor: the color of the background
1433  # Params out:
1434  # Notes:
1435  #
1436  {
1437      // *** A higher number results in a smoother shadow
1438    define('STEPS', $blur*2);
1439
1440    // *** Set the shadow distance
1441    $shadowDistance = $blur*0.25;
1442
1443    // *** Set blur width and height
1444    $blurWidth = $blurHeight = $blur;
1445
1446      if ($shadowAngle == 0) {
1447          $distWidth = 0;
1448          $distHeight = 0;
1449      } else {
1450          $distWidth = $shadowDistance * cos(deg2rad($shadowAngle));
1451          $distHeight = $shadowDistance * sin(deg2rad($shadowAngle));
1452      }
1453
1454    // *** Convert color
1455    if (fix_strtolower($bgColor) != 'transparent') {
1456        $rgbArray = $this->formatColor($bgColor);
1457        $r0 = $rgbArray['r'];
1458        $g0 = $rgbArray['g'];
1459        $b0 = $rgbArray['b'];
1460    }
1461
1462      $image = $this->imageResized;
1463      $width = $this->width;
1464      $height = $this->height;
1465
1466      $newImage = imagecreatetruecolor($width, $height);
1467      imagecopyresampled($newImage, $image, 0, 0, 0, 0, $width, $height, $width, $height);
1468
1469    // *** RGB
1470    $rgb = imagecreatetruecolor($width+$blurWidth, $height+$blurHeight);
1471      $colour = imagecolorallocate($rgb, 0, 0, 0);
1472      imagefilledrectangle($rgb, 0, 0, $width+$blurWidth, $height+$blurHeight, $colour);
1473      $colour = imagecolorallocate($rgb, 255, 255, 255);
1474    //imagefilledrectangle($rgb, $blurWidth*0.5-$distWidth, $blurHeight*0.5-$distHeight, $width+$blurWidth*0.5-$distWidth, $height+$blurWidth*0.5-$distHeight, $colour);
1475    imagefilledrectangle($rgb, $blurWidth*0.5-$distWidth, $blurHeight*0.5-$distHeight, $width+$blurWidth*0.5-$distWidth, $height+$blurWidth*0.5-$distHeight, $colour);
1476    //imagecopymerge($rgb, $newImage, 1+$blurWidth*0.5-$distWidth, 1+$blurHeight*0.5-$distHeight, 0,0, $width, $height, 100);
1477    imagecopymerge($rgb, $newImage, $blurWidth*0.5-$distWidth, $blurHeight*0.5-$distHeight, 0, 0, $width+$blurWidth, $height+$blurHeight, 100);
1478
1479    // *** Shadow (alpha)
1480    $shadow = imagecreatetruecolor($width+$blurWidth, $height+$blurHeight);
1481      imagealphablending($shadow, false);
1482      $colour = imagecolorallocate($shadow, 0, 0, 0);
1483      imagefilledrectangle($shadow, 0, 0, $width+$blurWidth, $height+$blurHeight, $colour);
1484
1485      for ($i=0; $i<=STEPS; $i++) {
1486          $t = ((1.0*$i)/STEPS);
1487          $intensity = 255*$t*$t;
1488
1489          $colour = imagecolorallocate($shadow, $intensity, $intensity, $intensity);
1490          $points = array(
1491              $blurWidth*$t,        $blurHeight,     // Point 1 (x, y)
1492              $blurWidth,         $blurHeight*$t,  // Point 2 (x, y)
1493              $width,           $blurHeight*$t,  // Point 3 (x, y)
1494              $width+$blurWidth*(1-$t), $blurHeight,     // Point 4 (x, y)
1495              $width+$blurWidth*(1-$t), $height,     // Point 5 (x, y)
1496              $width,           $height+$blurHeight*(1-$t),  // Point 6 (x, y)
1497              $blurWidth,         $height+$blurHeight*(1-$t),  // Point 7 (x, y)
1498              $blurWidth*$t,        $height,      // Point 8 (x, y)
1499          );
1500          imagepolygon($shadow, $points, 8, $colour);
1501      }
1502
1503      for ($i=0; $i<=STEPS; $i++) {
1504          $t = ((1.0*$i)/STEPS);
1505          $intensity = 255*$t*$t;
1506
1507          $colour = imagecolorallocate($shadow, $intensity, $intensity, $intensity);
1508          imagefilledarc($shadow, $blurWidth-1, $blurHeight-1, 2*(1-$t)*$blurWidth, 2*(1-$t)*$blurHeight, 180, 268, $colour, IMG_ARC_PIE);
1509          imagefilledarc($shadow, $width, $blurHeight-1, 2*(1-$t)*$blurWidth, 2*(1-$t)*$blurHeight, 270, 358, $colour, IMG_ARC_PIE);
1510          imagefilledarc($shadow, $width, $height, 2*(1-$t)*$blurWidth, 2*(1-$t)*$blurHeight, 0, 90, $colour, IMG_ARC_PIE);
1511          imagefilledarc($shadow, $blurWidth-1, $height, 2*(1-$t)*$blurWidth, 2*(1-$t)*$blurHeight, 90, 180, $colour, IMG_ARC_PIE);
1512      }
1513
1514      $colour = imagecolorallocate($shadow, 255, 255, 255);
1515      imagefilledrectangle($shadow, $blurWidth, $blurHeight, $width, $height, $colour);
1516      imagefilledrectangle($shadow, $blurWidth*0.5-$distWidth, $blurHeight*0.5-$distHeight, $width+$blurWidth*0.5-1-$distWidth, $height+$blurHeight*0.5-1-$distHeight, $colour);
1517
1518    // *** The magic
1519        imagealphablending($rgb, false);
1520
1521      for ($theX=0; $theX<imagesx($rgb); $theX++) {
1522          for ($theY=0; $theY<imagesy($rgb); $theY++) {
1523
1524        // *** Get the RGB values for every pixel of the RGB image
1525        $colArray = imagecolorat($rgb, $theX, $theY);
1526              $r = ($colArray >> 16) & 0xFF;
1527              $g = ($colArray >> 8) & 0xFF;
1528              $b = $colArray & 0xFF;
1529
1530        // *** Get the alpha value for every pixel of the shadow image
1531        $colArray = imagecolorat($shadow, $theX, $theY);
1532              $a = $colArray & 0xFF;
1533              $a = 127-floor($a/2);
1534              $t = $a/128.0;
1535
1536        // *** Create color
1537        if (fix_strtolower($bgColor) == 'transparent') {
1538            $myColour = imagecolorallocatealpha($rgb, $r, $g, $b, $a);
1539        } else {
1540            $myColour = imagecolorallocate($rgb, $r*(1.0-$t)+$r0*$t, $g*(1.0-$t)+$g0*$t, $b*(1.0-$t)+$b0*$t);
1541        }
1542
1543        // *** Add color to new rgb image
1544        imagesetpixel($rgb, $theX, $theY, $myColour);
1545          }
1546      }
1547
1548      imagealphablending($rgb, true);
1549      imagesavealpha($rgb, true);
1550
1551      $this->imageResized = $rgb;
1552
1553      imagedestroy($image);
1554      imagedestroy($newImage);
1555      imagedestroy($shadow);
1556  }
1557
1558/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*-
1559  Add Caption Box
1560*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*/
1561
1562  public function addCaptionBox($side='b', $thickness=50, $padding=0, $bgColor='#000', $transparencyAmount=30)
1563  #
1564  # Author:   Jarrod Oberto
1565  # Date:   26 May 2011
1566  # Purpose:  Add a caption box
1567  # Params in:  (str) $side: the side to add the caption box (t, r, b, or l).
1568  #       (int) $thickness: how thick you want the caption box to be.
1569  #       (mixed) $bgColor: The color of the caption box.
1570  #       (int) $transparencyAmount: The amount of transparency to be
1571  #       applied.
1572  # Params out: n/a
1573  # Notes:
1574  #
1575  {
1576      $side = fix_strtolower($side);
1577
1578      // *** Convert color
1579      $rgbArray = $this->formatColor($bgColor);
1580      $r = $rgbArray['r'];
1581      $g = $rgbArray['g'];
1582      $b = $rgbArray['b'];
1583
1584      $positionArray = $this->calculateCaptionBoxPosition($side, $thickness, $padding);
1585
1586      // *** Store incase we want to use method addTextToCaptionBox()
1587      $this->captionBoxPositionArray = $positionArray;
1588
1589      $transparencyAmount = $this->invertTransparency($transparencyAmount, 127, false);
1590      $transparent = imagecolorallocatealpha($this->imageResized, $r, $g, $b, $transparencyAmount);
1591      imagefilledrectangle($this->imageResized, $positionArray['x1'], $positionArray['y1'], $positionArray['x2'], $positionArray['y2'], $transparent);
1592  }
1593
1594  ## --------------------------------------------------------
1595
1596  public function addTextToCaptionBox($text, $fontColor='#fff', $fontSize = 12, $angle = 0, $font = null)
1597  #
1598  # Author:   Jarrod Oberto
1599  # Date:   03 Aug 11
1600  # Purpose:  Simplify adding text to a caption box by automatically
1601  #       locating the center of the caption box
1602  # Params in:  The usually text paams (less a couple)
1603  # Params out: n/a
1604  # Notes:
1605  #
1606  {
1607
1608    // *** Get the caption box measurements
1609    if (count($this->captionBoxPositionArray) == 4) {
1610        $x1 = $this->captionBoxPositionArray['x1'];
1611        $x2 = $this->captionBoxPositionArray['x2'];
1612        $y1 = $this->captionBoxPositionArray['y1'];
1613        $y2 = $this->captionBoxPositionArray['y2'];
1614    } else {
1615        if ($this->debug) {
1616            throw new Exception('No caption box found.');
1617        } else {
1618            return false;
1619        }
1620    }
1621
1622    // *** Get text font
1623    $font = $this->getTextFont($font);
1624
1625    // *** Get text size
1626    $textSizeArray = $this->getTextSize($fontSize, $angle, $font, $text);
1627      $textWidth = $textSizeArray['width'];
1628      $textHeight = $textSizeArray['height'];
1629
1630    // *** Find the width/height middle points
1631    $boxXMiddle = (($x2 - $x1) / 2);
1632      $boxYMiddle = (($y2 - $y1) / 2);
1633
1634    // *** Box middle - half the text width/height
1635    $xPos = ($x1 + $boxXMiddle) - ($textWidth/2);
1636      $yPos = ($y1 + $boxYMiddle) - ($textHeight/2);
1637
1638      $pos = $xPos . 'x' . $yPos;
1639
1640      $this->addText($text, $pos, $padding = 0, $fontColor, $fontSize, $angle, $font);
1641  }
1642
1643  ## --------------------------------------------------------
1644
1645  private function calculateCaptionBoxPosition($side, $thickness, $padding)
1646  {
1647      $positionArray = array();
1648
1649      switch ($side) {
1650      case 't':
1651        $positionArray['x1'] = 0;
1652        $positionArray['y1'] = $padding;
1653        $positionArray['x2'] = $this->width;
1654        $positionArray['y2'] = $thickness + $padding;
1655
1656        break;
1657      case 'r':
1658        $positionArray['x1'] = $this->width - $thickness - $padding;
1659        $positionArray['y1'] = 0;
1660        $positionArray['x2'] = $this->width - $padding;
1661        $positionArray['y2'] = $this->height;
1662
1663        break;
1664      case 'b':
1665        $positionArray['x1'] = 0;
1666        $positionArray['y1'] = $this->height - $thickness - $padding;
1667        $positionArray['x2'] = $this->width;
1668        $positionArray['y2'] = $this->height - $padding;
1669
1670        break;
1671      case 'l':
1672        $positionArray['x1'] = $padding;
1673        $positionArray['y1'] = 0;
1674        $positionArray['x2'] = $thickness + $padding;
1675        $positionArray['y2'] = $this->height;
1676
1677        break;
1678
1679      default:
1680        break;
1681    }
1682
1683      return $positionArray;
1684  }
1685
1686/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*-
1687  Get EXIF Data
1688*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*/
1689
1690  public function getExif($debug=false)
1691    # Author:     Jarrod Oberto
1692    # Date:       07-05-2011
1693    # Purpose:    Get image EXIF data
1694    # Param in:   n/a
1695    # Param out:  An associate array of EXIF data
1696    # Reference:
1697    # Notes:
1698    # 23 May 13 : added orientation flag -jco
1699    #
1700  {
1701      if (!$this->debug || !$debug) {
1702          $debug = false;
1703      }
1704
1705    // *** Check all is good - check the EXIF library exists and the file exists, too.
1706    if (!$this->testEXIFInstalled()) {
1707        if ($debug) {
1708            throw new Exception('The EXIF Library is not installed.');
1709        } else {
1710            return array();
1711        }
1712    }
1713      if (!file_exists($this->fileName)) {
1714          if ($debug) {
1715              throw new Exception('Image not found.');
1716          } else {
1717              return array();
1718          }
1719      }
1720      if ($this->fileExtension != '.jpg') {
1721          if ($debug) {
1722              throw new Exception('Metadata not supported for this image type.');
1723          } else {
1724              return array();
1725          }
1726      }
1727      $exifData = exif_read_data($this->fileName, 'IFD0');
1728
1729    // *** Format the apperture value
1730    $ev = $exifData['ApertureValue'];
1731      $apPeicesArray = explode('/', $ev);
1732      if (count($apPeicesArray) == 2) {
1733          $apertureValue = round($apPeicesArray[0] / $apPeicesArray[1], 2, PHP_ROUND_HALF_DOWN) . ' EV';
1734      } else {
1735          $apertureValue = '';
1736      }
1737
1738    // *** Format the focal length
1739    $focalLength = $exifData['FocalLength'];
1740      $flPeicesArray = explode('/', $focalLength);
1741      if (count($flPeicesArray) == 2) {
1742          $focalLength = $flPeicesArray[0] / $flPeicesArray[1] . '.0 mm';
1743      } else {
1744          $focalLength = '';
1745      }
1746
1747    // *** Format fNumber
1748    $fNumber = $exifData['FNumber'];
1749      $fnPeicesArray = explode('/', $fNumber);
1750      if (count($fnPeicesArray) == 2) {
1751          $fNumber = $fnPeicesArray[0] / $fnPeicesArray[1];
1752      } else {
1753          $fNumber = '';
1754      }
1755
1756    // *** Resolve ExposureProgram
1757    if (isset($exifData['ExposureProgram'])) {
1758        $ep =  $exifData['ExposureProgram'];
1759    }
1760      if (isset($ep)) {
1761          $ep = $this->resolveExposureProgram($ep);
1762      }
1763
1764    // *** Resolve MeteringMode
1765    $mm = $exifData['MeteringMode'];
1766      $mm = $this->resolveMeteringMode($mm);
1767
1768    // *** Resolve Flash
1769    $flash = $exifData['Flash'];
1770      $flash = $this->resolveFlash($flash);
1771
1772      if (isset($exifData['Make'])) {
1773          $exifDataArray['make'] = $exifData['Make'];
1774      } else {
1775          $exifDataArray['make'] = '';
1776      }
1777
1778      if (isset($exifData['Model'])) {
1779          $exifDataArray['model'] = $exifData['Model'];
1780      } else {
1781          $exifDataArray['model'] = '';
1782      }
1783
1784      if (isset($exifData['DateTime'])) {
1785          $exifDataArray['date'] = $exifData['DateTime'];
1786      } else {
1787          $exifDataArray['date'] = '';
1788      }
1789
1790      if (isset($exifData['ExposureTime'])) {
1791          $exifDataArray['exposure time'] = $exifData['ExposureTime'] . ' sec.';
1792      } else {
1793          $exifDataArray['exposure time'] = '';
1794      }
1795
1796      if ($apertureValue != '') {
1797          $exifDataArray['aperture value'] = $apertureValue;
1798      } else {
1799          $exifDataArray['aperture value'] = '';
1800      }
1801
1802      if (isset($exifData['COMPUTED']['ApertureFNumber'])) {
1803          $exifDataArray['f-stop'] = $exifData['COMPUTED']['ApertureFNumber'];
1804      } else {
1805          $exifDataArray['f-stop'] = '';
1806      }
1807
1808      if (isset($exifData['FNumber'])) {
1809          $exifDataArray['fnumber'] = $exifData['FNumber'];
1810      } else {
1811          $exifDataArray['fnumber'] = '';
1812      }
1813
1814      if ($fNumber != '') {
1815          $exifDataArray['fnumber value'] = $fNumber;
1816      } else {
1817          $exifDataArray['fnumber value'] = '';
1818      }
1819
1820      if (isset($exifData['ISOSpeedRatings'])) {
1821          $exifDataArray['iso'] = $exifData['ISOSpeedRatings'];
1822      } else {
1823          $exifDataArray['iso'] = '';
1824      }
1825
1826      if ($focalLength != '') {
1827          $exifDataArray['focal length'] = $focalLength;
1828      } else {
1829          $exifDataArray['focal length'] = '';
1830      }
1831
1832      if (isset($ep)) {
1833          $exifDataArray['exposure program'] = $ep;
1834      } else {
1835          $exifDataArray['exposure program'] = '';
1836      }
1837
1838      if ($mm != '') {
1839          $exifDataArray['metering mode'] = $mm;
1840      } else {
1841          $exifDataArray['metering mode'] = '';
1842      }
1843
1844      if ($flash != '') {
1845          $exifDataArray['flash status'] = $flash;
1846      } else {
1847          $exifDataArray['flash status'] = '';
1848      }
1849
1850      if (isset($exifData['Artist'])) {
1851          $exifDataArray['creator'] = $exifData['Artist'] ;
1852      } else {
1853          $exifDataArray['creator'] = '';
1854      }
1855
1856      if (isset($exifData['Copyright'])) {
1857          $exifDataArray['copyright'] = $exifData['Copyright'];
1858      } else {
1859          $exifDataArray['copyright'] = '';
1860      }
1861
1862    // *** Orientation
1863    if (isset($exifData['Orientation'])) {
1864        $exifDataArray['orientation'] = $exifData['Orientation'];
1865    } else {
1866        $exifDataArray['orientation'] = '';
1867    }
1868
1869      return $exifDataArray;
1870  }
1871
1872  ## --------------------------------------------------------
1873
1874  private function resolveExposureProgram($ep)
1875  {
1876      switch ($ep) {
1877      case 0:
1878        $ep = '';
1879
1880        break;
1881      case 1:
1882        $ep = 'manual';
1883
1884        break;
1885      case 2:
1886        $ep = 'normal program';
1887
1888        break;
1889      case 3:
1890        $ep = 'aperture priority';
1891
1892        break;
1893      case 4:
1894        $ep = 'shutter priority';
1895
1896        break;
1897      case 5:
1898        $ep = 'creative program';
1899
1900        break;
1901      case 6:
1902        $ep = 'action program';
1903
1904        break;
1905      case 7:
1906        $ep = 'portrait mode';
1907
1908        break;
1909      case 8:
1910        $ep = 'landscape mode';
1911
1912        break;
1913
1914      default:
1915        break;
1916    }
1917
1918      return $ep;
1919  }
1920
1921  ## --------------------------------------------------------
1922
1923  private function resolveMeteringMode($mm)
1924  {
1925      switch ($mm) {
1926      case 0:
1927        $mm = 'unknown';
1928
1929        break;
1930      case 1:
1931        $mm = 'average';
1932
1933        break;
1934      case 2:
1935        $mm = 'center weighted average';
1936
1937        break;
1938      case 3:
1939        $mm = 'spot';
1940
1941        break;
1942      case 4:
1943        $mm = 'multi spot';
1944
1945        break;
1946      case 5:
1947        $mm = 'pattern';
1948
1949        break;
1950      case 6:
1951        $mm = 'partial';
1952
1953        break;
1954      case 255:
1955        $mm = 'other';
1956
1957        break;
1958
1959      default:
1960        break;
1961    }
1962
1963      return $mm;
1964  }
1965
1966  ## --------------------------------------------------------
1967
1968  private function resolveFlash($flash)
1969  {
1970      switch ($flash) {
1971      case 0:
1972        $flash = 'flash did not fire';
1973
1974        break;
1975      case 1:
1976        $flash = 'flash fired';
1977
1978        break;
1979      case 5:
1980        $flash = 'strobe return light not detected';
1981
1982        break;
1983      case 7:
1984        $flash = 'strobe return light detected';
1985
1986        break;
1987      case 9:
1988        $flash = 'flash fired, compulsory flash mode';
1989
1990        break;
1991      case 13:
1992        $flash = 'flash fired, compulsory flash mode, return light not detected';
1993
1994        break;
1995      case 15:
1996        $flash = 'flash fired, compulsory flash mode, return light detected';
1997
1998        break;
1999      case 16:
2000        $flash = 'flash did not fire, compulsory flash mode';
2001
2002        break;
2003      case 24:
2004        $flash = 'flash did not fire, auto mode';
2005
2006        break;
2007      case 25:
2008        $flash = 'flash fired, auto mode';
2009
2010        break;
2011      case 29:
2012        $flash = 'flash fired, auto mode, return light not detected';
2013
2014        break;
2015      case 31:
2016        $flash = 'flash fired, auto mode, return light detected';
2017
2018        break;
2019      case 32:
2020        $flash = 'no flash function';
2021
2022        break;
2023      case 65:
2024        $flash = 'flash fired, red-eye reduction mode';
2025
2026        break;
2027      case 69:
2028        $flash = 'flash fired, red-eye reduction mode, return light not detected';
2029
2030        break;
2031      case 71:
2032        $flash = 'flash fired, red-eye reduction mode, return light detected';
2033
2034        break;
2035      case 73:
2036        $flash = 'flash fired, compulsory flash mode, red-eye reduction mode';
2037
2038        break;
2039      case 77:
2040        $flash = 'flash fired, compulsory flash mode, red-eye reduction mode, return light not detected';
2041
2042        break;
2043      case 79:
2044        $flash = 'flash fired, compulsory flash mode, red-eye reduction mode, return light detected';
2045
2046        break;
2047      case 89:
2048        $flash = 'flash fired, auto mode, red-eye reduction mode';
2049
2050        break;
2051      case 93:
2052        $flash = 'flash fired, auto mode, return light not detected, red-eye reduction mode';
2053
2054        break;
2055      case 95:
2056        $flash = 'flash fired, auto mode, return light detected, red-eye reduction mode';
2057
2058        break;
2059
2060      default:
2061        break;
2062    }
2063
2064      return $flash;
2065  }
2066
2067/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*-
2068  Get IPTC Data
2069*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*/
2070
2071/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*-
2072  Write IPTC Data
2073*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*/
2074
2075  public function writeIPTCcaption($value)
2076  # Caption
2077  {
2078      $this->writeIPTC(120, $value);
2079  }
2080
2081  ## --------------------------------------------------------
2082
2083  public function writeIPTCwriter($value)
2084  {
2085      //$this->writeIPTC(65, $value);
2086  }
2087
2088  ## --------------------------------------------------------
2089
2090  private function writeIPTC($dat, $value)
2091  {
2092
2093    # LIMIT TO JPG
2094
2095    $caption_block = $this->iptc_maketag(2, $dat, $value);
2096      $image_string = iptcembed($caption_block, $this->fileName);
2097      file_put_contents('iptc.jpg', $image_string);
2098  }
2099
2100## --------------------------------------------------------
2101
2102  private function iptc_maketag($rec, $dat, $val)
2103  # Author:   Thies C. Arntzen
2104  # Purpose:    Function to format the new IPTC text
2105  # Param in:   $rec: Application record. (We’re working with #2)
2106  #       $dat: Index. (120 for caption, 118 for contact. See the IPTC IIM
2107  #         specification:
2108  #         http://www.iptc.org/std/IIM/4.1/specification/IIMV4.1.pdf
2109  #       $val: Value/data/text. Make sure this is within the length
2110  #         constraints of the IPTC IIM specification
2111  # Ref:      http://blog.peterhaza.no/working-with-image-meta-data-in-exif-and-iptc-headers-from-php/
2112  #       http://php.net/manual/en/function.iptcembed.php
2113  #
2114  {
2115      $len = strlen($val);
2116      if ($len < 0x8000) {
2117          return chr(0x1c).chr($rec).chr($dat).
2118      chr($len >> 8).
2119      chr($len & 0xff).
2120      $val;
2121      } else {
2122          return chr(0x1c).chr($rec).chr($dat).
2123      chr(0x80).chr(0x04).
2124      chr(($len >> 24) & 0xff).
2125      chr(($len >> 16) & 0xff).
2126      chr(($len >> 8) & 0xff).
2127      chr(($len) & 0xff).
2128      $val;
2129      }
2130  }
2131
2132/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*-
2133  Write XMP Data
2134*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*/
2135
2136  //http://xmpphptoolkit.sourceforge.net/
2137
2138/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*-
2139  Add Text
2140*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*/
2141
2142  public function addText($text, $pos = '20x20', $padding = 0, $fontColor='#fff', $fontSize = 12, $angle = 0, $font = null)
2143    # Author:     Jarrod Oberto
2144  # Date:       18-11-09
2145    # Purpose:    Add text to an image
2146    # Param in:
2147    # Param out:
2148    # Reference:  http://php.net/manual/en/function.imagettftext.php
2149    # Notes:      Make sure you supply the font.
2150    #
2151  {
2152
2153    // *** Convert color
2154    $rgbArray = $this->formatColor($fontColor);
2155      $r = $rgbArray['r'];
2156      $g = $rgbArray['g'];
2157      $b = $rgbArray['b'];
2158
2159    // *** Get text font
2160    $font = $this->getTextFont($font);
2161
2162    // *** Get text size
2163    $textSizeArray = $this->getTextSize($fontSize, $angle, $font, $text);
2164      $textWidth = $textSizeArray['width'];
2165      $textHeight = $textSizeArray['height'];
2166
2167    // *** Find co-ords to place text
2168    $posArray = $this->calculatePosition($pos, $padding, $textWidth, $textHeight, false);
2169      $x = $posArray['width'];
2170      $y = $posArray['height'];
2171
2172      $fontColor = imagecolorallocate($this->imageResized, $r, $g, $b);
2173
2174    // *** Add text
2175    imagettftext($this->imageResized, $fontSize, $angle, $x, $y, $fontColor, $font, $text);
2176  }
2177
2178  ## --------------------------------------------------------
2179
2180  private function getTextFont($font)
2181  {
2182      // *** Font path (shou
2183    $fontPath =  dirname(__FILE__) . '/' . $this->fontDir;
2184
2185    // *** The below is/may be needed depending on your version (see ref)
2186    putenv('GDFONTPATH=' . realpath('.'));
2187
2188    // *** Check if the passed in font exsits...
2189    if ($font == null || !file_exists($font)) {
2190
2191      // *** ...If not, default to this font.
2192      $font = $fontPath . '/arimo.ttf';
2193
2194      // *** Check our default font exists...
2195      if (!file_exists($font)) {
2196
2197        // *** If not, return false
2198        if ($this->debug) {
2199            throw new Exception('Font not found');
2200        } else {
2201            return false;
2202        }
2203      }
2204    }
2205
2206      return $font;
2207  }
2208
2209  ## --------------------------------------------------------
2210
2211  private function getTextSize($fontSize, $angle, $font, $text)
2212  {
2213
2214    // *** Define box (so we can get the width)
2215    $box = @imagettfbbox($fontSize, $angle, $font, $text);
2216
2217    // ***  Get width of text from dimensions
2218    $textWidth = abs($box[4] - $box[0]);
2219
2220    // ***  Get height of text from dimensions (should also be same as $fontSize)
2221    $textHeight = abs($box[5] - $box[1]);
2222
2223      return array('height' => $textHeight, 'width' => $textWidth);
2224  }
2225
2226/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*-
2227  Add Watermark
2228*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*/
2229
2230  public function addWatermark($watermarkImage, $pos, $padding = 0, $opacity = 0)
2231    # Author:     Jarrod Oberto
2232    # Date:       18-11-09
2233    # Purpose:    Add watermark image
2234    # Param in:   (str) $watermark: The watermark image
2235  #       (str) $pos: Could be a pre-determined position such as:
2236  #           tl = top left,
2237  #           t  = top (middle),
2238  #           tr = top right,
2239  #           l  = left,
2240  #           m  = middle,
2241  #           r  = right,
2242  #           bl = bottom left,
2243  #           b  = bottom (middle),
2244  #           br = bottom right
2245  #         Or, it could be a co-ordinate position such as: 50x100
2246  #
2247  #       (int) $padding: If using a pre-determined position you can
2248  #         adjust the padding from the edges by passing an amount
2249  #         in pixels. If using co-ordinates, this value is ignored.
2250    # Param out:
2251    # Reference:  http://www.php.net/manual/en/image.examples-watermark.php
2252    # Notes:      Based on example in reference.
2253  #
2254    #
2255  {
2256
2257    // Load the stamp and the photo to apply the watermark to
2258    $stamp = $this->openImage($watermarkImage);    # stamp
2259    $im = $this->imageResized;            # photo
2260
2261    // *** Get stamps width and height
2262    $sx = imagesx($stamp);
2263      $sy = imagesy($stamp);
2264
2265    // *** Find co-ords to place image
2266    $posArray = $this->calculatePosition($pos, $padding, $sx, $sy);
2267      $x = $posArray['width'];
2268      $y = $posArray['height'];
2269
2270    // *** Set watermark opacity
2271    if (fix_strtolower(strrchr($watermarkImage, '.')) == '.png') {
2272        $opacity = $this->invertTransparency($opacity, 100);
2273        $this->filterOpacity($stamp, $opacity);
2274    }
2275
2276    // Copy the watermark image onto our photo
2277    imagecopy($im, $stamp, $x, $y, 0, 0, imagesx($stamp), imagesy($stamp));
2278  }
2279
2280  ## --------------------------------------------------------
2281
2282  private function calculatePosition($pos, $padding, $assetWidth, $assetHeight, $upperLeft = true)
2283  #
2284  # Author:   Jarrod Oberto
2285  # Date:   08-05-11
2286  # Purpose:  Calculate the x, y pixel cordinates of the asset to place
2287  # Params in:  (str) $pos: Either something like: "tl", "l", "br" or an
2288  #         exact position like: "100x50"
2289  #       (int) $padding: The amount of padding from the edge. Only
2290  #         used for the predefined $pos.
2291  #       (int) $assetWidth: The width of the asset to add to the image
2292  #       (int) $assetHeight: The height of the asset to add to the image
2293  #       (bol) $upperLeft: if true, the asset will be positioned based
2294  #         on the upper left x, y coords. If false, it means you're
2295  #         using the lower left as the basepoint and this will
2296  #         convert it to the upper left position
2297  # Params out:
2298  # NOTE: this is done from the UPPER left corner!! But will convert lower
2299  #   left basepoints to upper left if $upperleft is set to false
2300  #
2301  #
2302  {
2303      $pos = fix_strtolower($pos);
2304
2305    // *** If co-ords have been entered
2306    if (strstr($pos, 'x')) {
2307        $pos = str_replace(' ', '', $pos);
2308
2309        $xyArray = explode('x', $pos);
2310        list($width, $height) = $xyArray;
2311    } else {
2312        switch ($pos) {
2313        case 'tl':
2314          $width = 0 + $padding;
2315          $height = 0 + $padding;
2316
2317          break;
2318
2319        case 't':
2320          $width = ($this->width / 2) - ($assetWidth / 2);
2321          $height = 0 + $padding;
2322
2323          break;
2324
2325        case 'tr':
2326          $width = $this->width - $assetWidth - $padding;
2327          $height = 0 + $padding;
2328
2329          break;
2330
2331        case 'l':
2332          $width = 0 + $padding;
2333          $height = ($this->height / 2) - ($assetHeight / 2);
2334
2335          break;
2336
2337        case 'm':
2338          $width = ($this->width / 2) - ($assetWidth / 2);
2339          $height = ($this->height / 2) - ($assetHeight / 2);
2340
2341          break;
2342
2343        case 'r':
2344          $width = $this->width - $assetWidth - $padding;
2345          $height = ($this->height / 2) - ($assetHeight / 2);
2346
2347          break;
2348
2349        case 'bl':
2350          $width = 0 + $padding;
2351          $height = $this->height - $assetHeight - $padding;
2352
2353          break;
2354
2355        case 'b':
2356          $width = ($this->width / 2) - ($assetWidth / 2);
2357          $height = $this->height - $assetHeight - $padding;
2358
2359          break;
2360
2361        case 'br':
2362          $width = $this->width - $assetWidth - $padding;
2363          $height = $this->height - $assetHeight - $padding;
2364
2365          break;
2366
2367        default:
2368          $width = 0;
2369          $height = 0;
2370
2371          break;
2372      }
2373    }
2374
2375      if (!$upperLeft) {
2376          $height = $height + $assetHeight;
2377      }
2378
2379      return array('width' => $width, 'height' => $height);
2380  }
2381
2382  ## --------------------------------------------------------
2383
2384  private function filterOpacity(&$img, $opacity = 75)
2385  #
2386  # Author:     aiden dot mail at freemail dot hu
2387  # Author date:  29-03-08 08:16
2388  # Date added:   08-05-11
2389  # Purpose:    Change opacity of image
2390  # Params in:    $img: Image resource id
2391  #         (int) $opacity: the opacity amount: 0-100, 100 being not opaque.
2392  # Params out:   (bool) true on success, else false
2393  # Ref:      http://www.php.net/manual/en/function.imagefilter.php#82162
2394  # Notes:      png only
2395  #
2396  {
2397      if (!isset($opacity)) {
2398          return false;
2399      }
2400
2401      if ($opacity == 100) {
2402          return true;
2403      }
2404
2405      $opacity /= 100;
2406
2407    //get image width and height
2408    $w = imagesx($img);
2409      $h = imagesy($img);
2410
2411    //turn alpha blending off
2412    imagealphablending($img, false);
2413
2414    //find the most opaque pixel in the image (the one with the smallest alpha value)
2415    $minalpha = 127;
2416      for ($x = 0; $x < $w; $x++) {
2417          for ($y = 0; $y < $h; $y++) {
2418              $alpha = (imagecolorat($img, $x, $y) >> 24) & 0xFF;
2419              if ($alpha < $minalpha) {
2420                  $minalpha = $alpha;
2421              }
2422          }
2423      }
2424
2425    //loop through image pixels and modify alpha for each
2426    for ($x = 0; $x < $w; $x++) {
2427        for ($y = 0; $y < $h; $y++) {
2428            //get current alpha value (represents the TANSPARENCY!)
2429        $colorxy = imagecolorat($img, $x, $y);
2430            $alpha = ($colorxy >> 24) & 0xFF;
2431        //calculate new alpha
2432        if ($minalpha !== 127) {
2433            $alpha = 127 + 127 * $opacity * ($alpha - 127) / (127 - $minalpha);
2434        } else {
2435            $alpha += 127 * $opacity;
2436        }
2437        //get the color index with new alpha
2438        $alphacolorxy = imagecolorallocatealpha($img, ($colorxy >> 16) & 0xFF, ($colorxy >> 8) & 0xFF, $colorxy & 0xFF, $alpha);
2439        //set pixel with the new color + opacity
2440        if (!imagesetpixel($img, $x, $y, $alphacolorxy)) {
2441            return false;
2442        }
2443        }
2444    }
2445
2446      return true;
2447  }
2448
2449## --------------------------------------------------------
2450
2451    private function openImage($file)
2452    # Author:     Jarrod Oberto
2453    # Date:       27-02-08
2454    # Purpose:
2455    # Param in:
2456    # Param out:  n/a
2457    # Reference:
2458    # Notes:
2459    #
2460    {
2461        if (!file_exists($file) && !$this->checkStringStartsWith('http://', $file)) {
2462            if ($this->debug) {
2463                throw new Exception('Image not found.');
2464            } else {
2465                throw new Exception();
2466            }
2467        }
2468
2469        // *** Get extension
2470        $extension = strrchr($file, '.');
2471        $extension = fix_strtolower($extension);
2472        switch ($extension) {
2473            case '.jpg':
2474            case '.jpeg':
2475                $img = @imagecreatefromjpeg($file);
2476
2477                break;
2478            case '.gif':
2479                $img = @imagecreatefromgif($file);
2480
2481                break;
2482            case '.png':
2483                $img = @imagecreatefrompng($file);
2484
2485                break;
2486            case '.bmp':
2487                $img = @$this->ImageCreateFromBMP($file);
2488
2489                break;
2490            case '.psd':
2491                $img = @$this->imagecreatefrompsd($file);
2492
2493                break;
2494
2495            // ... etc
2496
2497            default:
2498                $img = false;
2499
2500                break;
2501        }
2502
2503        return $img;
2504    }
2505
2506## --------------------------------------------------------
2507
2508  public function reset()
2509  #
2510  # Author:   Jarrod Oberto
2511  # Date:   30-08-11
2512  # Purpose:  Reset the resource (allow further editing)
2513  # Params in:
2514  # Params out:
2515  # Notes:
2516  #
2517  {
2518      $this->__construct($this->fileName);
2519  }
2520
2521## --------------------------------------------------------
2522
2523    public function saveImage($savePath, $imageQuality="100")
2524    # Author:     Jarrod Oberto
2525    # Date:       27-02-08
2526    # Purpose:    Saves the image
2527    # Param in:   $savePath: Where to save the image including filename:
2528    #             $imageQuality: image quality you want the image saved at 0-100
2529    # Param out:  n/a
2530    # Reference:
2531    # Notes:    * gif doesn't have a quality parameter
2532  #       * jpg has a quality setting 0-100 (100 being the best)
2533    #       * png has a quality setting 0-9 (0 being the best)
2534  #
2535  #             * bmp files have no native support for bmp files. We use a
2536  #       third party class to save as bmp.
2537    {
2538
2539    // *** Perform a check or two.
2540    if (!is_resource($this->imageResized)) {
2541        if ($this->debug) {
2542            throw new Exception('saveImage: This is not a resource.');
2543        } else {
2544            throw new Exception();
2545        }
2546    }
2547        $fileInfoArray = pathinfo($savePath);
2548        clearstatcache();
2549        if (!is_writable($fileInfoArray['dirname'])) {
2550            if ($this->debug) {
2551                throw new Exception('The path is not writable. Please check your permissions.');
2552            } else {
2553                throw new Exception();
2554            }
2555        }
2556
2557    // *** Get extension
2558        $extension = strrchr($savePath, '.');
2559        $extension = fix_strtolower($extension);
2560
2561        $error = '';
2562
2563        switch ($extension) {
2564            case '.jpg':
2565            case '.jpeg':
2566        $this->checkInterlaceImage($this->isInterlace);
2567        if (imagetypes() & IMG_JPG) {
2568            imagejpeg($this->imageResized, $savePath, $imageQuality);
2569        } else {
2570            $error = 'jpg';
2571        }
2572
2573                break;
2574
2575            case '.gif':
2576        $this->checkInterlaceImage($this->isInterlace);
2577        if (imagetypes() & IMG_GIF) {
2578            imagegif($this->imageResized, $savePath);
2579        } else {
2580            $error = 'gif';
2581        }
2582
2583                break;
2584
2585            case '.png':
2586        // *** Scale quality from 0-100 to 0-9
2587        $scaleQuality = round(($imageQuality/100) * 9);
2588
2589        // *** Invert qualit setting as 0 is best, not 9
2590        $invertScaleQuality = 9 - $scaleQuality;
2591
2592        $this->checkInterlaceImage($this->isInterlace);
2593        if (imagetypes() & IMG_PNG) {
2594            imagepng($this->imageResized, $savePath, $invertScaleQuality);
2595        } else {
2596            $error = 'png';
2597        }
2598
2599                break;
2600
2601            case '.bmp':
2602        file_put_contents($savePath, $this->GD2BMPstring($this->imageResized));
2603
2604          break;
2605
2606            // ... etc
2607
2608            default:
2609        // *** No extension - No save.
2610        $this->errorArray[] = 'This file type (' . $extension . ') is not supported. File not saved.';
2611
2612                break;
2613        }
2614
2615    //imagedestroy($this->imageResized);
2616
2617    // *** Display error if a file type is not supported.
2618    if ($error != '') {
2619        $this->errorArray[] = $error . ' support is NOT enabled. File not saved.';
2620    }
2621    }
2622
2623## --------------------------------------------------------
2624
2625  public function displayImage($fileType = 'jpg', $imageQuality="100")
2626    # Author:     Jarrod Oberto
2627    # Date:       18-11-09
2628    # Purpose:    Display images directly to the browser
2629    # Param in:   The image type you want to display
2630    # Param out:
2631    # Reference:
2632    # Notes:
2633    #
2634  {
2635      if (!is_resource($this->imageResized)) {
2636          if ($this->debug) {
2637              throw new Exception('saveImage: This is not a resource.');
2638          } else {
2639              throw new Exception();
2640          }
2641      }
2642
2643      switch ($fileType) {
2644            case 'jpg':
2645            case 'jpeg':
2646        header('Content-type: image/jpeg');
2647        imagejpeg($this->imageResized, '', $imageQuality);
2648
2649                break;
2650            case 'gif':
2651        header('Content-type: image/gif');
2652        imagegif($this->imageResized);
2653
2654                break;
2655            case 'png':
2656        header('Content-type: image/png');
2657
2658        // *** Scale quality from 0-100 to 0-9
2659        $scaleQuality = round(($imageQuality/100) * 9);
2660
2661        // *** Invert qualit setting as 0 is best, not 9
2662        $invertScaleQuality = 9 - $scaleQuality;
2663
2664        imagepng($this->imageResized, '', $invertScaleQuality);
2665
2666        break;
2667      case 'bmp':
2668        echo 'bmp file format is not supported.';
2669
2670        break;
2671
2672            // ... etc
2673
2674            default:
2675        // *** No extension - No save.
2676                break;
2677        }
2678
2679    //imagedestroy($this->imageResized);
2680  }
2681
2682## --------------------------------------------------------
2683
2684  public function setTransparency($bool)
2685  # Sep 2011
2686  {
2687      $this->keepTransparency = $bool;
2688  }
2689
2690## --------------------------------------------------------
2691
2692  public function setFillColor($value)
2693  # Sep 2011
2694    # Param in:   (mixed) $value: (array) Could be an array of RGB
2695  #               (str) Could be hex #ffffff or #fff, fff, ffffff
2696  #
2697  # If the keepTransparency is set to false, then no transparency is to be used.
2698  # This is ideal when you want to save as jpg.
2699  #
2700  # this method allows you to set the background color to use instead of
2701  # transparency.
2702  #
2703  {
2704      $colorArray = $this->formatColor($value);
2705      $this->fillColorArray = $colorArray;
2706  }
2707
2708## --------------------------------------------------------
2709
2710  public function setCropFromTop($value)
2711  # Sep 2011
2712  {
2713      $this->cropFromTopPercent = $value;
2714  }
2715
2716## --------------------------------------------------------
2717
2718    public function testGDInstalled()
2719    # Author:     Jarrod Oberto
2720    # Date:       27-02-08
2721    # Purpose:    Test to see if GD is installed
2722    # Param in:   n/a
2723    # Param out:  (bool) True is gd extension loaded otherwise false
2724    # Reference:
2725    # Notes:
2726    #
2727    {
2728        if (extension_loaded('gd') && function_exists('gd_info')) {
2729            $gdInstalled = true;
2730        } else {
2731            $gdInstalled = false;
2732        }
2733
2734        return $gdInstalled;
2735    }
2736
2737## --------------------------------------------------------
2738
2739    public function testEXIFInstalled()
2740    # Author:     Jarrod Oberto
2741    # Date:       08-05-11
2742    # Purpose:    Test to see if EXIF is installed
2743    # Param in:   n/a
2744    # Param out:  (bool) True is exif extension loaded otherwise false
2745    # Reference:
2746    # Notes:
2747    #
2748    {
2749        if (extension_loaded('exif')) {
2750            $exifInstalled = true;
2751        } else {
2752            $exifInstalled = false;
2753        }
2754
2755        return $exifInstalled;
2756    }
2757
2758## --------------------------------------------------------
2759
2760    public function testIsImage($image)
2761    # Author:     Jarrod Oberto
2762    # Date:       27-02-08
2763    # Purpose:    Test if file is an image
2764    # Param in:   n/a
2765    # Param out:  n/a
2766    # Reference:
2767    # Notes:
2768    #
2769    {
2770        if ($image) {
2771            $fileIsImage = true;
2772        } else {
2773            $fileIsImage = false;
2774        }
2775
2776        return $fileIsImage;
2777    }
2778
2779## --------------------------------------------------------
2780
2781    public function testFunct()
2782    # Author:     Jarrod Oberto
2783    # Date:       27-02-08
2784    # Purpose:    Test Function
2785    # Param in:   n/a
2786    # Param out:  n/a
2787    # Reference:
2788    # Notes:
2789    #
2790    {
2791        echo $this->height;
2792    }
2793
2794## --------------------------------------------------------
2795
2796    public function setForceStretch($value)
2797    # Author:     Jarrod Oberto
2798    # Date:       23-12-10
2799    # Purpose:
2800    # Param in:   (bool) $value
2801    # Param out:  n/a
2802    # Reference:
2803    # Notes:
2804    #
2805    {
2806        $this->forceStretch = $value;
2807    }
2808
2809## --------------------------------------------------------
2810
2811    public function setFile($fileName)
2812    # Author:     Jarrod Oberto
2813    # Date:       28-02-08
2814    # Purpose:
2815    # Param in:   n/a
2816    # Param out:  n/a
2817    # Reference:
2818    # Notes:
2819    #
2820    {
2821        self::__construct($fileName);
2822    }
2823
2824## --------------------------------------------------------
2825
2826  public function getFileName()
2827    # Author:     Jarrod Oberto
2828    # Date:       10-09-08
2829    # Purpose:
2830    # Param in:   n/a
2831    # Param out:  n/a
2832    # Reference:
2833    # Notes:
2834    #
2835  {
2836      return $this->fileName;
2837  }
2838
2839## --------------------------------------------------------
2840
2841  public function getHeight()
2842  {
2843      return $this->height;
2844  }
2845
2846## --------------------------------------------------------
2847
2848  public function getWidth()
2849  {
2850      return $this->width;
2851  }
2852
2853## --------------------------------------------------------
2854
2855  public function getOriginalHeight()
2856  {
2857      return $this->heightOriginal;
2858  }
2859
2860## --------------------------------------------------------
2861
2862  public function getOriginalWidth()
2863  {
2864      return $this->widthOriginal;
2865  }
2866
2867## --------------------------------------------------------
2868
2869  public function getErrors()
2870    # Author:     Jarrod Oberto
2871    # Date:       19-11-09
2872    # Purpose:    Returns the error array
2873    # Param in:   n/a
2874    # Param out:  Array of errors
2875    # Reference:
2876    # Notes:
2877    #
2878  {
2879      return $this->errorArray;
2880  }
2881
2882## --------------------------------------------------------
2883
2884  private function checkInterlaceImage($isEnabled)
2885  # jpg will use progressive (they don't use interace)
2886  {
2887      if ($isEnabled) {
2888          imageinterlace($this->imageResized, $isEnabled);
2889      }
2890  }
2891
2892## --------------------------------------------------------
2893
2894  protected function formatColor($value)
2895    # Author:     Jarrod Oberto
2896    # Date:       09-05-11
2897    # Purpose:    Determine color method passed in and return color as RGB
2898    # Param in:   (mixed) $value: (array) Could be an array of RGB
2899  #               (str) Could be hex #ffffff or #fff, fff, ffffff
2900    # Param out:
2901    # Reference:
2902    # Notes:
2903    #
2904  {
2905      $rgbArray = array();
2906
2907    // *** If it's an array it should be R, G, B
2908    if (is_array($value)) {
2909        if (key($value) == 0 && count($value) == 3) {
2910            $rgbArray['r'] = $value[0];
2911            $rgbArray['g'] = $value[1];
2912            $rgbArray['b'] = $value[2];
2913        } else {
2914            $rgbArray = $value;
2915        }
2916    } elseif (fix_strtolower($value) == 'transparent') {
2917        $rgbArray = array(
2918            'r' => 255,
2919            'g' => 255,
2920            'b' => 255,
2921            'a' => 127,
2922        );
2923    } else {
2924
2925      // *** ...Else it should be hex. Let's make it RGB
2926      $rgbArray = $this -> hex2dec($value);
2927    }
2928
2929      return $rgbArray;
2930  }
2931
2932  ## --------------------------------------------------------
2933
2934  public function hex2dec($hex)
2935  # Purpose:  Convert #hex color to RGB
2936  {
2937      $color = str_replace('#', '', $hex);
2938
2939      if (strlen($color) == 3) {
2940          $color = $color . $color;
2941      }
2942
2943      $rgb = array(
2944          'r' => hexdec(substr($color, 0, 2)),
2945          'g' => hexdec(substr($color, 2, 2)),
2946          'b' => hexdec(substr($color, 4, 2)),
2947          'a' => 0,
2948      );
2949
2950      return $rgb;
2951  }
2952
2953  ## --------------------------------------------------------
2954
2955  private function createImageColor($colorArray)
2956  {
2957      $r = $colorArray['r'];
2958      $g = $colorArray['g'];
2959      $b = $colorArray['b'];
2960
2961      return imagecolorallocate($this->imageResized, $r, $g, $b);
2962  }
2963
2964  ## --------------------------------------------------------
2965
2966  private function testColorExists($colorArray)
2967  {
2968      $r = $colorArray['r'];
2969      $g = $colorArray['g'];
2970      $b = $colorArray['b'];
2971
2972      if (imagecolorexact($this->imageResized, $r, $g, $b) == -1) {
2973          return false;
2974      } else {
2975          return true;
2976      }
2977  }
2978
2979  ## --------------------------------------------------------
2980
2981  private function findUnusedGreen()
2982  # Purpose:  We find a green color suitable to use like green-screen effect.
2983  #     Therefore, the color must not exist in the image.
2984  {
2985      $green = 255;
2986
2987      do {
2988          $greenChroma = array(0, $green, 0);
2989          $colorArray = $this->formatColor($greenChroma);
2990          $match = $this->testColorExists($colorArray);
2991          $green--;
2992      } while ($match == false && $green > 0);
2993
2994    // *** If no match, just bite the bullet and use green value of 255
2995    if (!$match) {
2996        $greenChroma = array(0, $green, 0);
2997    }
2998
2999      return $greenChroma;
3000  }
3001
3002  ## --------------------------------------------------------
3003
3004  private function findUnusedBlue()
3005  # Purpose:  We find a green color suitable to use like green-screen effect.
3006  #     Therefore, the color must not exist in the image.
3007  {
3008      $blue = 255;
3009
3010      do {
3011          $blueChroma = array(0, 0, $blue);
3012          $colorArray = $this->formatColor($blueChroma);
3013          $match = $this->testColorExists($colorArray);
3014          $blue--;
3015      } while ($match == false && $blue > 0);
3016
3017    // *** If no match, just bite the bullet and use blue value of 255
3018    if (!$match) {
3019        $blueChroma = array(0, 0, $blue);
3020    }
3021
3022      return $blueChroma;
3023  }
3024
3025  ## --------------------------------------------------------
3026
3027  private function invertTransparency($value, $originalMax, $invert=true)
3028  # Purpose:  This does two things:
3029  #       1) Convert the range from 0-127 to 0-100
3030  #       2) Inverts value to 100 is not transparent while 0 is fully
3031  #          transparent (like Photoshop)
3032  {
3033      // *** Test max range
3034    if ($value > $originalMax) {
3035        $value = $originalMax;
3036    }
3037
3038    // *** Test min range
3039    if ($value < 0) {
3040        $value = 0;
3041    }
3042
3043      if ($invert) {
3044          return $originalMax - (($value/100) * $originalMax);
3045      } else {
3046          return ($value/100) * $originalMax;
3047      }
3048  }
3049
3050  ## --------------------------------------------------------
3051
3052  private function transparentImage($src)
3053  {
3054      // *** making images with white bg transparent
3055    $r1 = 0;
3056      $g1 = 255;
3057      $b1 = 0;
3058      for ($x = 0; $x < imagesx($src); ++$x) {
3059          for ($y = 0; $y < imagesy($src); ++$y) {
3060              $color = imagecolorat($src, $x, $y);
3061              $r = ($color >> 16) & 0xFF;
3062              $g = ($color >> 8) & 0xFF;
3063              $b = $color & 0xFF;
3064              for ($i = 0; $i < 270; $i++) {
3065                  //if ($r . $g . $b == ($r1 + $i) . ($g1 + $i) . ($b1 + $i)) {
3066          if ($r == 0 && $g == 255 && $b == 0) {
3067              //if ($g == 255) {
3068            $trans_colour = imagecolorallocatealpha($src, 0, 0, 0, 127);
3069              imagefill($src, $x, $y, $trans_colour);
3070          }
3071              }
3072          }
3073      }
3074
3075      return $src;
3076  }
3077
3078  ## --------------------------------------------------------
3079
3080  public function checkStringStartsWith($needle, $haystack)
3081  # Check if a string starts with a specific pattern
3082  {
3083      return substr($haystack, 0, strlen($needle))==$needle;
3084  }
3085
3086/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*-
3087  BMP SUPPORT (SAVING) - James Heinrich
3088*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*/
3089
3090  private function GD2BMPstring(&$gd_image)
3091    # Author:     James Heinrich
3092    # Purpose:    Save file as type bmp
3093    # Param in:   The image canvas (passed as ref)
3094    # Param out:
3095    # Reference:
3096    # Notes:    This code was stripped out of two external files
3097  #       (phpthumb.bmp.php,phpthumb.functions.php) and added below to
3098  #       avoid dependancies.
3099    #
3100  {
3101      $imageX = imagesx($gd_image);
3102      $imageY = imagesy($gd_image);
3103
3104      $BMP = '';
3105      for ($y = ($imageY - 1); $y >= 0; $y--) {
3106          $thisline = '';
3107          for ($x = 0; $x < $imageX; $x++) {
3108              $argb = $this->GetPixelColor($gd_image, $x, $y);
3109              $thisline .= chr($argb['blue']).chr($argb['green']).chr($argb['red']);
3110          }
3111          while (strlen($thisline) % 4) {
3112              $thisline .= "\x00";
3113          }
3114          $BMP .= $thisline;
3115      }
3116
3117      $bmpSize = strlen($BMP) + 14 + 40;
3118    // BITMAPFILEHEADER [14 bytes] - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_62uq.asp
3119    $BITMAPFILEHEADER  = 'BM';                                    // WORD    bfType;
3120    $BITMAPFILEHEADER .= $this->LittleEndian2String($bmpSize, 4); // DWORD   bfSize;
3121    $BITMAPFILEHEADER .= $this->LittleEndian2String(0, 2); // WORD    bfReserved1;
3122    $BITMAPFILEHEADER .= $this->LittleEndian2String(0, 2); // WORD    bfReserved2;
3123    $BITMAPFILEHEADER .= $this->LittleEndian2String(54, 4); // DWORD   bfOffBits;
3124
3125    // BITMAPINFOHEADER - [40 bytes] http://msdn.microsoft.com/library/en-us/gdi/bitmaps_1rw2.asp
3126    $BITMAPINFOHEADER  = $this->LittleEndian2String(40, 4); // DWORD  biSize;
3127    $BITMAPINFOHEADER .= $this->LittleEndian2String($imageX, 4); // LONG   biWidth;
3128    $BITMAPINFOHEADER .= $this->LittleEndian2String($imageY, 4); // LONG   biHeight;
3129    $BITMAPINFOHEADER .= $this->LittleEndian2String(1, 2); // WORD   biPlanes;
3130    $BITMAPINFOHEADER .= $this->LittleEndian2String(24, 2); // WORD   biBitCount;
3131    $BITMAPINFOHEADER .= $this->LittleEndian2String(0, 4); // DWORD  biCompression;
3132    $BITMAPINFOHEADER .= $this->LittleEndian2String(0, 4); // DWORD  biSizeImage;
3133    $BITMAPINFOHEADER .= $this->LittleEndian2String(2835, 4); // LONG   biXPelsPerMeter;
3134    $BITMAPINFOHEADER .= $this->LittleEndian2String(2835, 4); // LONG   biYPelsPerMeter;
3135    $BITMAPINFOHEADER .= $this->LittleEndian2String(0, 4); // DWORD  biClrUsed;
3136    $BITMAPINFOHEADER .= $this->LittleEndian2String(0, 4); // DWORD  biClrImportant;
3137
3138    return $BITMAPFILEHEADER.$BITMAPINFOHEADER.$BMP;
3139  }
3140
3141## --------------------------------------------------------
3142
3143  private function GetPixelColor(&$img, $x, $y)
3144    # Author:     James Heinrich
3145    # Purpose:
3146    # Param in:
3147    # Param out:
3148    # Reference:
3149    # Notes:
3150    #
3151  {
3152      if (!is_resource($img)) {
3153          return false;
3154      }
3155
3156      return @imagecolorsforindex($img, @imagecolorat($img, $x, $y));
3157  }
3158
3159## --------------------------------------------------------
3160
3161  private function LittleEndian2String($number, $minbytes=1)
3162    # Author:     James Heinrich
3163    # Purpose:    BMP SUPPORT (SAVING)
3164    # Param in:
3165    # Param out:
3166    # Reference:
3167    # Notes:
3168    #
3169  {
3170      $intstring = '';
3171      while ($number > 0) {
3172          $intstring = $intstring.chr($number & 255);
3173          $number >>= 8;
3174      }
3175
3176      return str_pad($intstring, $minbytes, "\x00", STR_PAD_RIGHT);
3177  }
3178
3179/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*-
3180  BMP SUPPORT (READING)
3181*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*/
3182
3183  private function ImageCreateFromBMP($filename)
3184    # Author:     DHKold
3185    # Date:     The 15th of June 2005
3186  # Version:    2.0B
3187    # Purpose:    To create an image from a BMP file.
3188    # Param in:   BMP file to open.
3189    # Param out:  Return a resource like the other ImageCreateFrom functions
3190    # Reference:  http://us3.php.net/manual/en/function.imagecreate.php#53879
3191  # Bug fix:    Author:   domelca at terra dot es
3192  #       Date:   06 March 2008
3193  #       Fix:    Correct 16bit BMP support
3194    # Notes:
3195  #
3196  {
3197
3198    //Ouverture du fichier en mode binaire
3199    if (!$f1 = fopen($filename, "rb")) {
3200        return false;
3201    }
3202
3203    //1 : Chargement des ent�tes FICHIER
3204    $FILE = unpack("vfile_type/Vfile_size/Vreserved/Vbitmap_offset", fread($f1, 14));
3205      if ($FILE['file_type'] != 19778) {
3206          return false;
3207      }
3208
3209    //2 : Chargement des ent�tes BMP
3210    $BMP = unpack('Vheader_size/Vwidth/Vheight/vplanes/vbits_per_pixel'.
3211           '/Vcompression/Vsize_bitmap/Vhoriz_resolution'.
3212           '/Vvert_resolution/Vcolors_used/Vcolors_important', fread($f1, 40));
3213      $BMP['colors'] = 2** $BMP['bits_per_pixel'];
3214
3215      if ($BMP['size_bitmap'] == 0) {
3216          $BMP['size_bitmap'] = $FILE['file_size'] - $FILE['bitmap_offset'];
3217      }
3218
3219      $BMP['bytes_per_pixel'] = $BMP['bits_per_pixel']/8;
3220      $BMP['bytes_per_pixel2'] = ceil($BMP['bytes_per_pixel']);
3221      $BMP['decal'] = ($BMP['width']*$BMP['bytes_per_pixel']/4);
3222      $BMP['decal'] -= floor($BMP['width']*$BMP['bytes_per_pixel']/4);
3223      $BMP['decal'] = 4-(4*$BMP['decal']);
3224
3225      if ($BMP['decal'] == 4) {
3226          $BMP['decal'] = 0;
3227      }
3228
3229    //3 : Chargement des couleurs de la palette
3230    $PALETTE = array();
3231      if ($BMP['colors'] < 16777216) {
3232          $PALETTE = unpack('V'.$BMP['colors'], fread($f1, $BMP['colors']*4));
3233      }
3234
3235    //4 : Cr�ation de l'image
3236    $IMG = fread($f1, $BMP['size_bitmap']);
3237      $VIDE = chr(0);
3238
3239      $res = imagecreatetruecolor($BMP['width'], $BMP['height']);
3240      $P = 0;
3241      $Y = $BMP['height']-1;
3242      while ($Y >= 0) {
3243          $X=0;
3244          while ($X < $BMP['width']) {
3245              if ($BMP['bits_per_pixel'] == 24) {
3246                  $COLOR = unpack("V", substr($IMG, $P, 3).$VIDE);
3247              } elseif ($BMP['bits_per_pixel'] == 16) {
3248
3249          /*
3250           * BMP 16bit fix
3251           * =================
3252           *
3253           * Ref: http://us3.php.net/manual/en/function.imagecreate.php#81604
3254           *
3255           * Notes:
3256           * "don't work with bmp 16 bits_per_pixel. change pixel
3257           * generator for this."
3258           *
3259           */
3260
3261          // *** Original code (don't work)
3262          //$COLOR = unpack("n",substr($IMG,$P,2));
3263          //$COLOR[1] = $PALETTE[$COLOR[1]+1];
3264
3265          $COLOR = unpack("v", substr($IMG, $P, 2));
3266                  $blue = ($COLOR[1] & 0x001f) << 3;
3267                  $green = ($COLOR[1] & 0x07e0) >> 3;
3268                  $red = ($COLOR[1] & 0xf800) >> 8;
3269                  $COLOR[1] = $red * 65536 + $green * 256 + $blue;
3270              } elseif ($BMP['bits_per_pixel'] == 8) {
3271                  $COLOR = unpack("n", $VIDE.substr($IMG, $P, 1));
3272                  $COLOR[1] = $PALETTE[$COLOR[1]+1];
3273              } elseif ($BMP['bits_per_pixel'] == 4) {
3274                  $COLOR = unpack("n", $VIDE.substr($IMG, floor($P), 1));
3275                  if (($P*2)%2 == 0) {
3276                      $COLOR[1] = ($COLOR[1] >> 4) ;
3277                  } else {
3278                      $COLOR[1] = ($COLOR[1] & 0x0F);
3279                  }
3280                  $COLOR[1] = $PALETTE[$COLOR[1]+1];
3281              } elseif ($BMP['bits_per_pixel'] == 1) {
3282                  $COLOR = unpack("n", $VIDE.substr($IMG, floor($P), 1));
3283                  if (($P*8)%8 == 0) {
3284                      $COLOR[1] =  $COLOR[1]        >>7;
3285                  } elseif (($P*8)%8 == 1) {
3286                      $COLOR[1] = ($COLOR[1] & 0x40)>>6;
3287                  } elseif (($P*8)%8 == 2) {
3288                      $COLOR[1] = ($COLOR[1] & 0x20)>>5;
3289                  } elseif (($P*8)%8 == 3) {
3290                      $COLOR[1] = ($COLOR[1] & 0x10)>>4;
3291                  } elseif (($P*8)%8 == 4) {
3292                      $COLOR[1] = ($COLOR[1] & 0x8)>>3;
3293                  } elseif (($P*8)%8 == 5) {
3294                      $COLOR[1] = ($COLOR[1] & 0x4)>>2;
3295                  } elseif (($P*8)%8 == 6) {
3296                      $COLOR[1] = ($COLOR[1] & 0x2)>>1;
3297                  } elseif (($P*8)%8 == 7) {
3298                      $COLOR[1] = ($COLOR[1] & 0x1);
3299                  }
3300                  $COLOR[1] = $PALETTE[$COLOR[1]+1];
3301              } else {
3302                  return false;
3303              }
3304
3305              imagesetpixel($res, $X, $Y, $COLOR[1]);
3306              $X++;
3307              $P += $BMP['bytes_per_pixel'];
3308          }
3309
3310          $Y--;
3311          $P+=$BMP['decal'];
3312      }
3313    //Fermeture du fichier
3314    fclose($f1);
3315
3316      return $res;
3317  }
3318
3319/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*-
3320  PSD SUPPORT (READING)
3321*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*/
3322
3323  private function imagecreatefrompsd($fileName)
3324  # Author:     Tim de Koning
3325  # Version:    1.3
3326  # Purpose:    To create an image from a PSD file.
3327  # Param in:   PSD file to open.
3328  # Param out:  Return a resource like the other ImageCreateFrom functions
3329  # Reference:  http://www.kingsquare.nl/phppsdreader
3330  # Notes:
3331  #
3332  {
3333      if (file_exists($this->psdReaderPath)) {
3334          include_once $this->psdReaderPath;
3335
3336          $psdReader = new PhpPsdReader($fileName);
3337
3338          if (isset($psdReader->infoArray['error'])) {
3339              return '';
3340          } else {
3341              return $psdReader->getImage();
3342          }
3343      } else {
3344          return false;
3345      }
3346  }
3347
3348## --------------------------------------------------------
3349
3350    public function __destruct()
3351    {
3352        if (is_resource($this->imageResized)) {
3353            imagedestroy($this->imageResized);
3354        }
3355    }
3356
3357## --------------------------------------------------------
3358}
3359
3360/*
3361 *    Example with some API calls (outdated):
3362 *
3363 *
3364 *      ===============================
3365 *      Compulsary
3366 *      ===============================
3367 *
3368 *      include("classes/resize_class.php");
3369 *
3370 *      // *** Initialise object
3371 *      $magicianObj = new resize('images/cars/large/a.jpg');
3372 *
3373 *      // *** Turn off stretching (optional)
3374 *      $magicianObj -> setForceStretch(false);
3375 *
3376 *      // *** Resize object
3377 *      $magicianObj -> resizeImage(150, 100, 0);
3378 *
3379 *      ===============================
3380 *      Image options - can run none, one, or all.
3381 *      ===============================
3382 *
3383 *      //  *** Add watermark
3384 *        $magicianObj -> addWatermark('stamp.png');
3385 *
3386 *          // *** Add text
3387 *      $magicianObj -> addText('testing...');
3388 *
3389 *      ===============================
3390 *      Output options - can run one, or the other, or both.
3391 *      ===============================
3392 *
3393 *      // *** Save image to disk
3394 *      $magicianObj -> saveImage('images/cars/large/b.jpg', 100);
3395 *
3396 *          // *** Or output to screen (params in can be jpg, gif, png)
3397 *      $magicianObj -> displayImage('png');
3398 *
3399 *      ===============================
3400 *      Return options - return errors. nice for debuggin.
3401 *      ===============================
3402 *
3403 *      // *** Return error array
3404 *      $errorArray = $magicianObj -> getErrors();
3405 *
3406 *
3407 *      ===============================
3408 *      Cleanup options - not really neccessary, but good practice
3409 *      ===============================
3410 *
3411 *      // *** Free used memory
3412 *      $magicianObj -> __destruct();
3413 */
3414