PageRenderTime 42ms CodeModel.GetById 7ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/Imagine/Gmagick/Drawer.php

https://gitlab.com/Blueprint-Marketing/Imagine
PHP | 356 lines | 233 code | 68 blank | 55 comment | 11 complexity | 2fd424ad3df2db3506f1f50624602dac 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\Draw\DrawerInterface;
  12. use Imagine\Exception\InvalidArgumentException;
  13. use Imagine\Exception\NotSupportedException;
  14. use Imagine\Exception\RuntimeException;
  15. use Imagine\Image\AbstractFont;
  16. use Imagine\Image\BoxInterface;
  17. use Imagine\Image\Palette\Color\ColorInterface;
  18. use Imagine\Image\Point;
  19. use Imagine\Image\PointInterface;
  20. /**
  21. * Drawer implementation using the Gmagick PHP extension
  22. */
  23. final class Drawer implements DrawerInterface
  24. {
  25. /**
  26. * @var \Gmagick
  27. */
  28. private $gmagick;
  29. /**
  30. * @param \Gmagick $gmagick
  31. */
  32. public function __construct(\Gmagick $gmagick)
  33. {
  34. $this->gmagick = $gmagick;
  35. }
  36. /**
  37. * {@inheritdoc}
  38. */
  39. public function arc(PointInterface $center, BoxInterface $size, $start, $end, ColorInterface $color, $thickness = 1)
  40. {
  41. $x = $center->getX();
  42. $y = $center->getY();
  43. $width = $size->getWidth();
  44. $height = $size->getHeight();
  45. try {
  46. $pixel = $this->getColor($color);
  47. $arc = new \GmagickDraw();
  48. $arc->setstrokecolor($pixel);
  49. $arc->setstrokewidth(max(1, (int) $thickness));
  50. $arc->setfillcolor('transparent');
  51. $arc->arc(
  52. $x - $width / 2,
  53. $y - $height / 2,
  54. $x + $width / 2,
  55. $y + $height / 2,
  56. $start,
  57. $end
  58. );
  59. $this->gmagick->drawImage($arc);
  60. $pixel = null;
  61. $arc = null;
  62. } catch (\GmagickException $e) {
  63. throw new RuntimeException('Draw arc operation failed', $e->getCode(), $e);
  64. }
  65. return $this;
  66. }
  67. /**
  68. * {@inheritdoc}
  69. */
  70. public function chord(PointInterface $center, BoxInterface $size, $start, $end, ColorInterface $color, $fill = false, $thickness = 1)
  71. {
  72. $x = $center->getX();
  73. $y = $center->getY();
  74. $width = $size->getWidth();
  75. $height = $size->getHeight();
  76. try {
  77. $pixel = $this->getColor($color);
  78. $chord = new \GmagickDraw();
  79. $chord->setstrokecolor($pixel);
  80. $chord->setstrokewidth(max(1, (int) $thickness));
  81. if ($fill) {
  82. $chord->setfillcolor($pixel);
  83. } else {
  84. $x1 = round($x + $width / 2 * cos(deg2rad($start)));
  85. $y1 = round($y + $height / 2 * sin(deg2rad($start)));
  86. $x2 = round($x + $width / 2 * cos(deg2rad($end)));
  87. $y2 = round($y + $height / 2 * sin(deg2rad($end)));
  88. $this->line(new Point($x1, $y1), new Point($x2, $y2), $color);
  89. $chord->setfillcolor('transparent');
  90. }
  91. $chord->arc($x - $width / 2, $y - $height / 2, $x + $width / 2, $y + $height / 2, $start, $end);
  92. $this->gmagick->drawImage($chord);
  93. $pixel = null;
  94. $chord = null;
  95. } catch (\GmagickException $e) {
  96. throw new RuntimeException('Draw chord operation failed', $e->getCode(), $e);
  97. }
  98. return $this;
  99. }
  100. /**
  101. * {@inheritdoc}
  102. */
  103. public function ellipse(PointInterface $center, BoxInterface $size, ColorInterface $color, $fill = false, $thickness = 1)
  104. {
  105. $width = $size->getWidth();
  106. $height = $size->getHeight();
  107. try {
  108. $pixel = $this->getColor($color);
  109. $ellipse = new \GmagickDraw();
  110. $ellipse->setstrokecolor($pixel);
  111. $ellipse->setstrokewidth(max(1, (int) $thickness));
  112. if ($fill) {
  113. $ellipse->setfillcolor($pixel);
  114. } else {
  115. $ellipse->setfillcolor('transparent');
  116. }
  117. $ellipse->ellipse(
  118. $center->getX(),
  119. $center->getY(),
  120. $width / 2,
  121. $height / 2,
  122. 0, 360
  123. );
  124. $this->gmagick->drawImage($ellipse);
  125. $pixel = null;
  126. $ellipse = null;
  127. } catch (\GmagickException $e) {
  128. throw new RuntimeException('Draw ellipse operation failed', $e->getCode(), $e);
  129. }
  130. return $this;
  131. }
  132. /**
  133. * {@inheritdoc}
  134. */
  135. public function line(PointInterface $start, PointInterface $end, ColorInterface $color, $thickness = 1)
  136. {
  137. try {
  138. $pixel = $this->getColor($color);
  139. $line = new \GmagickDraw();
  140. $line->setstrokecolor($pixel);
  141. $line->setstrokewidth(max(1, (int) $thickness));
  142. $line->setfillcolor($pixel);
  143. $line->line(
  144. $start->getX(),
  145. $start->getY(),
  146. $end->getX(),
  147. $end->getY()
  148. );
  149. $this->gmagick->drawImage($line);
  150. $pixel = null;
  151. $line = null;
  152. } catch (\GmagickException $e) {
  153. throw new RuntimeException('Draw line operation failed', $e->getCode(), $e);
  154. }
  155. return $this;
  156. }
  157. /**
  158. * {@inheritdoc}
  159. */
  160. public function pieSlice(PointInterface $center, BoxInterface $size, $start, $end, ColorInterface $color, $fill = false, $thickness = 1)
  161. {
  162. $width = $size->getWidth();
  163. $height = $size->getHeight();
  164. $x1 = round($center->getX() + $width / 2 * cos(deg2rad($start)));
  165. $y1 = round($center->getY() + $height / 2 * sin(deg2rad($start)));
  166. $x2 = round($center->getX() + $width / 2 * cos(deg2rad($end)));
  167. $y2 = round($center->getY() + $height / 2 * sin(deg2rad($end)));
  168. if ($fill) {
  169. $this->chord($center, $size, $start, $end, $color, true, $thickness);
  170. $this->polygon(
  171. array(
  172. $center,
  173. new Point($x1, $y1),
  174. new Point($x2, $y2),
  175. ),
  176. $color,
  177. true,
  178. $thickness
  179. );
  180. } else {
  181. $this->arc($center, $size, $start, $end, $color, $thickness);
  182. $this->line($center, new Point($x1, $y1), $color, $thickness);
  183. $this->line($center, new Point($x2, $y2), $color, $thickness);
  184. }
  185. return $this;
  186. }
  187. /**
  188. * {@inheritdoc}
  189. */
  190. public function dot(PointInterface $position, ColorInterface $color)
  191. {
  192. $x = $position->getX();
  193. $y = $position->getY();
  194. try {
  195. $pixel = $this->getColor($color);
  196. $point = new \GmagickDraw();
  197. $point->setfillcolor($pixel);
  198. $point->point($x, $y);
  199. $this->gmagick->drawimage($point);
  200. $pixel = null;
  201. $point = null;
  202. } catch (\GmagickException $e) {
  203. throw new RuntimeException('Draw point operation failed', $e->getCode(), $e);
  204. }
  205. return $this;
  206. }
  207. /**
  208. * {@inheritdoc}
  209. */
  210. public function polygon(array $coordinates, ColorInterface $color, $fill = false, $thickness = 1)
  211. {
  212. if (count($coordinates) < 3) {
  213. throw new InvalidArgumentException(sprintf('Polygon must consist of at least 3 coordinates, %d given', count($coordinates)));
  214. }
  215. $points = array_map(function (PointInterface $p) {
  216. return array('x' => $p->getX(), 'y' => $p->getY());
  217. }, $coordinates);
  218. try {
  219. $pixel = $this->getColor($color);
  220. $polygon = new \GmagickDraw();
  221. $polygon->setstrokecolor($pixel);
  222. $polygon->setstrokewidth(max(1, (int) $thickness));
  223. if ($fill) {
  224. $polygon->setfillcolor($pixel);
  225. } else {
  226. $polygon->setfillcolor('transparent');
  227. }
  228. $polygon->polygon($points);
  229. $this->gmagick->drawImage($polygon);
  230. unset($pixel, $polygon);
  231. } catch (\GmagickException $e) {
  232. throw new RuntimeException('Draw polygon operation failed', $e->getCode(), $e);
  233. }
  234. return $this;
  235. }
  236. /**
  237. * {@inheritdoc}
  238. */
  239. public function text($string, AbstractFont $font, PointInterface $position, $angle = 0, $width = null)
  240. {
  241. try {
  242. $pixel = $this->getColor($font->getColor());
  243. $text = new \GmagickDraw();
  244. $text->setfont($font->getFile());
  245. /**
  246. * @see http://www.php.net/manual/en/imagick.queryfontmetrics.php#101027
  247. *
  248. * ensure font resolution is the same as GD's hard-coded 96
  249. */
  250. $text->setfontsize((int) ($font->getSize() * (96 / 72)));
  251. $text->setfillcolor($pixel);
  252. $info = $this->gmagick->queryfontmetrics($text, $string);
  253. $rad = deg2rad($angle);
  254. $cos = cos($rad);
  255. $sin = sin($rad);
  256. $x1 = round(0 * $cos - 0 * $sin);
  257. $x2 = round($info['textWidth'] * $cos - $info['textHeight'] * $sin);
  258. $y1 = round(0 * $sin + 0 * $cos);
  259. $y2 = round($info['textWidth'] * $sin + $info['textHeight'] * $cos);
  260. $xdiff = 0 - min($x1, $x2);
  261. $ydiff = 0 - min($y1, $y2);
  262. if ($width !== null) {
  263. throw new NotSupportedException('Gmagick doesn\'t support queryfontmetrics function for multiline text', 1);
  264. }
  265. $this->gmagick->annotateimage($text, $position->getX() + $x1 + $xdiff, $position->getY() + $y2 + $ydiff, $angle, $string);
  266. unset($pixel, $text);
  267. } catch (\GmagickException $e) {
  268. throw new RuntimeException('Draw text operation failed', $e->getCode(), $e);
  269. }
  270. return $this;
  271. }
  272. /**
  273. * Gets specifically formatted color string from Color instance
  274. *
  275. * @param ColorInterface $color
  276. *
  277. * @return \GmagickPixel
  278. *
  279. * @throws InvalidArgumentException In case a non-opaque color is passed
  280. */
  281. private function getColor(ColorInterface $color)
  282. {
  283. if (!$color->isOpaque()) {
  284. throw new InvalidArgumentException('Gmagick doesn\'t support transparency');
  285. }
  286. return new \GmagickPixel((string) $color);
  287. }
  288. }