root/trunk/system/libraries/Image.php

Revision 3326, 9.9 kB (checked in by Shadowhand, 2 weeks ago)

Core cleanup:

  • Removed SYSPATH check from all files
  • Changed copyrights to 2007-2008 where they had not been updated
  • Started to add @example doc comments
  • Property svn:eol-style set to LF
  • Property copyright set to Copyright (c) 2007 Kohana Team
  • Property svn:keywords set to Id
Line 
1 <?php
2 /**
3  * Manipulate images using standard methods such as resize, crop, rotate, etc.
4  * This class must be re-initialized for every image you wish to manipulate.
5  *
6  * $Id$
7  *
8  * @package    Image
9  * @author     Kohana Team
10  * @copyright  (c) 2007-2008 Kohana Team
11  * @license    http://kohanaphp.com/license.html
12  */
13 class Image_Core {
14
15     // Master Dimension
16     const NONE = 1;
17     const AUTO = 2;
18     const HEIGHT = 3;
19     const WIDTH = 4;
20     // Flip Directions
21     const HORIZONTAL = 5;
22     const VERTICAL = 6;
23
24     // Allowed image types
25     public static $allowed_types = array
26     (
27         IMAGETYPE_GIF => 'gif',
28         IMAGETYPE_JPEG => 'jpg',
29         IMAGETYPE_PNG => 'png',
30         IMAGETYPE_TIFF_II => 'tiff',
31         IMAGETYPE_TIFF_MM => 'tiff',
32     );
33
34     // Driver instance
35     protected $driver;
36
37     // Driver actions
38     protected $actions = array();
39
40     // Reference to the current image filename
41     protected $image = '';
42
43     /**
44      * Creates a new Image instance and returns it.
45      *
46      * @param   string   filename of image
47      * @param   array    non-default configurations
48      * @return  object
49      */
50     public static function factory($image, $config = NULL)
51     {
52         return new Image($image, $config);
53     }
54
55     /**
56      * Creates a new image editor instance.
57      *
58      * @throws  Kohana_Exception
59      * @param   string   filename of image
60      * @param   array    non-default configurations
61      * @return  void
62      */
63     public function __construct($image, $config = NULL)
64     {
65         static $check;
66
67         // Make the check exactly once
68         ($check === NULL) and $check = function_exists('getimagesize');
69
70         if ($check === FALSE)
71             throw new Kohana_Exception('image.getimagesize_missing');
72
73         // Check to make sure the image exists
74         if ( ! is_file($image))
75             throw new Kohana_Exception('image.file_not_found', $image);
76
77         // Disable error reporting, to prevent PHP warnings
78         $ER = error_reporting(0);
79
80         // Fetch the image size and mime type
81         $image_info = getimagesize($image);
82
83         // Turn on error reporting again
84         error_reporting($ER);
85
86         // Make sure that the image is readable and valid
87         if ( ! is_array($image_info) OR count($image_info) < 3)
88             throw new Kohana_Exception('image.file_unreadable', $image);
89
90         // Check to make sure the image type is allowed
91         if ( ! isset(Image::$allowed_types[$image_info[2]]))
92             throw new Kohana_Exception('image.type_not_allowed', $image);
93
94         // Image has been validated, load it
95         $this->image = array
96         (
97             'file' => str_replace('\\', '/', realpath($image)),
98             'width' => $image_info[0],
99             'height' => $image_info[1],
100             'type' => $image_info[2],
101             'ext' => Image::$allowed_types[$image_info[2]],
102             'mime' => $image_info['mime']
103         );
104
105         // Load configuration
106         $this->config = (array) $config + Kohana::config('image');
107
108         // Set driver class name
109         $driver = 'Image_'.ucfirst($this->config['driver']).'_Driver';
110
111         // Load the driver
112         if ( ! Kohana::auto_load($driver))
113             throw new Kohana_Exception('core.driver_not_found', $this->config['driver'], get_class($this));
114
115         // Initialize the driver
116         $this->driver = new $driver($this->config['params']);
117
118         // Validate the driver
119         if ( ! ($this->driver instanceof Image_Driver))
120             throw new Kohana_Exception('core.driver_implements', $this->config['driver'], get_class($this), 'Image_Driver');
121     }
122
123     /**
124      * Handles retrieval of pre-save image properties
125      *
126      * @param   string  property name
127      * @return  mixed
128      */
129     public function __get($property)
130     {
131         if (isset($this->image[$property]))
132         {
133             return $this->image[$property];
134         }
135         else
136         {
137             throw new Kohana_Exception('core.invalid_property', $column, get_class($this));
138         }
139     }
140
141     /**
142      * Resize an image to a specific width and height. By default, Kohana will
143      * maintain the aspect ratio using the width as the master dimension. If you
144      * wish to use height as master dim, set $image->master_dim = Image::HEIGHT
145      * This method is chainable.
146      *
147      * @throws  Kohana_Exception
148      * @param   integer  width
149      * @param   integer  height
150      * @param   integer  one of: Image::NONE, Image::AUTO, Image::WIDTH, Image::HEIGHT
151      * @return  object
152      */
153     public function resize($width, $height, $master = NULL)
154     {
155         if ( ! $this->valid_size('width', $width))
156             throw new Kohana_Exception('image.invalid_width', $width);
157
158         if ( ! $this->valid_size('height', $height))
159             throw new Kohana_Exception('image.invalid_height', $height);
160
161         if (empty($width) AND empty($height))
162             throw new Kohana_Exception('image.invalid_dimensions', __FUNCTION__);
163
164         if ($master === NULL)
165         {
166             // Maintain the aspect ratio by default
167             $master = Image::AUTO;
168         }
169         elseif ( ! $this->valid_size('master', $master))
170             throw new Kohana_Exception('image.invalid_master');
171
172         $this->actions['resize'] = array
173         (
174             'width'  => $width,
175             'height' => $height,
176             'master' => $master,
177         );
178
179         return $this;
180     }
181
182     /**
183      * Crop an image to a specific width and height. You may also set the top
184      * and left offset.
185      * This method is chainable.
186      *
187      * @throws  Kohana_Exception
188      * @param   integer  width
189      * @param   integer  height
190      * @param   integer  top offset, pixel value or one of: top, center, bottom
191      * @param   integer  left offset, pixel value or one of: left, center, right
192      * @return  object
193      */
194     public function crop($width, $height, $top = 'center', $left = 'center')
195     {
196         if ( ! $this->valid_size('width', $width))
197             throw new Kohana_Exception('image.invalid_width', $width);
198
199         if ( ! $this->valid_size('height', $height))
200             throw new Kohana_Exception('image.invalid_height', $height);
201
202         if ( ! $this->valid_size('top', $top))
203             throw new Kohana_Exception('image.invalid_top', $top);
204
205         if ( ! $this->valid_size('left', $left))
206             throw new Kohana_Exception('image.invalid_left', $left);
207
208         if (empty($width) AND empty($height))
209             throw new Kohana_Exception('image.invalid_dimensions', __FUNCTION__);
210
211         $this->actions['crop'] = array
212         (
213             'width'  => $width,
214             'height' => $height,
215             'top'    => $top,
216             'left'   => $left,
217         );
218
219         return $this;
220     }
221
222     /**
223      * Allows rotation of an image by 180 degrees clockwise or counter clockwise.
224      *
225      * @param   integer  degrees
226      * @return  object
227      */
228     public function rotate($degrees)
229     {
230         $degrees = (int) $degrees;
231
232         if ($degrees > 180)
233         {
234             do
235             {
236                 // Keep subtracting full circles until the degrees have normalized
237                 $degrees -= 360;
238             }
239             while($degrees > 180);
240         }
241
242         if ($degrees < -180)
243         {
244             do
245             {
246                 // Keep adding full circles until the degrees have normalized
247                 $degrees += 360;
248             }
249             while($degrees < -180);
250         }
251
252         $this->actions['rotate'] = $degrees;
253
254         return $this;
255     }
256
257     /**
258      * Flip an image horizontally or vertically.
259      *
260      * @throws  Kohana_Exception
261      * @param   integer  direction
262      * @return  object
263      */
264     public function flip($direction)
265     {
266         if ($direction !== self::HORIZONTAL AND $direction !== self::VERTICAL)
267             throw new Kohana_Exception('image.invalid_flip');
268
269         $this->actions['flip'] = $direction;
270
271         return $this;
272     }
273
274     /**
275      * Change the quality of an image.
276      *
277      * @param   integer  quality as a percentage
278      * @return  object
279      */
280     public function quality($amount)
281     {
282         $this->actions['quality'] = max(1, min($amount, 100));
283
284         return $this;
285     }
286
287     /**
288      * Sharpen an image.
289      *
290      * @param   integer  amount to sharpen, usually ~20 is ideal
291      * @return  object
292      */
293     public function sharpen($amount)
294     {
295         $this->actions['sharpen'] = max(1, min($amount, 100));
296
297         return $this;
298     }
299
300     /**
301      * Save the image to a new image or overwrite this image.
302      *
303      * @throws  Kohana_Exception
304      * @param   string   new image filename
305      * @param   integer  permissions for new image
306      * @return  object
307      */
308     public function save($new_image = FALSE, $chmod = 0644)
309     {
310         // If no new image is defined, use the current image
311         empty($new_image) and $new_image = $this->image['file'];
312
313         // Separate the directory and filename
314         $dir  = pathinfo($new_image, PATHINFO_DIRNAME);
315         $file = pathinfo($new_image, PATHINFO_BASENAME);
316
317         // Normalize the path
318         $dir = str_replace('\\', '/', realpath($dir)).'/';
319
320         if ( ! is_writable($dir))
321             throw new Kohana_Exception('image.directory_unwritable', $dir);
322
323         if ($status = $this->driver->process($this->image, $this->actions, $dir, $file))
324         {
325             if ($chmod !== FALSE)
326             {
327                 // Set permissions
328                 chmod($new_image, $chmod);
329             }
330         }
331
332         // Reset the actions
333         $this->actions = array();
334
335         return $status;
336     }
337     
338      /**
339       * Output the image to the browser.
340       *
341       * @return    object
342       */
343      public function render()
344      {
345          $new_image = $this->image['file'];
346  
347          // Separate the directory and filename
348          $dir  = pathinfo($new_image, PATHINFO_DIRNAME);
349          $file = pathinfo($new_image, PATHINFO_BASENAME);
350  
351          // Normalize the path
352          $dir = str_replace('\\', '/', realpath($dir)).'/';
353  
354          // Process the image with the driver
355          $status = $this->driver->process($this->image, $this->actions, $dir, $file, $render = TRUE);
356  
357          // Reset the actions
358          $this->actions = array();
359  
360          return $status;
361      }
362
363     /**
364      * Sanitize a given value type.
365      *
366      * @param   string   type of property
367      * @param   mixed    property value
368      * @return  boolean
369      */
370     protected function valid_size($type, & $value)
371     {
372         if (is_null($value))
373             return TRUE;
374
375         if ( ! is_scalar($value))
376             return FALSE;
377
378         switch ($type)
379         {
380             case 'width':
381             case 'height':
382                 if (is_string($value) AND ! ctype_digit($value))
383                 {
384                     // Only numbers and percent signs
385                     if ( ! preg_match('/^[0-9]++%$/D', $value))
386                         return FALSE;
387                 }
388                 else
389                 {
390                     $value = (int) $value;
391                 }
392             break;
393             case 'top':
394                 if (is_string($value) AND ! ctype_digit($value))
395                 {
396                     if ( ! in_array($value, array('top', 'bottom', 'center')))
397                         return FALSE;
398                 }
399                 else
400                 {
401                     $value = (int) $value;
402                 }
403             break;
404             case 'left':
405                 if (is_string($value) AND ! ctype_digit($value))
406                 {
407                     if ( ! in_array($value, array('left', 'right', 'center')))
408                         return FALSE;
409                 }
410                 else
411                 {
412                     $value = (int) $value;
413                 }
414             break;
415             case 'master':
416                 if ($value !== Image::NONE AND
417                     $value !== Image::AUTO AND
418                     $value !== Image::WIDTH AND
419                     $value !== Image::HEIGHT)
420                     return FALSE;
421             break;
422         }
423
424         return TRUE;
425     }
426
427 } // End Image
Note: See TracBrowser for help on using the browser.