/lenasys/trunk

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/lenasys/trunk
20.1.1 by galaxyAbstractor
* Added an simple admin panel to the codeviewer-cmssy stuff
1
<?php  if ( ! defined('BASEPATH')) exit('No direct script access allowed');
2
/**
3
 * CodeIgniter
4
 *
5
 * An open source application development framework for PHP 5.1.6 or newer
6
 *
7
 * @package		CodeIgniter
8
 * @author		ExpressionEngine Dev Team
9
 * @copyright	Copyright (c) 2008 - 2011, EllisLab, Inc.
10
 * @license		http://codeigniter.com/user_guide/license.html
11
 * @link		http://codeigniter.com
12
 * @since		Version 1.0
13
 * @filesource
14
 */
15
16
// ------------------------------------------------------------------------
17
18
/**
19
 * Image Manipulation class
20
 *
21
 * @package		CodeIgniter
22
 * @subpackage	Libraries
23
 * @category	Image_lib
24
 * @author		ExpressionEngine Dev Team
25
 * @link		http://codeigniter.com/user_guide/libraries/image_lib.html
26
 */
27
class CI_Image_lib {
28
29
	var $image_library		= 'gd2';	// Can be:  imagemagick, netpbm, gd, gd2
30
	var $library_path		= '';
31
	var $dynamic_output		= FALSE;	// Whether to send to browser or write to disk
32
	var $source_image		= '';
33
	var $new_image			= '';
34
	var $width				= '';
35
	var $height				= '';
36
	var $quality			= '90';
37
	var $create_thumb		= FALSE;
38
	var $thumb_marker		= '_thumb';
39
	var $maintain_ratio		= TRUE;		// Whether to maintain aspect ratio when resizing or use hard values
40
	var $master_dim			= 'auto';	// auto, height, or width.  Determines what to use as the master dimension
41
	var $rotation_angle		= '';
42
	var $x_axis				= '';
43
	var	$y_axis				= '';
44
45
	// Watermark Vars
46
	var $wm_text			= '';			// Watermark text if graphic is not used
47
	var $wm_type			= 'text';		// Type of watermarking.  Options:  text/overlay
48
	var $wm_x_transp		= 4;
49
	var $wm_y_transp		= 4;
50
	var $wm_overlay_path	= '';			// Watermark image path
51
	var $wm_font_path		= '';			// TT font
52
	var $wm_font_size		= 17;			// Font size (different versions of GD will either use points or pixels)
53
	var $wm_vrt_alignment	= 'B';			// Vertical alignment:   T M B
54
	var $wm_hor_alignment	= 'C';			// Horizontal alignment: L R C
55
	var $wm_padding			= 0;			// Padding around text
56
	var $wm_hor_offset		= 0;			// Lets you push text to the right
57
	var $wm_vrt_offset		= 0;			// Lets you push  text down
58
	var $wm_font_color		= '#ffffff';	// Text color
59
	var $wm_shadow_color	= '';			// Dropshadow color
60
	var $wm_shadow_distance	= 2;			// Dropshadow distance
61
	var $wm_opacity			= 50;			// Image opacity: 1 - 100  Only works with image
62
63
	// Private Vars
64
	var $source_folder		= '';
65
	var $dest_folder		= '';
66
	var $mime_type			= '';
67
	var $orig_width			= '';
68
	var $orig_height		= '';
69
	var $image_type			= '';
70
	var $size_str			= '';
71
	var $full_src_path		= '';
72
	var $full_dst_path		= '';
73
	var $create_fnc			= 'imagecreatetruecolor';
74
	var $copy_fnc			= 'imagecopyresampled';
75
	var $error_msg			= array();
76
	var $wm_use_drop_shadow	= FALSE;
77
	var $wm_use_truetype	= FALSE;
78
79
	/**
80
	 * Constructor
81
	 *
82
	 * @param	string
83
	 * @return	void
84
	 */
85
	public function __construct($props = array())
86
	{
87
		if (count($props) > 0)
88
		{
89
			$this->initialize($props);
90
		}
91
92
		log_message('debug', "Image Lib Class Initialized");
93
	}
94
95
	// --------------------------------------------------------------------
96
97
	/**
98
	 * Initialize image properties
99
	 *
100
	 * Resets values in case this class is used in a loop
101
	 *
102
	 * @access	public
103
	 * @return	void
104
	 */
105
	function clear()
106
	{
107
		$props = array('source_folder', 'dest_folder', 'source_image', 'full_src_path', 'full_dst_path', 'new_image', 'image_type', 'size_str', 'quality', 'orig_width', 'orig_height', 'width', 'height', 'rotation_angle', 'x_axis', 'y_axis', 'create_fnc', 'copy_fnc', 'wm_overlay_path', 'wm_use_truetype', 'dynamic_output', 'wm_font_size', 'wm_text', 'wm_vrt_alignment', 'wm_hor_alignment', 'wm_padding', 'wm_hor_offset', 'wm_vrt_offset', 'wm_font_color', 'wm_use_drop_shadow', 'wm_shadow_color', 'wm_shadow_distance', 'wm_opacity');
108
109
		foreach ($props as $val)
110
		{
111
			$this->$val = '';
112
		}
113
114
		// special consideration for master_dim
115
		$this->master_dim = 'auto';
116
	}
117
118
	// --------------------------------------------------------------------
119
120
	/**
121
	 * initialize image preferences
122
	 *
123
	 * @access	public
124
	 * @param	array
125
	 * @return	bool
126
	 */
127
	function initialize($props = array())
128
	{
129
		/*
130
		 * Convert array elements into class variables
131
		 */
132
		if (count($props) > 0)
133
		{
134
			foreach ($props as $key => $val)
135
			{
136
				$this->$key = $val;
137
			}
138
		}
139
140
		/*
141
		 * Is there a source image?
142
		 *
143
		 * If not, there's no reason to continue
144
		 *
145
		 */
146
		if ($this->source_image == '')
147
		{
148
			$this->set_error('imglib_source_image_required');
149
			return FALSE;	
150
		}
151
152
		/*
153
		 * Is getimagesize() Available?
154
		 *
155
		 * We use it to determine the image properties (width/height).
156
		 * Note:  We need to figure out how to determine image
157
		 * properties using ImageMagick and NetPBM
158
		 *
159
		 */
160
		if ( ! function_exists('getimagesize'))
161
		{
162
			$this->set_error('imglib_gd_required_for_props');
163
			return FALSE;
164
		}
165
166
		$this->image_library = strtolower($this->image_library);
167
168
		/*
169
		 * Set the full server path
170
		 *
171
		 * The source image may or may not contain a path.
172
		 * Either way, we'll try use realpath to generate the
173
		 * full server path in order to more reliably read it.
174
		 *
175
		 */
176
		if (function_exists('realpath') AND @realpath($this->source_image) !== FALSE)
177
		{
178
			$full_source_path = str_replace("\\", "/", realpath($this->source_image));
179
		}
180
		else
181
		{
182
			$full_source_path = $this->source_image;
183
		}
184
185
		$x = explode('/', $full_source_path);
186
		$this->source_image = end($x);
187
		$this->source_folder = str_replace($this->source_image, '', $full_source_path);
188
189
		// Set the Image Properties
190
		if ( ! $this->get_image_properties($this->source_folder.$this->source_image))
191
		{
192
			return FALSE;	
193
		}
194
195
		/*
196
		 * Assign the "new" image name/path
197
		 *
198
		 * If the user has set a "new_image" name it means
199
		 * we are making a copy of the source image. If not
200
		 * it means we are altering the original.  We'll
201
		 * set the destination filename and path accordingly.
202
		 *
203
		 */
204
		if ($this->new_image == '')
205
		{
206
			$this->dest_image = $this->source_image;
207
			$this->dest_folder = $this->source_folder;
208
		}
209
		else
210
		{
211
			if (strpos($this->new_image, '/') === FALSE AND strpos($this->new_image, '\\') === FALSE)
212
			{
213
				$this->dest_folder = $this->source_folder;
214
				$this->dest_image = $this->new_image;
215
			}
216
			else
217
			{
218
				if (function_exists('realpath') AND @realpath($this->new_image) !== FALSE)
219
				{
220
					$full_dest_path = str_replace("\\", "/", realpath($this->new_image));
221
				}
222
				else
223
				{
224
					$full_dest_path = $this->new_image;
225
				}
226
227
				// Is there a file name?
228
				if ( ! preg_match("#\.(jpg|jpeg|gif|png)$#i", $full_dest_path))
229
				{
230
					$this->dest_folder = $full_dest_path.'/';
231
					$this->dest_image = $this->source_image;
232
				}
233
				else
234
				{
235
					$x = explode('/', $full_dest_path);
236
					$this->dest_image = end($x);
237
					$this->dest_folder = str_replace($this->dest_image, '', $full_dest_path);
238
				}
239
			}
240
		}
241
242
		/*
243
		 * Compile the finalized filenames/paths
244
		 *
245
		 * We'll create two master strings containing the
246
		 * full server path to the source image and the
247
		 * full server path to the destination image.
248
		 * We'll also split the destination image name
249
		 * so we can insert the thumbnail marker if needed.
250
		 *
251
		 */
252
		if ($this->create_thumb === FALSE OR $this->thumb_marker == '')
253
		{
254
			$this->thumb_marker = '';
255
		}
256
257
		$xp	= $this->explode_name($this->dest_image);
258
259
		$filename = $xp['name'];
260
		$file_ext = $xp['ext'];
261
262
		$this->full_src_path = $this->source_folder.$this->source_image;
263
		$this->full_dst_path = $this->dest_folder.$filename.$this->thumb_marker.$file_ext;
264
265
		/*
266
		 * Should we maintain image proportions?
267
		 *
268
		 * When creating thumbs or copies, the target width/height
269
		 * might not be in correct proportion with the source
270
		 * image's width/height.  We'll recalculate it here.
271
		 *
272
		 */
273
		if ($this->maintain_ratio === TRUE && ($this->width != '' AND $this->height != ''))
274
		{
275
			$this->image_reproportion();
276
		}
277
278
		/*
279
		 * Was a width and height specified?
280
		 *
281
		 * If the destination width/height was
282
		 * not submitted we will use the values
283
		 * from the actual file
284
		 *
285
		 */
286
		if ($this->width == '')
287
			$this->width = $this->orig_width;
288
289
		if ($this->height == '')
290
			$this->height = $this->orig_height;
291
292
		// Set the quality
293
		$this->quality = trim(str_replace("%", "", $this->quality));
294
295
		if ($this->quality == '' OR $this->quality == 0 OR ! is_numeric($this->quality))
296
			$this->quality = 90;
297
298
		// Set the x/y coordinates
299
		$this->x_axis = ($this->x_axis == '' OR ! is_numeric($this->x_axis)) ? 0 : $this->x_axis;
300
		$this->y_axis = ($this->y_axis == '' OR ! is_numeric($this->y_axis)) ? 0 : $this->y_axis;
301
302
		// Watermark-related Stuff...
303
		if ($this->wm_font_color != '')
304
		{
305
			if (strlen($this->wm_font_color) == 6)
306
			{
307
				$this->wm_font_color = '#'.$this->wm_font_color;
308
			}
309
		}
310
311
		if ($this->wm_shadow_color != '')
312
		{
313
			if (strlen($this->wm_shadow_color) == 6)
314
			{
315
				$this->wm_shadow_color = '#'.$this->wm_shadow_color;
316
			}
317
		}
318
319
		if ($this->wm_overlay_path != '')
320
		{
321
			$this->wm_overlay_path = str_replace("\\", "/", realpath($this->wm_overlay_path));
322
		}
323
324
		if ($this->wm_shadow_color != '')
325
		{
326
			$this->wm_use_drop_shadow = TRUE;
327
		}
328
329
		if ($this->wm_font_path != '')
330
		{
331
			$this->wm_use_truetype = TRUE;
332
		}
333
334
		return TRUE;
335
	}
336
337
	// --------------------------------------------------------------------
338
339
	/**
340
	 * Image Resize
341
	 *
342
	 * This is a wrapper function that chooses the proper
343
	 * resize function based on the protocol specified
344
	 *
345
	 * @access	public
346
	 * @return	bool
347
	 */
348
	function resize()
349
	{
350
		$protocol = 'image_process_'.$this->image_library;
351
352
		if (preg_match('/gd2$/i', $protocol))
353
		{
354
			$protocol = 'image_process_gd';
355
		}
356
357
		return $this->$protocol('resize');
358
	}
359
360
	// --------------------------------------------------------------------
361
362
	/**
363
	 * Image Crop
364
	 *
365
	 * This is a wrapper function that chooses the proper
366
	 * cropping function based on the protocol specified
367
	 *
368
	 * @access	public
369
	 * @return	bool
370
	 */
371
	function crop()
372
	{
373
		$protocol = 'image_process_'.$this->image_library;
374
375
		if (preg_match('/gd2$/i', $protocol))
376
		{
377
			$protocol = 'image_process_gd';
378
		}
379
380
		return $this->$protocol('crop');
381
	}
382
383
	// --------------------------------------------------------------------
384
385
	/**
386
	 * Image Rotate
387
	 *
388
	 * This is a wrapper function that chooses the proper
389
	 * rotation function based on the protocol specified
390
	 *
391
	 * @access	public
392
	 * @return	bool
393
	 */
394
	function rotate()
395
	{
396
		// Allowed rotation values
397
		$degs = array(90, 180, 270, 'vrt', 'hor');
398
399
		if ($this->rotation_angle == '' OR ! in_array($this->rotation_angle, $degs))
400
		{
401
			$this->set_error('imglib_rotation_angle_required');
402
			return FALSE;	
403
		}
404
405
		// Reassign the width and height
406
		if ($this->rotation_angle == 90 OR $this->rotation_angle == 270)
407
		{
408
			$this->width	= $this->orig_height;
409
			$this->height	= $this->orig_width;
410
		}
411
		else
412
		{
413
			$this->width	= $this->orig_width;
414
			$this->height	= $this->orig_height;
415
		}
416
417
418
		// Choose resizing function
419
		if ($this->image_library == 'imagemagick' OR $this->image_library == 'netpbm')
420
		{
421
			$protocol = 'image_process_'.$this->image_library;
422
423
			return $this->$protocol('rotate');
424
		}
425
426
		if ($this->rotation_angle == 'hor' OR $this->rotation_angle == 'vrt')
427
		{
428
			return $this->image_mirror_gd();
429
		}
430
		else
431
		{
432
			return $this->image_rotate_gd();
433
		}
434
	}
435
436
	// --------------------------------------------------------------------
437
438
	/**
439
	 * Image Process Using GD/GD2
440
	 *
441
	 * This function will resize or crop
442
	 *
443
	 * @access	public
444
	 * @param	string
445
	 * @return	bool
446
	 */
447
	function image_process_gd($action = 'resize')
448
	{
449
		$v2_override = FALSE;
450
451
		// If the target width/height match the source, AND if the new file name is not equal to the old file name
452
		// we'll simply make a copy of the original with the new name... assuming dynamic rendering is off.
453
		if ($this->dynamic_output === FALSE)
454
		{
455
			if ($this->orig_width == $this->width AND $this->orig_height == $this->height)
456
			{
457
				if ($this->source_image != $this->new_image)
458
				{
459
					if (@copy($this->full_src_path, $this->full_dst_path))
460
					{
461
						@chmod($this->full_dst_path, FILE_WRITE_MODE);
462
					}
463
				}
464
465
				return TRUE;
466
			}
467
		}
468
469
		// Let's set up our values based on the action
470
		if ($action == 'crop')
471
		{
472
			//  Reassign the source width/height if cropping
473
			$this->orig_width  = $this->width;
474
			$this->orig_height = $this->height;
475
476
			// GD 2.0 has a cropping bug so we'll test for it
477
			if ($this->gd_version() !== FALSE)
478
			{
479
				$gd_version = str_replace('0', '', $this->gd_version());
480
				$v2_override = ($gd_version == 2) ? TRUE : FALSE;
481
			}
482
		}
483
		else
484
		{
485
			// If resizing the x/y axis must be zero
486
			$this->x_axis = 0;
487
			$this->y_axis = 0;
488
		}
489
490
		//  Create the image handle
491
		if ( ! ($src_img = $this->image_create_gd()))
492
		{
493
			return FALSE;
494
		}
495
496
		//  Create The Image
497
		//
498
		//  old conditional which users report cause problems with shared GD libs who report themselves as "2.0 or greater"
499
		//  it appears that this is no longer the issue that it was in 2004, so we've removed it, retaining it in the comment
500
		//  below should that ever prove inaccurate.
501
		//
502
		//  if ($this->image_library == 'gd2' AND function_exists('imagecreatetruecolor') AND $v2_override == FALSE)
503
		if ($this->image_library == 'gd2' AND function_exists('imagecreatetruecolor'))
504
		{
505
			$create	= 'imagecreatetruecolor';
506
			$copy	= 'imagecopyresampled';
507
		}
508
		else
509
		{
510
			$create	= 'imagecreate';
511
			$copy	= 'imagecopyresized';
512
		}
513
514
		$dst_img = $create($this->width, $this->height);
515
516
		if ($this->image_type == 3) // png we can actually preserve transparency
517
		{
518
			imagealphablending($dst_img, FALSE);
519
			imagesavealpha($dst_img, TRUE);
520
		}
521
522
		$copy($dst_img, $src_img, 0, 0, $this->x_axis, $this->y_axis, $this->width, $this->height, $this->orig_width, $this->orig_height);
523
524
		//  Show the image
525
		if ($this->dynamic_output == TRUE)
526
		{
527
			$this->image_display_gd($dst_img);
528
		}
529
		else
530
		{
531
			// Or save it
532
			if ( ! $this->image_save_gd($dst_img))
533
			{
534
				return FALSE;
535
			}
536
		}
537
538
		//  Kill the file handles
539
		imagedestroy($dst_img);
540
		imagedestroy($src_img);
541
542
		// Set the file to 777
543
		@chmod($this->full_dst_path, FILE_WRITE_MODE);
544
545
		return TRUE;
546
	}
547
548
	// --------------------------------------------------------------------
549
550
	/**
551
	 * Image Process Using ImageMagick
552
	 *
553
	 * This function will resize, crop or rotate
554
	 *
555
	 * @access	public
556
	 * @param	string
557
	 * @return	bool
558
	 */
559
	function image_process_imagemagick($action = 'resize')
560
	{
561
		//  Do we have a vaild library path?
562
		if ($this->library_path == '')
563
		{
564
			$this->set_error('imglib_libpath_invalid');
565
			return FALSE;
566
		}
567
568
		if ( ! preg_match("/convert$/i", $this->library_path))
569
		{
570
			$this->library_path = rtrim($this->library_path, '/').'/';
571
572
			$this->library_path .= 'convert';
573
		}
574
575
		// Execute the command
576
		$cmd = $this->library_path." -quality ".$this->quality;
577
578
		if ($action == 'crop')
579
		{
580
			$cmd .= " -crop ".$this->width."x".$this->height."+".$this->x_axis."+".$this->y_axis." \"$this->full_src_path\" \"$this->full_dst_path\" 2>&1";
581
		}
582
		elseif ($action == 'rotate')
583
		{
584
			switch ($this->rotation_angle)
585
			{
586
				case 'hor'	: $angle = '-flop';
587
					break;
588
				case 'vrt'	: $angle = '-flip';
589
					break;
590
				default		: $angle = '-rotate '.$this->rotation_angle;
591
					break;
592
			}
593
594
			$cmd .= " ".$angle." \"$this->full_src_path\" \"$this->full_dst_path\" 2>&1";
595
		}
596
		else  // Resize
597
		{
598
			$cmd .= " -resize ".$this->width."x".$this->height." \"$this->full_src_path\" \"$this->full_dst_path\" 2>&1";
599
		}
600
601
		$retval = 1;
602
603
		@exec($cmd, $output, $retval);
604
605
		//	Did it work?
606
		if ($retval > 0)
607
		{
608
			$this->set_error('imglib_image_process_failed');
609
			return FALSE;
610
		}
611
612
		// Set the file to 777
613
		@chmod($this->full_dst_path, FILE_WRITE_MODE);
614
615
		return TRUE;
616
	}
617
618
	// --------------------------------------------------------------------
619
620
	/**
621
	 * Image Process Using NetPBM
622
	 *
623
	 * This function will resize, crop or rotate
624
	 *
625
	 * @access	public
626
	 * @param	string
627
	 * @return	bool
628
	 */
629
	function image_process_netpbm($action = 'resize')
630
	{
631
		if ($this->library_path == '')
632
		{
633
			$this->set_error('imglib_libpath_invalid');
634
			return FALSE;
635
		}
636
637
		//  Build the resizing command
638
		switch ($this->image_type)
639
		{
640
			case 1 :
641
						$cmd_in		= 'giftopnm';
642
						$cmd_out	= 'ppmtogif';
643
				break;
644
			case 2 :
645
						$cmd_in		= 'jpegtopnm';
646
						$cmd_out	= 'ppmtojpeg';
647
				break;
648
			case 3 :
649
						$cmd_in		= 'pngtopnm';
650
						$cmd_out	= 'ppmtopng';
651
				break;
652
		}
653
654
		if ($action == 'crop')
655
		{
656
			$cmd_inner = 'pnmcut -left '.$this->x_axis.' -top '.$this->y_axis.' -width '.$this->width.' -height '.$this->height;
657
		}
658
		elseif ($action == 'rotate')
659
		{
660
			switch ($this->rotation_angle)
661
			{
662
				case 90		:	$angle = 'r270';
663
					break;
664
				case 180	:	$angle = 'r180';
665
					break;
666
				case 270	:	$angle = 'r90';
667
					break;
668
				case 'vrt'	:	$angle = 'tb';
669
					break;
670
				case 'hor'	:	$angle = 'lr';
671
					break;
672
			}
673
674
			$cmd_inner = 'pnmflip -'.$angle.' ';
675
		}
676
		else // Resize
677
		{
678
			$cmd_inner = 'pnmscale -xysize '.$this->width.' '.$this->height;
679
		}
680
681
		$cmd = $this->library_path.$cmd_in.' '.$this->full_src_path.' | '.$cmd_inner.' | '.$cmd_out.' > '.$this->dest_folder.'netpbm.tmp';
682
683
		$retval = 1;
684
685
		@exec($cmd, $output, $retval);
686
687
		//  Did it work?
688
		if ($retval > 0)
689
		{
690
			$this->set_error('imglib_image_process_failed');
691
			return FALSE;
692
		}
693
694
		// With NetPBM we have to create a temporary image.
695
		// If you try manipulating the original it fails so
696
		// we have to rename the temp file.
697
		copy ($this->dest_folder.'netpbm.tmp', $this->full_dst_path);
698
		unlink ($this->dest_folder.'netpbm.tmp');
699
		@chmod($this->full_dst_path, FILE_WRITE_MODE);
700
701
		return TRUE;
702
	}
703
704
	// --------------------------------------------------------------------
705
706
	/**
707
	 * Image Rotate Using GD
708
	 *
709
	 * @access	public
710
	 * @return	bool
711
	 */
712
	function image_rotate_gd()
713
	{
714
		//  Create the image handle
715
		if ( ! ($src_img = $this->image_create_gd()))
716
		{
717
			return FALSE;
718
		}
719
720
		// Set the background color
721
		// This won't work with transparent PNG files so we are
722
		// going to have to figure out how to determine the color
723
		// of the alpha channel in a future release.
724
725
		$white	= imagecolorallocate($src_img, 255, 255, 255);
726
727
		//  Rotate it!
728
		$dst_img = imagerotate($src_img, $this->rotation_angle, $white);
729
730
		//  Save the Image
731
		if ($this->dynamic_output == TRUE)
732
		{
733
			$this->image_display_gd($dst_img);
734
		}
735
		else
736
		{
737
			// Or save it
738
			if ( ! $this->image_save_gd($dst_img))
739
			{
740
				return FALSE;
741
			}
742
		}
743
744
		//  Kill the file handles
745
		imagedestroy($dst_img);
746
		imagedestroy($src_img);
747
748
		// Set the file to 777
749
750
		@chmod($this->full_dst_path, FILE_WRITE_MODE);
751
752
		return TRUE;
753
	}
754
755
	// --------------------------------------------------------------------
756
757
	/**
758
	 * Create Mirror Image using GD
759
	 *
760
	 * This function will flip horizontal or vertical
761
	 *
762
	 * @access	public
763
	 * @return	bool
764
	 */
765
	function image_mirror_gd()
766
	{
767
		if ( ! $src_img = $this->image_create_gd())
768
		{
769
			return FALSE;
770
		}
771
772
		$width  = $this->orig_width;
773
		$height = $this->orig_height;
774
775
		if ($this->rotation_angle == 'hor')
776
		{
777
			for ($i = 0; $i < $height; $i++)
778
			{
779
				$left  = 0;
780
				$right = $width-1;
781
782
				while ($left < $right)
783
				{
784
					$cl = imagecolorat($src_img, $left, $i);
785
					$cr = imagecolorat($src_img, $right, $i);
786
787
					imagesetpixel($src_img, $left, $i, $cr);
788
					imagesetpixel($src_img, $right, $i, $cl);
789
790
					$left++;
791
					$right--;
792
				}
793
			}
794
		}
795
		else
796
		{
797
			for ($i = 0; $i < $width; $i++)
798
			{
799
				$top = 0;
800
				$bot = $height-1;
801
802
				while ($top < $bot)
803
				{
804
					$ct = imagecolorat($src_img, $i, $top);
805
					$cb = imagecolorat($src_img, $i, $bot);
806
807
					imagesetpixel($src_img, $i, $top, $cb);
808
					imagesetpixel($src_img, $i, $bot, $ct);
809
810
					$top++;
811
					$bot--;
812
				}
813
			}
814
		}
815
816
		//  Show the image
817
		if ($this->dynamic_output == TRUE)
818
		{
819
			$this->image_display_gd($src_img);
820
		}
821
		else
822
		{
823
			// Or save it
824
			if ( ! $this->image_save_gd($src_img))
825
			{
826
				return FALSE;
827
			}
828
		}
829
830
		//  Kill the file handles
831
		imagedestroy($src_img);
832
833
		// Set the file to 777
834
		@chmod($this->full_dst_path, FILE_WRITE_MODE);
835
836
		return TRUE;
837
	}
838
839
	// --------------------------------------------------------------------
840
841
	/**
842
	 * Image Watermark
843
	 *
844
	 * This is a wrapper function that chooses the type
845
	 * of watermarking based on the specified preference.
846
	 *
847
	 * @access	public
848
	 * @param	string
849
	 * @return	bool
850
	 */
851
	function watermark()
852
	{
853
		if ($this->wm_type == 'overlay')
854
		{
855
			return $this->overlay_watermark();
856
		}
857
		else
858
		{
859
			return $this->text_watermark();
860
		}
861
	}
862
863
	// --------------------------------------------------------------------
864
865
	/**
866
	 * Watermark - Graphic Version
867
	 *
868
	 * @access	public
869
	 * @return	bool
870
	 */
871
	function overlay_watermark()
872
	{
873
		if ( ! function_exists('imagecolortransparent'))
874
		{
875
			$this->set_error('imglib_gd_required');
876
			return FALSE;
877
		}
878
879
		//  Fetch source image properties
880
		$this->get_image_properties();
881
882
		//  Fetch watermark image properties
883
		$props			= $this->get_image_properties($this->wm_overlay_path, TRUE);
884
		$wm_img_type	= $props['image_type'];
885
		$wm_width		= $props['width'];
886
		$wm_height		= $props['height'];
887
888
		//  Create two image resources
889
		$wm_img  = $this->image_create_gd($this->wm_overlay_path, $wm_img_type);
890
		$src_img = $this->image_create_gd($this->full_src_path);
891
892
		// Reverse the offset if necessary
893
		// When the image is positioned at the bottom
894
		// we don't want the vertical offset to push it
895
		// further down.  We want the reverse, so we'll
896
		// invert the offset.  Same with the horizontal
897
		// offset when the image is at the right
898
899
		$this->wm_vrt_alignment = strtoupper(substr($this->wm_vrt_alignment, 0, 1));
900
		$this->wm_hor_alignment = strtoupper(substr($this->wm_hor_alignment, 0, 1));
901
902
		if ($this->wm_vrt_alignment == 'B')
903
			$this->wm_vrt_offset = $this->wm_vrt_offset * -1;
904
905
		if ($this->wm_hor_alignment == 'R')
906
			$this->wm_hor_offset = $this->wm_hor_offset * -1;
907
908
		//  Set the base x and y axis values
909
		$x_axis = $this->wm_hor_offset + $this->wm_padding;
910
		$y_axis = $this->wm_vrt_offset + $this->wm_padding;
911
912
		//  Set the vertical position
913
		switch ($this->wm_vrt_alignment)
914
		{
915
			case 'T':
916
				break;
917
			case 'M':	$y_axis += ($this->orig_height / 2) - ($wm_height / 2);
918
				break;
919
			case 'B':	$y_axis += $this->orig_height - $wm_height;
920
				break;
921
		}
922
923
		//  Set the horizontal position
924
		switch ($this->wm_hor_alignment)
925
		{
926
			case 'L':
927
				break;
928
			case 'C':	$x_axis += ($this->orig_width / 2) - ($wm_width / 2);
929
				break;
930
			case 'R':	$x_axis += $this->orig_width - $wm_width;
931
				break;
932
		}
933
934
		//  Build the finalized image
935
		if ($wm_img_type == 3 AND function_exists('imagealphablending'))
936
		{
937
			@imagealphablending($src_img, TRUE);
938
		}
939
940
		// Set RGB values for text and shadow
941
		$rgba = imagecolorat($wm_img, $this->wm_x_transp, $this->wm_y_transp);
942
		$alpha = ($rgba & 0x7F000000) >> 24;
943
944
		// make a best guess as to whether we're dealing with an image with alpha transparency or no/binary transparency
945
		if ($alpha > 0)
946
		{
947
			// copy the image directly, the image's alpha transparency being the sole determinant of blending
948
			imagecopy($src_img, $wm_img, $x_axis, $y_axis, 0, 0, $wm_width, $wm_height);
949
		}
950
		else
951
		{
952
			// set our RGB value from above to be transparent and merge the images with the specified opacity
953
			imagecolortransparent($wm_img, imagecolorat($wm_img, $this->wm_x_transp, $this->wm_y_transp));
954
			imagecopymerge($src_img, $wm_img, $x_axis, $y_axis, 0, 0, $wm_width, $wm_height, $this->wm_opacity);
955
		}
956
957
		//  Output the image
958
		if ($this->dynamic_output == TRUE)
959
		{
960
			$this->image_display_gd($src_img);
961
		}
962
		else
963
		{
964
			if ( ! $this->image_save_gd($src_img))
965
			{
966
				return FALSE;
967
			}
968
		}
969
970
		imagedestroy($src_img);
971
		imagedestroy($wm_img);
972
973
		return TRUE;
974
	}
975
976
	// --------------------------------------------------------------------
977
978
	/**
979
	 * Watermark - Text Version
980
	 *
981
	 * @access	public
982
	 * @return	bool
983
	 */
984
	function text_watermark()
985
	{
986
		if ( ! ($src_img = $this->image_create_gd()))
987
		{
988
			return FALSE;
989
		}
990
991
		if ($this->wm_use_truetype == TRUE AND ! file_exists($this->wm_font_path))
992
		{
993
			$this->set_error('imglib_missing_font');
994
			return FALSE;
995
		}
996
997
		//  Fetch source image properties
998
		$this->get_image_properties();
999
1000
		// Set RGB values for text and shadow
1001
		$this->wm_font_color	= str_replace('#', '', $this->wm_font_color);
1002
		$this->wm_shadow_color	= str_replace('#', '', $this->wm_shadow_color);
1003
1004
		$R1 = hexdec(substr($this->wm_font_color, 0, 2));
1005
		$G1 = hexdec(substr($this->wm_font_color, 2, 2));
1006
		$B1 = hexdec(substr($this->wm_font_color, 4, 2));
1007
1008
		$R2 = hexdec(substr($this->wm_shadow_color, 0, 2));
1009
		$G2 = hexdec(substr($this->wm_shadow_color, 2, 2));
1010
		$B2 = hexdec(substr($this->wm_shadow_color, 4, 2));
1011
1012
		$txt_color	= imagecolorclosest($src_img, $R1, $G1, $B1);
1013
		$drp_color	= imagecolorclosest($src_img, $R2, $G2, $B2);
1014
1015
		// Reverse the vertical offset
1016
		// When the image is positioned at the bottom
1017
		// we don't want the vertical offset to push it
1018
		// further down.  We want the reverse, so we'll
1019
		// invert the offset.  Note: The horizontal
1020
		// offset flips itself automatically
1021
1022
		if ($this->wm_vrt_alignment == 'B')
1023
			$this->wm_vrt_offset = $this->wm_vrt_offset * -1;
1024
1025
		if ($this->wm_hor_alignment == 'R')
1026
			$this->wm_hor_offset = $this->wm_hor_offset * -1;
1027
1028
		// Set font width and height
1029
		// These are calculated differently depending on
1030
		// whether we are using the true type font or not
1031
		if ($this->wm_use_truetype == TRUE)
1032
		{
1033
			if ($this->wm_font_size == '')
1034
				$this->wm_font_size = '17';
1035
1036
			$fontwidth  = $this->wm_font_size-($this->wm_font_size/4);
1037
			$fontheight = $this->wm_font_size;
1038
			$this->wm_vrt_offset += $this->wm_font_size;
1039
		}
1040
		else
1041
		{
1042
			$fontwidth  = imagefontwidth($this->wm_font_size);
1043
			$fontheight = imagefontheight($this->wm_font_size);
1044
		}
1045
1046
		// Set base X and Y axis values
1047
		$x_axis = $this->wm_hor_offset + $this->wm_padding;
1048
		$y_axis = $this->wm_vrt_offset + $this->wm_padding;
1049
1050
		// Set verticle alignment
1051
		if ($this->wm_use_drop_shadow == FALSE)
1052
			$this->wm_shadow_distance = 0;
1053
1054
		$this->wm_vrt_alignment = strtoupper(substr($this->wm_vrt_alignment, 0, 1));
1055
		$this->wm_hor_alignment = strtoupper(substr($this->wm_hor_alignment, 0, 1));
1056
1057
		switch ($this->wm_vrt_alignment)
1058
		{
1059
			case	 "T" :
1060
				break;
1061
			case "M":	$y_axis += ($this->orig_height/2)+($fontheight/2);
1062
				break;
1063
			case "B":	$y_axis += ($this->orig_height - $fontheight - $this->wm_shadow_distance - ($fontheight/2));
1064
				break;
1065
		}
1066
1067
		$x_shad = $x_axis + $this->wm_shadow_distance;
1068
		$y_shad = $y_axis + $this->wm_shadow_distance;
1069
1070
		// Set horizontal alignment
1071
		switch ($this->wm_hor_alignment)
1072
		{
1073
			case "L":
1074
				break;
1075
			case "R":
1076
						if ($this->wm_use_drop_shadow)
1077
							$x_shad += ($this->orig_width - $fontwidth*strlen($this->wm_text));
1078
							$x_axis += ($this->orig_width - $fontwidth*strlen($this->wm_text));
1079
				break;
1080
			case "C":
1081
						if ($this->wm_use_drop_shadow)
1082
							$x_shad += floor(($this->orig_width - $fontwidth*strlen($this->wm_text))/2);
1083
							$x_axis += floor(($this->orig_width  -$fontwidth*strlen($this->wm_text))/2);
1084
				break;
1085
		}
1086
1087
		//  Add the text to the source image
1088
		if ($this->wm_use_truetype)
1089
		{
1090
			if ($this->wm_use_drop_shadow)
1091
				imagettftext($src_img, $this->wm_font_size, 0, $x_shad, $y_shad, $drp_color, $this->wm_font_path, $this->wm_text);
1092
				imagettftext($src_img, $this->wm_font_size, 0, $x_axis, $y_axis, $txt_color, $this->wm_font_path, $this->wm_text);
1093
		}
1094
		else
1095
		{
1096
			if ($this->wm_use_drop_shadow)
1097
				imagestring($src_img, $this->wm_font_size, $x_shad, $y_shad, $this->wm_text, $drp_color);
1098
				imagestring($src_img, $this->wm_font_size, $x_axis, $y_axis, $this->wm_text, $txt_color);
1099
		}
1100
1101
		//  Output the final image
1102
		if ($this->dynamic_output == TRUE)
1103
		{
1104
			$this->image_display_gd($src_img);
1105
		}
1106
		else
1107
		{
1108
			$this->image_save_gd($src_img);
1109
		}
1110
1111
		imagedestroy($src_img);
1112
1113
		return TRUE;
1114
	}
1115
1116
	// --------------------------------------------------------------------
1117
1118
	/**
1119
	 * Create Image - GD
1120
	 *
1121
	 * This simply creates an image resource handle
1122
	 * based on the type of image being processed
1123
	 *
1124
	 * @access	public
1125
	 * @param	string
1126
	 * @return	resource
1127
	 */
1128
	function image_create_gd($path = '', $image_type = '')
1129
	{
1130
		if ($path == '')
1131
			$path = $this->full_src_path;
1132
1133
		if ($image_type == '')
1134
			$image_type = $this->image_type;
1135
1136
1137
		switch ($image_type)
1138
		{
1139
			case	 1 :
1140
						if ( ! function_exists('imagecreatefromgif'))
1141
						{
1142
							$this->set_error(array('imglib_unsupported_imagecreate', 'imglib_gif_not_supported'));
1143
							return FALSE;
1144
						}
1145
1146
						return imagecreatefromgif($path);
1147
				break;
1148
			case 2 :
1149
						if ( ! function_exists('imagecreatefromjpeg'))
1150
						{
1151
							$this->set_error(array('imglib_unsupported_imagecreate', 'imglib_jpg_not_supported'));
1152
							return FALSE;
1153
						}
1154
1155
						return imagecreatefromjpeg($path);
1156
				break;
1157
			case 3 :
1158
						if ( ! function_exists('imagecreatefrompng'))
1159
						{
1160
							$this->set_error(array('imglib_unsupported_imagecreate', 'imglib_png_not_supported'));
1161
							return FALSE;
1162
						}
1163
1164
						return imagecreatefrompng($path);
1165
				break;
1166
1167
		}
1168
1169
		$this->set_error(array('imglib_unsupported_imagecreate'));
1170
		return FALSE;
1171
	}
1172
1173
	// --------------------------------------------------------------------
1174
1175
	/**
1176
	 * Write image file to disk - GD
1177
	 *
1178
	 * Takes an image resource as input and writes the file
1179
	 * to the specified destination
1180
	 *
1181
	 * @access	public
1182
	 * @param	resource
1183
	 * @return	bool
1184
	 */
1185
	function image_save_gd($resource)
1186
	{
1187
		switch ($this->image_type)
1188
		{
1189
			case 1 :
1190
						if ( ! function_exists('imagegif'))
1191
						{
1192
							$this->set_error(array('imglib_unsupported_imagecreate', 'imglib_gif_not_supported'));
1193
							return FALSE;
1194
						}
1195
1196
						if ( ! @imagegif($resource, $this->full_dst_path))
1197
						{
1198
							$this->set_error('imglib_save_failed');
1199
							return FALSE;
1200
						}
1201
				break;
1202
			case 2	:
1203
						if ( ! function_exists('imagejpeg'))
1204
						{
1205
							$this->set_error(array('imglib_unsupported_imagecreate', 'imglib_jpg_not_supported'));
1206
							return FALSE;
1207
						}
1208
1209
						if ( ! @imagejpeg($resource, $this->full_dst_path, $this->quality))
1210
						{
1211
							$this->set_error('imglib_save_failed');
1212
							return FALSE;
1213
						}
1214
				break;
1215
			case 3	:
1216
						if ( ! function_exists('imagepng'))
1217
						{
1218
							$this->set_error(array('imglib_unsupported_imagecreate', 'imglib_png_not_supported'));
1219
							return FALSE;
1220
						}
1221
1222
						if ( ! @imagepng($resource, $this->full_dst_path))
1223
						{
1224
							$this->set_error('imglib_save_failed');
1225
							return FALSE;
1226
						}
1227
				break;
1228
			default		:
1229
							$this->set_error(array('imglib_unsupported_imagecreate'));
1230
							return FALSE;
1231
				break;
1232
		}
1233
1234
		return TRUE;
1235
	}
1236
1237
	// --------------------------------------------------------------------
1238
1239
	/**
1240
	 * Dynamically outputs an image
1241
	 *
1242
	 * @access	public
1243
	 * @param	resource
1244
	 * @return	void
1245
	 */
1246
	function image_display_gd($resource)
1247
	{
1248
		header("Content-Disposition: filename={$this->source_image};");
1249
		header("Content-Type: {$this->mime_type}");
1250
		header('Content-Transfer-Encoding: binary');
1251
		header('Last-Modified: '.gmdate('D, d M Y H:i:s', time()).' GMT');
1252
1253
		switch ($this->image_type)
1254
		{
1255
			case 1		:	imagegif($resource);
1256
				break;
1257
			case 2		:	imagejpeg($resource, '', $this->quality);
1258
				break;
1259
			case 3		:	imagepng($resource);
1260
				break;
1261
			default		:	echo 'Unable to display the image';
1262
				break;
1263
		}
1264
	}
1265
1266
	// --------------------------------------------------------------------
1267
1268
	/**
1269
	 * Re-proportion Image Width/Height
1270
	 *
1271
	 * When creating thumbs, the desired width/height
1272
	 * can end up warping the image due to an incorrect
1273
	 * ratio between the full-sized image and the thumb.
1274
	 *
1275
	 * This function lets us re-proportion the width/height
1276
	 * if users choose to maintain the aspect ratio when resizing.
1277
	 *
1278
	 * @access	public
1279
	 * @return	void
1280
	 */
1281
	function image_reproportion()
1282
	{
1283
		if ( ! is_numeric($this->width) OR ! is_numeric($this->height) OR $this->width == 0 OR $this->height == 0)
1284
			return;
1285
1286
		if ( ! is_numeric($this->orig_width) OR ! is_numeric($this->orig_height) OR $this->orig_width == 0 OR $this->orig_height == 0)
1287
			return;
1288
1289
		$new_width	= ceil($this->orig_width*$this->height/$this->orig_height);
1290
		$new_height	= ceil($this->width*$this->orig_height/$this->orig_width);
1291
1292
		$ratio = (($this->orig_height/$this->orig_width) - ($this->height/$this->width));
1293
1294
		if ($this->master_dim != 'width' AND $this->master_dim != 'height')
1295
		{
1296
			$this->master_dim = ($ratio < 0) ? 'width' : 'height';
1297
		}
1298
1299
		if (($this->width != $new_width) AND ($this->height != $new_height))
1300
		{
1301
			if ($this->master_dim == 'height')
1302
			{
1303
				$this->width = $new_width;
1304
			}
1305
			else
1306
			{
1307
				$this->height = $new_height;
1308
			}
1309
		}
1310
	}
1311
1312
	// --------------------------------------------------------------------
1313
1314
	/**
1315
	 * Get image properties
1316
	 *
1317
	 * A helper function that gets info about the file
1318
	 *
1319
	 * @access	public
1320
	 * @param	string
1321
	 * @return	mixed
1322
	 */
1323
	function get_image_properties($path = '', $return = FALSE)
1324
	{
1325
		// For now we require GD but we should
1326
		// find a way to determine this using IM or NetPBM
1327
1328
		if ($path == '')
1329
			$path = $this->full_src_path;
1330
1331
		if ( ! file_exists($path))
1332
		{
1333
			$this->set_error('imglib_invalid_path');
1334
			return FALSE;
1335
		}
1336
1337
		$vals = @getimagesize($path);
1338
1339
		$types = array(1 => 'gif', 2 => 'jpeg', 3 => 'png');
1340
1341
		$mime = (isset($types[$vals['2']])) ? 'image/'.$types[$vals['2']] : 'image/jpg';
1342
1343
		if ($return == TRUE)
1344
		{
1345
			$v['width']			= $vals['0'];
1346
			$v['height']		= $vals['1'];
1347
			$v['image_type']	= $vals['2'];
1348
			$v['size_str']		= $vals['3'];
1349
			$v['mime_type']		= $mime;
1350
1351
			return $v;
1352
		}
1353
1354
		$this->orig_width	= $vals['0'];
1355
		$this->orig_height	= $vals['1'];
1356
		$this->image_type	= $vals['2'];
1357
		$this->size_str		= $vals['3'];
1358
		$this->mime_type	= $mime;
1359
1360
		return TRUE;
1361
	}
1362
1363
	// --------------------------------------------------------------------
1364
1365
	/**
1366
	 * Size calculator
1367
	 *
1368
	 * This function takes a known width x height and
1369
	 * recalculates it to a new size.  Only one
1370
	 * new variable needs to be known
1371
	 *
1372
	 *	$props = array(
1373
	 *					'width'			=> $width,
1374
	 *					'height'		=> $height,
1375
	 *					'new_width'		=> 40,
1376
	 *					'new_height'	=> ''
1377
	 *				  );
1378
	 *
1379
	 * @access	public
1380
	 * @param	array
1381
	 * @return	array
1382
	 */
1383
	function size_calculator($vals)
1384
	{
1385
		if ( ! is_array($vals))
1386
		{
1387
			return;
1388
		}
1389
1390
		$allowed = array('new_width', 'new_height', 'width', 'height');
1391
1392
		foreach ($allowed as $item)
1393
		{
1394
			if ( ! isset($vals[$item]) OR $vals[$item] == '')
1395
				$vals[$item] = 0;
1396
		}
1397
1398
		if ($vals['width'] == 0 OR $vals['height'] == 0)
1399
		{
1400
			return $vals;
1401
		}
1402
1403
		if ($vals['new_width'] == 0)
1404
		{
1405
			$vals['new_width'] = ceil($vals['width']*$vals['new_height']/$vals['height']);
1406
		}
1407
		elseif ($vals['new_height'] == 0)
1408
		{
1409
			$vals['new_height'] = ceil($vals['new_width']*$vals['height']/$vals['width']);
1410
		}
1411
1412
		return $vals;
1413
	}
1414
1415
	// --------------------------------------------------------------------
1416
1417
	/**
1418
	 * Explode source_image
1419
	 *
1420
	 * This is a helper function that extracts the extension
1421
	 * from the source_image.  This function lets us deal with
1422
	 * source_images with multiple periods, like:  my.cool.jpg
1423
	 * It returns an associative array with two elements:
1424
	 * $array['ext']  = '.jpg';
1425
	 * $array['name'] = 'my.cool';
1426
	 *
1427
	 * @access	public
1428
	 * @param	array
1429
	 * @return	array
1430
	 */
1431
	function explode_name($source_image)
1432
	{
1433
		$ext = strrchr($source_image, '.');
1434
		$name = ($ext === FALSE) ? $source_image : substr($source_image, 0, -strlen($ext));
1435
1436
		return array('ext' => $ext, 'name' => $name);
1437
	}
1438
1439
	// --------------------------------------------------------------------
1440
1441
	/**
1442
	 * Is GD Installed?
1443
	 *
1444
	 * @access	public
1445
	 * @return	bool
1446
	 */
1447
	function gd_loaded()
1448
	{
1449
		if ( ! extension_loaded('gd'))
1450
		{
1451
			if ( ! dl('gd.so'))
1452
			{
1453
				return FALSE;
1454
			}
1455
		}
1456
1457
		return TRUE;
1458
	}
1459
1460
	// --------------------------------------------------------------------
1461
1462
	/**
1463
	 * Get GD version
1464
	 *
1465
	 * @access	public
1466
	 * @return	mixed
1467
	 */
1468
	function gd_version()
1469
	{
1470
		if (function_exists('gd_info'))
1471
		{
1472
			$gd_version = @gd_info();
1473
			$gd_version = preg_replace("/\D/", "", $gd_version['GD Version']);
1474
1475
			return $gd_version;
1476
		}
1477
1478
		return FALSE;
1479
	}
1480
1481
	// --------------------------------------------------------------------
1482
1483
	/**
1484
	 * Set error message
1485
	 *
1486
	 * @access	public
1487
	 * @param	string
1488
	 * @return	void
1489
	 */
1490
	function set_error($msg)
1491
	{
1492
		$CI =& get_instance();
1493
		$CI->lang->load('imglib');
1494
1495
		if (is_array($msg))
1496
		{
1497
			foreach ($msg as $val)
1498
			{
1499
1500
				$msg = ($CI->lang->line($val) == FALSE) ? $val : $CI->lang->line($val);
1501
				$this->error_msg[] = $msg;
1502
				log_message('error', $msg);
1503
			}
1504
		}
1505
		else
1506
		{
1507
			$msg = ($CI->lang->line($msg) == FALSE) ? $msg : $CI->lang->line($msg);
1508
			$this->error_msg[] = $msg;
1509
			log_message('error', $msg);
1510
		}
1511
	}
1512
1513
	// --------------------------------------------------------------------
1514
1515
	/**
1516
	 * Show error messages
1517
	 *
1518
	 * @access	public
1519
	 * @param	string
1520
	 * @return	string
1521
	 */
1522
	function display_errors($open = '<p>', $close = '</p>')
1523
	{
1524
		$str = '';
1525
		foreach ($this->error_msg as $val)
1526
		{
1527
			$str .= $open.$val.$close;
1528
		}
1529
1530
		return $str;
1531
	}
1532
1533
}
1534
// END Image_lib Class
1535
1536
/* End of file Image_lib.php */
1537
/* Location: ./system/libraries/Image_lib.php */