PageRenderTime 47ms CodeModel.GetById 21ms RepoModel.GetById 1ms app.codeStats 0ms

/inc/Image/CropCanvas.php

https://github.com/chregu/fluxcms
PHP | 543 lines | 316 code | 57 blank | 170 comment | 73 complexity | f98b92423a05f7cc184849a21e7e7cde MD5 | raw file
Possible License(s): GPL-2.0, BSD-3-Clause, Apache-2.0, LGPL-2.1
  1. <?php
  2. //
  3. // class.cropcanvas.php
  4. // version 1.2.0, 26th November, 2003
  5. //
  6. // Description
  7. //
  8. // This is a class allows you to crop an image in a variety of ways.
  9. // You can crop in an absolute or relative way (to a certain size or
  10. // by a certain size), both as a pixel number or a percentage. You
  11. // can also save or display the cropped image. The cropping can be
  12. // done in 9 different positions: top left, top, top right, left,
  13. // centre, right, bottom left, bottom, or bottom right. Or you can
  14. // crop automatically based on a threshold limit. The original
  15. // image can be loaded from the file system or from a string (for
  16. // example, data returned from a database.)
  17. //
  18. // Author
  19. //
  20. // Andrew Collington, 2003
  21. // php@amnuts.com, http://php.amnuts.com/
  22. //
  23. //
  24. // Feedback
  25. //
  26. // There is message board at the following address:
  27. //
  28. // http://php.amnuts.com/forums/index.php
  29. //
  30. // Please use that to post up any comments, questions, bug reports, etc. You
  31. // can also use the board to show off your use of the script.
  32. //
  33. // Support
  34. //
  35. // If you like this script, or any of my others, then please take a moment
  36. // to consider giving a donation. This will encourage me to make updates and
  37. // create new scripts which I would make available to you. If you would like
  38. // to donate anything, then there is a link from my website to PayPal.
  39. //
  40. // Example of use
  41. //
  42. // require 'class.cropcanvas.php';
  43. // $cc = new canvasCrop();
  44. //
  45. // $cc->loadImage('original1.png');
  46. // $cc->cropBySize(100, 100, ccBOTTOMRIGHT);
  47. // $cc->saveImage('final1.png');
  48. //
  49. // $cc->flushImages();
  50. //
  51. // $cc->loadImage('original2.png');
  52. // $cc->cropByPercent(15, 50, ccCENTER);
  53. // $cc->saveImage('final2.jpg', 90);
  54. //
  55. // $cc->flushImages();
  56. //
  57. // $cc->loadImage('original3.png');
  58. // $cc->cropToDimensions(67, 37, 420, 255);
  59. // $cc->showImage('png');
  60. //
  61. define("ccTOPLEFT", 0);
  62. define("ccTOP", 1);
  63. define("ccTOPRIGHT", 2);
  64. define("ccLEFT", 3);
  65. define("ccCENTRE", 4);
  66. define("ccCENTER", 4);
  67. define("ccRIGHT", 5);
  68. define("ccBOTTOMLEFT", 6);
  69. define("ccBOTTOM", 7);
  70. define("ccBOTTOMRIGHT", 8);
  71. class Image_CropCanvas
  72. {
  73. public $_imgOrig;
  74. public $_imgFinal;
  75. public $_showDebug;
  76. public $_gdVersion;
  77. /**
  78. * @return canvasCrop
  79. * @param bool $debug
  80. * @desc Class initializer
  81. */
  82. function __construct($debug = false)
  83. {
  84. $this->_showDebug = ($debug ? true : false);
  85. $this->_gdVersion = (function_exists('imagecreatetruecolor')) ? 2 : 1;
  86. }
  87. /**
  88. * @return bool
  89. * @param string $filename
  90. * @desc Load an image from the file system - method based on file extension
  91. */
  92. function loadImage($filename)
  93. {
  94. if (!@file_exists($filename))
  95. {
  96. $this->_debug('loadImage', "The supplied file name '$filename' does not point to a readable file.");
  97. return false;
  98. }
  99. $ext = strtolower($this->_getExtension($filename));
  100. $func = "imagecreatefrom$ext";
  101. if (!@function_exists($func))
  102. {
  103. $this->_debug('loadImage', "That file cannot be loaded with the function '$func'.");
  104. return false;
  105. }
  106. $this->_imgOrig = @$func($filename);
  107. if ($this->_imgOrig == null)
  108. {
  109. $this->_debug('loadImage', 'The image could not be loaded.');
  110. return false;
  111. }
  112. return true;
  113. }
  114. /**
  115. * @return bool
  116. * @param string $string
  117. * @desc Load an image from a string (eg. from a database table)
  118. */
  119. function loadImageFromString($string)
  120. {
  121. $this->_imgOrig = @ImageCreateFromString($string);
  122. if (!$this->_imgOrig)
  123. {
  124. $this->_debug('loadImageFromString', 'The image could not be loaded.');
  125. return false;
  126. }
  127. return true;
  128. }
  129. /**
  130. * @return bool
  131. * @param string $filename
  132. * @param int $quality
  133. * @desc Save the cropped image
  134. */
  135. function saveImage($filename, $quality = 100)
  136. {
  137. if ($this->_imgFinal == null)
  138. {
  139. $this->_debug('saveImage', 'There is no processed image to save.');
  140. return false;
  141. }
  142. $ext = strtolower($this->_getExtension($filename));
  143. $func = "image$ext";
  144. if (!@function_exists($func))
  145. {
  146. $this->_debug('saveImage', "That file cannot be saved with the function '$func'.");
  147. return false;
  148. }
  149. $saved = false;
  150. if ($ext == 'png') $saved = $func($this->_imgFinal, $filename);
  151. if ($ext == 'jpeg') $saved = $func($this->_imgFinal, $filename, $quality);
  152. if ($saved == false)
  153. {
  154. $this->_debug('saveImage', "Could not save the output file '$filename' as a $ext.");
  155. return false;
  156. }
  157. return true;
  158. }
  159. /**
  160. * @return bool
  161. * @param string $type
  162. * @param int $quality
  163. * @desc Shows the cropped image without any saving
  164. */
  165. function showImage($type = 'png', $quality = 100)
  166. {
  167. if ($this->_imgFinal == null)
  168. {
  169. $this->_debug('showImage', 'There is no processed image to show.');
  170. return false;
  171. }
  172. if ($type == 'png')
  173. {
  174. echo @ImagePNG($this->_imgFinal);
  175. return true;
  176. }
  177. else if ($type == 'jpg' || $type == 'jpeg')
  178. {
  179. echo @ImageJPEG($this->_imgFinal, '', $quality);
  180. return true;
  181. }
  182. else
  183. {
  184. $this->_debug('showImage', "Could not show the output file as a $type.");
  185. return false;
  186. }
  187. }
  188. /**
  189. * @return int
  190. * @param int $x
  191. * @param int $y
  192. * @param int $position
  193. * @desc Determines the dimensions to crop to if using the 'crop by size' method
  194. */
  195. function cropBySize($x, $y, $position = ccCENTRE)
  196. {
  197. if ($x == 0)
  198. {
  199. $nx = @ImageSX($this->_imgOrig);
  200. }
  201. else
  202. {
  203. $nx = @ImageSX($this->_imgOrig) - $x;
  204. }
  205. if ($y == 0)
  206. {
  207. $ny = @ImageSY($this->_imgOrig);
  208. }
  209. else
  210. {
  211. $ny = @ImageSY($this->_imgOrig) - $y;
  212. }
  213. return ($this->_cropSize(-1, -1, $nx, $ny, $position, 'cropBySize'));
  214. }
  215. /**
  216. * @return int
  217. * @param int $x
  218. * @param int $y
  219. * @param int $position
  220. * @desc Determines the dimensions to crop to if using the 'crop to size' method
  221. */
  222. function cropToSize($x, $y, $position = ccCENTRE)
  223. {
  224. if ($x == 0) $x = 1;
  225. if ($y == 0) $y = 1;
  226. return ($this->_cropSize(-1, -1, $x, $y, $position, 'cropToSize'));
  227. }
  228. /**
  229. * @return int
  230. * @param int $sx
  231. * @param int $sy
  232. * @param int $ex
  233. * @param int $ey
  234. * @desc Determines the dimensions to crop to if using the 'crop to dimensions' method
  235. */
  236. function cropToDimensions($sx, $sy, $ex, $ey)
  237. {
  238. $nx = abs($ex - $sx);
  239. $ny = abs($ey - $sy);
  240. $position = ccCENTRE;
  241. return ($this->_cropSize($sx, $sy, $nx, $ny, $position, 'cropToDimensions'));
  242. }
  243. /**
  244. * @return int
  245. * @param int $percentx
  246. * @param int $percenty
  247. * @param int $position
  248. * @desc Determines the dimensions to crop to if using the 'crop by percentage' method
  249. */
  250. function cropByPercent($percentx, $percenty, $position = ccCENTRE)
  251. {
  252. if ($percentx == 0)
  253. {
  254. $nx = @ImageSX($this->_imgOrig);
  255. }
  256. else
  257. {
  258. $nx = @ImageSX($this->_imgOrig) - (($percentx / 100) * @ImageSX($this->_imgOrig));
  259. }
  260. if ($percenty == 0)
  261. {
  262. $ny = @ImageSY($this->_imgOrig);
  263. }
  264. else
  265. {
  266. $ny = @ImageSY($this->_imgOrig) - (($percenty / 100) * @ImageSY($this->_imgOrig));
  267. }
  268. return ($this->_cropSize(-1, -1, $nx, $ny, $position, 'cropByPercent'));
  269. }
  270. /**
  271. * @return int
  272. * @param int $percentx
  273. * @param int $percenty
  274. * @param int $position
  275. * @desc Determines the dimensions to crop to if using the 'crop to percentage' method
  276. */
  277. function cropToPercent($percentx, $percenty, $position = ccCENTRE)
  278. {
  279. if ($percentx == 0)
  280. {
  281. $nx = @ImageSX($this->_imgOrig);
  282. }
  283. else
  284. {
  285. $nx = ($percentx / 100) * @ImageSX($this->_imgOrig);
  286. }
  287. if ($percenty == 0)
  288. {
  289. $ny = @ImageSY($this->_imgOrig);
  290. }
  291. else
  292. {
  293. $ny = ($percenty / 100) * @ImageSY($this->_imgOrig);
  294. }
  295. return ($this->_cropSize(-1, -1, $nx, $ny, $position, 'cropByPercent'));
  296. }
  297. /**
  298. * @return bool
  299. * @param int $threshold
  300. * @desc Determines the dimensions to crop to if using the 'automatic crop by threshold' method
  301. */
  302. function cropByAuto($threshold = 254)
  303. {
  304. if ($threshold < 0) $threshold = 0;
  305. if ($threshold > 255) $threshold = 255;
  306. $sizex = @ImageSX($this->_imgOrig);
  307. $sizey = @ImageSY($this->_imgOrig);
  308. $sx = $sy = $ex = $ey = -1;
  309. for ($y = 0; $y < $sizey; $y++)
  310. {
  311. for ($x = 0; $x < $sizex; $x++)
  312. {
  313. if ($threshold >= $this->_getThresholdValue($this->_imgOrig, $x, $y))
  314. {
  315. if ($sy == -1) $sy = $y;
  316. else $ey = $y;
  317. if ($sx == -1) $sx = $x;
  318. else
  319. {
  320. if ($x < $sx) $sx = $x;
  321. else if ($x > $ex) $ex = $x;
  322. }
  323. }
  324. }
  325. }
  326. $nx = abs($ex - $sx);
  327. $ny = abs($ey - $sy);
  328. return ($this->_cropSize($sx, $sy, $nx, $ny, ccTOPLEFT, 'cropByAuto'));
  329. }
  330. /**
  331. * @return void
  332. * @desc Destroy the resources used by the images
  333. */
  334. function flushImages()
  335. {
  336. @ImageDestroy($this->_imgOrig);
  337. @ImageDestroy($this->_imgFinal);
  338. $this->_imgOrig = $this->_imgFinal = null;
  339. }
  340. /**
  341. * @return bool
  342. * @param int $ox Original image width
  343. * @param int $oy Original image height
  344. * @param int $nx New width
  345. * @param int $ny New height
  346. * @param int $position Where to place the crop
  347. * @param string $function Name of the calling function
  348. * @desc Creates the cropped image based on passed parameters
  349. */
  350. function _cropSize($ox, $oy, $nx, $ny, $position, $function)
  351. {
  352. if ($this->_imgOrig == null)
  353. {
  354. $this->_debug($function, 'The original image has not been loaded.');
  355. return false;
  356. }
  357. if (($nx <= 0) || ($ny <= 0))
  358. {
  359. $this->_debug($function, 'The image could not be cropped because the size given is not valid.');
  360. return false;
  361. }
  362. if (($nx > @ImageSX($this->_imgOrig)) || ($ny > @ImageSY($this->_imgOrig)))
  363. {
  364. $this->_debug($function, 'The image could not be cropped because the size given is larger than the original image.');
  365. return false;
  366. }
  367. if ($ox == -1 || $oy == -1)
  368. {
  369. list($ox, $oy) = $this->_getCopyPosition($nx, $ny, $position);
  370. }
  371. if ($this->_gdVersion == 2)
  372. {
  373. $this->_imgFinal = @ImageCreateTrueColor($nx, $ny);
  374. @ImageCopyResampled($this->_imgFinal, $this->_imgOrig, 0, 0, $ox, $oy, $nx, $ny, $nx, $ny);
  375. }
  376. else
  377. {
  378. $this->_imgFinal = @ImageCreate($nx, $ny);
  379. @ImageCopyResized($this->_imgFinal, $this->_imgOrig, 0, 0, $ox, $oy, $nx, $ny, $nx, $ny);
  380. }
  381. return true;
  382. }
  383. /**
  384. * @return array
  385. * @param int $nx
  386. * @param int $ny
  387. * @param int $position
  388. * @desc Determines dimensions of the crop
  389. */
  390. function _getCopyPosition($nx, $ny, $position)
  391. {
  392. $ox = @ImageSX($this->_imgOrig);
  393. $oy = @ImageSY($this->_imgOrig);
  394. switch($position)
  395. {
  396. case ccTOPLEFT:
  397. return array(0, 0);
  398. case ccTOP:
  399. return array(ceil(($ox - $nx) / 2), 0);
  400. case ccTOPRIGHT:
  401. return array(($ox - $nx), 0);
  402. case ccLEFT:
  403. return array(0, ceil(($oy - $ny) / 2));
  404. case ccCENTRE:
  405. return array(ceil(($ox - $nx) / 2), ceil(($oy - $ny) / 2));
  406. case ccRIGHT:
  407. return array(($ox - $nx), ceil(($oy - $ny) / 2));
  408. case ccBOTTOMLEFT:
  409. return array(0, ($oy - $ny));
  410. case ccBOTTOM:
  411. return array(ceil(($ox - $nx) / 2), ($oy - $ny));
  412. case ccBOTTOMRIGHT:
  413. return array(($ox - $nx), ($oy - $ny));
  414. }
  415. }
  416. /**
  417. * @return float
  418. * @param resource $im
  419. * @param int $x
  420. * @param int $y
  421. * @desc Determines the intensity value of a pixel at the passed co-ordinates
  422. */
  423. function _getThresholdValue($im, $x, $y)
  424. {
  425. $rgb = ImageColorAt($im, $x, $y);
  426. $r = ($rgb >> 16) & 0xFF;
  427. $g = ($rgb >> 8) & 0xFF;
  428. $b = $rgb & 0xFF;
  429. $intensity = ($r + $g + $b) / 3;
  430. return $intensity;
  431. }
  432. /**
  433. * @return string
  434. * @param string $filename
  435. * @desc Get the extension of a file name
  436. */
  437. function _getExtension($filename)
  438. {
  439. $ext = @strtolower(@substr($filename, (@strrpos($filename, ".") ? @strrpos($filename, ".") + 1 : @strlen($filename)), @strlen($filename)));
  440. return ($ext == 'jpg') ? 'jpeg' : $ext;
  441. }
  442. /**
  443. * @return void
  444. * @param string $function
  445. * @param string $string
  446. * @desc Shows debugging information
  447. */
  448. function _debug($function, $string)
  449. {
  450. if ($this->_showDebug)
  451. {
  452. echo "<p><strong style=\"color:#FF0000\">Error in function $function:</strong> $string</p>\n";
  453. } else {
  454. error_log("Error in function $function: $string");
  455. }
  456. }
  457. /**
  458. * @return array
  459. * @desc Try to ascertain what the version of GD being used is, based on phpinfo output
  460. */
  461. function _getGDVersion()
  462. {
  463. static $version = array();
  464. if (empty($version))
  465. {
  466. ob_start();
  467. phpinfo();
  468. $buffer = ob_get_contents();
  469. ob_end_clean();
  470. if (preg_match("|<B>GD Version</B></td><TD ALIGN=\"left\">([^<]*)</td>|i", $buffer, $matches))
  471. {
  472. $version = explode('.', $matches[1]);
  473. }
  474. else if (preg_match("|GD Version </td><td class=\"v\">bundled \(([^ ]*)|i", $buffer, $matches))
  475. {
  476. $version = explode('.', $matches[1]);
  477. }
  478. else if (preg_match("|GD Version </td><td class=\"v\">([^ ]*)|i", $buffer, $matches))
  479. {
  480. $version = explode('.', $matches[1]);
  481. }
  482. }
  483. return $version;
  484. }
  485. }
  486. ?>