image.lib.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473
  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. Display::display_warning_message('Class Imagick not found');
  26. exit;
  27. }
  28. }
  29. }
  30. public function resize($max_size_for_picture) {
  31. $image_size = $this->get_image_size($this->image_wrapper->path);
  32. $width = $image_size['width'];
  33. $height = $image_size['height'];
  34. if ($width >= $height) {
  35. if ($width >= $max_size_for_picture) {
  36. // scale height
  37. $new_height = round($height * ($max_size_for_picture / $width));
  38. $this->image_wrapper->resize($max_size_for_picture, $new_height, 0);
  39. }
  40. } else { // height > $width
  41. if ($height >= $max_size_for_picture) {
  42. // scale width
  43. $new_width = round($width * ($max_size_for_picture / $height));
  44. $this->image_wrapper->resize($new_width, $max_size_for_picture, 0);
  45. }
  46. }
  47. }
  48. public function crop($cropParameters) {
  49. $image_size = $this->get_image_size($this->image_wrapper->path);
  50. $src_width = $image_size['width'];
  51. $src_height = $image_size['height'];
  52. $cropParameters = explode(",", $cropParameters);
  53. $x = intval($cropParameters[0]);
  54. $y = intval($cropParameters[1]);
  55. $width = intval($cropParameters[2]);
  56. $height = intval($cropParameters[3]);
  57. $image = $this->image_wrapper->crop($x, $y, $width, $height, $src_width, $src_height);
  58. return $image;
  59. }
  60. public function send_image(
  61. $file = '',
  62. $compress = -1,
  63. $convert_file_to = null
  64. ) {
  65. return $this->image_wrapper->send_image(
  66. $file,
  67. $compress,
  68. $convert_file_to
  69. );
  70. }
  71. public function get_image_size()
  72. {
  73. return $this->image_wrapper->get_image_size();
  74. }
  75. public function get_image_info()
  76. {
  77. return $this->image_wrapper->get_image_info();
  78. }
  79. public function convert2bw()
  80. {
  81. $this->image_wrapper->convert2bw();
  82. }
  83. }
  84. /**
  85. * Image wrapper class
  86. *
  87. * @package chamilo.include.image
  88. */
  89. abstract class ImageWrapper
  90. {
  91. public $debug = true;
  92. public $path;
  93. public $width;
  94. public $height;
  95. public $type;
  96. public $allowed_extensions = array('jpeg', 'jpg', 'png', 'gif');
  97. public $image_validated = false;
  98. public function __construct($path)
  99. {
  100. if (empty($path)) {
  101. return false;
  102. }
  103. $this->path = $path;
  104. $this->set_image_wrapper(); //Creates image obj
  105. }
  106. abstract function set_image_wrapper();
  107. abstract function fill_image_info();
  108. abstract function get_image_size();
  109. abstract function resize($thumbw, $thumbh, $border, $specific_size = false);
  110. abstract function crop($x, $y, $width, $height, $src_width, $src_height);
  111. abstract function send_image($file = '', $compress = -1, $convert_file_to = null);
  112. public function get_image_info()
  113. {
  114. return array(
  115. 'width' => $this->width,
  116. 'height' => $this->height,
  117. 'type' => $this->type,
  118. );
  119. }
  120. }
  121. /**
  122. * Imagick Chamilo wrapper
  123. *
  124. * @author jmontoya
  125. *
  126. * @package chamilo.include.image
  127. */
  128. class ImagickWrapper extends ImageWrapper
  129. {
  130. public $image;
  131. public $filter = Imagick::FILTER_LANCZOS;
  132. public function __construct($path)
  133. {
  134. parent::__construct($path);
  135. }
  136. public function set_image_wrapper()
  137. {
  138. if ($this->debug) error_log('Image::set_image_wrapper loaded');
  139. try {
  140. if (file_exists($this->path)) {
  141. $this->image = new Imagick($this->path);
  142. if ($this->image) {
  143. $this->fill_image_info(); //Fills height, width and type
  144. }
  145. } else {
  146. if ($this->debug) error_log('Image::image does not exist');
  147. }
  148. } catch(ImagickException $e) {
  149. if ($this->debug) error_log($e->getMessage());
  150. }
  151. }
  152. public function fill_image_info()
  153. {
  154. $image_info = $this->image->identifyImage();
  155. $this->width = $image_info['geometry']['width'];
  156. $this->height = $image_info['geometry']['height'];
  157. $this->type = strtolower($this->image->getImageFormat());
  158. if (in_array($this->type, $this->allowed_extensions)) {
  159. $this->image_validated = true;
  160. if ($this->debug) error_log('image_validated true');
  161. }
  162. }
  163. public function get_image_size()
  164. {
  165. $imagesize = array('width'=>0,'height'=>0);
  166. if ($this->image_validated) {
  167. $imagesize = $this->image->getImageGeometry();
  168. }
  169. return $imagesize;
  170. }
  171. //@todo implement border logic case for Imagick
  172. public function resize($thumbw, $thumbh, $border, $specific_size = false)
  173. {
  174. if (!$this->image_validated) return false;
  175. if ($specific_size) {
  176. $width = $thumbw;
  177. $height = $thumbh;
  178. } else {
  179. $scale = ($this->width > 0 && $this->height > 0) ? min($thumbw / $this->width, $thumbh / $this->height) : 0;
  180. $width = (int)($this->width * $scale);
  181. $height = (int)($this->height * $scale);
  182. }
  183. $result = $this->image->resizeImage($width, $height, $this->filter, 1);
  184. $this->width = $thumbw;
  185. $this->height = $thumbh;
  186. }
  187. /**
  188. * @author José Loguercio <jose.loguercio@beeznest.com>
  189. * @param int $x coordinate of the cropped region top left corner
  190. * @param int $y coordinate of the cropped region top left corner
  191. * @param int $width the width of the crop
  192. * @param int $height the height of the crop
  193. * @param int $src_width the source width of the original image
  194. * @param int $src_height the source height of the original image
  195. */
  196. public function crop($x, $y, $width, $height, $src_width, $src_height) {
  197. if (!$this->image_validated) return false;
  198. $this->image->cropimage($width, $height, $x, $y);
  199. $this->width = $width;
  200. $this->height = $height;
  201. }
  202. public function send_image($file = '', $compress = -1, $convert_file_to = null)
  203. {
  204. if (!$this->image_validated) return false;
  205. $type = $this->type;
  206. if (!empty($convert_file_to) && in_array($convert_file_to, $this->allowed_extensions)) {
  207. $type = $convert_file_to;
  208. }
  209. switch ($type) {
  210. case 'jpeg':
  211. case 'jpg':
  212. if (!$file) header("Content-type: image/jpeg");
  213. break;
  214. case 'png':
  215. if (!$file) header("Content-type: image/png");
  216. break;
  217. case 'gif':
  218. if (!$file) header("Content-type: image/gif");
  219. break;
  220. }
  221. $result = false;
  222. try {
  223. $result = $this->image->writeImage($file);
  224. } catch(ImagickException $e) {
  225. if ($this->debug) error_log($e->getMessage());
  226. }
  227. if (!$file) {
  228. echo $this->image;
  229. $this->image->clear();
  230. $this->image->destroy();
  231. } else {
  232. $this->image->clear();
  233. $this->image->destroy();
  234. return $result;
  235. }
  236. }
  237. }
  238. /**
  239. * php-gd wrapper
  240. * @package chamilo.include.image
  241. */
  242. class GDWrapper extends ImageWrapper
  243. {
  244. public $bg;
  245. function __construct($path) {
  246. parent::__construct($path);
  247. }
  248. public function set_image_wrapper()
  249. {
  250. $handler = null;
  251. $this->fill_image_info();
  252. switch ($this->type) {
  253. case 0:
  254. $handler = false;
  255. break;
  256. case 1 :
  257. $handler = @imagecreatefromgif($this->path);
  258. $this->type = 'gif';
  259. break;
  260. case 2 :
  261. $handler = @imagecreatefromjpeg($this->path);
  262. $this->type = 'jpg';
  263. break;
  264. case 3 :
  265. $handler = @imagecreatefrompng($this->path);
  266. $this->type = 'png';
  267. break;
  268. }
  269. if ($handler) {
  270. $this->image_validated = true;
  271. $this->bg = $handler;
  272. @imagealphablending($this->bg, false);
  273. @imagesavealpha($this->bg, true);
  274. }
  275. }
  276. public function get_image_size()
  277. {
  278. $return_array = array('width'=>0,'height'=>0);
  279. if ($this->image_validated) {
  280. $return_array = array('width'=>$this->width,'height'=>$this->height);
  281. }
  282. return $return_array;
  283. }
  284. public function fill_image_info()
  285. {
  286. if (file_exists($this->path)) {
  287. $image_info = getimagesize($this->path);
  288. $this->width = $image_info[0];
  289. $this->height = $image_info[1];
  290. $this->type = $image_info[2];
  291. } else {
  292. $this->width = 0;
  293. $this->height = 0;
  294. $this->type = 0;
  295. }
  296. }
  297. public function resize($thumbw, $thumbh, $border, $specific_size = false)
  298. {
  299. if (!$this->image_validated) return false;
  300. if ($border == 1) {
  301. if ($specific_size) {
  302. $width = $thumbw;
  303. $height = $thumbh;
  304. } else {
  305. $scale = min($thumbw / $this->width, $thumbh / $this->height);
  306. $width = (int)($this->width * $scale);
  307. $height = (int)($this->height * $scale);
  308. }
  309. $deltaw = (int)(($thumbw - $width) / 2);
  310. $deltah = (int)(($thumbh - $height) / 2);
  311. $dst_img = @ImageCreateTrueColor($thumbw, $thumbh);
  312. @imagealphablending($dst_img, false);
  313. @imagesavealpha($dst_img, true);
  314. if (!empty($this->color)) {
  315. @imagefill($dst_img, 0, 0, $this->color);
  316. }
  317. $this->width = $thumbw;
  318. $this->height = $thumbh;
  319. } elseif ($border == 0) {
  320. if ($specific_size) {
  321. $width = $thumbw;
  322. $height = $thumbh;
  323. } else {
  324. $scale = ($this->width > 0 && $this->height > 0) ? min($thumbw / $this->width, $thumbh / $this->height) : 0;
  325. $width = (int)($this->width * $scale);
  326. $height = (int)($this->height * $scale);
  327. }
  328. $deltaw = 0;
  329. $deltah = 0;
  330. $dst_img = @ImageCreateTrueColor($width, $height);
  331. @imagealphablending($dst_img, false);
  332. @imagesavealpha($dst_img, true);
  333. $this->width = $width;
  334. $this->height = $height;
  335. }
  336. $src_img = $this->bg;
  337. @ImageCopyResampled($dst_img, $src_img, $deltaw, $deltah, 0, 0, $width, $height, ImageSX($src_img), ImageSY($src_img));
  338. $this->bg = $dst_img;
  339. @imagedestroy($src_img);
  340. }
  341. /**
  342. * @author José Loguercio <jose.loguercio@beeznest.com>
  343. * @param int $x coordinate of the cropped region top left corner
  344. * @param int $y coordinate of the cropped region top left corner
  345. * @param int $width the width of the crop
  346. * @param int $height the height of the crop
  347. * @param int $src_width the source width of the original image
  348. * @param int $src_height the source height of the original image
  349. */
  350. public function crop($x, $y, $width, $height, $src_width, $src_height) {
  351. if (!$this->image_validated) return false;
  352. $this->width = $width;
  353. $this->height = $height;
  354. $src = null;
  355. $dest = @imagecreatetruecolor($width, $height);
  356. $type = $this->type;
  357. switch ($type) {
  358. case 'jpeg' :
  359. case 'jpg' :
  360. $src = @imagecreatefromjpeg($this->path);
  361. @imagecopy($dest, $src, 0, 0, $x, $y, $src_width, $src_height);
  362. @imagejpeg($dest, $this->path);
  363. break;
  364. case 'png' :
  365. $src = @imagecreatefrompng($this->path);
  366. @imagecopy($dest, $src, 0, 0, $x, $y, $src_width, $src_height);
  367. @imagepng($dest, $this->path);
  368. break;
  369. case 'gif' :
  370. $src = @imagecreatefromgif($this->path);
  371. @imagecopy($dest, $src, 0, 0, $x, $y, $src_width, $src_height);
  372. @imagegif($dest, $this->path);
  373. break;
  374. default: return 0;
  375. }
  376. @imagedestroy($dest);
  377. @imagedestroy($src);
  378. }
  379. public function send_image($file = '', $compress = -1, $convert_file_to = null)
  380. {
  381. if (!$this->image_validated) return false;
  382. $compress = (int)$compress;
  383. $type = $this->type;
  384. if (!empty($convert_file_to) && in_array($convert_file_to, $this->allowed_extensions)) {
  385. $type = $convert_file_to;
  386. }
  387. switch ($type) {
  388. case 'jpeg':
  389. case 'jpg':
  390. if (!$file) header("Content-type: image/jpeg");
  391. if ($compress == -1) $compress = 100;
  392. return imagejpeg($this->bg, $file, $compress);
  393. break;
  394. case 'png':
  395. if (!$file) header("Content-type: image/png");
  396. if ($compress != -1) {
  397. @imagetruecolortopalette($this->bg, true, $compress);
  398. }
  399. return imagepng($this->bg, $file, $compress);
  400. break;
  401. case 'gif':
  402. if (!$file) header("Content-type: image/gif");
  403. if ($compress != -1) {
  404. @imagetruecolortopalette($this->bg, true, $compress);
  405. }
  406. return imagegif($this->bg, $file, $compress);
  407. break;
  408. default: return 0;
  409. }
  410. // TODO: Occupied memory is not released, because the following fragment of code is actually dead.
  411. @imagedestroy($this->bg);
  412. }
  413. /**
  414. * Convert image to black & white
  415. */
  416. function convert2bw()
  417. {
  418. if (!$this->image_validated) return false;
  419. $dest_img = imagecreatetruecolor(imagesx($this->bg), imagesy($this->bg));
  420. /* copy ignore the transparent color
  421. * so that we can use black (0,0,0) as transparent, which is what
  422. * the image is filled with when created.
  423. */
  424. $transparent = imagecolorallocate($dest_img, 0,0,0);
  425. imagealphablending($dest_img, false);
  426. imagesavealpha($dest_img, true);
  427. imagecolortransparent($dest_img, $transparent);
  428. imagecopy($dest_img, $this->bg, 0,0, 0, 0,imagesx($this->bg), imagesx($this->bg));
  429. imagefilter($dest_img, IMG_FILTER_GRAYSCALE);
  430. $this->bg = $dest_img;
  431. return true;
  432. }
  433. }