/lib/Imagine/Gd/Drawer.php

https://github.com/schmanat/Imagine · PHP · 274 lines · 176 code · 37 blank · 61 comment · 21 complexity · 56965db8db173e5fdfe6245a983988e0 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\Gd;
  11. use Imagine\Draw\DrawerInterface;
  12. use Imagine\Exception\InvalidArgumentException;
  13. use Imagine\Exception\OutOfBoundsException;
  14. use Imagine\Exception\RuntimeException;
  15. use Imagine\Image\AbstractFont;
  16. use Imagine\Image\BoxInterface;
  17. use Imagine\Image\Color;
  18. use Imagine\Image\PointInterface;
  19. final class Drawer implements DrawerInterface
  20. {
  21. /**
  22. * @var resource
  23. */
  24. private $resource;
  25. /**
  26. * Constructs Drawer with a given gd image resource
  27. *
  28. * @param resource $resource
  29. */
  30. public function __construct($resource)
  31. {
  32. $this->resource = $resource;
  33. }
  34. /**
  35. * (non-PHPdoc)
  36. * @see Imagine\Draw\DrawerInterface::arc()
  37. */
  38. public function arc(PointInterface $center, BoxInterface $size, $start, $end, Color $color)
  39. {
  40. if (false === imagearc(
  41. $this->resource, $center->getX(), $center->getY(),
  42. $size->getWidth(), $size->getHeight(), $start, $end,
  43. $this->getColor($color)
  44. )) {
  45. throw new RuntimeException('Draw arc operation failed');
  46. }
  47. return $this;
  48. }
  49. /**
  50. * This function doesn't work properly because of a bug in GD
  51. *
  52. * (non-PHPdoc)
  53. * @see Imagine\Draw\DrawerInterface::chord()
  54. */
  55. public function chord(PointInterface $center, BoxInterface $size, $start, $end, Color $color, $fill = false)
  56. {
  57. if ($fill) {
  58. $style = IMG_ARC_CHORD;
  59. } else {
  60. $style = IMG_ARC_CHORD | IMG_ARC_NOFILL;
  61. }
  62. if (false === imagefilledarc(
  63. $this->resource, $center->getX(), $center->getY(),
  64. $size->getWidth(), $size->getHeight(), $start, $end,
  65. $this->getColor($color), $style
  66. )) {
  67. throw new RuntimeException('Draw chord operation failed');
  68. }
  69. return $this;
  70. }
  71. /**
  72. * (non-PHPdoc)
  73. * @see Imagine\Draw\DrawerInterface::ellipse()
  74. */
  75. public function ellipse(PointInterface $center, BoxInterface $size, Color $color, $fill = false)
  76. {
  77. if ($fill) {
  78. $callback = 'imagefilledellipse';
  79. } else {
  80. $callback = 'imageellipse';
  81. }
  82. if (false === $callback(
  83. $this->resource, $center->getX(), $center->getY(),
  84. $size->getWidth(), $size->getHeight(), $this->getColor($color))
  85. ) {
  86. throw new RuntimeException('Draw ellipse operation failed');
  87. }
  88. return $this;
  89. }
  90. /**
  91. * (non-PHPdoc)
  92. * @see Imagine\Draw\DrawerInterface::line()
  93. */
  94. public function line(PointInterface $start, PointInterface $end, Color $color)
  95. {
  96. if (false === imageline(
  97. $this->resource, $start->getX(), $start->getY(),
  98. $end->getX(), $end->getY(), $this->getColor($color)
  99. )) {
  100. throw new RuntimeException('Draw line operation failed');
  101. }
  102. return $this;
  103. }
  104. /**
  105. * (non-PHPdoc)
  106. * @see Imagine\Draw\DrawerInterface::pieSlice()
  107. */
  108. public function pieSlice(PointInterface $center, BoxInterface $size, $start, $end, Color $color, $fill = false)
  109. {
  110. if ($fill) {
  111. $style = IMG_ARC_EDGED;
  112. } else {
  113. $style = IMG_ARC_EDGED | IMG_ARC_NOFILL;
  114. }
  115. if (false === imagefilledarc(
  116. $this->resource, $center->getX(), $center->getY(),
  117. $size->getWidth(), $size->getHeight(), $start, $end,
  118. $this->getColor($color), $style
  119. )) {
  120. throw new RuntimeException('Draw chord operation failed');
  121. }
  122. return $this;
  123. }
  124. /**
  125. * (non-PHPdoc)
  126. * @see Imagine\Draw\DrawerInterface::dot()
  127. */
  128. public function dot(PointInterface $position, Color $color)
  129. {
  130. if (false === imagesetpixel(
  131. $this->resource, $position->getX(), $position->getY(),
  132. $this->getColor($color)
  133. )) {
  134. throw new RuntimeException('Draw point operation failed');
  135. }
  136. return $this;
  137. }
  138. /**
  139. * (non-PHPdoc)
  140. * @see Imagine\Draw\DrawerInterface::polygon()
  141. */
  142. public function polygon(array $coordinates, Color $color, $fill = false)
  143. {
  144. if (count($coordinates) < 3) {
  145. throw new InvalidArgumentException(sprintf(
  146. 'A polygon must consist of at least 3 points, %d given',
  147. count($coordinates)
  148. ));
  149. }
  150. $points = array();
  151. foreach ($coordinates as $coordinate) {
  152. if (!$coordinate instanceof PointInterface) {
  153. throw new InvalidArgumentException(sprintf(
  154. 'Each entry in coordinates array must be instance of '.
  155. 'Imagine\Image\PointInterface, %s given', var_export($coordinate)
  156. ));
  157. }
  158. $points[] = $coordinate->getX();
  159. $points[] = $coordinate->getY();
  160. }
  161. if ($fill) {
  162. $callback = 'imagefilledpolygon';
  163. } else {
  164. $callback = 'imagepolygon';
  165. }
  166. if (false === $callback(
  167. $this->resource, $points, count($coordinates),
  168. $this->getColor($color)
  169. )) {
  170. throw new RuntimeException('Draw polygon operation failed');
  171. }
  172. return $this;
  173. }
  174. /**
  175. * (non-PHPdoc)
  176. * @see Imagine\Draw\DrawerInterface::text()
  177. */
  178. public function text($string, AbstractFont $font, PointInterface $position, $angle = 0)
  179. {
  180. $angle = -1 * $angle;
  181. $fontsize = $font->getSize();
  182. $fontfile = $font->getFile();
  183. $info = imageftbbox($fontsize, $angle, $fontfile, $string);
  184. $xs = array($info[0], $info[2], $info[4], $info[6]);
  185. $ys = array($info[1], $info[3], $info[5], $info[7]);
  186. $width = abs(max($xs) - min($xs));
  187. $height = abs(max($ys) - min($ys));
  188. $xdiff = 0 - min($xs) + $position->getX();
  189. $ydiff = 0 - min($ys) + $position->getY();
  190. foreach ($xs as &$x) {
  191. $x += $xdiff;
  192. }
  193. foreach ($ys as &$y) {
  194. $y += $ydiff;
  195. }
  196. if (false === imagealphablending($this->resource, true)) {
  197. throw new RuntimeException('Font mask operation failed');
  198. }
  199. if (false === imagefttext(
  200. $this->resource, $fontsize, $angle, $xs[0], $ys[0],
  201. $this->getColor($font->getColor()), $fontfile, $string
  202. )) {
  203. throw new RuntimeException('Font mask operation failed');
  204. }
  205. if (false === imagealphablending($this->resource, false)) {
  206. throw new RuntimeException('Font mask operation failed');
  207. }
  208. return $this;
  209. }
  210. /**
  211. * Internal
  212. *
  213. * Generates a GD color from Color instance
  214. *
  215. * @param Imagine\Image\Color $color
  216. *
  217. * @return resource
  218. *
  219. * @throws Imagine\Exception\RuntimeException
  220. */
  221. private function getColor(Color $color)
  222. {
  223. $color = imagecolorallocatealpha(
  224. $this->resource,
  225. $color->getRed(), $color->getGreen(), $color->getBlue(),
  226. round(127 * $color->getAlpha() / 100)
  227. );
  228. if (false === $color) {
  229. throw new RuntimeException(sprintf(
  230. 'Unable to allocate color "RGB(%s, %s, %s)" with '.
  231. 'transparency of %d percent', $color->getRed(),
  232. $color->getGreen(), $color->getBlue(), $color->getAlpha()
  233. ));
  234. }
  235. return $color;
  236. }
  237. }