1 <?php
defined('SYSPATH') or die('No direct script access.');
3 * Image manipulation abstract class.
5 * @package Kohana/Image
8 * @copyright (c) 2008-2009 Kohana Team
9 * @license http://kohanaphp.com/license.html
11 abstract class Kohana_Image
{
13 // Resizing contraints
20 // Flipping directions
21 const HORIZONTAL
= 0x11;
22 const VERTICAL
= 0x12;
25 * @var string default driver: GD, ImageMagick, etc
27 public static $default_driver = 'GD';
29 // Status of the driver check
30 protected static $_checked = FALSE;
33 * Creates an image wrapper.
35 * @param string image file path
36 * @param string driver type: GD, ImageMagick, etc
39 public static function factory($file, $driver = NULL)
43 // Use the default driver
44 $driver = Image
::$default_driver;
48 $class = 'Image_'.$driver;
50 return new $class($file);
54 * @var string image file path
59 * @var integer image width
64 * @var integer image height
69 * @var integer one of the IMAGETYPE_* constants
74 * Loads information about the image.
76 * @throws Kohana_Exception
77 * @param string image file path
80 public function __construct($file)
84 // Get the real path to the file
85 $file = realpath($file);
87 // Get the image information
88 $info = getimagesize($file);
92 // Ignore all errors while reading the image
95 if (empty($file) OR empty($info))
97 throw new Kohana_Exception('Not an image or invalid image: :file',
98 array(':file' => Kohana
::debug_path($file)));
101 // Store the image information
103 $this->width
= $info[0];
104 $this->height
= $info[1];
105 $this->type
= $info[2];
106 $this->mime
= image_type_to_mime_type($this->type
);
110 * Render the current image.
112 * The output of this function is binary and must be rendered with the
113 * appropriate Content-Type header or it will not be displayed correctly!
117 public function __toString()
121 // Render the current image
122 return $this->render();
126 if (is_object(Kohana
::$log))
128 // Get the text of the exception
129 $error = Kohana
::exception_text($e);
131 // Add this exception to the log
132 Kohana
::$log->add(Kohana
::ERROR
, $error);
135 // Showing any kind of error will be "inside" image data
141 * Resize the image to the given size. Either the width or the height can
142 * be omitted and the image will be resized proportionally.
144 * @param integer new width
145 * @param integer new height
146 * @param integer master dimension
149 public function resize($width = NULL, $height = NULL, $master = NULL)
151 if ($master === NULL)
153 // Choose the master dimension automatically
154 $master = Image
::AUTO
;
156 // Image::WIDTH and Image::HEIGHT depricated. You can use it in old projects,
157 // but in new you must pass empty value for non-master dimension
158 elseif ($master == Image
::WIDTH
AND ! empty($width))
160 $master = Image
::AUTO
;
162 // Set empty height for backvard compatibility
165 elseif ($master == Image
::HEIGHT
AND ! empty($height))
167 $master = Image
::AUTO
;
169 // Set empty width for backvard compatibility
176 if ($master === Image
::NONE
)
178 // Use the current width
179 $width = $this->width
;
183 // If width not set, master will be height
184 $master = Image
::HEIGHT
;
190 if ($master === Image
::NONE
)
192 // Use the current height
193 $height = $this->height
;
197 // If height not set, master will be width
198 $master = Image
::WIDTH
;
205 // Choose direction with the greatest reduction ratio
206 $master = ($this->width
/ $width) > ($this->height
/ $height) ? Image
::WIDTH
: Image
::HEIGHT
;
209 // Choose direction with the minimum reduction ratio
210 $master = ($this->width
/ $width) > ($this->height
/ $height) ? Image
::HEIGHT
: Image
::WIDTH
;
217 // Recalculate the height based on the width proportions
218 $height = $this->height
* $width / $this->width
;
221 // Recalculate the width based on the height proportions
222 $width = $this->width
* $height / $this->height
;
226 // Convert the width and height to integers
227 $width = round($width);
228 $height = round($height);
230 $this->_do_resize($width, $height);
236 * Crop an image to the given size. Either the width or the height can be
237 * omitted and the current width or height will be used.
239 * If no offset is specified, the center of the axis will be used.
240 * If an offset of TRUE is specified, the bottom of the axis will be used.
242 * @param integer new width
243 * @param integer new height
244 * @param mixed offset from the left
245 * @param mixed offset from the top
248 public function crop($width, $height, $offset_x = NULL, $offset_y = NULL)
250 if ($width > $this->width
)
252 // Use the current width
253 $width = $this->width
;
256 if ($height > $this->height
)
258 // Use the current height
259 $height = $this->height
;
262 if ($offset_x === NULL)
264 // Center the X offset
265 $offset_x = round(($this->width
- $width) / 2);
267 elseif ($offset_x === TRUE)
269 // Bottom the X offset
270 $offset_x = $this->width
- $width;
272 elseif ($offset_x < 0)
274 // Set the X offset from the right
275 $offset_x = $this->width
- $width +
$offset_x;
278 if ($offset_y === NULL)
280 // Center the Y offset
281 $offset_y = round(($this->height
- $height) / 2);
283 elseif ($offset_y === TRUE)
285 // Bottom the Y offset
286 $offset_y = $this->height
- $height;
288 elseif ($offset_y < 0)
290 // Set the Y offset from the bottom
291 $offset_y = $this->height
- $height +
$offset_y;
294 // Determine the maximum possible width and height
295 $max_width = $this->width
- $offset_x;
296 $max_height = $this->height
- $offset_y;
298 if ($width > $max_width)
300 // Use the maximum available width
304 if ($height > $max_height)
306 // Use the maximum available height
307 $height = $max_height;
310 $this->_do_crop($width, $height, $offset_x, $offset_y);
318 * @param integer degrees to rotate: -360-360
321 public function rotate($degrees)
323 // Make the degrees an integer
324 $degrees = (int) $degrees;
330 // Keep subtracting full circles until the degrees have normalized
333 while($degrees > 180);
340 // Keep adding full circles until the degrees have normalized
343 while($degrees < -180);
346 $this->_do_rotate($degrees);
352 * Flip the image along the horizontal or vertical axis.
354 * @param integer direction: Image::HORIZONTAL, Image::VERTICAL
357 public function flip($direction)
359 if ($direction !== Image
::HORIZONTAL
)
362 $direction = Image
::VERTICAL
;
365 $this->_do_flip($direction);
373 * @param integer amount to sharpen: 1-100
376 public function sharpen($amount)
378 // The amount must be in the range of 1 to 100
379 $amount = min(max($amount, 1), 100);
381 $this->_do_sharpen($amount);
387 * Add a reflection to an image. The most opaque part of the reflection
388 * will be equal to the opacity setting and fade out to full transparent.
389 * By default, the reflection will be most transparent at the top
391 * @param integer reflection height
392 * @param integer reflection opacity: 0-100
393 * @param boolean TRUE to fade in, FALSE to fade out
396 public function reflection($height = NULL, $opacity = 100, $fade_in = FALSE)
398 if ($height === NULL OR $height > $this->height
)
400 // Use the current height
401 $height = $this->height
;
404 // The opacity must be in the range of 0 to 100
405 $opacity = min(max($opacity, 0), 100);
407 $this->_do_reflection($height, $opacity, $fade_in);
413 * Add a watermark to an image with a specified opacity.
415 * If no offset is specified, the center of the axis will be used.
416 * If an offset of TRUE is specified, the bottom of the axis will be used.
418 * @param object watermark Image instance
419 * @param integer offset from the left
420 * @param integer offset from the top
421 * @param integer opacity of watermark: 1-100
424 public function watermark(Image
$watermark, $offset_x = NULL, $offset_y = NULL, $opacity = 100)
426 if ($offset_x === NULL)
428 // Center the X offset
429 $offset_x = round(($this->width
- $watermark->width
) / 2);
431 elseif ($offset_x === TRUE)
433 // Bottom the X offset
434 $offset_x = $this->width
- $watermark->width
;
436 elseif ($offset_x < 0)
438 // Set the X offset from the right
439 $offset_x = $this->width
- $watermark->width +
$offset_x;
442 if ($offset_y === NULL)
444 // Center the Y offset
445 $offset_y = round(($this->height
- $watermark->height
) / 2);
447 elseif ($offset_y === TRUE)
449 // Bottom the Y offset
450 $offset_y = $this->height
- $watermark->height
;
452 elseif ($offset_y < 0)
454 // Set the Y offset from the bottom
455 $offset_y = $this->height
- $watermark->height +
$offset_y;
458 // The opacity must be in the range of 1 to 100
459 $opacity = min(max($opacity, 1), 100);
461 $this->_do_watermark($watermark, $offset_x, $offset_y, $opacity);
467 * Set the background color of an image.
469 * @param string hexadecimal color value
470 * @param integer background opacity: 0-100
473 public function background($color, $opacity = 100)
475 if ($color[0] === '#')
478 $color = substr($color, 1);
481 if (strlen($color) === 3)
483 // Convert shorthand into longhand hex notation
484 $color = preg_replace('/./', '$0$0', $color);
487 // Convert the hex into RGB values
488 list ($r, $g, $b) = array_map('hexdec', str_split($color, 2));
490 // The opacity must be in the range of 0 to 100
491 $opacity = min(max($opacity, 0), 100);
493 $this->_do_background($r, $g, $b, $opacity);
499 * Save the image. If the filename is omitted, the original image will
502 * @param string new image path
503 * @param integer quality of image: 1-100
506 public function save($file = NULL, $quality = 100)
510 // Overwrite the file
516 if ( ! is_writable($file))
518 throw new Kohana_Exception('File must be writable: :file',
519 array(':file' => Kohana
::debug_path($file)));
524 // Get the directory of the file
525 $directory = realpath(pathinfo($file, PATHINFO_DIRNAME
));
527 if ( ! is_dir($directory) OR ! is_writable($directory))
529 throw new Kohana_Exception('Directory must be writable: :directory',
530 array(':directory' => Kohana
::debug_path($directory)));
534 // The quality must be in the range of 1 to 100
535 $quality = min(max($quality, 1), 100);
537 return $this->_do_save($file, $quality);
541 * Render the image and return the data.
543 * @param string image type to return: png, jpg, gif, etc
544 * @param integer quality of image: 1-100
547 public function render($type = NULL, $quality = 100)
551 // Use the current image type
552 $type = image_type_to_extension($this->type
, FALSE);
555 return $this->_do_render($type, $quality);
561 * @param integer new width
562 * @param integer new height
565 abstract protected function _do_resize($width, $height);
570 * @param integer new width
571 * @param integer new height
572 * @param integer offset from the left
573 * @param integer offset from the top
576 abstract protected function _do_crop($width, $height, $offset_x, $offset_y);
579 * Execute a rotation.
581 * @param integer degrees to rotate
584 abstract protected function _do_rotate($degrees);
589 * @param integer direction to flip
592 abstract protected function _do_flip($direction);
597 * @param integer amount to sharpen
600 abstract protected function _do_sharpen($amount);
603 * Execute a reflection.
605 * @param integer reflection height
606 * @param integer reflection opacity
607 * @param boolean TRUE to fade out, FALSE to fade in
610 abstract protected function _do_reflection($height, $opacity, $fade_in);
613 * Execute a watermarking.
615 * @param object watermarking Image
616 * @param integer offset from the left
617 * @param integer offset from the top
618 * @param integer opacity of watermark
621 abstract protected function _do_watermark(Image
$image, $offset_x, $offset_y, $opacity);
624 * Execute a background.
627 * @param integer green
628 * @param integer blue
629 * @param integer opacity
632 abstract protected function _do_background($r, $g, $b, $opacity);
637 * @param string new image filename
638 * @param integer quality
641 abstract protected function _do_save($file, $quality);
646 * @param string image type: png, jpg, gif, etc
647 * @param integer quality
650 abstract protected function _do_render($type, $quality);