image.lib.php 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585
  1. <?php
  2. /* For licensing terms, see /license.txt */
  3. /**
  4. * Image class
  5. * This class provides a layer to manage images
  6. * @author Julio Montoya <gugli100@gmail.com>
  7. * @package chamilo.include.image
  8. * @todo move in a DB configuration setting
  9. */
  10. class Image
  11. {
  12. public $image_wrapper = null;
  13. /**
  14. * Image constructor.
  15. * @param string $path
  16. */
  17. public function __construct($path)
  18. {
  19. if (IMAGE_PROCESSOR == 'gd') {
  20. $this->image_wrapper = new GDWrapper($path);
  21. } else {
  22. if (class_exists('Imagick')) {
  23. $this->image_wrapper = new ImagickWrapper($path);
  24. } else {
  25. echo Display::return_message(
  26. 'Class Imagick not found',
  27. 'warning'
  28. );
  29. exit;
  30. }
  31. }
  32. }
  33. public function resize($max_size_for_picture)
  34. {
  35. $image_size = $this->get_image_size($this->image_wrapper->path);
  36. $width = $image_size['width'];
  37. $height = $image_size['height'];
  38. if ($width >= $height) {
  39. if ($width >= $max_size_for_picture) {
  40. // scale height
  41. $new_height = round($height * ($max_size_for_picture / $width));
  42. $this->image_wrapper->resize(
  43. $max_size_for_picture,
  44. $new_height,
  45. 0
  46. );
  47. }
  48. } else { // height > $width
  49. if ($height >= $max_size_for_picture) {
  50. // scale width
  51. $new_width = round($width * ($max_size_for_picture / $height));
  52. $this->image_wrapper->resize(
  53. $new_width,
  54. $max_size_for_picture,
  55. 0
  56. );
  57. }
  58. }
  59. }
  60. public function crop($cropParameters)
  61. {
  62. $image_size = $this->get_image_size($this->image_wrapper->path);
  63. $src_width = $image_size['width'];
  64. $src_height = $image_size['height'];
  65. $cropParameters = explode(",", $cropParameters);
  66. $x = intval($cropParameters[0]);
  67. $y = intval($cropParameters[1]);
  68. $width = intval($cropParameters[2]);
  69. $height = intval($cropParameters[3]);
  70. $image = $this->image_wrapper->crop(
  71. $x,
  72. $y,
  73. $width,
  74. $height,
  75. $src_width,
  76. $src_height
  77. );
  78. return $image;
  79. }
  80. /**
  81. * @param string $file
  82. * @param int $compress
  83. * @param null $convert_file_to
  84. * @return bool
  85. */
  86. public function send_image(
  87. $file = '',
  88. $compress = -1,
  89. $convert_file_to = null
  90. ) {
  91. return $this->image_wrapper->send_image(
  92. $file,
  93. $compress,
  94. $convert_file_to
  95. );
  96. }
  97. /**
  98. * @return array
  99. */
  100. public function get_image_size()
  101. {
  102. return $this->image_wrapper->get_image_size();
  103. }
  104. /**
  105. * @return array
  106. */
  107. public function get_image_info()
  108. {
  109. return $this->image_wrapper->get_image_info();
  110. }
  111. public function convert2bw()
  112. {
  113. $this->image_wrapper->convert2bw();
  114. }
  115. }
  116. /**
  117. * Image wrapper class
  118. *
  119. * @package chamilo.include.image
  120. */
  121. abstract class ImageWrapper
  122. {
  123. public $debug = true;
  124. public $path;
  125. public $width;
  126. public $height;
  127. public $type;
  128. public $allowed_extensions = array('jpeg', 'jpg', 'png', 'gif');
  129. public $image_validated = false;
  130. public function __construct($path)
  131. {
  132. if (empty($path)) {
  133. return false;
  134. }
  135. $this->path = $path;
  136. $this->set_image_wrapper(); //Creates image obj
  137. }
  138. abstract function set_image_wrapper();
  139. abstract function fill_image_info();
  140. abstract function get_image_size();
  141. abstract function resize($thumbw, $thumbh, $border, $specific_size = false);
  142. abstract function crop($x, $y, $width, $height, $src_width, $src_height);
  143. abstract function send_image($file = '', $compress = -1, $convert_file_to = null);
  144. /**
  145. * @return array
  146. */
  147. public function get_image_info()
  148. {
  149. return array(
  150. 'width' => $this->width,
  151. 'height' => $this->height,
  152. 'type' => $this->type,
  153. );
  154. }
  155. }
  156. /**
  157. * Imagick Chamilo wrapper
  158. *
  159. * @author jmontoya
  160. *
  161. * @package chamilo.include.image
  162. */
  163. class ImagickWrapper extends ImageWrapper
  164. {
  165. public $image;
  166. public $filter = Imagick::FILTER_LANCZOS;
  167. /**
  168. * ImagickWrapper constructor.
  169. * @param $path
  170. */
  171. public function __construct($path)
  172. {
  173. parent::__construct($path);
  174. }
  175. /**
  176. *
  177. */
  178. public function set_image_wrapper()
  179. {
  180. if ($this->debug) error_log('Image::set_image_wrapper loaded');
  181. try {
  182. if (file_exists($this->path)) {
  183. $this->image = new Imagick($this->path);
  184. if ($this->image) {
  185. $this->fill_image_info(); //Fills height, width and type
  186. }
  187. } else {
  188. if ($this->debug) error_log('Image::image does not exist');
  189. }
  190. } catch (ImagickException $e) {
  191. if ($this->debug) error_log($e->getMessage());
  192. }
  193. }
  194. public function fill_image_info()
  195. {
  196. $image_info = $this->image->identifyImage();
  197. $this->width = $image_info['geometry']['width'];
  198. $this->height = $image_info['geometry']['height'];
  199. $this->type = strtolower($this->image->getImageFormat());
  200. if (in_array($this->type, $this->allowed_extensions)) {
  201. $this->image_validated = true;
  202. if ($this->debug) error_log('image_validated true');
  203. }
  204. }
  205. public function get_image_size()
  206. {
  207. $imagesize = array('width'=>0, 'height'=>0);
  208. if ($this->image_validated) {
  209. $imagesize = $this->image->getImageGeometry();
  210. }
  211. return $imagesize;
  212. }
  213. //@todo implement border logic case for Imagick
  214. public function resize($thumbw, $thumbh, $border, $specific_size = false)
  215. {
  216. if (!$this->image_validated) return false;
  217. if ($specific_size) {
  218. $width = $thumbw;
  219. $height = $thumbh;
  220. } else {
  221. $scale = ($this->width > 0 && $this->height > 0) ? min($thumbw / $this->width, $thumbh / $this->height) : 0;
  222. $width = (int) ($this->width * $scale);
  223. $height = (int) ($this->height * $scale);
  224. }
  225. $result = $this->image->resizeImage($width, $height, $this->filter, 1);
  226. $this->width = $thumbw;
  227. $this->height = $thumbh;
  228. }
  229. /**
  230. * @author José Loguercio <jose.loguercio@beeznest.com>
  231. * @param int $x coordinate of the cropped region top left corner
  232. * @param int $y coordinate of the cropped region top left corner
  233. * @param int $width the width of the crop
  234. * @param int $height the height of the crop
  235. * @param int $src_width the source width of the original image
  236. * @param int $src_height the source height of the original image
  237. */
  238. public function crop($x, $y, $width, $height, $src_width, $src_height)
  239. {
  240. if (!$this->image_validated) {
  241. return false;
  242. }
  243. $this->image->cropimage($width, $height, $x, $y);
  244. $this->width = $width;
  245. $this->height = $height;
  246. }
  247. public function send_image(
  248. $file = '',
  249. $compress = -1,
  250. $convert_file_to = null
  251. ) {
  252. if (!$this->image_validated) {
  253. return false;
  254. }
  255. $type = $this->type;
  256. if (!empty($convert_file_to) && in_array($convert_file_to, $this->allowed_extensions)) {
  257. $type = $convert_file_to;
  258. }
  259. switch ($type) {
  260. case 'jpeg':
  261. case 'jpg':
  262. if (!$file) {
  263. header("Content-type: image/jpeg");
  264. }
  265. break;
  266. case 'png':
  267. if (!$file) {
  268. header("Content-type: image/png");
  269. }
  270. break;
  271. case 'gif':
  272. if (!$file) {
  273. header("Content-type: image/gif");
  274. }
  275. break;
  276. }
  277. $result = false;
  278. try {
  279. $result = $this->image->writeImage($file);
  280. } catch (ImagickException $e) {
  281. if ($this->debug) {
  282. error_log($e->getMessage());
  283. }
  284. }
  285. if (!$file) {
  286. echo $this->image;
  287. $this->image->clear();
  288. $this->image->destroy();
  289. } else {
  290. $this->image->clear();
  291. $this->image->destroy();
  292. return $result;
  293. }
  294. }
  295. }
  296. /**
  297. * php-gd wrapper
  298. * @package chamilo.include.image
  299. */
  300. class GDWrapper extends ImageWrapper
  301. {
  302. public $bg;
  303. /**
  304. * GDWrapper constructor.
  305. * @param $path
  306. */
  307. public function __construct($path)
  308. {
  309. parent::__construct($path);
  310. }
  311. public function set_image_wrapper()
  312. {
  313. $handler = null;
  314. $this->fill_image_info();
  315. switch ($this->type) {
  316. case 0:
  317. $handler = false;
  318. break;
  319. case 1:
  320. $handler = @imagecreatefromgif($this->path);
  321. $this->type = 'gif';
  322. break;
  323. case 2:
  324. $handler = @imagecreatefromjpeg($this->path);
  325. $this->type = 'jpg';
  326. break;
  327. case 3:
  328. $handler = @imagecreatefrompng($this->path);
  329. $this->type = 'png';
  330. break;
  331. }
  332. if ($handler) {
  333. $this->image_validated = true;
  334. $this->bg = $handler;
  335. @imagealphablending($this->bg, false);
  336. @imagesavealpha($this->bg, true);
  337. }
  338. }
  339. /**
  340. * @return array
  341. */
  342. public function get_image_size()
  343. {
  344. $return_array = array('width'=>0, 'height'=>0);
  345. if ($this->image_validated) {
  346. $return_array = array('width'=>$this->width, 'height'=>$this->height);
  347. }
  348. return $return_array;
  349. }
  350. public function fill_image_info()
  351. {
  352. if (file_exists($this->path)) {
  353. $image_info = getimagesize($this->path);
  354. $this->width = $image_info[0];
  355. $this->height = $image_info[1];
  356. $this->type = $image_info[2];
  357. } else {
  358. $this->width = 0;
  359. $this->height = 0;
  360. $this->type = 0;
  361. }
  362. }
  363. public function resize($thumbw, $thumbh, $border, $specific_size = false)
  364. {
  365. if (!$this->image_validated) {
  366. return false;
  367. }
  368. if ($border == 1) {
  369. if ($specific_size) {
  370. $width = $thumbw;
  371. $height = $thumbh;
  372. } else {
  373. $scale = min($thumbw / $this->width, $thumbh / $this->height);
  374. $width = (int) ($this->width * $scale);
  375. $height = (int) ($this->height * $scale);
  376. }
  377. $deltaw = (int) (($thumbw - $width) / 2);
  378. $deltah = (int) (($thumbh - $height) / 2);
  379. $dst_img = @ImageCreateTrueColor($thumbw, $thumbh);
  380. @imagealphablending($dst_img, false);
  381. @imagesavealpha($dst_img, true);
  382. if (!empty($this->color)) {
  383. @imagefill($dst_img, 0, 0, $this->color);
  384. }
  385. $this->width = $thumbw;
  386. $this->height = $thumbh;
  387. } elseif ($border == 0) {
  388. if ($specific_size) {
  389. $width = $thumbw;
  390. $height = $thumbh;
  391. } else {
  392. $scale = ($this->width > 0 && $this->height > 0) ? min($thumbw / $this->width, $thumbh / $this->height) : 0;
  393. $width = (int) ($this->width * $scale);
  394. $height = (int) ($this->height * $scale);
  395. }
  396. $deltaw = 0;
  397. $deltah = 0;
  398. $dst_img = @ImageCreateTrueColor($width, $height);
  399. @imagealphablending($dst_img, false);
  400. @imagesavealpha($dst_img, true);
  401. $this->width = $width;
  402. $this->height = $height;
  403. }
  404. $src_img = $this->bg;
  405. @ImageCopyResampled(
  406. $dst_img,
  407. $src_img,
  408. $deltaw,
  409. $deltah,
  410. 0,
  411. 0,
  412. $width,
  413. $height,
  414. ImageSX($src_img),
  415. ImageSY($src_img)
  416. );
  417. $this->bg = $dst_img;
  418. @imagedestroy($src_img);
  419. }
  420. /**
  421. * @author José Loguercio <jose.loguercio@beeznest.com>
  422. * @param int $x coordinate of the cropped region top left corner
  423. * @param int $y coordinate of the cropped region top left corner
  424. * @param int $width the width of the crop
  425. * @param int $height the height of the crop
  426. * @param int $src_width the source width of the original image
  427. * @param int $src_height the source height of the original image
  428. */
  429. public function crop($x, $y, $width, $height, $src_width, $src_height)
  430. {
  431. if (!$this->image_validated) {
  432. return false;
  433. }
  434. $this->width = $width;
  435. $this->height = $height;
  436. $src = null;
  437. $dest = @imagecreatetruecolor($width, $height);
  438. $type = $this->type;
  439. switch ($type) {
  440. case 'jpeg':
  441. case 'jpg':
  442. $src = @imagecreatefromjpeg($this->path);
  443. @imagecopy($dest, $src, 0, 0, $x, $y, $src_width, $src_height);
  444. @imagejpeg($dest, $this->path);
  445. break;
  446. case 'png':
  447. $src = @imagecreatefrompng($this->path);
  448. @imagecopy($dest, $src, 0, 0, $x, $y, $src_width, $src_height);
  449. @imagepng($dest, $this->path);
  450. break;
  451. case 'gif':
  452. $src = @imagecreatefromgif($this->path);
  453. @imagecopy($dest, $src, 0, 0, $x, $y, $src_width, $src_height);
  454. @imagegif($dest, $this->path);
  455. break;
  456. default:
  457. return 0;
  458. }
  459. @imagedestroy($dest);
  460. @imagedestroy($src);
  461. }
  462. /**
  463. * @param string $file
  464. * @param int $compress
  465. * @param null $convert_file_to
  466. * @return bool|int
  467. */
  468. public function send_image($file = '', $compress = -1, $convert_file_to = null)
  469. {
  470. if (!$this->image_validated) {
  471. return false;
  472. }
  473. $compress = (int) $compress;
  474. $type = $this->type;
  475. if (!empty($convert_file_to) && in_array($convert_file_to, $this->allowed_extensions)) {
  476. $type = $convert_file_to;
  477. }
  478. switch ($type) {
  479. case 'jpeg':
  480. case 'jpg':
  481. if (!$file) {
  482. header("Content-type: image/jpeg");
  483. }
  484. if ($compress == -1) {
  485. $compress = 100;
  486. }
  487. return imagejpeg($this->bg, $file, $compress);
  488. break;
  489. case 'png':
  490. if (!$file) {
  491. header("Content-type: image/png");
  492. }
  493. if ($compress != -1) {
  494. @imagetruecolortopalette($this->bg, true, $compress);
  495. }
  496. return imagepng($this->bg, $file, $compress);
  497. break;
  498. case 'gif':
  499. if (!$file) {
  500. header("Content-type: image/gif");
  501. }
  502. if ($compress != -1) {
  503. @imagetruecolortopalette($this->bg, true, $compress);
  504. }
  505. return imagegif($this->bg, $file, $compress);
  506. break;
  507. default:
  508. return 0;
  509. }
  510. // TODO: Occupied memory is not released, because the following fragment of code is actually dead.
  511. @imagedestroy($this->bg);
  512. }
  513. /**
  514. * Convert image to black & white
  515. */
  516. public function convert2bw()
  517. {
  518. if (!$this->image_validated) {
  519. return false;
  520. }
  521. $dest_img = imagecreatetruecolor(imagesx($this->bg), imagesy($this->bg));
  522. /* copy ignore the transparent color
  523. * so that we can use black (0,0,0) as transparent, which is what
  524. * the image is filled with when created.
  525. */
  526. $transparent = imagecolorallocate($dest_img, 0, 0, 0);
  527. imagealphablending($dest_img, false);
  528. imagesavealpha($dest_img, true);
  529. imagecolortransparent($dest_img, $transparent);
  530. imagecopy(
  531. $dest_img,
  532. $this->bg,
  533. 0,
  534. 0,
  535. 0,
  536. 0,
  537. imagesx($this->bg),
  538. imagesx($this->bg)
  539. );
  540. imagefilter($dest_img, IMG_FILTER_GRAYSCALE);
  541. $this->bg = $dest_img;
  542. return true;
  543. }
  544. }