PageRenderTime 26ms CodeModel.GetById 31ms RepoModel.GetById 1ms app.codeStats 0ms

/lib/Imagine/Gmagick/Image.php

https://github.com/nicodmf/Imagine
PHP | 473 lines | 312 code | 60 blank | 101 comment | 14 complexity | 69093215bf4cf89e6341a5c7c68ebb1d 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\Fill\FillInterface;
  15. use Imagine\Gmagick\Imagine;
  16. use Imagine\ImageInterface;
  17. use Imagine\Image\Box;
  18. use Imagine\Image\BoxInterface;
  19. use Imagine\Image\Color;
  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. $this->gmagick->writeimage($path);
  201. } catch (\GmagickException $e) {
  202. throw new RuntimeException(
  203. 'Save operation failed', $e->getCode(), $e
  204. );
  205. }
  206. return $this;
  207. }
  208. /**
  209. * (non-PHPdoc)
  210. * @see Imagine\Image\ManipulatorInterface::show()
  211. */
  212. public function show($format, array $options = array())
  213. {
  214. echo $this->get($format, $options);
  215. return $this;
  216. }
  217. /**
  218. * (non-PHPdoc)
  219. * @see Imagine\ImageInterface::get()
  220. */
  221. public function get($format, array $options = array())
  222. {
  223. try {
  224. $this->gmagick->setimageformat($format);
  225. } catch (\GmagickException $e) {
  226. throw new RuntimeException(
  227. 'Show operation failed', $e->getCode(), $e
  228. );
  229. }
  230. return (string) $this->gmagick;
  231. }
  232. /**
  233. * (non-PHPdoc)
  234. * @see Imagine\ImageInterface::__toString()
  235. */
  236. public function __toString()
  237. {
  238. return $this->get('png');
  239. }
  240. /**
  241. * (non-PHPdoc)
  242. * @see Imagine\Image\ManipulatorInterface::thumbnail()
  243. */
  244. public function thumbnail(BoxInterface $size, $mode = ImageInterface::THUMBNAIL_INSET)
  245. {
  246. if ($mode !== ImageInterface::THUMBNAIL_INSET &&
  247. $mode !== ImageInterface::THUMBNAIL_OUTBOUND) {
  248. throw new InvalidArgumentException('Invalid mode specified');
  249. }
  250. $thumbnail = $this->copy();
  251. try {
  252. if ($mode === ImageInterface::THUMBNAIL_INSET) {
  253. $thumbnail->gmagick->thumbnailimage(
  254. $size->getWidth(),
  255. $size->getHeight(),
  256. true
  257. );
  258. } elseif ($mode === ImageInterface::THUMBNAIL_OUTBOUND) {
  259. $thumbnail->gmagick->cropthumbnailimage(
  260. $size->getWidth(),
  261. $size->getHeight()
  262. );
  263. }
  264. } catch (\GmagickException $e) {
  265. throw new RuntimeException(
  266. 'Thumbnail operation failed', $e->getCode(), $e
  267. );
  268. }
  269. return $thumbnail;
  270. }
  271. /**
  272. * (non-PHPdoc)
  273. * @see Imagine\ImageInterface::draw()
  274. */
  275. public function draw()
  276. {
  277. return new Drawer($this->gmagick);
  278. }
  279. /**
  280. * (non-PHPdoc)
  281. * @see Imagine\ImageInterface::getSize()
  282. */
  283. public function getSize()
  284. {
  285. try {
  286. $width = $this->gmagick->getimagewidth();
  287. $height = $this->gmagick->getimageheight();
  288. } catch (\GmagickException $e) {
  289. throw new RuntimeException(
  290. 'Get size operation failed', $e->getCode(), $e
  291. );
  292. }
  293. return new Box($width, $height);
  294. }
  295. /**
  296. * (non-PHPdoc)
  297. * @see Imagine\Image\ManipulatorInterface::applyMask()
  298. */
  299. public function applyMask(ImageInterface $mask)
  300. {
  301. if (!$mask instanceof self) {
  302. throw new InvalidArgumentException(
  303. 'Can only apply instances of Imagine\Gmagick\Image as masks'
  304. );
  305. }
  306. $size = $this->getSize();
  307. $maskSize = $mask->getSize();
  308. if ($size != $maskSize) {
  309. throw new InvalidArgumentException(sprintf(
  310. 'The given mask doesn\'t match current image\'s size, current '.
  311. 'mask\'s dimensions are %s, while image\'s dimensions are %s',
  312. $maskSize, $size
  313. ));
  314. }
  315. try {
  316. $mask = $mask->copy();
  317. $this->gmagick->compositeimage(
  318. $mask->gmagick,
  319. \Gmagick::COMPOSITE_DEFAULT,
  320. 0, 0
  321. );
  322. } catch (\Exception $e) {
  323. throw new RuntimeException(
  324. 'Apply mask operation failed', $e->getCode(), $e
  325. );
  326. }
  327. return $this;
  328. }
  329. /**
  330. * (non-PHPdoc)
  331. * @see Imagine\ImageInterface::mask()
  332. */
  333. public function mask()
  334. {
  335. $mask = $this->copy();
  336. try {
  337. $mask->gmagick->modulateimage(100, 0, 100);
  338. } catch (\GmagickException $e) {
  339. throw new RuntimeException(
  340. 'Mask operation failed', $e->getCode(), $e
  341. );
  342. }
  343. return $mask;
  344. }
  345. /**
  346. * (non-PHPdoc)
  347. * @see Imagine\Image\ManipulatorInterface::fill()
  348. */
  349. public function fill(FillInterface $fill)
  350. {
  351. try {
  352. $draw = new \GmagickDraw();
  353. $size = $this->getSize();
  354. for ($x = 0; $x <= $size->getWidth(); $x++) {
  355. for ($y = 0; $y <= $size->getHeight(); $y++) {
  356. $pixel = $this->getColor($fill->getColor(new Point($x, $y)));
  357. $draw->setfillcolor($pixel);
  358. $draw->point($x, $y);
  359. $pixel = null;
  360. }
  361. }
  362. $this->gmagick->drawimage($draw);
  363. $draw = null;
  364. } catch (\GmagickException $e) {
  365. throw new RuntimeException(
  366. 'Fill operation failed', $e->getCode(), $e
  367. );
  368. }
  369. return $this;
  370. }
  371. /**
  372. * (non-PHPdoc)
  373. * @see Imagine\ImageInterface::histogram()
  374. */
  375. public function histogram()
  376. {
  377. $pixels = $this->gmagick->getimagehistogram();
  378. return array_map(
  379. function(\GmagickPixel $pixel)
  380. {
  381. $info = $pixel->getColor();
  382. return new Color(
  383. array(
  384. $info['r'],
  385. $info['g'],
  386. $info['b'],
  387. ),
  388. (int) round($info['a'] * 100)
  389. );
  390. },
  391. $pixels
  392. );
  393. }
  394. /**
  395. * Gets specifically formatted color string from Color instance
  396. *
  397. * @param Imagine\Image\Color $color
  398. *
  399. * @return string
  400. */
  401. private function getColor(Color $color)
  402. {
  403. if (!$color->isOpaque()) {
  404. throw new InvalidArgumentException('Gmagick doesn\'t support transparency');
  405. }
  406. $pixel = new \GmagickPixel((string) $color);
  407. $pixel->setColorValue(
  408. \Gmagick::COLOR_OPACITY,
  409. number_format(abs(round($color->getAlpha() / 100, 1)), 1)
  410. );
  411. return $pixel;
  412. }
  413. }