Removed dep on API
[ninja.git] / system / libraries / drivers / Image / GD.php
blobe6c18d42798e9f27dab87db6f4cb7c18df630c09
1 <?php defined('SYSPATH') OR die('No direct access allowed.');
2 /**
3 * GD Image Driver.
5 * $Id: GD.php 3917 2009-01-21 03:06:22Z zombor $
7 * @package Image
8 * @author Kohana Team
9 * @copyright (c) 2007-2008 Kohana Team
10 * @license http://kohanaphp.com/license.html
12 class Image_GD_Driver extends Image_Driver {
14 // A transparent PNG as a string
15 protected static $blank_png;
16 protected static $blank_png_width;
17 protected static $blank_png_height;
19 public function __construct()
21 // Make sure that GD2 is available
22 if ( ! function_exists('gd_info'))
23 throw new Kohana_Exception('image.gd.requires_v2');
25 // Get the GD information
26 $info = gd_info();
28 // Make sure that the GD2 is installed
29 if (strpos($info['GD Version'], '2.') === FALSE)
30 throw new Kohana_Exception('image.gd.requires_v2');
33 public function process($image, $actions, $dir, $file, $render = FALSE)
35 // Set the "create" function
36 switch ($image['type'])
38 case IMAGETYPE_JPEG:
39 $create = 'imagecreatefromjpeg';
40 break;
41 case IMAGETYPE_GIF:
42 $create = 'imagecreatefromgif';
43 break;
44 case IMAGETYPE_PNG:
45 $create = 'imagecreatefrompng';
46 break;
49 // Set the "save" function
50 switch (strtolower(substr(strrchr($file, '.'), 1)))
52 case 'jpg':
53 case 'jpeg':
54 $save = 'imagejpeg';
55 break;
56 case 'gif':
57 $save = 'imagegif';
58 break;
59 case 'png':
60 $save = 'imagepng';
61 break;
64 // Make sure the image type is supported for import
65 if (empty($create) OR ! function_exists($create))
66 throw new Kohana_Exception('image.type_not_allowed', $image['file']);
68 // Make sure the image type is supported for saving
69 if (empty($save) OR ! function_exists($save))
70 throw new Kohana_Exception('image.type_not_allowed', $dir.$file);
72 // Load the image
73 $this->image = $image;
75 // Create the GD image resource
76 $this->tmp_image = $create($image['file']);
78 // Get the quality setting from the actions
79 $quality = arr::remove('quality', $actions);
81 if ($status = $this->execute($actions))
83 // Prevent the alpha from being lost
84 imagealphablending($this->tmp_image, TRUE);
85 imagesavealpha($this->tmp_image, TRUE);
87 switch ($save)
89 case 'imagejpeg':
90 // Default the quality to 95
91 ($quality === NULL) and $quality = 95;
92 break;
93 case 'imagegif':
94 // Remove the quality setting, GIF doesn't use it
95 unset($quality);
96 break;
97 case 'imagepng':
98 // Always use a compression level of 9 for PNGs. This does not
99 // affect quality, it only increases the level of compression!
100 $quality = 9;
101 break;
104 if ($render === FALSE)
106 // Set the status to the save return value, saving with the quality requested
107 $status = isset($quality) ? $save($this->tmp_image, $dir.$file, $quality) : $save($this->tmp_image, $dir.$file);
109 else
111 // Output the image directly to the browser
112 switch ($save)
114 case 'imagejpeg':
115 header('Content-Type: image/jpeg');
116 break;
117 case 'imagegif':
118 header('Content-Type: image/gif');
119 break;
120 case 'imagepng':
121 header('Content-Type: image/png');
122 break;
125 $status = isset($quality) ? $save($this->tmp_image, NULL, $quality) : $save($this->tmp_image);
128 // Destroy the temporary image
129 imagedestroy($this->tmp_image);
132 return $status;
135 public function flip($direction)
137 // Get the current width and height
138 $width = imagesx($this->tmp_image);
139 $height = imagesy($this->tmp_image);
141 // Create the flipped image
142 $flipped = $this->imagecreatetransparent($width, $height);
144 if ($direction === Image::HORIZONTAL)
146 for ($x = 0; $x < $width; $x++)
148 $status = imagecopy($flipped, $this->tmp_image, $x, 0, $width - $x - 1, 0, 1, $height);
151 elseif ($direction === Image::VERTICAL)
153 for ($y = 0; $y < $height; $y++)
155 $status = imagecopy($flipped, $this->tmp_image, 0, $y, 0, $height - $y - 1, $width, 1);
158 else
160 // Do nothing
161 return TRUE;
164 if ($status === TRUE)
166 // Swap the new image for the old one
167 imagedestroy($this->tmp_image);
168 $this->tmp_image = $flipped;
171 return $status;
174 public function crop($properties)
176 // Sanitize the cropping settings
177 $this->sanitize_geometry($properties);
179 // Get the current width and height
180 $width = imagesx($this->tmp_image);
181 $height = imagesy($this->tmp_image);
183 // Create the temporary image to copy to
184 $img = $this->imagecreatetransparent($properties['width'], $properties['height']);
186 // Execute the crop
187 if ($status = imagecopyresampled($img, $this->tmp_image, 0, 0, $properties['left'], $properties['top'], $width, $height, $width, $height))
189 // Swap the new image for the old one
190 imagedestroy($this->tmp_image);
191 $this->tmp_image = $img;
194 return $status;
197 public function resize($properties)
199 // Get the current width and height
200 $width = imagesx($this->tmp_image);
201 $height = imagesy($this->tmp_image);
203 if (substr($properties['width'], -1) === '%')
205 // Recalculate the percentage to a pixel size
206 $properties['width'] = round($width * (substr($properties['width'], 0, -1) / 100));
209 if (substr($properties['height'], -1) === '%')
211 // Recalculate the percentage to a pixel size
212 $properties['height'] = round($height * (substr($properties['height'], 0, -1) / 100));
215 // Recalculate the width and height, if they are missing
216 empty($properties['width']) and $properties['width'] = round($width * $properties['height'] / $height);
217 empty($properties['height']) and $properties['height'] = round($height * $properties['width'] / $width);
219 if ($properties['master'] === Image::AUTO)
221 // Change an automatic master dim to the correct type
222 $properties['master'] = (($width / $properties['width']) > ($height / $properties['height'])) ? Image::WIDTH : Image::HEIGHT;
225 if (empty($properties['height']) OR $properties['master'] === Image::WIDTH)
227 // Recalculate the height based on the width
228 $properties['height'] = round($height * $properties['width'] / $width);
231 if (empty($properties['width']) OR $properties['master'] === Image::HEIGHT)
233 // Recalculate the width based on the height
234 $properties['width'] = round($width * $properties['height'] / $height);
237 // Test if we can do a resize without resampling to speed up the final resize
238 if ($properties['width'] > $width / 2 AND $properties['height'] > $height / 2)
240 // Presize width and height
241 $pre_width = $width;
242 $pre_height = $height;
244 // The maximum reduction is 10% greater than the final size
245 $max_reduction_width = round($properties['width'] * 1.1);
246 $max_reduction_height = round($properties['height'] * 1.1);
248 // Reduce the size using an O(2n) algorithm, until it reaches the maximum reduction
249 while ($pre_width / 2 > $max_reduction_width AND $pre_height / 2 > $max_reduction_height)
251 $pre_width /= 2;
252 $pre_height /= 2;
255 // Create the temporary image to copy to
256 $img = $this->imagecreatetransparent($pre_width, $pre_height);
258 if ($status = imagecopyresized($img, $this->tmp_image, 0, 0, 0, 0, $pre_width, $pre_height, $width, $height))
260 // Swap the new image for the old one
261 imagedestroy($this->tmp_image);
262 $this->tmp_image = $img;
265 // Set the width and height to the presize
266 $width = $pre_width;
267 $height = $pre_height;
270 // Create the temporary image to copy to
271 $img = $this->imagecreatetransparent($properties['width'], $properties['height']);
273 // Execute the resize
274 if ($status = imagecopyresampled($img, $this->tmp_image, 0, 0, 0, 0, $properties['width'], $properties['height'], $width, $height))
276 // Swap the new image for the old one
277 imagedestroy($this->tmp_image);
278 $this->tmp_image = $img;
281 return $status;
284 public function rotate($amount)
286 // Use current image to rotate
287 $img = $this->tmp_image;
289 // White, with an alpha of 0
290 $transparent = imagecolorallocatealpha($img, 255, 255, 255, 127);
292 // Rotate, setting the transparent color
293 $img = imagerotate($img, 360 - $amount, $transparent, -1);
295 // Fill the background with the transparent "color"
296 imagecolortransparent($img, $transparent);
298 // Merge the images
299 if ($status = imagecopymerge($this->tmp_image, $img, 0, 0, 0, 0, imagesx($this->tmp_image), imagesy($this->tmp_image), 100))
301 // Prevent the alpha from being lost
302 imagealphablending($img, TRUE);
303 imagesavealpha($img, TRUE);
305 // Swap the new image for the old one
306 imagedestroy($this->tmp_image);
307 $this->tmp_image = $img;
310 return $status;
313 public function sharpen($amount)
315 // Make sure that the sharpening function is available
316 if ( ! function_exists('imageconvolution'))
317 throw new Kohana_Exception('image.unsupported_method', __FUNCTION__);
319 // Amount should be in the range of 18-10
320 $amount = round(abs(-18 + ($amount * 0.08)), 2);
322 // Gaussian blur matrix
323 $matrix = array
325 array(-1, -1, -1),
326 array(-1, $amount, -1),
327 array(-1, -1, -1),
330 // Perform the sharpen
331 return imageconvolution($this->tmp_image, $matrix, $amount - 8, 0);
334 protected function properties()
336 return array(imagesx($this->tmp_image), imagesy($this->tmp_image));
340 * Returns an image with a transparent background. Used for rotating to
341 * prevent unfilled backgrounds.
343 * @param integer image width
344 * @param integer image height
345 * @return resource
347 protected function imagecreatetransparent($width, $height)
349 if (self::$blank_png === NULL)
351 // Decode the blank PNG if it has not been done already
352 self::$blank_png = imagecreatefromstring(base64_decode
354 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29'.
355 'mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAADqSURBVHjaYvz//z/DYAYAAcTEMMgBQAANegcCBN'.
356 'CgdyBAAA16BwIE0KB3IEAADXoHAgTQoHcgQAANegcCBNCgdyBAAA16BwIE0KB3IEAADXoHAgTQoHcgQ'.
357 'AANegcCBNCgdyBAAA16BwIE0KB3IEAADXoHAgTQoHcgQAANegcCBNCgdyBAAA16BwIE0KB3IEAADXoH'.
358 'AgTQoHcgQAANegcCBNCgdyBAAA16BwIE0KB3IEAADXoHAgTQoHcgQAANegcCBNCgdyBAAA16BwIE0KB'.
359 '3IEAADXoHAgTQoHcgQAANegcCBNCgdyBAgAEAMpcDTTQWJVEAAAAASUVORK5CYII='
362 // Set the blank PNG width and height
363 self::$blank_png_width = imagesx(self::$blank_png);
364 self::$blank_png_height = imagesy(self::$blank_png);
367 $img = imagecreatetruecolor($width, $height);
369 // Resize the blank image
370 imagecopyresized($img, self::$blank_png, 0, 0, 0, 0, $width, $height, self::$blank_png_width, self::$blank_png_height);
372 // Prevent the alpha from being lost
373 imagealphablending($img, FALSE);
374 imagesavealpha($img, TRUE);
376 return $img;
379 } // End Image GD Driver