Project

General

Profile

1
<?php
2
/**
3
 * PhpThumb GD Thumb Class Definition File
4
 * 
5
 * This file contains the definition for the GdThumb object
6
 * 
7
 * PHP Version 5 with GD 2.0+
8
 * PhpThumb : PHP Thumb Library <http://phpthumb.gxdlabs.com>
9
 * Copyright (c) 2009, Ian Selby/Gen X Design
10
 * 
11
 * Author(s): Ian Selby <ian@gen-x-design.com>
12
 * 
13
 * Licensed under the MIT License
14
 * Redistributions of files must retain the above copyright notice.
15
 * 
16
 * @author Ian Selby <ian@gen-x-design.com>
17
 * @copyright Copyright (c) 2009 Gen X Design
18
 * @link http://phpthumb.gxdlabs.com
19
 * @license http://www.opensource.org/licenses/mit-license.php The MIT License
20
 * @version 3.0
21
 * @package PhpThumb
22
 * @filesource
23
 */
24

    
25
/**
26
 * GdThumb Class Definition
27
 * 
28
 * This is the GD Implementation of the PHP Thumb library.
29
 * 
30
 * @package PhpThumb
31
 * @subpackage Core
32
 */
33
class GdThumb extends ThumbBase
34
{
35
    /**
36
     * The prior image (before manipulation)
37
     * 
38
     * @var resource
39
     */
40
    protected $oldImage;
41
    /**
42
     * The working image (used during manipulation)
43
     * 
44
     * @var resource
45
     */
46
    protected $workingImage;
47
    /**
48
     * The current dimensions of the image
49
     * 
50
     * @var array
51
     */
52
    protected $currentDimensions;
53
    /**
54
     * The new, calculated dimensions of the image
55
     * 
56
     * @var array
57
     */
58
    protected $newDimensions;
59
    /**
60
     * The options for this class
61
     * 
62
     * This array contains various options that determine the behavior in
63
     * various functions throughout the class.  Functions note which specific 
64
     * option key / values are used in their documentation
65
     * 
66
     * @var array
67
     */
68
    protected $options;
69
    /**
70
     * The maximum width an image can be after resizing (in pixels)
71
     * 
72
     * @var int
73
     */
74
    protected $maxWidth;
75
    /**
76
     * The maximum height an image can be after resizing (in pixels)
77
     * 
78
     * @var int
79
     */
80
    protected $maxHeight;
81
    /**
82
     * The percentage to resize the image by
83
     * 
84
     * @var int
85
     */
86
    protected $percent;
87
    
88
    /**
89
     * Class Constructor
90
     * 
91
     * @return GdThumb 
92
     * @param string $fileName
93
     */
94
    public function __construct ($fileName, $options = array(), $isDataStream = false)
95
    {
96
        parent::__construct($fileName, $isDataStream);
97
        
98
        $this->determineFormat();
99
        
100
        if ($this->isDataStream === false)
101
        {
102
            $this->verifyFormatCompatiblity();
103
        }
104
        
105
        switch ($this->format)
106
        {
107
            case 'GIF':
108
                $this->oldImage = imagecreatefromgif($this->fileName);
109
                break;
110
            case 'JPG':
111
                $this->oldImage = imagecreatefromjpeg($this->fileName);
112
                break;
113
            case 'PNG':
114
                $this->oldImage = imagecreatefrompng($this->fileName);
115
                break;
116
            case 'STRING':
117
                $this->oldImage = imagecreatefromstring($this->fileName);
118
                break;
119
        }
120
    
121
        $this->currentDimensions = array
122
        (
123
            'width'     => imagesx($this->oldImage),
124
            'height'    => imagesy($this->oldImage)
125
        );
126
        
127
        $this->setOptions($options);
128
        
129
        // TODO: Port gatherImageMeta to a separate function that can be called to extract exif data
130
    }
131
    
132
    /**
133
     * Class Destructor
134
     * 
135
     */
136
    public function __destruct ()
137
    {
138
        if (is_resource($this->oldImage))
139
        {
140
            imagedestroy($this->oldImage);
141
        }
142
        
143
        if (is_resource($this->workingImage))
144
        {
145
            imagedestroy($this->workingImage);
146
        }
147
    }
148
    
149
    ##############################
150
    # ----- API FUNCTIONS ------ #
151
    ##############################
152
    
153
    /**
154
     * Resizes an image to be no larger than $maxWidth or $maxHeight
155
     * 
156
     * If either param is set to zero, then that dimension will not be considered as a part of the resize.
157
     * Additionally, if $this->options['resizeUp'] is set to true (false by default), then this function will
158
     * also scale the image up to the maximum dimensions provided.
159
     * 
160
     * @param int $maxWidth The maximum width of the image in pixels
161
     * @param int $maxHeight The maximum height of the image in pixels
162
     * @return GdThumb
163
     */
164
    public function resize ($maxWidth = 0, $maxHeight = 0)
165
    {
166
        // make sure our arguments are valid
167
        if (!is_numeric($maxWidth))
168
        {
169
            throw new InvalidArgumentException('$maxWidth must be numeric');
170
        }
171
        
172
        if (!is_numeric($maxHeight))
173
        {
174
            throw new InvalidArgumentException('$maxHeight must be numeric');
175
        }
176
        
177
        // make sure we're not exceeding our image size if we're not supposed to
178
        if ($this->options['resizeUp'] === false)
179
        {
180
            $this->maxHeight    = (intval($maxHeight) > $this->currentDimensions['height']) ? $this->currentDimensions['height'] : $maxHeight;
181
            $this->maxWidth        = (intval($maxWidth) > $this->currentDimensions['width']) ? $this->currentDimensions['width'] : $maxWidth;
182
        }
183
        else
184
        {
185
            $this->maxHeight    = intval($maxHeight);
186
            $this->maxWidth        = intval($maxWidth);
187
        }
188
        
189
        // get the new dimensions...
190
        $this->calcImageSize($this->currentDimensions['width'], $this->currentDimensions['height']);
191
        
192
        // create the working image
193
        if (function_exists('imagecreatetruecolor'))
194
        {
195
            $this->workingImage = imagecreatetruecolor($this->newDimensions['newWidth'], $this->newDimensions['newHeight']);
196
        }
197
        else
198
        {
199
            $this->workingImage = imagecreate($this->newDimensions['newWidth'], $this->newDimensions['newHeight']);
200
        }
201
        
202
        $this->preserveAlpha();        
203
        
204
        // and create the newly sized image
205
        imagecopyresampled
206
        (
207
            $this->workingImage,
208
            $this->oldImage,
209
            0,
210
            0,
211
            0,
212
            0,
213
            $this->newDimensions['newWidth'],
214
            $this->newDimensions['newHeight'],
215
            $this->currentDimensions['width'],
216
            $this->currentDimensions['height']
217
        );
218

    
219
        // update all the variables and resources to be correct
220
        $this->oldImage                     = $this->workingImage;
221
        $this->currentDimensions['width']     = $this->newDimensions['newWidth'];
222
        $this->currentDimensions['height']     = $this->newDimensions['newHeight'];
223
        
224
        return $this;
225
    }
226
    
227
    /**
228
     * Adaptively Resizes the Image
229
     * 
230
     * This function attempts to get the image to as close to the provided dimensions as possible, and then crops the 
231
     * remaining overflow (from the center) to get the image to be the size specified
232
     * 
233
     * @param int $maxWidth
234
     * @param int $maxHeight
235
     * @return GdThumb
236
     */
237
    public function adaptiveResize ($width, $height)
238
    {
239
        // make sure our arguments are valid
240
        if (!is_numeric($width) || $width  == 0)
241
        {
242
            throw new InvalidArgumentException('$width must be numeric and greater than zero');
243
        }
244
        
245
        if (!is_numeric($height) || $height == 0)
246
        {
247
            throw new InvalidArgumentException('$height must be numeric and greater than zero');
248
        }
249
        
250
        // make sure we're not exceeding our image size if we're not supposed to
251
        if ($this->options['resizeUp'] === false)
252
        {
253
            $this->maxHeight    = (intval($height) > $this->currentDimensions['height']) ? $this->currentDimensions['height'] : $height;
254
            $this->maxWidth        = (intval($width) > $this->currentDimensions['width']) ? $this->currentDimensions['width'] : $width;
255
        }
256
        else
257
        {
258
            $this->maxHeight    = intval($height);
259
            $this->maxWidth        = intval($width);
260
        }
261
        
262
        $this->calcImageSizeStrict($this->currentDimensions['width'], $this->currentDimensions['height']);
263
        
264
        // resize the image to be close to our desired dimensions
265
        $this->resize($this->newDimensions['newWidth'], $this->newDimensions['newHeight']);
266
        
267
        // reset the max dimensions...
268
        if ($this->options['resizeUp'] === false)
269
        {
270
            $this->maxHeight    = (intval($height) > $this->currentDimensions['height']) ? $this->currentDimensions['height'] : $height;
271
            $this->maxWidth        = (intval($width) > $this->currentDimensions['width']) ? $this->currentDimensions['width'] : $width;
272
        }
273
        else
274
        {
275
            $this->maxHeight    = intval($height);
276
            $this->maxWidth        = intval($width);
277
        }
278
        
279
        // create the working image
280
        if (function_exists('imagecreatetruecolor'))
281
        {
282
            $this->workingImage = imagecreatetruecolor($this->maxWidth, $this->maxHeight);
283
        }
284
        else
285
        {
286
            $this->workingImage = imagecreate($this->maxWidth, $this->maxHeight);
287
        }
288
        
289
        $this->preserveAlpha();
290
        
291
        $cropWidth    = $this->maxWidth;
292
        $cropHeight    = $this->maxHeight;
293
        $cropX         = 0;
294
        $cropY         = 0;
295
        
296
        // now, figure out how to crop the rest of the image...
297
        if ($this->currentDimensions['width'] > $this->maxWidth)
298
        {
299
            $cropX = intval(($this->currentDimensions['width'] - $this->maxWidth) / 2);
300
        }
301
        elseif ($this->currentDimensions['height'] > $this->maxHeight)
302
        {
303
            $cropY = intval(($this->currentDimensions['height'] - $this->maxHeight) / 2);
304
        }
305
        
306
        imagecopyresampled
307
        (
308
            $this->workingImage,
309
            $this->oldImage,
310
            0,
311
            0,
312
            $cropX,
313
            $cropY,
314
            $cropWidth,
315
            $cropHeight,
316
            $cropWidth,
317
            $cropHeight
318
        );
319
        
320
        // update all the variables and resources to be correct
321
        $this->oldImage                     = $this->workingImage;
322
        $this->currentDimensions['width']     = $this->maxWidth;
323
        $this->currentDimensions['height']     = $this->maxHeight;
324
        
325
        return $this;
326
    }
327
    
328
    /**
329
     * Resizes an image by a given percent uniformly
330
     * 
331
     * Percentage should be whole number representation (i.e. 1-100)
332
     * 
333
     * @param int $percent
334
     * @return GdThumb
335
     */
336
    public function resizePercent ($percent = 0)
337
    {
338
        if (!is_numeric($percent))
339
        {
340
            throw new InvalidArgumentException ('$percent must be numeric');
341
        }
342
        
343
        $this->percent = intval($percent);
344
        
345
        $this->calcImageSizePercent($this->currentDimensions['width'], $this->currentDimensions['height']);
346
        
347
        if (function_exists('imagecreatetruecolor'))
348
        {
349
            $this->workingImage = imagecreatetruecolor($this->newDimensions['newWidth'], $this->newDimensions['newHeight']);
350
        }
351
        else
352
        {
353
            $this->workingImage = imagecreate($this->newDimensions['newWidth'], $this->newDimensions['newHeight']);
354
        }
355
        
356
        $this->preserveAlpha();
357
        
358
        ImageCopyResampled(
359
            $this->workingImage,
360
            $this->oldImage,
361
            0,
362
            0,
363
            0,
364
            0,
365
            $this->newDimensions['newWidth'],
366
            $this->newDimensions['newHeight'],
367
            $this->currentDimensions['width'],
368
            $this->currentDimensions['height']
369
        );
370

    
371
        $this->oldImage                     = $this->workingImage;
372
        $this->currentDimensions['width']     = $this->newDimensions['newWidth'];
373
        $this->currentDimensions['height']     = $this->newDimensions['newHeight'];
374
        
375
        return $this;
376
    }
377
    
378
    /**
379
     * Crops an image from the center with provided dimensions
380
     * 
381
     * If no height is given, the width will be used as a height, thus creating a square crop
382
     * 
383
     * @param int $cropWidth
384
     * @param int $cropHeight
385
     * @return GdThumb
386
     */
387
    public function cropFromCenter ($cropWidth, $cropHeight = null)
388
    {
389
        if (!is_numeric($cropWidth))
390
        {
391
            throw new InvalidArgumentException('$cropWidth must be numeric');
392
        }
393
        
394
        if ($cropHeight !== null && !is_numeric($cropHeight))
395
        {
396
            throw new InvalidArgumentException('$cropHeight must be numeric');
397
        }
398
        
399
        if ($cropHeight === null)
400
        {
401
            $cropHeight = $cropWidth;
402
        }
403
        
404
        $cropWidth    = ($this->currentDimensions['width'] < $cropWidth) ? $this->currentDimensions['width'] : $cropWidth;
405
        $cropHeight = ($this->currentDimensions['height'] < $cropHeight) ? $this->currentDimensions['height'] : $cropHeight;
406
        
407
        $cropX = intval(($this->currentDimensions['width'] - $cropWidth) / 2);
408
        $cropY = intval(($this->currentDimensions['height'] - $cropHeight) / 2);
409
        
410
        $this->crop($cropX, $cropY, $cropWidth, $cropHeight);
411
        
412
        return $this;
413
    }
414
    
415
    /**
416
     * Vanilla Cropping - Crops from x,y with specified width and height
417
     * 
418
     * @param int $startX
419
     * @param int $startY
420
     * @param int $cropWidth
421
     * @param int $cropHeight
422
     * @return GdThumb
423
     */
424
    public function crop ($startX, $startY, $cropWidth, $cropHeight)
425
    {
426
        // validate input
427
        if (!is_numeric($startX))
428
        {
429
            throw new InvalidArgumentException('$startX must be numeric');
430
        }
431
        
432
        if (!is_numeric($startY))
433
        {
434
            throw new InvalidArgumentException('$startY must be numeric');
435
        }
436
        
437
        if (!is_numeric($cropWidth))
438
        {
439
            throw new InvalidArgumentException('$cropWidth must be numeric');
440
        }
441
        
442
        if (!is_numeric($cropHeight))
443
        {
444
            throw new InvalidArgumentException('$cropHeight must be numeric');
445
        }
446
        
447
        // do some calculations
448
        $cropWidth    = ($this->currentDimensions['width'] < $cropWidth) ? $this->currentDimensions['width'] : $cropWidth;
449
        $cropHeight = ($this->currentDimensions['height'] < $cropHeight) ? $this->currentDimensions['height'] : $cropHeight;
450
        
451
        // ensure everything's in bounds
452
        if (($startX + $cropWidth) > $this->currentDimensions['width'])
453
        {
454
            $startX = ($this->currentDimensions['width'] - $cropWidth);
455
            
456
        }
457
        
458
        if (($startY + $cropHeight) > $this->currentDimensions['height'])
459
        {
460
            $startY = ($this->currentDimensions['height'] - $cropHeight);
461
        }
462
        
463
        if ($startX < 0) 
464
        {
465
            $startX = 0;
466
        }
467
        
468
        if ($startY < 0) 
469
        {
470
            $startY = 0;
471
        }
472
        
473
        // create the working image
474
        if (function_exists('imagecreatetruecolor'))
475
        {
476
            $this->workingImage = imagecreatetruecolor($cropWidth, $cropHeight);
477
        }
478
        else
479
        {
480
            $this->workingImage = imagecreate($cropWidth, $cropHeight);
481
        }
482
        
483
        $this->preserveAlpha();
484
        
485
        imagecopyresampled
486
        (
487
            $this->workingImage,
488
            $this->oldImage,
489
            0,
490
            0,
491
            $startX,
492
            $startY,
493
            $cropWidth,
494
            $cropHeight,
495
            $cropWidth,
496
            $cropHeight
497
        );
498
        
499
        $this->oldImage                     = $this->workingImage;
500
        $this->currentDimensions['width']     = $cropWidth;
501
        $this->currentDimensions['height']     = $cropHeight;
502
        
503
        return $this;
504
    }
505
    
506
    /**
507
     * Rotates image either 90 degrees clockwise or counter-clockwise
508
     * 
509
     * @param string $direction
510
     * @retunrn GdThumb
511
     */
512
    public function rotateImage ($direction = 'CW') 
513
    {
514
        if ($direction == 'CW') 
515
        {
516
            $this->rotateImageNDegrees(90);
517
        }
518
        else 
519
        {
520
            $this->rotateImageNDegrees(-90);
521
        }
522
        
523
        return $this;
524
    }
525
    
526
    /**
527
     * Rotates image specified number of degrees
528
     * 
529
     * @param int $degrees
530
     * @return GdThumb
531
     */
532
    public function rotateImageNDegrees ($degrees)
533
    {
534
        if (!is_numeric($degrees))
535
        {
536
            throw new InvalidArgumentException('$degrees must be numeric');
537
        }
538
        
539
        if (!function_exists('imagerotate'))
540
        {
541
            throw new RuntimeException('Your version of GD does not support image rotation.');
542
        }
543
        
544
        $this->workingImage = imagerotate($this->oldImage, $degrees, 0);
545
        
546
        $newWidth                             = $this->currentDimensions['height'];
547
        $newHeight                             = $this->currentDimensions['width'];
548
        $this->oldImage                     = $this->workingImage;
549
        $this->currentDimensions['width']     = $newWidth;
550
        $this->currentDimensions['height']     = $newHeight;
551
        
552
        return $this;
553
    }
554
    
555
    /**
556
     * Shows an image
557
     * 
558
     * This function will show the current image by first sending the appropriate header
559
     * for the format, and then outputting the image data. If headers have already been sent, 
560
     * a runtime exception will be thrown 
561
     * 
562
     * @param bool $rawData Whether or not the raw image stream should be output
563
     * @return GdThumb
564
     */
565
    public function show ($rawData = false) 
566
    {
567
        if (headers_sent())
568
        {
569
            throw new RuntimeException('Cannot show image, headers have already been sent');
570
        }
571
        
572
        switch ($this->format) 
573
        {
574
            case 'GIF':
575
                if ($rawData === false) 
576
                { 
577
                    header('Content-type: image/gif'); 
578
                }
579
                imagegif($this->oldImage);
580
                break;
581
            case 'JPG':
582
                if ($rawData === false) 
583
                { 
584
                    header('Content-type: image/jpeg'); 
585
                }
586
                imagejpeg($this->oldImage, null, $this->options['jpegQuality']);
587
                break;
588
            case 'PNG':
589
            case 'STRING':
590
                if ($rawData === false) 
591
                { 
592
                    header('Content-type: image/png'); 
593
                }
594
                imagepng($this->oldImage);
595
                break;
596
        }
597
        
598
        return $this;
599
    }
600
    
601
    /**
602
     * Returns the Working Image as a String
603
     *
604
     * This function is useful for getting the raw image data as a string for storage in
605
     * a database, or other similar things.
606
     *
607
     * @return string
608
     */
609
    public function getImageAsString ()
610
    {
611
        $data = null;
612
        ob_start();
613
        $this->show(true);
614
        $data = ob_get_contents();
615
        ob_end_clean();
616
        
617
        return $data;
618
    }
619
    
620
    /**
621
     * Saves an image
622
     * 
623
     * This function will make sure the target directory is writeable, and then save the image.
624
     * 
625
     * If the target directory is not writeable, the function will try to correct the permissions (if allowed, this
626
     * is set as an option ($this->options['correctPermissions']).  If the target cannot be made writeable, then a
627
     * RuntimeException is thrown.
628
     * 
629
     * TODO: Create additional paramter for color matte when saving images with alpha to non-alpha formats (i.e. PNG => JPG)
630
     * 
631
     * @param string $fileName The full path and filename of the image to save
632
     * @param string $format The format to save the image in (optional, must be one of [GIF,JPG,PNG]
633
     * @return GdThumb
634
     */
635
    public function save ($fileName, $format = null)
636
    {
637
        $validFormats = array('GIF', 'JPG', 'PNG');
638
        $format = ($format !== null) ? strtoupper($format) : $this->format;
639

    
640
        if (!in_array($format, $validFormats))
641
        {
642
            throw new InvalidArgumentException ('Invalid format type specified in save function: ' . $format);
643
        }
644
        
645
        // make sure the directory is writeable
646
        if (!is_writeable(dirname($fileName)))
647
        {
648
            // try to correct the permissions
649
            if ($this->options['correctPermissions'] === true)
650
            {
651
                @chmod(dirname($fileName), 0777);
652
                
653
                // throw an exception if not writeable
654
                if (!is_writeable(dirname($fileName)))
655
                {
656
                    throw new RuntimeException ('File is not writeable, and could not correct permissions: ' . $fileName);
657
                }
658
            }
659
            // throw an exception if not writeable
660
            else
661
            {
662
                throw new RuntimeException ('File not writeable: ' . $fileName);
663
            }
664
        }
665
        
666
        switch ($format) 
667
        {
668
            case 'GIF':
669
                imagegif($this->oldImage, $fileName);
670
                break;
671
            case 'JPG':
672
                imagejpeg($this->oldImage, $fileName, $this->options['jpegQuality']);
673
                break;
674
            case 'PNG':
675
                imagepng($this->oldImage, $fileName);
676
                break;
677
        }
678
        
679
        return $this;
680
    }
681
    
682
    #################################
683
    # ----- GETTERS / SETTERS ----- #
684
    #################################
685
    
686
    /**
687
     * Sets $this->options to $options
688
     * 
689
     * @param array $options
690
     */
691
    public function setOptions ($options = array())
692
    {
693
        // make sure we've got an array for $this->options (could be null)
694
        if (!is_array($this->options))
695
        {
696
            $this->options = array();
697
        }
698
        
699
        // make sure we've gotten a proper argument
700
        if (!is_array($options))
701
        {
702
            throw new InvalidArgumentException ('setOptions requires an array');
703
        }
704
        
705
        // we've yet to init the default options, so create them here
706
        if (sizeof($this->options) == 0)
707
        {
708
            $defaultOptions = array 
709
            (
710
                'resizeUp'                => false,
711
                'jpegQuality'            => 100,
712
                'correctPermissions'    => true,
713
                'preserveAlpha'            => true,
714
                'alphaMaskColor'        => array (255, 255, 255),
715
                'preserveTransparency'    => true,
716
                'transparencyMaskColor'    => array (0, 0, 0)
717
            );
718
        }
719
        // otherwise, let's use what we've got already
720
        else
721
        {
722
            $defaultOptions = $this->options;
723
        }
724
        
725
        $this->options = array_merge($defaultOptions, $options);
726
    }
727
    
728
    /**
729
     * Returns $currentDimensions.
730
     *
731
     * @see GdThumb::$currentDimensions
732
     */
733
    public function getCurrentDimensions ()
734
    {
735
        return $this->currentDimensions;
736
    }
737
    
738
    /**
739
     * Sets $currentDimensions.
740
     *
741
     * @param object $currentDimensions
742
     * @see GdThumb::$currentDimensions
743
     */
744
    public function setCurrentDimensions ($currentDimensions)
745
    {
746
        $this->currentDimensions = $currentDimensions;
747
    }
748
    
749
    /**
750
     * Returns $maxHeight.
751
     *
752
     * @see GdThumb::$maxHeight
753
     */
754
    public function getMaxHeight ()
755
    {
756
        return $this->maxHeight;
757
    }
758
    
759
    /**
760
     * Sets $maxHeight.
761
     *
762
     * @param object $maxHeight
763
     * @see GdThumb::$maxHeight
764
     */
765
    public function setMaxHeight ($maxHeight)
766
    {
767
        $this->maxHeight = $maxHeight;
768
    }
769
    
770
    /**
771
     * Returns $maxWidth.
772
     *
773
     * @see GdThumb::$maxWidth
774
     */
775
    public function getMaxWidth ()
776
    {
777
        return $this->maxWidth;
778
    }
779
    
780
    /**
781
     * Sets $maxWidth.
782
     *
783
     * @param object $maxWidth
784
     * @see GdThumb::$maxWidth
785
     */
786
    public function setMaxWidth ($maxWidth)
787
    {
788
        $this->maxWidth = $maxWidth;
789
    }
790
    
791
    /**
792
     * Returns $newDimensions.
793
     *
794
     * @see GdThumb::$newDimensions
795
     */
796
    public function getNewDimensions ()
797
    {
798
        return $this->newDimensions;
799
    }
800
    
801
    /**
802
     * Sets $newDimensions.
803
     *
804
     * @param object $newDimensions
805
     * @see GdThumb::$newDimensions
806
     */
807
    public function setNewDimensions ($newDimensions)
808
    {
809
        $this->newDimensions = $newDimensions;
810
    }
811
    
812
    /**
813
     * Returns $options.
814
     *
815
     * @see GdThumb::$options
816
     */
817
    public function getOptions ()
818
    {
819
        return $this->options;
820
    }
821
    
822
    /**
823
     * Returns $percent.
824
     *
825
     * @see GdThumb::$percent
826
     */
827
    public function getPercent ()
828
    {
829
        return $this->percent;
830
    }
831
    
832
    /**
833
     * Sets $percent.
834
     *
835
     * @param object $percent
836
     * @see GdThumb::$percent
837
     */
838
    public function setPercent ($percent)
839
    {
840
        $this->percent = $percent;
841
    } 
842
    
843
    /**
844
     * Returns $oldImage.
845
     *
846
     * @see GdThumb::$oldImage
847
     */
848
    public function getOldImage ()
849
    {
850
        return $this->oldImage;
851
    }
852
    
853
    /**
854
     * Sets $oldImage.
855
     *
856
     * @param object $oldImage
857
     * @see GdThumb::$oldImage
858
     */
859
    public function setOldImage ($oldImage)
860
    {
861
        $this->oldImage = $oldImage;
862
    }
863
    
864
    /**
865
     * Returns $workingImage.
866
     *
867
     * @see GdThumb::$workingImage
868
     */
869
    public function getWorkingImage ()
870
    {
871
        return $this->workingImage;
872
    }
873
    
874
    /**
875
     * Sets $workingImage.
876
     *
877
     * @param object $workingImage
878
     * @see GdThumb::$workingImage
879
     */
880
    public function setWorkingImage ($workingImage)
881
    {
882
        $this->workingImage = $workingImage;
883
    } 
884
    
885
    
886
    #################################
887
    # ----- UTILITY FUNCTIONS ----- #
888
    #################################
889
    
890
    /**
891
     * Calculates a new width and height for the image based on $this->maxWidth and the provided dimensions
892
     * 
893
     * @return array 
894
     * @param int $width
895
     * @param int $height
896
     */
897
    protected function calcWidth ($width, $height)
898
    {
899
        $newWidthPercentage    = (100 * $this->maxWidth) / $width;
900
        $newHeight            = ($height * $newWidthPercentage) / 100;
901
        
902
        return array
903
        (
904
            'newWidth'    => intval($this->maxWidth),
905
            'newHeight'    => intval($newHeight)
906
        );
907
    }
908
    
909
    /**
910
     * Calculates a new width and height for the image based on $this->maxWidth and the provided dimensions
911
     * 
912
     * @return array 
913
     * @param int $width
914
     * @param int $height
915
     */
916
    protected function calcHeight ($width, $height)
917
    {
918
        $newHeightPercentage    = (100 * $this->maxHeight) / $height;
919
        $newWidth                 = ($width * $newHeightPercentage) / 100;
920
        
921
        return array
922
        (
923
            'newWidth'    => ceil($newWidth),
924
            'newHeight'    => ceil($this->maxHeight)
925
        );
926
    }
927
    
928
    /**
929
     * Calculates a new width and height for the image based on $this->percent and the provided dimensions
930
     * 
931
     * @return array 
932
     * @param int $width
933
     * @param int $height
934
     */
935
    protected function calcPercent ($width, $height)
936
    {
937
        $newWidth    = ($width * $this->percent) / 100;
938
        $newHeight    = ($height * $this->percent) / 100;
939
        
940
        return array 
941
        (
942
            'newWidth'    => ceil($newWidth),
943
            'newHeight'    => ceil($newHeight)
944
        );
945
    }
946
    
947
    /**
948
     * Calculates the new image dimensions
949
     * 
950
     * These calculations are based on both the provided dimensions and $this->maxWidth and $this->maxHeight
951
     * 
952
     * @param int $width
953
     * @param int $height
954
     */
955
    protected function calcImageSize ($width, $height)
956
    {
957
        $newSize = array
958
        (
959
            'newWidth'    => $width,
960
            'newHeight'    => $height
961
        );
962
        
963
        if ($this->maxWidth > 0)
964
        {
965
            $newSize = $this->calcWidth($width, $height);
966
            
967
            if ($this->maxHeight > 0 && $newSize['newHeight'] > $this->maxHeight)
968
            {
969
                $newSize = $this->calcHeight($newSize['newWidth'], $newSize['newHeight']);
970
            }
971
        }
972
        
973
        if ($this->maxHeight > 0)
974
        {
975
            $newSize = $this->calcHeight($width, $height);
976
            
977
            if ($this->maxWidth > 0 && $newSize['newWidth'] > $this->maxWidth)
978
            {
979
                $newSize = $this->calcWidth($newSize['newWidth'], $newSize['newHeight']);
980
            }
981
        }
982
        
983
        $this->newDimensions = $newSize;
984
    }
985
    
986
    /**
987
     * Calculates new image dimensions, not allowing the width and height to be less than either the max width or height 
988
     * 
989
     * @param int $width
990
     * @param int $height
991
     */
992
    protected function calcImageSizeStrict ($width, $height)
993
    {
994
        // first, we need to determine what the longest resize dimension is..
995
        if ($this->maxWidth >= $this->maxHeight)
996
        {
997
            // and determine the longest original dimension
998
            if ($width > $height)
999
            {
1000
                $newDimensions = $this->calcHeight($width, $height);
1001
                
1002
                if ($newDimensions['newWidth'] < $this->maxWidth)
1003
                {
1004
                    $newDimensions = $this->calcWidth($width, $height);
1005
                }
1006
            }
1007
            elseif ($height >= $width)
1008
            {
1009
                $newDimensions = $this->calcWidth($width, $height);
1010
                
1011
                if ($newDimensions['newHeight'] < $this->maxHeight)
1012
                {
1013
                    $newDimensions = $this->calcHeight($width, $height);
1014
                }
1015
            }
1016
        }
1017
        elseif ($this->maxHeight > $this->maxWidth)
1018
        {
1019
            if ($width >= $height)
1020
            {
1021
                $newDimensions = $this->calcWidth($width, $height);
1022
                
1023
                if ($newDimensions['newHeight'] < $this->maxHeight)
1024
                {
1025
                    $newDimensions = $this->calcHeight($width, $height);
1026
                }
1027
            }
1028
            elseif ($height > $width)
1029
            {
1030
                $newDimensions = $this->calcHeight($width, $height);
1031
                
1032
                if ($newDimensions['newWidth'] < $this->maxWidth)
1033
                {
1034
                    $newDimensions = $this->calcWidth($width, $height);
1035
                }
1036
            }
1037
        }
1038
        
1039
        $this->newDimensions = $newDimensions;
1040
    }
1041
    
1042
    /**
1043
     * Calculates new dimensions based on $this->percent and the provided dimensions
1044
     * 
1045
     * @param int $width
1046
     * @param int $height
1047
     */
1048
    protected function calcImageSizePercent ($width, $height)
1049
    {
1050
        if ($this->percent > 0)
1051
        {
1052
            $this->newDimensions = $this->calcPercent($width, $height);
1053
        }
1054
    }
1055
    
1056
    /**
1057
     * Determines the file format by mime-type
1058
     * 
1059
     * This function will throw exceptions for invalid images / mime-types
1060
     * 
1061
     */
1062
    protected function determineFormat ()
1063
    {
1064
        if ($this->isDataStream === true)
1065
        {
1066
            $this->format = 'STRING';
1067
            return;
1068
        }
1069
        
1070
        $formatInfo = getimagesize($this->fileName);
1071
        
1072
        // non-image files will return false
1073
        if ($formatInfo === false)
1074
        {
1075
            if ($this->remoteImage)
1076
            {
1077
                $this->triggerError('Could not determine format of remote image: ' . $this->fileName);
1078
            }
1079
            else
1080
            {
1081
                $this->triggerError('File is not a valid image: ' . $this->fileName);
1082
            }
1083
            
1084
            // make sure we really stop execution
1085
            return;
1086
        }
1087
        
1088
        $mimeType = isset($formatInfo['mime']) ? $formatInfo['mime'] : null;
1089
        
1090
        switch ($mimeType)
1091
        {
1092
            case 'image/gif':
1093
                $this->format = 'GIF';
1094
                break;
1095
            case 'image/jpeg':
1096
                $this->format = 'JPG';
1097
                break;
1098
            case 'image/png':
1099
                $this->format = 'PNG';
1100
                break;
1101
            default:
1102
                $this->triggerError('Image format not supported: ' . $mimeType);
1103
        }
1104
    }
1105
    
1106
    /**
1107
     * Makes sure the correct GD implementation exists for the file type
1108
     * 
1109
     */
1110
    protected function verifyFormatCompatiblity ()
1111
    {
1112
        $isCompatible     = true;
1113
        $gdInfo            = gd_info();
1114
        
1115
        switch ($this->format)
1116
        {
1117
            case 'GIF':
1118
                $isCompatible = $gdInfo['GIF Create Support'];
1119
                break;
1120
            case 'JPG':
1121
                $isCompatible = (isset($gdInfo['JPG Support']) || isset($gdInfo['JPEG Support'])) ? true : false;
1122
                break;
1123
            case 'PNG':
1124
                $isCompatible = $gdInfo[$this->format . ' Support'];
1125
                break;
1126
            default:
1127
                $isCompatible = false;
1128
        }
1129
        
1130
        if (!$isCompatible)
1131
        {
1132
            // one last check for "JPEG" instead
1133
            $isCompatible = $gdInfo['JPEG Support'];
1134
            
1135
            if (!$isCompatible)
1136
            {
1137
                $this->triggerError('Your GD installation does not support ' . $this->format . ' image types');
1138
            }
1139
        }
1140
    }
1141
    
1142
    /**
1143
     * Preserves the alpha or transparency for PNG and GIF files
1144
     * 
1145
     * Alpha / transparency will not be preserved if the appropriate options are set to false.
1146
     * Also, the GIF transparency is pretty skunky (the results aren't awesome), but it works like a 
1147
     * champ... that's the nature of GIFs tho, so no huge surprise.
1148
     * 
1149
     * This functionality was originally suggested by commenter Aimi (no links / site provided) - Thanks! :)
1150
     *   
1151
     */
1152
    protected function preserveAlpha ()
1153
    {
1154
        if ($this->format == 'PNG' && $this->options['preserveAlpha'] === true)
1155
        {
1156
            imagealphablending($this->workingImage, false);
1157
            
1158
            $colorTransparent = imagecolorallocatealpha
1159
            (
1160
                $this->workingImage, 
1161
                $this->options['alphaMaskColor'][0], 
1162
                $this->options['alphaMaskColor'][1], 
1163
                $this->options['alphaMaskColor'][2], 
1164
                0
1165
            );
1166
            
1167
            imagefill($this->workingImage, 0, 0, $colorTransparent);
1168
            imagesavealpha($this->workingImage, true);
1169
        }
1170
        // preserve transparency in GIFs... this is usually pretty rough tho
1171
        if ($this->format == 'GIF' && $this->options['preserveTransparency'] === true)
1172
        {
1173
            $colorTransparent = imagecolorallocate
1174
            (
1175
                $this->workingImage, 
1176
                $this->options['transparencyMaskColor'][0], 
1177
                $this->options['transparencyMaskColor'][1], 
1178
                $this->options['transparencyMaskColor'][2] 
1179
            );
1180
            
1181
            imagecolortransparent($this->workingImage, $colorTransparent);
1182
            imagetruecolortopalette($this->workingImage, true, 256);
1183
        }
1184
    }
1185
}
(1-1/5)