1 /* This is potentially great stuff, but fails against the test 2 program at the end. This would probably be much more 3 efficient than the implementation currently in gd.c if the 4 errors in the output were corrected. TBB */ 5 6 #if 0 7 8 #include "gd.h" 9 #include <math.h> 10 11 /* Courtesy of F J Franklin. */ 12 13 static gdPoint gdArcClosest (int width, int height, int angle); 14 15 void 16 gdImageFilledEllipse (gdImagePtr im, int cx, int cy, int width, int height, int color) 17 { 18 gdImageFilledArc (im, cx, cy, width, height, 0, 360, color, gdChord); 19 } 20 21 void 22 gdImageFilledArc (gdImagePtr im, int cx, int cy, int width, int height, int s, int e, int color, int style) 23 { 24 gdPoint pt[7]; 25 gdPoint axis_pt[4]; 26 27 int angle; 28 29 int have_s = 0; 30 int have_e = 0; 31 32 int flip_x = 0; 33 int flip_y = 0; 34 35 int conquer = 0; 36 37 int i; 38 39 int a; 40 int b; 41 42 int x; 43 int y; 44 45 long s_sin = 0; 46 long s_cos = 0; 47 long e_sin = 0; 48 long e_cos = 0; 49 50 long w; /* a * 2 */ 51 long h; /* b * 2 */ 52 53 long x2; /* x * 2 */ 54 long y2; /* y * 2 */ 55 long lx2; /* x * 2 (line) */ 56 long ly2; /* y * 2 (line) */ 57 58 long ws; /* (a * 2)^2 */ 59 long hs; /* (b * 2)^2 */ 60 61 long whs; /* (a * 2)^2 * (b * 2)^2 */ 62 63 long g; /* decision variable */ 64 long lg; /* decision variable (line) */ 65 66 width = (width & 1) ? (width + 1) : (width); 67 height = (height & 1) ? (height + 1) : (height); 68 69 a = width / 2; 70 b = height / 2; 71 72 axis_pt[0].x = a; 73 axis_pt[0].y = 0; 74 axis_pt[1].x = 0; 75 axis_pt[1].y = b; 76 axis_pt[2].x = -a; 77 axis_pt[2].y = 0; 78 axis_pt[3].x = 0; 79 axis_pt[3].y = -b; 80 81 if (s == e) 82 return; 83 84 if ((e - s) >= 360) 85 { 86 s = 0; 87 e = 0; 88 } 89 90 while (s < 0) 91 s += 360; 92 while (s >= 360) 93 s -= 360; 94 while (e < 0) 95 e += 360; 96 while (e >= 360) 97 e -= 360; 98 99 if (e <= s) 100 e += 360; 101 102 /* I'm assuming a chord-rule at the moment. Need to add origin to get a 103 * pie-rule, but will need to set chord-rule before recursion... 104 */ 105 106 for (i = 0; i < 4; i++) 107 { 108 if ((s < (i + 1) * 90) && (e > (i + 1) * 90)) 109 { 110 gdImageFilledArc (im, cx, cy, width, height, s, (i + 1) * 90, color, gdChord); 111 pt[0] = gdArcClosest (width, height, s); 112 pt[0].x += cx; 113 pt[0].y += cy; 114 pt[1].x = cx + axis_pt[(i + 1) & 3].x; 115 pt[1].y = cy + axis_pt[(i + 1) & 3].y; 116 if (e <= (i + 2) * 90) 117 { 118 gdImageFilledArc (im, cx, cy, width, height, (i + 1) * 90, e, color, gdChord); 119 pt[2] = gdArcClosest (width, height, e); 120 pt[2].x += cx; 121 pt[2].y += cy; 122 if (style == gdChord) 123 { 124 gdImageFilledPolygon (im, pt, 3, color); 125 gdImagePolygon (im, pt, 3, color); 126 } 127 else if (style == gdPie) 128 { 129 pt[3].x = cx; 130 pt[3].y = cy; 131 gdImageFilledPolygon (im, pt, 4, color); 132 gdImagePolygon (im, pt, 4, color); 133 } 134 } 135 else 136 { 137 gdImageFilledArc (im, cx, cy, width, height, (i + 1) * 90, (i + 2) * 90, color, gdChord); 138 pt[2].x = cx + axis_pt[(i + 2) & 3].x; 139 pt[2].y = cy + axis_pt[(i + 2) & 3].y; 140 if (e <= (i + 3) * 90) 141 { 142 gdImageFilledArc (im, cx, cy, width, height, (i + 2) * 90, e, color, gdChord); 143 pt[3] = gdArcClosest (width, height, e); 144 pt[3].x += cx; 145 pt[3].y += cy; 146 if (style == gdChord) 147 { 148 gdImageFilledPolygon (im, pt, 4, color); 149 gdImagePolygon (im, pt, 4, color); 150 } 151 else if (style == gdPie) 152 { 153 pt[4].x = cx; 154 pt[4].y = cy; 155 gdImageFilledPolygon (im, pt, 5, color); 156 gdImagePolygon (im, pt, 5, color); 157 } 158 } 159 else 160 { 161 gdImageFilledArc (im, cx, cy, width, height, (i + 2) * 90, (i + 3) * 90, color, gdChord); 162 pt[3].x = cx + axis_pt[(i + 3) & 3].x; 163 pt[3].y = cy + axis_pt[(i + 3) & 3].y; 164 if (e <= (i + 4) * 90) 165 { 166 gdImageFilledArc (im, cx, cy, width, height, (i + 3) * 90, e, color, gdChord); 167 pt[4] = gdArcClosest (width, height, e); 168 pt[4].x += cx; 169 pt[4].y += cy; 170 if (style == gdChord) 171 { 172 gdImageFilledPolygon (im, pt, 5, color); 173 gdImagePolygon (im, pt, 5, color); 174 } 175 else if (style == gdPie) 176 { 177 pt[5].x = cx; 178 pt[5].y = cy; 179 gdImageFilledPolygon (im, pt, 6, color); 180 gdImagePolygon (im, pt, 6, color); 181 } 182 } 183 else 184 { 185 gdImageFilledArc (im, cx, cy, width, height, (i + 3) * 90, (i + 4) * 90, color, gdChord); 186 pt[4].x = cx + axis_pt[(i + 4) & 3].x; 187 pt[4].y = cy + axis_pt[(i + 4) & 3].y; 188 189 gdImageFilledArc (im, cx, cy, width, height, (i + 4) * 90, e, color, gdChord); 190 pt[5] = gdArcClosest (width, height, e); 191 pt[5].x += cx; 192 pt[5].y += cy; 193 if (style == gdChord) 194 { 195 gdImageFilledPolygon (im, pt, 6, color); 196 gdImagePolygon (im, pt, 6, color); 197 } 198 else if (style == gdPie) 199 { 200 pt[6].x = cx; 201 pt[6].y = cy; 202 gdImageFilledPolygon (im, pt, 7, color); 203 gdImagePolygon (im, pt, 7, color); 204 } 205 } 206 } 207 } 208 return; 209 } 210 } 211 212 /* At this point we have only arcs that lies within a quadrant - 213 * map this to first quadrant... 214 */ 215 216 if ((s >= 90) && (e <= 180)) 217 { 218 angle = s; 219 s = 180 - e; 220 e = 180 - angle; 221 flip_x = 1; 222 } 223 if ((s >= 180) && (e <= 270)) 224 { 225 s = s - 180; 226 e = e - 180; 227 flip_x = 1; 228 flip_y = 1; 229 } 230 if ((s >= 270) && (e <= 360)) 231 { 232 angle = s; 233 s = 360 - e; 234 e = 360 - angle; 235 flip_y = 1; 236 } 237 238 if (s == 0) 239 { 240 s_sin = 0; 241 s_cos = (long) ((double) 32768); 242 } 243 else 244 { 245 s_sin = (long) ((double) 32768 * sin ((double) s * M_PI / (double) 180)); 246 s_cos = (long) ((double) 32768 * cos ((double) s * M_PI / (double) 180)); 247 } 248 if (e == 0) 249 { 250 e_sin = (long) ((double) 32768); 251 e_cos = 0; 252 } 253 else 254 { 255 e_sin = (long) ((double) 32768 * sin ((double) e * M_PI / (double) 180)); 256 e_cos = (long) ((double) 32768 * cos ((double) e * M_PI / (double) 180)); 257 } 258 259 w = (long) width; 260 h = (long) height; 261 262 ws = w * w; 263 hs = h * h; 264 265 whs = 1; 266 while ((ws > 32768) || (hs > 32768)) 267 { 268 ws = (ws + 1) / 2; /* Unfortunate limitations on integers makes */ 269 hs = (hs + 1) / 2; /* drawing large ellipses problematic... */ 270 whs *= 2; 271 } 272 while ((ws * hs) > (0x04000000L / whs)) 273 { 274 ws = (ws + 1) / 2; 275 hs = (hs + 1) / 2; 276 whs *= 2; 277 } 278 whs *= ws * hs; 279 280 pt[0].x = w / 2; 281 pt[0].y = 0; 282 283 pt[2].x = 0; 284 pt[2].y = h / 2; 285 286 have_s = 0; 287 have_e = 0; 288 289 if (s == 0) 290 have_s = 1; 291 if (e == 90) 292 have_e = 1; 293 294 x2 = w; 295 y2 = 0; /* Starting point is exactly on ellipse */ 296 297 g = x2 - 1; 298 g = g * g * hs + 4 * ws - whs; 299 300 while ((x2 * hs) > (y2 * ws)) /* Keep |tangent| > 1 */ 301 { 302 y2 += 2; 303 g += ws * 4 * (y2 + 1); 304 305 if (g > 0) /* Need to drop */ 306 { 307 x2 -= 2; 308 g -= hs * 4 * x2; 309 } 310 311 if ((have_s == 0) && ((s_sin * x2) <= (y2 * s_cos))) 312 { 313 pt[0].x = (int) (x2 / 2); 314 pt[0].y = (int) (y2 / 2); 315 have_s = 1; 316 } 317 318 if ((have_e == 0) && ((e_sin * x2) <= (y2 * e_cos))) 319 { 320 pt[2].x = (int) (x2 / 2); 321 pt[2].y = (int) (y2 / 2); 322 have_e = 1; 323 } 324 } 325 pt[1].x = (int) (x2 / 2); 326 pt[1].y = (int) (y2 / 2); 327 328 x2 = 0; 329 y2 = h; /* Starting point is exactly on ellipse */ 330 331 g = y2 - 1; 332 g = g * g * ws + 4 * hs - whs; 333 334 while ((x2 * hs) < (y2 * ws)) 335 { 336 x2 += 2; 337 g += hs * 4 * (x2 + 1); 338 339 if (g > 0) /* Need to drop */ 340 { 341 y2 -= 2; 342 g -= ws * 4 * y2; 343 } 344 345 if ((have_s == 0) && ((s_sin * x2) >= (y2 * s_cos))) 346 { 347 pt[0].x = (int) (x2 / 2); 348 pt[0].y = (int) (y2 / 2); 349 have_s = 1; 350 } 351 352 if ((have_e == 0) && ((e_sin * x2) >= (y2 * e_cos))) 353 { 354 pt[2].x = (int) (x2 / 2); 355 pt[2].y = (int) (y2 / 2); 356 have_e = 1; 357 } 358 } 359 360 if ((have_s == 0) || (have_e == 0)) 361 return; /* Bizarre case */ 362 363 if (style == gdPie) 364 { 365 pt[3] = pt[0]; 366 pt[4] = pt[1]; 367 pt[5] = pt[2]; 368 369 pt[0].x = cx + (flip_x ? (-pt[0].x) : pt[0].x); 370 pt[0].y = cy + (flip_y ? (-pt[0].y) : pt[0].y); 371 pt[1].x = cx; 372 pt[1].y = cy; 373 pt[2].x = cx + (flip_x ? (-pt[2].x) : pt[2].x); 374 pt[2].y = cy + (flip_y ? (-pt[2].y) : pt[2].y); 375 gdImageFilledPolygon (im, pt, 3, color); 376 gdImagePolygon (im, pt, 3, color); 377 378 pt[0] = pt[3]; 379 pt[1] = pt[4]; 380 pt[2] = pt[5]; 381 } 382 383 if (((s_cos * hs) > (s_sin * ws)) && ((e_cos * hs) < (e_sin * ws))) 384 { /* the points are on different parts of the curve... 385 * this is too tricky to try to handle, so divide and conquer: 386 */ 387 pt[3] = pt[0]; 388 pt[4] = pt[1]; 389 pt[5] = pt[2]; 390 391 pt[0].x = cx + (flip_x ? (-pt[0].x) : pt[0].x); 392 pt[0].y = cy + (flip_y ? (-pt[0].y) : pt[0].y); 393 pt[1].x = cx + (flip_x ? (-pt[1].x) : pt[1].x); 394 pt[1].y = cy + (flip_y ? (-pt[1].y) : pt[1].y); 395 pt[2].x = cx + (flip_x ? (-pt[2].x) : pt[2].x); 396 pt[2].y = cy + (flip_y ? (-pt[2].y) : pt[2].y); 397 gdImageFilledPolygon (im, pt, 3, color); 398 gdImagePolygon (im, pt, 3, color); 399 400 pt[0] = pt[3]; 401 pt[2] = pt[4]; 402 403 conquer = 1; 404 } 405 406 if (conquer || (((s_cos * hs) > (s_sin * ws)) && ((e_cos * hs) > (e_sin * ws)))) 407 { /* This is the best bit... */ 408 /* steep line + ellipse */ 409 /* go up & left from pt[0] to pt[2] */ 410 411 x2 = w; 412 y2 = 0; /* Starting point is exactly on ellipse */ 413 414 g = x2 - 1; 415 g = g * g * hs + 4 * ws - whs; 416 417 while ((x2 * hs) > (y2 * ws)) /* Keep |tangent| > 1 */ 418 { 419 if ((s_sin * x2) <= (y2 * s_cos)) 420 break; 421 422 y2 += 2; 423 g += ws * 4 * (y2 + 1); 424 425 if (g > 0) /* Need to drop */ 426 { 427 x2 -= 2; 428 g -= hs * 4 * x2; 429 } 430 } 431 432 lx2 = x2; 433 ly2 = y2; 434 435 lg = lx2 * (pt[0].y - pt[2].y) - ly2 * (pt[0].x - pt[2].x); 436 lg = (lx2 - 1) * (pt[0].y - pt[2].y) - (ly2 + 2) * (pt[0].x - pt[2].x) - lg; 437 438 while (y2 < (2 * pt[2].y)) 439 { 440 y2 += 2; 441 g += ws * 4 * (y2 + 1); 442 443 if (g > 0) /* Need to drop */ 444 { 445 x2 -= 2; 446 g -= hs * 4 * x2; 447 } 448 449 ly2 += 2; 450 lg -= 2 * (pt[0].x - pt[2].x); 451 452 if (lg < 0) /* Need to drop */ 453 { 454 lx2 -= 2; 455 lg -= 2 * (pt[0].y - pt[2].y); 456 } 457 458 y = (int) (y2 / 2); 459 for (x = (int) (lx2 / 2); x <= (int) (x2 / 2); x++) 460 { 461 gdImageSetPixel (im, ((flip_x) ? (cx - x) : (cx + x)), 462 ((flip_y) ? (cy - y) : (cy + y)), color); 463 } 464 } 465 } 466 if (conquer) 467 { 468 pt[0] = pt[4]; 469 pt[2] = pt[5]; 470 } 471 if (conquer || (((s_cos * hs) < (s_sin * ws)) && ((e_cos * hs) < (e_sin * ws)))) 472 { /* This is the best bit... */ 473 /* gradual line + ellipse */ 474 /* go down & right from pt[2] to pt[0] */ 475 476 x2 = 0; 477 y2 = h; /* Starting point is exactly on ellipse */ 478 479 g = y2 - 1; 480 g = g * g * ws + 4 * hs - whs; 481 482 while ((x2 * hs) < (y2 * ws)) 483 { 484 x2 += 2; 485 g += hs * 4 * (x2 + 1); 486 487 if (g > 0) /* Need to drop */ 488 { 489 y2 -= 2; 490 g -= ws * 4 * y2; 491 } 492 493 if ((e_sin * x2) >= (y2 * e_cos)) 494 break; 495 } 496 497 lx2 = x2; 498 ly2 = y2; 499 500 lg = lx2 * (pt[0].y - pt[2].y) - ly2 * (pt[0].x - pt[2].x); 501 lg = (lx2 + 2) * (pt[0].y - pt[2].y) - (ly2 - 1) * (pt[0].x - pt[2].x) - lg; 502 503 while (x2 < (2 * pt[0].x)) 504 { 505 x2 += 2; 506 g += hs * 4 * (x2 + 1); 507 508 if (g > 0) /* Need to drop */ 509 { 510 y2 -= 2; 511 g -= ws * 4 * y2; 512 } 513 514 lx2 += 2; 515 lg += 2 * (pt[0].y - pt[2].y); 516 517 if (lg < 0) /* Need to drop */ 518 { 519 ly2 -= 2; 520 lg += 2 * (pt[0].x - pt[2].x); 521 } 522 523 x = (int) (x2 / 2); 524 for (y = (int) (ly2 / 2); y <= (int) (y2 / 2); y++) 525 { 526 gdImageSetPixel (im, ((flip_x) ? (cx - x) : (cx + x)), 527 ((flip_y) ? (cy - y) : (cy + y)), color); 528 } 529 } 530 } 531 } 532 533 static gdPoint 534 gdArcClosest (int width, int height, int angle) 535 { 536 gdPoint pt; 537 538 int flip_x = 0; 539 int flip_y = 0; 540 541 long a_sin = 0; 542 long a_cos = 0; 543 544 long w; /* a * 2 */ 545 long h; /* b * 2 */ 546 547 long x2; /* x * 2 */ 548 long y2; /* y * 2 */ 549 550 long ws; /* (a * 2)^2 */ 551 long hs; /* (b * 2)^2 */ 552 553 long whs; /* (a * 2)^2 * (b * 2)^2 */ 554 555 long g; /* decision variable */ 556 557 w = (long) ((width & 1) ? (width + 1) : (width)); 558 h = (long) ((height & 1) ? (height + 1) : (height)); 559 560 while (angle < 0) 561 angle += 360; 562 while (angle >= 360) 563 angle -= 360; 564 565 if (angle == 0) 566 { 567 pt.x = w / 2; 568 pt.y = 0; 569 return (pt); 570 } 571 if (angle == 90) 572 { 573 pt.x = 0; 574 pt.y = h / 2; 575 return (pt); 576 } 577 if (angle == 180) 578 { 579 pt.x = -w / 2; 580 pt.y = 0; 581 return (pt); 582 } 583 if (angle == 270) 584 { 585 pt.x = 0; 586 pt.y = -h / 2; 587 return (pt); 588 } 589 590 pt.x = 0; 591 pt.y = 0; 592 593 if ((angle > 90) && (angle < 180)) 594 { 595 angle = 180 - angle; 596 flip_x = 1; 597 } 598 if ((angle > 180) && (angle < 270)) 599 { 600 angle = angle - 180; 601 flip_x = 1; 602 flip_y = 1; 603 } 604 if ((angle > 270) && (angle < 360)) 605 { 606 angle = 360 - angle; 607 flip_y = 1; 608 } 609 610 a_sin = (long) ((double) 32768 * sin ((double) angle * M_PI / (double) 180)); 611 a_cos = (long) ((double) 32768 * cos ((double) angle * M_PI / (double) 180)); 612 613 ws = w * w; 614 hs = h * h; 615 616 whs = 1; 617 while ((ws > 32768) || (hs > 32768)) 618 { 619 ws = (ws + 1) / 2; /* Unfortunate limitations on integers makes */ 620 hs = (hs + 1) / 2; /* drawing large ellipses problematic... */ 621 whs *= 2; 622 } 623 while ((ws * hs) > (0x04000000L / whs)) 624 { 625 ws = (ws + 1) / 2; 626 hs = (hs + 1) / 2; 627 whs *= 2; 628 } 629 whs *= ws * hs; 630 631 if ((a_cos * hs) > (a_sin * ws)) 632 { 633 x2 = w; 634 y2 = 0; /* Starting point is exactly on ellipse */ 635 636 g = x2 - 1; 637 g = g * g * hs + 4 * ws - whs; 638 639 while ((x2 * hs) > (y2 * ws)) /* Keep |tangent| > 1 */ 640 { 641 y2 += 2; 642 g += ws * 4 * (y2 + 1); 643 644 if (g > 0) /* Need to drop */ 645 { 646 x2 -= 2; 647 g -= hs * 4 * x2; 648 } 649 650 if ((a_sin * x2) <= (y2 * a_cos)) 651 { 652 pt.x = (int) (x2 / 2); 653 pt.y = (int) (y2 / 2); 654 break; 655 } 656 } 657 } 658 else 659 { 660 x2 = 0; 661 y2 = h; /* Starting point is exactly on ellipse */ 662 663 g = y2 - 1; 664 g = g * g * ws + 4 * hs - whs; 665 666 while ((x2 * hs) < (y2 * ws)) 667 { 668 x2 += 2; 669 g += hs * 4 * (x2 + 1); 670 671 if (g > 0) /* Need to drop */ 672 { 673 y2 -= 2; 674 g -= ws * 4 * y2; 675 } 676 677 if ((a_sin * x2) >= (y2 * a_cos)) 678 { 679 pt.x = (int) (x2 / 2); 680 pt.y = (int) (y2 / 2); 681 break; 682 } 683 } 684 } 685 686 if (flip_x) 687 pt.x = -pt.x; 688 if (flip_y) 689 pt.y = -pt.y; 690 691 return (pt); 692 } 693 694 #include "gd.h" 695 #include <string.h> 696 #include <math.h> 697 698 #define WIDTH 500 699 #define HEIGHT 300 700 701 int 702 main (int argc, char *argv[]) 703 { 704 gdImagePtr im = gdImageCreate (WIDTH, HEIGHT); 705 int white = gdImageColorResolve (im, 0xFF, 0xFF, 0xFF), black = gdImageColorResolve (im, 0, 0, 0), 706 red = gdImageColorResolve (im, 0xFF, 0xA0, 0xA0); 707 FILE *out; 708 709 /* filled arc - circle */ 710 gdImageFilledArc (im, WIDTH / 5, HEIGHT / 4, 200, 200, 45, 90, red, gdPie); 711 gdImageArc (im, WIDTH / 5, HEIGHT / 4, 200, 200, 45, 90, black); 712 713 /* filled arc - ellipse */ 714 gdImageFilledArc (im, WIDTH / 2, HEIGHT / 4, 200, 150, 45, 90, red, gdPie); 715 gdImageArc (im, WIDTH / 2, HEIGHT / 4, 200, 150, 45, 90, black); 716 717 718 /* reference lines */ 719 gdImageLine (im, 0, HEIGHT / 4, WIDTH, HEIGHT / 4, black); 720 gdImageLine (im, WIDTH / 5, 0, WIDTH / 5, HEIGHT, black); 721 gdImageLine (im, WIDTH / 2, 0, WIDTH / 2, HEIGHT, black); 722 gdImageLine (im, WIDTH / 2, HEIGHT / 4, WIDTH / 2 + 300, HEIGHT / 4 + 300, black); 723 gdImageLine (im, WIDTH / 5, HEIGHT / 4, WIDTH / 5 + 300, HEIGHT / 4 + 300, black); 724 725 /* TBB: Write img to test/arctest.png */ 726 out = fopen ("test/arctest.png", "wb"); 727 if (!out) 728 { 729 php_gd_error("Can't create test/arctest.png"); 730 exit (1); 731 } 732 gdImagePng (im, out); 733 fclose (out); 734 php_gd_error("Test image written to test/arctest.png"); 735 /* Destroy it */ 736 gdImageDestroy (im); 737 738 return 0; 739 } 740 741 #endif 742