QuadraticBezier.php 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. <?php
  2. namespace Grafika\Gd\DrawingObject;
  3. use Grafika\DrawingObject\QuadraticBezier as Base;
  4. use Grafika\DrawingObjectInterface;
  5. use Grafika\Gd\Image;
  6. use Grafika\ImageInterface;
  7. /**
  8. * Class QuadraticBezier
  9. * @package Grafika
  10. */
  11. class QuadraticBezier extends Base implements DrawingObjectInterface
  12. {
  13. /**
  14. * @link http://members.chello.at/easyfilter/bresenham.pdf
  15. * @param ImageInterface $image
  16. * @return Image
  17. */
  18. public function draw($image)
  19. {
  20. // Localize vars
  21. $width = $image->getWidth();
  22. $height = $image->getHeight();
  23. $gd = $image->getCore();
  24. list($x0, $y0) = $this->point1;
  25. list($x1, $y1) = $this->control;
  26. list($x2, $y2) = $this->point2;
  27. $this->plot($gd, $x0, $y0, $x1, $y1, $x2, $y2);
  28. $type = $image->getType();
  29. $file = $image->getImageFile();
  30. return new Image($gd, $file, $width, $height, $type); // Create new image with updated core
  31. }
  32. protected function plot($gd, $x0, $y0, $x1, $y1, $x2, $y2)
  33. {
  34. /* plot any quadratic Bezier curve */
  35. $x = $x0 - $x1;
  36. $y = $y0 - $y1;
  37. $t = $x0 - 2 * $x1 + $x2; //double
  38. if ((int)$x * ($x2 - $x1) > 0) { /* horizontal cut at P4? */
  39. if ((int)$y * ($y2 - $y1) > 0) /* vertical cut at P6 too? */ {
  40. if (abs(($y0 - 2 * $y1 + $y2) / $t * $x) > abs($y)) { /* which first? */
  41. $x0 = $x2;
  42. $x2 = $x + $x1;
  43. $y0 = $y2;
  44. $y2 = $y + $y1; /* swap points */
  45. }
  46. } /* now horizontal cut at P4 comes first */
  47. $t = ($x0 - $x1) / $t;
  48. $r = (1 - $t) * ((1 - $t) * $y0 + 2.0 * $t * $y1) + $t * $t * $y2; /* By(t=P4) */
  49. $t = ($x0 * $x2 - $x1 * $x1) * $t / ($x0 - $x1); /* gradient dP4/dx=0 */
  50. $x = floor($t + 0.5);
  51. $y = floor($r + 0.5);
  52. $r = ($y1 - $y0) * ($t - $x0) / ($x1 - $x0) + $y0; /* intersect P3 | P0 P1 */
  53. $this->plotSegment($gd, $x0, $y0, $x, floor($r + 0.5), $x, $y);
  54. $r = ($y1 - $y2) * ($t - $x2) / ($x1 - $x2) + $y2; /* intersect P4 | P1 P2 */
  55. $x0 = $x1 = $x;
  56. $y0 = $y;
  57. $y1 = floor($r + 0.5); /* P0 = P4, P1 = P8 */
  58. }
  59. if ((int)($y0 - $y1) * ($y2 - $y1) > 0) { /* vertical cut at P6? */
  60. $t = $y0 - 2 * $y1 + $y2;
  61. $t = ($y0 - $y1) / $t;
  62. $r = (1 - $t) * ((1 - $t) * $x0 + 2.0 * $t * $x1) + $t * $t * $x2; /* Bx(t=P6) */
  63. $t = ($y0 * $y2 - $y1 * $y1) * $t / ($y0 - $y1); /* gradient dP6/dy=0 */
  64. $x = floor($r + 0.5);
  65. $y = floor($t + 0.5);
  66. $r = ($x1 - $x0) * ($t - $y0) / ($y1 - $y0) + $x0; /* intersect P6 | P0 P1 */
  67. $this->plotSegment($gd, $x0, $y0, floor($r + 0.5), $y, $x, $y);
  68. $r = ($x1 - $x2) * ($t - $y2) / ($y1 - $y2) + $x2; /* intersect P7 | P1 P2 */
  69. $x0 = $x;
  70. $x1 = floor($r + 0.5);
  71. $y0 = $y1 = $y; /* P0 = P6, P1 = P7 */
  72. }
  73. $this->plotSegment($gd, $x0, $y0, $x1, $y1, $x2, $y2); /* remaining part */
  74. }
  75. /**
  76. * Draw an limited anti-aliased quadratic Bezier segment.
  77. * @param $gd
  78. * @param $x0
  79. * @param $y0
  80. * @param $x1
  81. * @param $y1
  82. * @param $x2
  83. * @param $y2
  84. */
  85. protected function plotSegment($gd, $x0, $y0, $x1, $y1, $x2, $y2)
  86. {
  87. $sx = $x2 - $x1;
  88. $sy = $y2 - $y1;
  89. $xx = $x0 - $x1;
  90. $yy = $y0 - $y1;
  91. $cur = $xx * $sy - $yy * $sx; /* $curvature */
  92. assert($xx * $sx <= 0 && $yy * $sy <= 0);
  93. if ($sx * (int)$sx + $sy * (int)$sy > $xx * $xx + $yy * $yy) { /* begin with longer part */
  94. $x2 = $x0;
  95. $x0 = $sx + $x1;
  96. $y2 = $y0;
  97. $y0 = $sy + $y1;
  98. $cur = -$cur; /* swap P0 P2 */
  99. }
  100. if ($cur != 0) { /* no straight line */
  101. $xx += $sx;
  102. $xx *= $sx = $x0 < $x2 ? 1 : -1; /* x step direction */
  103. $yy += $sy;
  104. $yy *= $sy = $y0 < $y2 ? 1 : -1; /* y step direction */
  105. $xy = 2 * $xx * $yy;
  106. $xx *= $xx;
  107. $yy *= $yy; /* differences 2nd degree */
  108. if ($cur * $sx * $sy < 0) { /* negat$ed $curvature? */
  109. $xx = -$xx;
  110. $yy = -$yy;
  111. $xy = -$xy;
  112. $cur = -$cur;
  113. }
  114. $dx = 4.0 * $sy * ($x1 - $x0) * $cur + $xx - $xy; /* differences 1st degree */
  115. $dy = 4.0 * $sx * ($y0 - $y1) * $cur + $yy - $xy;
  116. $xx += $xx;
  117. $yy += $yy;
  118. $err = $dx + $dy + $xy; /* $error 1st step */
  119. do {
  120. $cur = min($dx + $xy, -$xy - $dy);
  121. $ed = max($dx + $xy, -$xy - $dy); /* approximate $error distance */
  122. $ed += 2 * $ed * $cur * $cur / (4 * $ed * $ed + $cur * $cur);
  123. $this->setPixel($gd, $x0, $y0, abs($err - $dx - $dy - $xy) / $ed); /* plot $curve */
  124. if ($x0 == $x2 || $y0 == $y2) {
  125. break;
  126. } /* $curve finish$ed */
  127. $x1 = $x0;
  128. $cur = $dx - $err;
  129. $y1 = 2 * $err + $dy < 0;
  130. if (2 * $err + $dx > 0) { /* x step */
  131. if ($err - $dy < $ed) {
  132. $this->setPixel($gd, $x0, $y0 + $sy, abs($err - $dy) / $ed);
  133. }
  134. $x0 += $sx;
  135. $dx -= $xy;
  136. $err += $dy += $yy;
  137. }
  138. if ($y1) { /* y step */
  139. if ($cur < $ed) {
  140. $this->setPixel($gd, $x1 + $sx, $y0, abs($cur) / $ed);
  141. }
  142. $y0 += $sy;
  143. $dy -= $xy;
  144. $err += $dx += $xx;
  145. }
  146. } while ($dy < $dx); /* gradient negates -> close curves */
  147. }
  148. $this->plotLine($gd, $x0, $y0, $x2, $y2); /* plot remaining needle to end */
  149. }
  150. protected function plotLine($gd, $x0, $y0, $x1, $y1)
  151. {
  152. $dx = abs($x1 - $x0);
  153. $sx = $x0 < $x1 ? 1 : -1;
  154. $dy = -abs($y1 - $y0);
  155. $sy = $y0 < $y1 ? 1 : -1;
  156. $err = $dx + $dy;
  157. $ed = $dx - $dy == 0 ? 1 : sqrt((float)$dx * $dx + (float)$dy * $dy);
  158. for (; ;) { /* pixel loop */
  159. $this->setPixel($gd, $x0, $y0, abs($err - $dx - $dy) / $ed);
  160. $e2 = $err;
  161. $x2 = $x0;
  162. if (2 * $e2 + $dx >= 0) { /* x step */
  163. if ($x0 == $x1) {
  164. break;
  165. }
  166. if ($e2 - $dy < $ed) {
  167. $this->setPixel($gd, $x0, $y0 + $sy, ($e2 - $dy) / $ed);
  168. }
  169. $err += $dy;
  170. $x0 += $sx;
  171. }
  172. if (2 * $e2 + $dy <= 0) { /* y step */
  173. if ($y0 == $y1) {
  174. break;
  175. }
  176. if ($dx - $e2 < $ed) {
  177. $this->setPixel($gd, $x2 + $sx, $y0, ($dx - $e2) / $ed);
  178. }
  179. $err += $dx;
  180. $y0 += $sy;
  181. }
  182. }
  183. }
  184. /**
  185. * @param resource $gd
  186. * @param int $x
  187. * @param int $y
  188. * @param float $ar Alpha ratio
  189. */
  190. protected function setPixel($gd, $x, $y, $ar)
  191. {
  192. list($r, $g, $b) = $this->color->getRgb();
  193. $c = imagecolorallocatealpha($gd, $r, $g, $b, 127 * $ar);
  194. imagesetpixel($gd, $x, $y, $c);
  195. }
  196. }