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)