PageRenderTime 63ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 0ms

/modules/image/classes/kohana/image/gd.php

https://bitbucket.org/seyar/ari100krat.local
PHP | 582 lines | 326 code | 105 blank | 151 comment | 32 complexity | 8603de5e87a9585fe48c91c6ede36040 MD5 | raw file
Possible License(s): BSD-3-Clause, LGPL-2.1
  1. <?php defined('SYSPATH') or die('No direct script access.');
  2. /**
  3. * Support for image manipulation using [GD](http://php.net/GD).
  4. *
  5. * @package Kohana/Image
  6. * @category Drivers
  7. * @author Kohana Team
  8. * @copyright (c) 2008-2009 Kohana Team
  9. * @license http://kohanaphp.com/license.html
  10. */
  11. class Kohana_Image_GD extends Image {
  12. // Is GD bundled or separate?
  13. protected static $_bundled;
  14. /**
  15. * Checks if GD is enabled and bundled. Bundled GD is required for some
  16. * methods to work. Exceptions will be thrown from those methods when GD is
  17. * not bundled.
  18. *
  19. * @return boolean
  20. */
  21. public static function check()
  22. {
  23. if ( ! function_exists('gd_info'))
  24. {
  25. throw new Kohana_Exception('GD is either not installed or not enabled, check your configuration');
  26. }
  27. if (defined('GD_BUNDLED'))
  28. {
  29. // Get the version via a constant, available in PHP 5.
  30. Image_GD::$_bundled = GD_BUNDLED;
  31. }
  32. else
  33. {
  34. // Get the version information
  35. $info = gd_info();
  36. // Extract the bundled status
  37. Image_GD::$_bundled = (bool) preg_match('/\bbundled\b/i', $info['GD Version']);
  38. }
  39. if (defined('GD_VERSION'))
  40. {
  41. // Get the version via a constant, available in PHP 5.2.4+
  42. $version = GD_VERSION;
  43. }
  44. else
  45. {
  46. // Get the version information
  47. $info = gd_info();
  48. // Extract the version number
  49. preg_match('/\d+\.\d+(?:\.\d+)?/', $info['GD Version'], $matches);
  50. // Get the major version
  51. $version = $matches[0];
  52. }
  53. if ( ! version_compare($version, '2.0.1', '>='))
  54. {
  55. throw new Kohana_Exception('Image_GD requires GD version :required or greater, you have :version',
  56. array('required' => '2.0.1', ':version' => $version));
  57. }
  58. return Image_GD::$_checked = TRUE;
  59. }
  60. // Temporary image resource
  61. protected $_image;
  62. // Function name to open Image
  63. protected $_create_function;
  64. /**
  65. * Runs [Image_GD::check] and loads the image.
  66. *
  67. * @return void
  68. * @throws Kohana_Exception
  69. */
  70. public function __construct($file)
  71. {
  72. if ( ! Image_GD::$_checked)
  73. {
  74. // Run the install check
  75. Image_GD::check();
  76. }
  77. parent::__construct($file);
  78. // Set the image creation function name
  79. switch ($this->type)
  80. {
  81. case IMAGETYPE_JPEG:
  82. $create = 'imagecreatefromjpeg';
  83. break;
  84. case IMAGETYPE_GIF:
  85. $create = 'imagecreatefromgif';
  86. break;
  87. case IMAGETYPE_PNG:
  88. $create = 'imagecreatefrompng';
  89. break;
  90. }
  91. if ( ! isset($create) OR ! function_exists($create))
  92. {
  93. throw new Kohana_Exception('Installed GD does not support :type images',
  94. array(':type' => image_type_to_extension($this->type, FALSE)));
  95. }
  96. // Save function for future use
  97. $this->_create_function = $create;
  98. // Save filename for lazy loading
  99. $this->_image = $this->file;
  100. }
  101. /**
  102. * Destroys the loaded image to free up resources.
  103. *
  104. * @return void
  105. */
  106. public function __destruct()
  107. {
  108. if (is_resource($this->_image))
  109. {
  110. // Free all resources
  111. imagedestroy($this->_image);
  112. }
  113. }
  114. /**
  115. * Loads an image into GD.
  116. *
  117. * @return void
  118. */
  119. protected function _load_image()
  120. {
  121. if ( ! is_resource($this->_image))
  122. {
  123. // Gets create function
  124. $create = $this->_create_function;
  125. // Open the temporary image
  126. $this->_image = $create($this->file);
  127. // Preserve transparency when saving
  128. imagesavealpha($this->_image, TRUE);
  129. }
  130. }
  131. protected function _do_resize($width, $height)
  132. {
  133. // Presize width and height
  134. $pre_width = $this->width;
  135. $pre_height = $this->height;
  136. // Loads image if not yet loaded
  137. $this->_load_image();
  138. // Test if we can do a resize without resampling to speed up the final resize
  139. if ($width > ($this->width / 2) AND $height > ($this->height / 2))
  140. {
  141. // The maximum reduction is 10% greater than the final size
  142. $reduction_width = round($width * 1.1);
  143. $reduction_height = round($height * 1.1);
  144. while ($pre_width / 2 > $reduction_width AND $pre_height / 2 > $reduction_height)
  145. {
  146. // Reduce the size using an O(2n) algorithm, until it reaches the maximum reduction
  147. $pre_width /= 2;
  148. $pre_height /= 2;
  149. }
  150. // Create the temporary image to copy to
  151. $image = $this->_create($pre_width, $pre_height);
  152. if (imagecopyresized($image, $this->_image, 0, 0, 0, 0, $pre_width, $pre_height, $this->width, $this->height))
  153. {
  154. // Swap the new image for the old one
  155. imagedestroy($this->_image);
  156. $this->_image = $image;
  157. }
  158. }
  159. // Create the temporary image to copy to
  160. $image = $this->_create($width, $height);
  161. // Execute the resize
  162. if (imagecopyresampled($image, $this->_image, 0, 0, 0, 0, $width, $height, $pre_width, $pre_height))
  163. {
  164. // Swap the new image for the old one
  165. imagedestroy($this->_image);
  166. $this->_image = $image;
  167. // Reset the width and height
  168. $this->width = imagesx($image);
  169. $this->height = imagesy($image);
  170. }
  171. }
  172. protected function _do_crop($width, $height, $offset_x, $offset_y)
  173. {
  174. // Create the temporary image to copy to
  175. $image = $this->_create($width, $height);
  176. // Loads image if not yet loaded
  177. $this->_load_image();
  178. // Execute the crop
  179. if (imagecopyresampled($image, $this->_image, 0, 0, $offset_x, $offset_y, $width, $height, $width, $height))
  180. {
  181. // Swap the new image for the old one
  182. imagedestroy($this->_image);
  183. $this->_image = $image;
  184. // Reset the width and height
  185. $this->width = imagesx($image);
  186. $this->height = imagesy($image);
  187. }
  188. }
  189. protected function _do_rotate($degrees)
  190. {
  191. if ( ! Image_GD::$_bundled)
  192. {
  193. throw new Kohana_Exception('This method requires :function, which is only available in the bundled version of GD',
  194. array(':function' => 'imagerotate'));
  195. }
  196. // Loads image if not yet loaded
  197. $this->_load_image();
  198. // Transparent black will be used as the background for the uncovered region
  199. $transparent = imagecolorallocatealpha($this->_image, 0, 0, 0, 127);
  200. // Rotate, setting the transparent color
  201. $image = imagerotate($this->_image, 360 - $degrees, $transparent, 1);
  202. // Save the alpha of the rotated image
  203. imagesavealpha($image, TRUE);
  204. // Get the width and height of the rotated image
  205. $width = imagesx($image);
  206. $height = imagesy($image);
  207. if (imagecopymerge($this->_image, $image, 0, 0, 0, 0, $width, $height, 100))
  208. {
  209. // Swap the new image for the old one
  210. imagedestroy($this->_image);
  211. $this->_image = $image;
  212. // Reset the width and height
  213. $this->width = $width;
  214. $this->height = $height;
  215. }
  216. }
  217. protected function _do_flip($direction)
  218. {
  219. // Create the flipped image
  220. $flipped = $this->_create($this->width, $this->height);
  221. // Loads image if not yet loaded
  222. $this->_load_image();
  223. if ($direction === Image::HORIZONTAL)
  224. {
  225. for ($x = 0; $x < $this->width; $x++)
  226. {
  227. // Flip each row from top to bottom
  228. imagecopy($flipped, $this->_image, $x, 0, $this->width - $x - 1, 0, 1, $this->height);
  229. }
  230. }
  231. else
  232. {
  233. for ($y = 0; $y < $this->height; $y++)
  234. {
  235. // Flip each column from left to right
  236. imagecopy($flipped, $this->_image, 0, $y, 0, $this->height - $y - 1, $this->width, 1);
  237. }
  238. }
  239. // Swap the new image for the old one
  240. imagedestroy($this->_image);
  241. $this->_image = $flipped;
  242. // Reset the width and height
  243. $this->width = imagesx($flipped);
  244. $this->height = imagesy($flipped);
  245. }
  246. protected function _do_sharpen($amount)
  247. {
  248. if ( ! Image_GD::$_bundled)
  249. {
  250. throw new Kohana_Exception('This method requires :function, which is only available in the bundled version of GD',
  251. array(':function' => 'imageconvolution'));
  252. }
  253. // Loads image if not yet loaded
  254. $this->_load_image();
  255. // Amount should be in the range of 18-10
  256. $amount = round(abs(-18 + ($amount * 0.08)), 2);
  257. // Gaussian blur matrix
  258. $matrix = array
  259. (
  260. array(-1, -1, -1),
  261. array(-1, $amount, -1),
  262. array(-1, -1, -1),
  263. );
  264. // Perform the sharpen
  265. if (imageconvolution($this->_image, $matrix, $amount - 8, 0))
  266. {
  267. // Reset the width and height
  268. $this->width = imagesx($this->_image);
  269. $this->height = imagesy($this->_image);
  270. }
  271. }
  272. protected function _do_reflection($height, $opacity, $fade_in)
  273. {
  274. if ( ! Image_GD::$_bundled)
  275. {
  276. throw new Kohana_Exception('This method requires :function, which is only available in the bundled version of GD',
  277. array(':function' => 'imagefilter'));
  278. }
  279. // Loads image if not yet loaded
  280. $this->_load_image();
  281. // Convert an opacity range of 0-100 to 127-0
  282. $opacity = round(abs(($opacity * 127 / 100) - 127));
  283. if ($opacity < 127)
  284. {
  285. // Calculate the opacity stepping
  286. $stepping = (127 - $opacity) / $height;
  287. }
  288. else
  289. {
  290. // Avoid a "divide by zero" error
  291. $stepping = 127 / $height;
  292. }
  293. // Create the reflection image
  294. $reflection = $this->_create($this->width, $this->height + $height);
  295. // Copy the image to the reflection
  296. imagecopy($reflection, $this->_image, 0, 0, 0, 0, $this->width, $this->height);
  297. for ($offset = 0; $height >= $offset; $offset++)
  298. {
  299. // Read the next line down
  300. $src_y = $this->height - $offset - 1;
  301. // Place the line at the bottom of the reflection
  302. $dst_y = $this->height + $offset;
  303. if ($fade_in === TRUE)
  304. {
  305. // Start with the most transparent line first
  306. $dst_opacity = round($opacity + ($stepping * ($height - $offset)));
  307. }
  308. else
  309. {
  310. // Start with the most opaque line first
  311. $dst_opacity = round($opacity + ($stepping * $offset));
  312. }
  313. // Create a single line of the image
  314. $line = $this->_create($this->width, 1);
  315. // Copy a single line from the current image into the line
  316. imagecopy($line, $this->_image, 0, 0, 0, $src_y, $this->width, 1);
  317. // Colorize the line to add the correct alpha level
  318. imagefilter($line, IMG_FILTER_COLORIZE, 0, 0, 0, $dst_opacity);
  319. // Copy a the line into the reflection
  320. imagecopy($reflection, $line, 0, $dst_y, 0, 0, $this->width, 1);
  321. }
  322. // Swap the new image for the old one
  323. imagedestroy($this->_image);
  324. $this->_image = $reflection;
  325. // Reset the width and height
  326. $this->width = imagesx($reflection);
  327. $this->height = imagesy($reflection);
  328. }
  329. protected function _do_watermark(Image $watermark, $offset_x, $offset_y, $opacity)
  330. {
  331. if ( ! Image_GD::$_bundled)
  332. {
  333. throw new Kohana_Exception('This method requires :function, which is only available in the bundled version of GD',
  334. array(':function' => 'imagelayereffect'));
  335. }
  336. // Loads image if not yet loaded
  337. $this->_load_image();
  338. // Create the watermark image resource
  339. $overlay = imagecreatefromstring($watermark->render());
  340. // Get the width and height of the watermark
  341. $width = imagesx($overlay);
  342. $height = imagesy($overlay);
  343. if ($opacity < 100)
  344. {
  345. // Convert an opacity range of 0-100 to 127-0
  346. $opacity = round(abs(($opacity * 127 / 100) - 127));
  347. // Allocate transparent white
  348. $color = imagecolorallocatealpha($overlay, 255, 255, 255, $opacity);
  349. // The transparent image will overlay the watermark
  350. imagelayereffect($overlay, IMG_EFFECT_OVERLAY);
  351. // Fill the background with transparent white
  352. imagefilledrectangle($overlay, 0, 0, $width, $height, $color);
  353. }
  354. // Alpha blending must be enabled on the background!
  355. imagealphablending($this->_image, TRUE);
  356. if (imagecopy($this->_image, $overlay, $offset_x, $offset_y, 0, 0, $width, $height))
  357. {
  358. // Destroy the overlay image
  359. imagedestroy($overlay);
  360. }
  361. }
  362. protected function _do_background($r, $g, $b, $opacity)
  363. {
  364. // Loads image if not yet loaded
  365. $this->_load_image();
  366. // Convert an opacity range of 0-100 to 127-0
  367. $opacity = round(abs(($opacity * 127 / 100) - 127));
  368. // Create a new background
  369. $background = $this->_create($this->width, $this->height);
  370. // Allocate the color
  371. $color = imagecolorallocatealpha($background, $r, $g, $b, $opacity);
  372. // Fill the image with white
  373. imagefilledrectangle($background, 0, 0, $this->width, $this->height, $color);
  374. // Alpha blending must be enabled on the background!
  375. imagealphablending($background, TRUE);
  376. // Copy the image onto a white background to remove all transparency
  377. if (imagecopy($background, $this->_image, 0, 0, 0, 0, $this->width, $this->height))
  378. {
  379. // Swap the new image for the old one
  380. imagedestroy($this->_image);
  381. $this->_image = $background;
  382. }
  383. }
  384. protected function _do_save($file, $quality)
  385. {
  386. // Loads image if not yet loaded
  387. $this->_load_image();
  388. // Get the extension of the file
  389. $extension = pathinfo($file, PATHINFO_EXTENSION);
  390. // Get the save function and IMAGETYPE
  391. list($save, $type) = $this->_save_function($extension, $quality);
  392. // Save the image to a file
  393. $status = isset($quality) ? $save($this->_image, $file, $quality) : $save($this->_image, $file);
  394. if ($status === TRUE AND $type !== $this->type)
  395. {
  396. // Reset the image type and mime type
  397. $this->type = $type;
  398. $this->mime = image_type_to_mime_type($type);
  399. }
  400. return TRUE;
  401. }
  402. protected function _do_render($type, $quality)
  403. {
  404. // Loads image if not yet loaded
  405. $this->_load_image();
  406. // Get the save function and IMAGETYPE
  407. list($save, $type) = $this->_save_function($type, $quality);
  408. // Capture the output
  409. ob_start();
  410. // Render the image
  411. $status = isset($quality) ? $save($this->_image, NULL, $quality) : $save($this->_image, NULL);
  412. if ($status === TRUE AND $type !== $this->type)
  413. {
  414. // Reset the image type and mime type
  415. $this->type = $type;
  416. $this->mime = image_type_to_mime_type($type);
  417. }
  418. return ob_get_clean();
  419. }
  420. /**
  421. * Get the GD saving function and image type for this extension.
  422. * Also normalizes the quality setting
  423. *
  424. * @param string image type: png, jpg, etc
  425. * @param integer image quality
  426. * @return array save function, IMAGETYPE_* constant
  427. * @throws Kohana_Exception
  428. */
  429. protected function _save_function($extension, & $quality)
  430. {
  431. switch (strtolower($extension))
  432. {
  433. case 'jpg':
  434. case 'jpeg':
  435. // Save a JPG file
  436. $save = 'imagejpeg';
  437. $type = IMAGETYPE_JPEG;
  438. break;
  439. case 'gif':
  440. // Save a GIF file
  441. $save = 'imagegif';
  442. $type = IMAGETYPE_GIF;
  443. // GIFs do not a quality setting
  444. $quality = NULL;
  445. break;
  446. case 'png':
  447. // Save a PNG file
  448. $save = 'imagepng';
  449. $type = IMAGETYPE_PNG;
  450. // Use a compression level of 9 (does not affect quality!)
  451. $quality = 9;
  452. break;
  453. default:
  454. throw new Kohana_Exception('Installed GD does not support :type images',
  455. array(':type' => $extension));
  456. break;
  457. }
  458. return array($save, $type);
  459. }
  460. /**
  461. * Create an empty image with the given width and height.
  462. *
  463. * @param integer image width
  464. * @param integer image height
  465. * @return resource
  466. */
  467. protected function _create($width, $height)
  468. {
  469. // Create an empty image
  470. $image = imagecreatetruecolor($width, $height);
  471. // Do not apply alpha blending
  472. imagealphablending($image, FALSE);
  473. // Save alpha levels
  474. imagesavealpha($image, TRUE);
  475. return $image;
  476. }
  477. } // End Image_GD