/protected/components/CImageHandler.php

https://bitbucket.org/markmoskalenko/svitor · PHP · 591 lines · 453 code · 129 blank · 9 comment · 31 complexity · 1e4155346394a409f3786db6f1e980c4 MD5 · raw file

  1. <?php
  2. class CImageHandler extends CApplicationComponent
  3. {
  4. private $image = null;
  5. private $format = 0;
  6. private $width = 0;
  7. private $height = 0;
  8. private $mimeType = '';
  9. private $fileName = '';
  10. public $transparencyColor = array(0, 0, 0);
  11. const IMG_GIF = 1;
  12. const IMG_JPEG = 2;
  13. const IMG_PNG = 3;
  14. const CORNER_LEFT_TOP = 1;
  15. const CORNER_RIGHT_TOP = 2;
  16. const CORNER_LEFT_BOTTOM = 3;
  17. const CORNER_RIGHT_BOTTOM = 4;
  18. const CORNER_CENTER = 5;
  19. const FLIP_HORIZONTAL = 1;
  20. const FLIP_VERTICAL = 2;
  21. const FLIP_BOTH = 3;
  22. public function getImage()
  23. {
  24. return $this->image;
  25. }
  26. public function getFormat()
  27. {
  28. return $this->format;
  29. }
  30. public function getWidth()
  31. {
  32. return $this->width;
  33. }
  34. public function getHeight()
  35. {
  36. return $this->height;
  37. }
  38. public function getMimeType()
  39. {
  40. return $this->mimeType;
  41. }
  42. public function __destruct()
  43. {
  44. $this->freeImage();
  45. }
  46. private function freeImage()
  47. {
  48. if (is_resource($this->image))
  49. {
  50. imagedestroy($this->image);
  51. }
  52. }
  53. private function checkLoaded()
  54. {
  55. if (!is_resource($this->image))
  56. {
  57. //throw new Exception('Load image first');
  58. return false;
  59. }
  60. }
  61. private function loadImage($file)
  62. {
  63. $result = array();
  64. if ($imageInfo = @getimagesize($file))
  65. {
  66. $result['width'] = $imageInfo[0];
  67. $result['height'] = $imageInfo[1];
  68. $result['mimeType'] = $imageInfo['mime'];
  69. switch ($result['format'] = $imageInfo[2])
  70. {
  71. case self::IMG_GIF:
  72. if ($result['image'] = imagecreatefromgif($file))
  73. {
  74. return $result;
  75. }
  76. else
  77. {
  78. //throw new Exception('Invalid image gif format');
  79. return false;
  80. }
  81. break;
  82. case self::IMG_JPEG:
  83. if ($result['image'] = imagecreatefromjpeg($file))
  84. {
  85. return $result;
  86. }
  87. else
  88. {
  89. //throw new Exception('Invalid image jpeg format');
  90. return false;
  91. }
  92. break;
  93. case self::IMG_PNG:
  94. if ($result['image'] = imagecreatefrompng($file))
  95. {
  96. return $result;
  97. }
  98. else
  99. {
  100. //throw new Exception('Invalid image png format');
  101. return false;
  102. }
  103. break;
  104. default:
  105. //throw new Exception('Not supported image format');
  106. return false;
  107. }
  108. }
  109. else
  110. {
  111. //throw new Exception('Invalid image file');
  112. return false;
  113. }
  114. }
  115. public function load($file)
  116. {
  117. $this->freeImage();
  118. if ($img = $this->loadImage($file))
  119. {
  120. $this->width = $img['width'];
  121. $this->height = $img['height'];
  122. $this->mimeType = $img['mimeType'];
  123. $this->format = $img['format'];
  124. $this->image = $img['image'];
  125. $this->fileName = $file;
  126. return $this;
  127. }
  128. else
  129. {
  130. return false;
  131. }
  132. }
  133. public function reload()
  134. {
  135. $this->checkLoaded();
  136. if ($this->fileName != '')
  137. {
  138. return $this->load($this->fileName);
  139. }
  140. }
  141. private function preserveTransparency($newImage)
  142. {
  143. switch($this->format)
  144. {
  145. case self::IMG_GIF:
  146. $color = imagecolorallocate(
  147. $newImage,
  148. $this->transparencyColor[0],
  149. $this->transparencyColor[1],
  150. $this->transparencyColor[2]
  151. );
  152. imagecolortransparent($newImage, $color);
  153. imagetruecolortopalette($newImage, false, 256);
  154. break;
  155. case self::IMG_PNG:
  156. imagealphablending($newImage, false);
  157. $color = imagecolorallocatealpha (
  158. $newImage,
  159. $this->transparencyColor[0],
  160. $this->transparencyColor[1],
  161. $this->transparencyColor[2],
  162. 0
  163. );
  164. imagefill($newImage, 0, 0, $color);
  165. imagesavealpha($newImage, true);
  166. break;
  167. }
  168. }
  169. public function resize($toWidth, $toHeight, $proportional = true)
  170. {
  171. $this->checkLoaded();
  172. $toWidth = $toWidth !== false ? $toWidth : $this->width;
  173. $toHeight = $toHeight !== false ? $toHeight : $this->height;
  174. if($proportional)
  175. {
  176. $newHeight = $toHeight;
  177. $newWidth = round($newHeight / $this->height * $this->width);
  178. if($newWidth > $toWidth)
  179. {
  180. $newWidth = $toWidth;
  181. $newHeight = round($newWidth / $this->width * $this->height);
  182. }
  183. }
  184. else
  185. {
  186. $newWidth = $toWidth;
  187. $newHeight = $toHeight;
  188. }
  189. $newImage = imagecreatetruecolor($newWidth, $newHeight);
  190. $this->preserveTransparency($newImage);
  191. imagecopyresampled($newImage, $this->image, 0, 0, 0, 0, $newWidth, $newHeight, $this->width, $this->height);
  192. imagedestroy($this->image);
  193. $this->image = $newImage;
  194. $this->width = $newWidth;
  195. $this->height = $newHeight;
  196. return $this;
  197. }
  198. public function thumb($toWidth, $toHeight, $proportional = true)
  199. {
  200. $this->checkLoaded();
  201. if($toWidth !== false)
  202. $toWidth = min($toWidth, $this->width);
  203. if($toHeight !== false)
  204. $toHeight = min($toHeight, $this->height);
  205. $this->resize($toWidth, $toHeight, $proportional);
  206. return $this;
  207. }
  208. public function watermark($watermarkFile, $offsetX, $offsetY, $corner = self::CORNER_RIGHT_BOTTOM)
  209. {
  210. $this->checkLoaded();
  211. if ($wImg = $this->loadImage($watermarkFile))
  212. {
  213. $posX = 0;
  214. $posY = 0;
  215. switch ($corner)
  216. {
  217. case self::CORNER_LEFT_TOP:
  218. $posX = $offsetX;
  219. $posY = $offsetY;
  220. break;
  221. case self::CORNER_RIGHT_TOP:
  222. $posX = $this->width - $wImg['width'] - $offsetX;
  223. $posY = $offsetY;
  224. break;
  225. case self::CORNER_LEFT_BOTTOM:
  226. $posX = $offsetX;
  227. $posY = $this->height - $wImg['height'] - $offsetY;
  228. break;
  229. case self::CORNER_RIGHT_BOTTOM:
  230. $posX = $this->width - $wImg['width'] - $offsetX;
  231. $posY = $this->height - $wImg['height'] - $offsetY;
  232. break;
  233. case self::CORNER_CENTER:
  234. $posX = floor(($this->width - $wImg['width']) / 2);
  235. $posY = floor(($this->height - $wImg['height']) / 2);
  236. break;
  237. default:
  238. throw new Exception('Invalid $corner value');
  239. }
  240. imagecopy($this->image, $wImg['image'], $posX, $posY, 0, 0, $wImg['width'], $wImg['height']);
  241. imagedestroy($wImg['image']);
  242. return $this;
  243. }
  244. else
  245. {
  246. return false;
  247. }
  248. }
  249. public function flip($mode)
  250. {
  251. $this->checkLoaded();
  252. $srcX = 0;
  253. $srcY = 0;
  254. $srcWidth = $this->width;
  255. $srcHeight = $this->height;
  256. switch ($mode)
  257. {
  258. case self::FLIP_HORIZONTAL:
  259. $srcX = $this->width - 1;
  260. $srcWidth = -$this->width;
  261. break;
  262. case self::FLIP_VERTICAL:
  263. $srcY = $this->height - 1;
  264. $srcHeight = -$this->height;
  265. break;
  266. case self::FLIP_BOTH:
  267. $srcX = $this->width - 1;
  268. $srcY = $this->height - 1;
  269. $srcWidth = -$this->width;
  270. $srcHeight = -$this->height;
  271. break;
  272. default:
  273. throw new Exception('Invalid $mode value');
  274. }
  275. $newImage = imagecreatetruecolor($this->width, $this->height);
  276. $this->preserveTransparency($newImage);
  277. imagecopyresampled($newImage, $this->image, 0, 0, $srcX, $srcY, $this->width, $this->height, $srcWidth, $srcHeight);
  278. imagedestroy($this->image);
  279. $this->image = $newImage;
  280. //dimensions not changed
  281. return $this;
  282. }
  283. public function rotate($degrees)
  284. {
  285. $this->checkLoaded();
  286. $degrees = (int) $degrees;
  287. $this->image = imagerotate($this->image, $degrees, 0);
  288. $this->width = imagesx($this->image);
  289. $this->height = imagesy($this->image);
  290. return $this;
  291. }
  292. public function crop($width, $height, $startX = false, $startY = false)
  293. {
  294. $this->checkLoaded();
  295. $width = (int) $width;
  296. $height = (int) $height;
  297. //Centered crop
  298. $startX = $startX === false ? floor(($this->width - $width) / 2) : intval($startX);
  299. $startY = $startY === false ? floor(($this->height - $height) / 2) : intval($startY);
  300. //Check dimensions
  301. $startX = max(0, min($this->width, $startX));
  302. $startY = max(0, min($this->height, $startY));
  303. $width = min($width, $this->width - $startX);
  304. $height = min($height, $this->height - $startY);
  305. $newImage = imagecreatetruecolor($width, $height);
  306. $this->preserveTransparency($newImage);
  307. imagecopyresampled($newImage, $this->image, 0, 0, $startX, $startY, $width, $height, $width, $height);
  308. imagedestroy($this->image);
  309. $this->image = $newImage;
  310. $this->width = $width;
  311. $this->height = $height;
  312. return $this;
  313. }
  314. public function text($text, $fontFile, $size=12, $color=array(0, 0, 0),
  315. $corner=self::CORNER_LEFT_TOP, $offsetX=0, $offsetY=0, $angle=0)
  316. {
  317. $this->checkLoaded();
  318. $bBox = imagettfbbox($size, $angle, $fontFile, $text);
  319. $textHeight = $bBox[1] - $bBox[7];
  320. $textWidth = $bBox[2] - $bBox[0];
  321. $color = imagecolorallocate($this->image, $color[0], $color[1], $color[2]);
  322. switch ($corner)
  323. {
  324. case self::CORNER_LEFT_TOP:
  325. $posX = $offsetX;
  326. $posY = $offsetY;
  327. break;
  328. case self::CORNER_RIGHT_TOP:
  329. $posX = $this->width - $textWidth - $offsetX;
  330. $posY = $offsetY;
  331. break;
  332. case self::CORNER_LEFT_BOTTOM:
  333. $posX = $offsetX;
  334. $posY = $this->height - $textHeight - $offsetY;
  335. break;
  336. case self::CORNER_RIGHT_BOTTOM:
  337. $posX = $this->width - $textWidth - $offsetX;
  338. $posY = $this->height - $textHeight - $offsetY;
  339. break;
  340. case self::CORNER_CENTER:
  341. $posX = floor(($this->width - $textWidth) / 2);
  342. $posY = floor(($this->height - $textHeight) / 2);
  343. break;
  344. default:
  345. throw new Exception('Invalid $corner value');
  346. }
  347. imagettftext($this->image, $size, $angle, $posX, $posY + $textHeight, $color, $fontFile, $text);
  348. return $this;
  349. }
  350. public function adaptiveThumb($width, $height)
  351. {
  352. $this->checkLoaded();
  353. $width = intval($width);
  354. $height = intval($height);
  355. $widthProportion = $width / $this->width;
  356. $heightProportion = $height / $this->height;
  357. if ($widthProportion > $heightProportion)
  358. {
  359. $newWidth = $width;
  360. $newHeight = round($newWidth / $this->width * $this->height);
  361. }
  362. else
  363. {
  364. $newHeight = $height;
  365. $newWidth = round($newHeight / $this->height * $this->width);
  366. }
  367. $this->resize($newWidth, $newHeight);
  368. $this->crop($width, $height);
  369. return $this;
  370. }
  371. public function resizeCanvas($toWidth, $toHeight, $backgroundColor = array(255, 255, 255))
  372. {
  373. $this->checkLoaded();
  374. $newWidth = min($toWidth, $this->width);
  375. $newHeight = min($toHeight, $this->height);
  376. if ($this->width > $this->height)
  377. {
  378. $newHeight = round($newWidth / $this->width * $this->height);
  379. }
  380. else
  381. {
  382. $newWidth = round($newHeight / $this->height * $this->width);
  383. }
  384. $posX = floor(($toWidth - $newWidth) / 2);
  385. $posY = floor(($toHeight - $newHeight) / 2);
  386. $newImage = imagecreatetruecolor($toWidth, $toHeight);
  387. $backgroundColor = imagecolorallocate($newImage, $backgroundColor[0], $backgroundColor[1], $backgroundColor[2]);
  388. imagefill($newImage, 0, 0, $backgroundColor);
  389. imagecopyresampled($newImage, $this->image, $posX, $posY, 0, 0, $newWidth, $newHeight, $this->width, $this->height);
  390. imagedestroy($this->image);
  391. $this->image = $newImage;
  392. $this->width = $toWidth;
  393. $this->height = $toHeight;
  394. return $this;
  395. }
  396. public function show($inFormat = false, $jpegQuality = 75)
  397. {
  398. $this->checkLoaded();
  399. if (!$inFormat)
  400. {
  401. $inFormat = $this->format;
  402. }
  403. switch ($inFormat)
  404. {
  405. case self::IMG_GIF:
  406. header('Content-type: image/gif');
  407. imagegif($this->image);
  408. break;
  409. case self::IMG_JPEG:
  410. header('Content-type: image/jpeg');
  411. imagejpeg($this->image, null, $jpegQuality);
  412. break;
  413. case self::IMG_PNG:
  414. header('Content-type: image/png');
  415. imagepng($this->image);
  416. break;
  417. default:
  418. throw new Exception('Invalid image format for putput');
  419. }
  420. return $this;
  421. }
  422. public function save($file = false, $toFormat = false, $jpegQuality = 75, $touch = false)
  423. {
  424. if (empty($file))
  425. {
  426. $file = $this->fileName;
  427. }
  428. $this->checkLoaded();
  429. if (!$toFormat)
  430. {
  431. $toFormat = $this->format;
  432. }
  433. switch ($toFormat)
  434. {
  435. case self::IMG_GIF:
  436. if (!imagegif($this->image, $file))
  437. {
  438. throw new Exception('Can\'t save gif file');
  439. }
  440. break;
  441. case self::IMG_JPEG:
  442. if (!imagejpeg($this->image, $file, $jpegQuality))
  443. {
  444. throw new Exception('Can\'t save jpeg file');
  445. }
  446. break;
  447. case self::IMG_PNG:
  448. if (!imagepng($this->image, $file))
  449. {
  450. throw new Exception('Can\'t save png file');
  451. }
  452. break;
  453. default:
  454. throw new Exception('Invalid image format for save');
  455. }
  456. if ($touch && $file != $this->fileName)
  457. {
  458. touch($file, filemtime($this->fileName));
  459. }
  460. return $this;
  461. }
  462. }