1 <?php
defined('SYSPATH') OR die('No direct access allowed.');
5 * $Id: GD.php 3917 2009-01-21 03:06:22Z zombor $
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
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'])
39 $create = 'imagecreatefromjpeg';
42 $create = 'imagecreatefromgif';
45 $create = 'imagecreatefrompng';
49 // Set the "save" function
50 switch (strtolower(substr(strrchr($file, '.'), 1)))
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);
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);
90 // Default the quality to 95
91 ($quality === NULL) and $quality = 95;
94 // Remove the quality setting, GIF doesn't use it
98 // Always use a compression level of 9 for PNGs. This does not
99 // affect quality, it only increases the level of compression!
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);
111 // Output the image directly to the browser
115 header('Content-Type: image/jpeg');
118 header('Content-Type: image/gif');
121 header('Content-Type: image/png');
125 $status = isset($quality) ?
$save($this->tmp_image
, NULL, $quality) : $save($this->tmp_image
);
128 // Destroy the temporary image
129 imagedestroy($this->tmp_image
);
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);
164 if ($status === TRUE)
166 // Swap the new image for the old one
167 imagedestroy($this->tmp_image
);
168 $this->tmp_image
= $flipped;
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']);
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;
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
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)
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
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;
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);
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;
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
326 array(-1, $amount, -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
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);
379 } // End Image GD Driver