PageRenderTime 57ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/Imagine/Gmagick/Image.php

http://github.com/avalanche123/Imagine
PHP | 523 lines | 342 code | 65 blank | 116 comment | 17 complexity | 7168dc0f4d85bcde9a42d80665a8e186 MD5 | raw file
  1. <?php
  2. /*
  3. * This file is part of the Imagine package.
  4. *
  5. * (c) Bulat Shakirzyanov <mallluhuct@gmail.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Imagine\Gmagick;
  11. use Imagine\Exception\OutOfBoundsException;
  12. use Imagine\Exception\InvalidArgumentException;
  13. use Imagine\Exception\RuntimeException;
  14. use Imagine\Gmagick\Imagine;
  15. use Imagine\Image\ImageInterface;
  16. use Imagine\Image\Box;
  17. use Imagine\Image\BoxInterface;
  18. use Imagine\Image\Color;
  19. use Imagine\Image\Fill\FillInterface;
  20. use Imagine\Image\Point;
  21. use Imagine\Image\PointInterface;
  22. class Image implements ImageInterface
  23. {
  24. /**
  25. * @var Gmagick
  26. */
  27. private $gmagick;
  28. /**
  29. * Constructs Image with Gmagick and Imagine instances
  30. *
  31. * @param Gmagick $gmagick
  32. */
  33. public function __construct(\Gmagick $gmagick)
  34. {
  35. $this->gmagick = $gmagick;
  36. }
  37. /**
  38. * Destroys allocated gmagick resources
  39. */
  40. public function __destruct()
  41. {
  42. if (null !== $this->gmagick && $this->gmagick instanceof \Gmagick) {
  43. $this->gmagick->clear();
  44. $this->gmagick->destroy();
  45. }
  46. }
  47. /**
  48. * (non-PHPdoc)
  49. * @see Imagine\Image\ManipulatorInterface::copy()
  50. */
  51. public function copy()
  52. {
  53. return new self(clone $this->gmagick);
  54. }
  55. /**
  56. * (non-PHPdoc)
  57. * @see Imagine\Image\ManipulatorInterface::crop()
  58. */
  59. public function crop(PointInterface $start, BoxInterface $size)
  60. {
  61. if (!$start->in($this->getSize())) {
  62. throw new OutOfBoundsException(
  63. 'Crop coordinates must start at minimum 0, 0 position from '.
  64. 'top left corner, crop height and width must be positive '.
  65. 'integers and must not exceed the current image borders'
  66. );
  67. }
  68. try {
  69. $this->gmagick->cropimage(
  70. $size->getWidth(),
  71. $size->getHeight(),
  72. $start->getX(),
  73. $start->getY()
  74. );
  75. } catch (\GmagickException $e) {
  76. throw new RuntimeException(
  77. 'Crop operation failed', $e->getCode(), $e
  78. );
  79. }
  80. return $this;
  81. }
  82. /**
  83. * (non-PHPdoc)
  84. * @see Imagine\Image\ManipulatorInterface::flipHorizontally()
  85. */
  86. public function flipHorizontally()
  87. {
  88. try {
  89. $this->gmagick->flopimage();
  90. } catch (\GmagickException $e) {
  91. throw new RuntimeException(
  92. 'Horizontal flip operation failed', $e->getCode(), $e
  93. );
  94. }
  95. return $this;
  96. }
  97. /**
  98. * (non-PHPdoc)
  99. * @see Imagine\Image\ManipulatorInterface::flipVertically()
  100. */
  101. public function flipVertically()
  102. {
  103. try {
  104. $this->gmagick->flipimage();
  105. } catch (\GmagickException $e) {
  106. throw new RuntimeException(
  107. 'Vertical flip operation failed', $e->getCode(), $e
  108. );
  109. }
  110. return $this;
  111. }
  112. /**
  113. * (non-PHPdoc)
  114. * @see Imagine\Image\ManipulatorInterface::paste()
  115. */
  116. public function paste(ImageInterface $image, PointInterface $start)
  117. {
  118. if (!$image instanceof self) {
  119. throw new InvalidArgumentException(sprintf(
  120. 'Gmagick\Image can only paste() Gmagick\Image instances, '.
  121. '%s given', get_class($image)
  122. ));
  123. }
  124. if (!$this->getSize()->contains($image->getSize(), $start)) {
  125. throw new OutOfBoundsException(
  126. 'Cannot paste image of the given size at the specified '.
  127. 'position, as it moves outside of the current image\'s box'
  128. );
  129. }
  130. try {
  131. $this->gmagick->compositeimage(
  132. $image->gmagick,
  133. \Gmagick::COMPOSITE_DEFAULT,
  134. $start->getX(),
  135. $start->getY()
  136. );
  137. } catch (\GmagickException $e) {
  138. throw new RuntimeException(
  139. 'Paste operation failed', $e->getCode(), $e
  140. );
  141. }
  142. /**
  143. * @see http://pecl.php.net/bugs/bug.php?id=22435
  144. */
  145. if (method_exists($this->gmagick, 'flattenImages')) {
  146. try {
  147. $this->gmagick->flattenImages();
  148. } catch (\GmagickException $e) {
  149. throw new RuntimeException(
  150. 'Paste operation failed', $e->getCode(), $e
  151. );
  152. }
  153. }
  154. return $this;
  155. }
  156. /**
  157. * (non-PHPdoc)
  158. * @see Imagine\Image\ManipulatorInterface::resize()
  159. */
  160. public function resize(BoxInterface $size)
  161. {
  162. try {
  163. $this->gmagick->resizeimage(
  164. $size->getWidth(),
  165. $size->getHeight(),
  166. \Gmagick::FILTER_UNDEFINED,
  167. 1
  168. );
  169. } catch (\GmagickException $e) {
  170. throw new RuntimeException(
  171. 'Resize operation failed', $e->getCode(), $e
  172. );
  173. }
  174. return $this;
  175. }
  176. /**
  177. * (non-PHPdoc)
  178. * @see Imagine\Image\ManipulatorInterface::rotate()
  179. */
  180. public function rotate($angle, Color $background = null)
  181. {
  182. try {
  183. $pixel = $this->getColor($background);
  184. $this->gmagick->rotateimage($pixel, $angle);
  185. $pixel = null;
  186. } catch (\GmagickException $e) {
  187. throw new RuntimeException(
  188. 'Rotate operation failed', $e->getCode(), $e
  189. );
  190. }
  191. return $this;
  192. }
  193. /**
  194. * (non-PHPdoc)
  195. * @see Imagine\Image\ManipulatorInterface::save()
  196. */
  197. public function save($path, array $options = array())
  198. {
  199. try {
  200. if (isset($options['format'])) {
  201. $this->gmagick->setimageformat($options['format']);
  202. }
  203. $this->gmagick->writeimage($path);
  204. } catch (\GmagickException $e) {
  205. throw new RuntimeException(
  206. 'Save operation failed', $e->getCode(), $e
  207. );
  208. }
  209. return $this;
  210. }
  211. /**
  212. * (non-PHPdoc)
  213. * @see Imagine\Image\ManipulatorInterface::show()
  214. */
  215. public function show($format, array $options = array())
  216. {
  217. header('Content-type: '.$this->getMimeType($format));
  218. echo $this->get($format, $options);
  219. return $this;
  220. }
  221. /**
  222. * (non-PHPdoc)
  223. * @see Imagine\Image\ImageInterface::get()
  224. */
  225. public function get($format, array $options = array())
  226. {
  227. try {
  228. $this->gmagick->setimageformat($format);
  229. } catch (\GmagickException $e) {
  230. throw new RuntimeException(
  231. 'Show operation failed', $e->getCode(), $e
  232. );
  233. }
  234. return (string) $this->gmagick;
  235. }
  236. /**
  237. * (non-PHPdoc)
  238. * @see Imagine\Image\ImageInterface::__toString()
  239. */
  240. public function __toString()
  241. {
  242. return $this->get('png');
  243. }
  244. /**
  245. * (non-PHPdoc)
  246. * @see Imagine\Image\ManipulatorInterface::thumbnail()
  247. */
  248. public function thumbnail(BoxInterface $size, $mode = ImageInterface::THUMBNAIL_INSET)
  249. {
  250. if ($mode !== ImageInterface::THUMBNAIL_INSET &&
  251. $mode !== ImageInterface::THUMBNAIL_OUTBOUND) {
  252. throw new InvalidArgumentException('Invalid mode specified');
  253. }
  254. $thumbnail = $this->copy();
  255. try {
  256. if ($mode === ImageInterface::THUMBNAIL_INSET) {
  257. $thumbnail->gmagick->thumbnailimage(
  258. $size->getWidth(),
  259. $size->getHeight(),
  260. true
  261. );
  262. } elseif ($mode === ImageInterface::THUMBNAIL_OUTBOUND) {
  263. $thumbnail->gmagick->cropthumbnailimage(
  264. $size->getWidth(),
  265. $size->getHeight()
  266. );
  267. }
  268. } catch (\GmagickException $e) {
  269. throw new RuntimeException(
  270. 'Thumbnail operation failed', $e->getCode(), $e
  271. );
  272. }
  273. return $thumbnail;
  274. }
  275. /**
  276. * (non-PHPdoc)
  277. * @see Imagine\Image\ImageInterface::draw()
  278. */
  279. public function draw()
  280. {
  281. return new Drawer($this->gmagick);
  282. }
  283. /**
  284. * (non-PHPdoc)
  285. * @see Imagine\Image\ImageInterface::getSize()
  286. */
  287. public function getSize()
  288. {
  289. try {
  290. $width = $this->gmagick->getimagewidth();
  291. $height = $this->gmagick->getimageheight();
  292. } catch (\GmagickException $e) {
  293. throw new RuntimeException(
  294. 'Get size operation failed', $e->getCode(), $e
  295. );
  296. }
  297. return new Box($width, $height);
  298. }
  299. /**
  300. * (non-PHPdoc)
  301. * @see Imagine\Image\ManipulatorInterface::applyMask()
  302. */
  303. public function applyMask(ImageInterface $mask)
  304. {
  305. if (!$mask instanceof self) {
  306. throw new InvalidArgumentException(
  307. 'Can only apply instances of Imagine\Gmagick\Image as masks'
  308. );
  309. }
  310. $size = $this->getSize();
  311. $maskSize = $mask->getSize();
  312. if ($size != $maskSize) {
  313. throw new InvalidArgumentException(sprintf(
  314. 'The given mask doesn\'t match current image\'s size, current '.
  315. 'mask\'s dimensions are %s, while image\'s dimensions are %s',
  316. $maskSize, $size
  317. ));
  318. }
  319. try {
  320. $mask = $mask->copy();
  321. $this->gmagick->compositeimage(
  322. $mask->gmagick,
  323. \Gmagick::COMPOSITE_DEFAULT,
  324. 0, 0
  325. );
  326. } catch (\Exception $e) {
  327. throw new RuntimeException(
  328. 'Apply mask operation failed', $e->getCode(), $e
  329. );
  330. }
  331. return $this;
  332. }
  333. /**
  334. * (non-PHPdoc)
  335. * @see Imagine\Image\ImageInterface::mask()
  336. */
  337. public function mask()
  338. {
  339. $mask = $this->copy();
  340. try {
  341. $mask->gmagick->modulateimage(100, 0, 100);
  342. } catch (\GmagickException $e) {
  343. throw new RuntimeException(
  344. 'Mask operation failed', $e->getCode(), $e
  345. );
  346. }
  347. return $mask;
  348. }
  349. /**
  350. * (non-PHPdoc)
  351. * @see Imagine\Image\ManipulatorInterface::fill()
  352. */
  353. public function fill(FillInterface $fill)
  354. {
  355. try {
  356. $draw = new \GmagickDraw();
  357. $size = $this->getSize();
  358. for ($x = 0; $x <= $size->getWidth(); $x++) {
  359. for ($y = 0; $y <= $size->getHeight(); $y++) {
  360. $pixel = $this->getColor($fill->getColor(new Point($x, $y)));
  361. $draw->setfillcolor($pixel);
  362. $draw->point($x, $y);
  363. $pixel = null;
  364. }
  365. }
  366. $this->gmagick->drawimage($draw);
  367. $draw = null;
  368. } catch (\GmagickException $e) {
  369. throw new RuntimeException(
  370. 'Fill operation failed', $e->getCode(), $e
  371. );
  372. }
  373. return $this;
  374. }
  375. /**
  376. * (non-PHPdoc)
  377. * @see Imagine\Image\ImageInterface::histogram()
  378. */
  379. public function histogram()
  380. {
  381. $pixels = $this->gmagick->getimagehistogram();
  382. return array_map(
  383. function(\GmagickPixel $pixel)
  384. {
  385. $info = $pixel->getColor();
  386. return new Color(
  387. array(
  388. $info['r'],
  389. $info['g'],
  390. $info['b'],
  391. ),
  392. (int) round($info['a'] * 100)
  393. );
  394. },
  395. $pixels
  396. );
  397. }
  398. /**
  399. * (non-PHPdoc)
  400. * @see Imagine\Image\ImageInterface::getColorAt()
  401. */
  402. public function getColorAt(PointInterface $point) {
  403. if(!$point->in($this->getSize())) {
  404. throw new RuntimeException(sprintf(
  405. 'Error getting color at point [%s,%s]. The point must be inside the image of size [%s,%s]',
  406. $point->getX(), $point->getY(), $this->getSize()->getWidth(), $this->getSize()->getHeight()
  407. ));
  408. }
  409. throw new RuntimeException('Not Implemented!');
  410. }
  411. /**
  412. * Gets specifically formatted color string from Color instance
  413. *
  414. * @param Imagine\Image\Color $color
  415. *
  416. * @return string
  417. */
  418. private function getColor(Color $color)
  419. {
  420. if (!$color->isOpaque()) {
  421. throw new InvalidArgumentException('Gmagick doesn\'t support transparency');
  422. }
  423. $pixel = new \GmagickPixel((string) $color);
  424. $pixel->setColorValue(
  425. \Gmagick::COLOR_OPACITY,
  426. number_format(abs(round($color->getAlpha() / 100, 1)), 1)
  427. );
  428. return $pixel;
  429. }
  430. /**
  431. * Internal
  432. *
  433. * Get the mime type based on format.
  434. *
  435. * @param string $format
  436. *
  437. * @return string mime-type
  438. *
  439. * @throws RuntimeException
  440. */
  441. private function getMimeType($format) {
  442. static $mimeTypes = array(
  443. 'jpeg' => 'image/jpeg',
  444. 'jpg' => 'image/jpeg',
  445. 'gif' => 'image/gif',
  446. 'png' => 'image/png',
  447. 'wbmp' => 'image/vnd.wap.wbmp',
  448. 'xbm' => 'image/xbm',
  449. );
  450. if (!isset($mimeTypes[$format])) {
  451. throw new RuntimeException(sprintf(
  452. 'Unsupported format given. Only %s are supported, %s given',
  453. implode(", ", array_keys($mimeTypes)), $format
  454. ));
  455. }
  456. return $mimeTypes[$format];
  457. }
  458. }