ProcessPath.java revision 10444:f08705540498
1/* 2 * Copyright (c) 2005, 2014, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26package sun.java2d.loops; 27 28import java.awt.geom.Path2D; 29import java.awt.geom.PathIterator; 30import java.awt.geom.QuadCurve2D; 31import sun.awt.SunHints; 32import java.util.*; 33 34/* This is the java implementation of the native code from 35 * src/share/native/sun/java2d/loops/ProcessPath.[c,h] 36 * This code is written to be as much similar to the native 37 * as it possible. So, it sometimes does not follow java naming conventions. 38 * 39 * It's important to keep this code synchronized with native one. See more 40 * comments, description and high level scheme of the rendering process in the 41 * ProcessPath.c 42 */ 43 44public class ProcessPath { 45 46 /* Public interfaces and methods for drawing and filling general paths */ 47 48 public static abstract class DrawHandler { 49 public int xMin; 50 public int yMin; 51 public int xMax; 52 public int yMax; 53 public float xMinf; 54 public float yMinf; 55 public float xMaxf; 56 public float yMaxf; 57 58 public int strokeControl; 59 60 public DrawHandler(int xMin, int yMin, int xMax, int yMax, 61 int strokeControl) 62 { 63 setBounds(xMin, yMin, xMax, yMax, strokeControl); 64 } 65 66 public void setBounds(int xMin, int yMin, int xMax, int yMax) 67 { 68 this.xMin = xMin; 69 this.yMin = yMin; 70 this.xMax = xMax; 71 this.yMax = yMax; 72 73 /* Setting up fractional clipping box 74 * 75 * We are using following float -> int mapping: 76 * 77 * xi = floor(xf + 0.5) 78 * 79 * So, fractional values that hit the [xmin, xmax) integer interval 80 * will be situated inside the [xmin-0.5, xmax - 0.5) fractional 81 * interval. We are using EPSF constant to provide that upper 82 * boundary is not included. 83 */ 84 xMinf = xMin - 0.5f; 85 yMinf = yMin - 0.5f; 86 xMaxf = xMax - 0.5f - EPSF; 87 yMaxf = yMax - 0.5f - EPSF; 88 } 89 90 public void setBounds(int xMin, int yMin, int xMax, int yMax, 91 int strokeControl) 92 { 93 this.strokeControl = strokeControl; 94 setBounds(xMin, yMin, xMax, yMax); 95 } 96 97 public void adjustBounds(int bxMin, int byMin, int bxMax, int byMax) 98 { 99 if (xMin > bxMin) bxMin = xMin; 100 if (xMax < bxMax) bxMax = xMax; 101 if (yMin > byMin) byMin = yMin; 102 if (yMax < byMax) byMax = yMax; 103 setBounds(bxMin, byMin, bxMax, byMax); 104 } 105 106 public DrawHandler(int xMin, int yMin, int xMax, int yMax) { 107 this(xMin, yMin, xMax, yMax, SunHints.INTVAL_STROKE_DEFAULT); 108 } 109 110 public abstract void drawLine(int x0, int y0, int x1, int y1); 111 112 public abstract void drawPixel(int x0, int y0); 113 114 public abstract void drawScanline(int x0, int x1, int y0); 115 } 116 117 public interface EndSubPathHandler { 118 public void processEndSubPath(); 119 } 120 121 public static final int PH_MODE_DRAW_CLIP = 0; 122 public static final int PH_MODE_FILL_CLIP = 1; 123 124 public static abstract class ProcessHandler implements EndSubPathHandler { 125 DrawHandler dhnd; 126 int clipMode; 127 128 public ProcessHandler(DrawHandler dhnd, 129 int clipMode) { 130 this.dhnd = dhnd; 131 this.clipMode = clipMode; 132 } 133 134 public abstract void processFixedLine(int x1, int y1, 135 int x2, int y2, int [] pixelInfo, 136 boolean checkBounds, 137 boolean endSubPath); 138 } 139 140 public static EndSubPathHandler noopEndSubPathHandler = 141 new EndSubPathHandler() { 142 public void processEndSubPath() { } 143 }; 144 145 public static boolean fillPath(DrawHandler dhnd, Path2D.Float p2df, 146 int transX, int transY) 147 { 148 FillProcessHandler fhnd = new FillProcessHandler(dhnd); 149 if (!doProcessPath(fhnd, p2df, transX, transY)) { 150 return false; 151 } 152 FillPolygon(fhnd, p2df.getWindingRule()); 153 return true; 154 } 155 156 public static boolean drawPath(DrawHandler dhnd, 157 EndSubPathHandler endSubPath, 158 Path2D.Float p2df, 159 int transX, int transY) 160 { 161 return doProcessPath(new DrawProcessHandler(dhnd, endSubPath), 162 p2df, transX, transY); 163 } 164 165 public static boolean drawPath(DrawHandler dhnd, 166 Path2D.Float p2df, 167 int transX, int transY) 168 { 169 return doProcessPath(new DrawProcessHandler(dhnd, 170 noopEndSubPathHandler), 171 p2df, transX, transY); 172 } 173 174 /* Private implementation of the rendering process */ 175 176 /* Boundaries used for skipping huge path segments */ 177 private static final float UPPER_BND = Float.MAX_VALUE/4.0f; 178 private static final float LOWER_BND = -UPPER_BND; 179 180 /* Precision (in bits) used in forward differencing */ 181 private static final int FWD_PREC = 7; 182 183 /* Precision (in bits) used for the rounding in the midpoint test */ 184 private static final int MDP_PREC = 10; 185 186 private static final int MDP_MULT = 1 << MDP_PREC; 187 private static final int MDP_HALF_MULT = MDP_MULT >> 1; 188 189 /* Boundaries used for clipping large path segments (those are inside 190 * [UPPER/LOWER]_BND boundaries) 191 */ 192 private static final int UPPER_OUT_BND = 1 << (30 - MDP_PREC); 193 private static final int LOWER_OUT_BND = -UPPER_OUT_BND; 194 195 196 /* Calculation boundaries. They are used for switching to the more slow but 197 * allowing larger input values method of calculation of the initial values 198 * of the scan converted line segments inside the FillPolygon 199 */ 200 private static final float CALC_UBND = 1 << (30 - MDP_PREC); 201 private static final float CALC_LBND = -CALC_UBND; 202 203 204 /* Following constants are used for providing open boundaries of the 205 * intervals 206 */ 207 public static final int EPSFX = 1; 208 public static final float EPSF = ((float)EPSFX)/MDP_MULT; 209 210 /* Bit mask used to separate whole part from the fraction part of the 211 * number 212 */ 213 private static final int MDP_W_MASK = -MDP_MULT; 214 215 /* Bit mask used to separate fractional part from the whole part of the 216 * number 217 */ 218 private static final int MDP_F_MASK = MDP_MULT - 1; 219 220 /* 221 * Constants for the forward differencing 222 * of the cubic and quad curves 223 */ 224 225 /* Maximum size of the cubic curve (calculated as the size of the bounding 226 * box of the control points) which could be rendered without splitting 227 */ 228 private static final int MAX_CUB_SIZE = 256; 229 230 /* Maximum size of the quad curve (calculated as the size of the bounding 231 * box of the control points) which could be rendered without splitting 232 */ 233 private static final int MAX_QUAD_SIZE = 1024; 234 235 /* Default power of 2 steps used in the forward differencing. Here DF prefix 236 * stands for DeFault. Constants below are used as initial values for the 237 * adaptive forward differencing algorithm. 238 */ 239 private static final int DF_CUB_STEPS = 3; 240 private static final int DF_QUAD_STEPS = 2; 241 242 /* Shift of the current point of the curve for preparing to the midpoint 243 * rounding 244 */ 245 private static final int DF_CUB_SHIFT = FWD_PREC + DF_CUB_STEPS*3 - 246 MDP_PREC; 247 private static final int DF_QUAD_SHIFT = FWD_PREC + DF_QUAD_STEPS*2 - 248 MDP_PREC; 249 250 /* Default amount of steps of the forward differencing */ 251 private static final int DF_CUB_COUNT = (1<<DF_CUB_STEPS); 252 private static final int DF_QUAD_COUNT = (1<<DF_QUAD_STEPS); 253 254 /* Default boundary constants used to check the necessity of the restepping 255 */ 256 private static final int DF_CUB_DEC_BND = 1<<DF_CUB_STEPS*3 + FWD_PREC + 2; 257 private static final int DF_CUB_INC_BND = 1<<DF_CUB_STEPS*3 + FWD_PREC - 1; 258 private static final int DF_QUAD_DEC_BND = 1<<DF_QUAD_STEPS*2 + 259 FWD_PREC + 2; 260 private static final int DF_QUAD_INC_BND = 1<<DF_QUAD_STEPS*2 + 261 FWD_PREC - 1; 262 263 /* Multipliers for the coefficients of the polynomial form of the cubic and 264 * quad curves representation 265 */ 266 private static final int CUB_A_SHIFT = FWD_PREC; 267 private static final int CUB_B_SHIFT = (DF_CUB_STEPS + FWD_PREC + 1); 268 private static final int CUB_C_SHIFT = (DF_CUB_STEPS*2 + FWD_PREC); 269 270 private static final int CUB_A_MDP_MULT = (1<<CUB_A_SHIFT); 271 private static final int CUB_B_MDP_MULT = (1<<CUB_B_SHIFT); 272 private static final int CUB_C_MDP_MULT = (1<<CUB_C_SHIFT); 273 274 private static final int QUAD_A_SHIFT = FWD_PREC; 275 private static final int QUAD_B_SHIFT = (DF_QUAD_STEPS + FWD_PREC); 276 277 private static final int QUAD_A_MDP_MULT = (1<<QUAD_A_SHIFT); 278 private static final int QUAD_B_MDP_MULT = (1<<QUAD_B_SHIFT); 279 280 /* Clipping macros for drawing and filling algorithms */ 281 private static float CLIP(float a1, float b1, float a2, float b2, 282 double t) { 283 return (float)(b1 + (t - a1)*(b2 - b1) / (a2 - a1)); 284 } 285 286 private static int CLIP(int a1, int b1, int a2, int b2, double t) { 287 return (int)(b1 + (t - a1)*(b2 - b1) / (a2 - a1)); 288 } 289 290 291 private static final int CRES_MIN_CLIPPED = 0; 292 private static final int CRES_MAX_CLIPPED = 1; 293 private static final int CRES_NOT_CLIPPED = 3; 294 private static final int CRES_INVISIBLE = 4; 295 296 private static boolean IS_CLIPPED(int res) { 297 return res == CRES_MIN_CLIPPED || res == CRES_MAX_CLIPPED; 298 } 299 300 /* This is java implementation of the macro from ProcessGeneralPath.c. 301 * To keep the logic of the java code similar to the native one 302 * array and set of indexes are used to point out the data. 303 */ 304 private static int TESTANDCLIP(float LINE_MIN, float LINE_MAX, float[] c, 305 int a1, int b1, int a2, int b2) { 306 double t; 307 int res = CRES_NOT_CLIPPED; 308 if (c[a1] < (LINE_MIN) || c[a1] > (LINE_MAX)) { 309 if (c[a1] < (LINE_MIN)) { 310 if (c[a2] < (LINE_MIN)) { 311 return CRES_INVISIBLE; 312 }; 313 res = CRES_MIN_CLIPPED; 314 t = (LINE_MIN); 315 } else { 316 if (c[a2] > (LINE_MAX)) { 317 return CRES_INVISIBLE; 318 }; 319 res = CRES_MAX_CLIPPED; 320 t = (LINE_MAX); 321 } 322 c[b1] = CLIP(c[a1], c[b1], c[a2], c[b2], t); 323 c[a1] = (float)t; 324 } 325 return res; 326 } 327 328 /* Integer version of the method above */ 329 private static int TESTANDCLIP(int LINE_MIN, int LINE_MAX, int[] c, 330 int a1, int b1, int a2, int b2) { 331 double t; 332 int res = CRES_NOT_CLIPPED; 333 if (c[a1] < (LINE_MIN) || c[a1] > (LINE_MAX)) { 334 if (c[a1] < (LINE_MIN)) { 335 if (c[a2] < (LINE_MIN)) { 336 return CRES_INVISIBLE; 337 }; 338 res = CRES_MIN_CLIPPED; 339 t = (LINE_MIN); 340 } else { 341 if (c[a2] > (LINE_MAX)) { 342 return CRES_INVISIBLE; 343 }; 344 res = CRES_MAX_CLIPPED; 345 t = (LINE_MAX); 346 } 347 c[b1] = CLIP(c[a1], c[b1], c[a2], c[b2], t); 348 c[a1] = (int)t; 349 } 350 return res; 351 } 352 353 354 355 /* Following method is used for clipping and clumping filled shapes. 356 * An example of this process is shown on the picture below: 357 * ----+ ----+ 358 * |/ | |/ | 359 * + | + | 360 * /| | I | 361 * / | | I | 362 * | | | ===> I | 363 * \ | | I | 364 * \| | I | 365 * + | + | 366 * |\ | |\ | 367 * | ----+ | ----+ 368 * boundary boundary 369 * 370 * We can only perform clipping in case of right side of the output area 371 * because all segments passed out the right boundary don't influence on the 372 * result of scan conversion algorithm (it correctly handles half open 373 * contours). 374 * 375 * This is java implementation of the macro from ProcessGeneralPath.c. 376 * To keep the logic of the java code similar to the native one 377 * array and set of indexes are used to point out the data. 378 * 379 */ 380 private static int CLIPCLAMP(float LINE_MIN, float LINE_MAX, float[] c, 381 int a1, int b1, int a2, int b2, 382 int a3, int b3) { 383 c[a3] = c[a1]; 384 c[b3] = c[b1]; 385 int res = TESTANDCLIP(LINE_MIN, LINE_MAX, c, a1, b1, a2, b2); 386 if (res == CRES_MIN_CLIPPED) { 387 c[a3] = c[a1]; 388 } else if (res == CRES_MAX_CLIPPED) { 389 c[a3] = c[a1]; 390 res = CRES_MAX_CLIPPED; 391 } else if (res == CRES_INVISIBLE) { 392 if (c[a1] > LINE_MAX) { 393 res = CRES_INVISIBLE; 394 } else { 395 c[a1] = LINE_MIN; 396 c[a2] = LINE_MIN; 397 res = CRES_NOT_CLIPPED; 398 } 399 } 400 return res; 401 } 402 403 /* Integer version of the method above */ 404 private static int CLIPCLAMP(int LINE_MIN, int LINE_MAX, int[] c, 405 int a1, int b1, int a2, int b2, 406 int a3, int b3) { 407 c[a3] = c[a1]; 408 c[b3] = c[b1]; 409 int res = TESTANDCLIP(LINE_MIN, LINE_MAX, c, a1, b1, a2, b2); 410 if (res == CRES_MIN_CLIPPED) { 411 c[a3] = c[a1]; 412 } else if (res == CRES_MAX_CLIPPED) { 413 c[a3] = c[a1]; 414 res = CRES_MAX_CLIPPED; 415 } else if (res == CRES_INVISIBLE) { 416 if (c[a1] > LINE_MAX) { 417 res = CRES_INVISIBLE; 418 } else { 419 c[a1] = LINE_MIN; 420 c[a2] = LINE_MIN; 421 res = CRES_NOT_CLIPPED; 422 } 423 } 424 return res; 425 } 426 427 private static class DrawProcessHandler extends ProcessHandler { 428 429 EndSubPathHandler processESP; 430 431 public DrawProcessHandler(DrawHandler dhnd, 432 EndSubPathHandler processESP) { 433 super(dhnd, PH_MODE_DRAW_CLIP); 434 this.dhnd = dhnd; 435 this.processESP = processESP; 436 } 437 438 public void processEndSubPath() { 439 processESP.processEndSubPath(); 440 } 441 442 void PROCESS_LINE(int fX0, int fY0, int fX1, int fY1, 443 boolean checkBounds, int[] pixelInfo) { 444 int X0 = fX0 >> MDP_PREC; 445 int Y0 = fY0 >> MDP_PREC; 446 int X1 = fX1 >> MDP_PREC; 447 int Y1 = fY1 >> MDP_PREC; 448 449 /* Handling lines having just one pixel */ 450 if (((X0^X1) | (Y0^Y1)) == 0) { 451 if (checkBounds && 452 (dhnd.yMin > Y0 || 453 dhnd.yMax <= Y0 || 454 dhnd.xMin > X0 || 455 dhnd.xMax <= X0)) return; 456 457 if (pixelInfo[0] == 0) { 458 pixelInfo[0] = 1; 459 pixelInfo[1] = X0; 460 pixelInfo[2] = Y0; 461 pixelInfo[3] = X0; 462 pixelInfo[4] = Y0; 463 dhnd.drawPixel(X0, Y0); 464 } else if ((X0 != pixelInfo[3] || Y0 != pixelInfo[4]) && 465 (X0 != pixelInfo[1] || Y0 != pixelInfo[2])) { 466 dhnd.drawPixel(X0, Y0); 467 pixelInfo[3] = X0; 468 pixelInfo[4] = Y0; 469 } 470 return; 471 } 472 473 if (!checkBounds || 474 (dhnd.yMin <= Y0 && 475 dhnd.yMax > Y0 && 476 dhnd.xMin <= X0 && 477 dhnd.xMax > X0)) 478 { 479 /* Switch off first pixel of the line before drawing */ 480 if (pixelInfo[0] == 1 && 481 ((pixelInfo[1] == X0 && pixelInfo[2] == Y0) || 482 (pixelInfo[3] == X0 && pixelInfo[4] == Y0))) 483 { 484 dhnd.drawPixel(X0, Y0); 485 } 486 } 487 488 dhnd.drawLine(X0, Y0, X1, Y1); 489 490 if (pixelInfo[0] == 0) { 491 pixelInfo[0] = 1; 492 pixelInfo[1] = X0; 493 pixelInfo[2] = Y0; 494 pixelInfo[3] = X0; 495 pixelInfo[4] = Y0; 496 } 497 498 /* Switch on last pixel of the line if it was already 499 * drawn during rendering of the previous segments 500 */ 501 if ((pixelInfo[1] == X1 && pixelInfo[2] == Y1) || 502 (pixelInfo[3] == X1 && pixelInfo[4] == Y1)) 503 { 504 if (checkBounds && 505 (dhnd.yMin > Y1 || 506 dhnd.yMax <= Y1 || 507 dhnd.xMin > X1 || 508 dhnd.xMax <= X1)) { 509 return; 510 } 511 512 dhnd.drawPixel(X1, Y1); 513 } 514 pixelInfo[3] = X1; 515 pixelInfo[4] = Y1; 516 } 517 518 void PROCESS_POINT(int fX, int fY, boolean checkBounds, 519 int[] pixelInfo) { 520 int _X = fX>> MDP_PREC; 521 int _Y = fY>> MDP_PREC; 522 if (checkBounds && 523 (dhnd.yMin > _Y || 524 dhnd.yMax <= _Y || 525 dhnd.xMin > _X || 526 dhnd.xMax <= _X)) return; 527 /* 528 * (_X,_Y) should be inside boundaries 529 * 530 * assert(dhnd.yMin <= _Y && 531 * dhnd.yMax > _Y && 532 * dhnd.xMin <= _X && 533 * dhnd.xMax > _X); 534 * 535 */ 536 if (pixelInfo[0] == 0) { 537 pixelInfo[0] = 1; 538 pixelInfo[1] = _X; 539 pixelInfo[2] = _Y; 540 pixelInfo[3] = _X; 541 pixelInfo[4] = _Y; 542 dhnd.drawPixel(_X, _Y); 543 } else if ((_X != pixelInfo[3] || _Y != pixelInfo[4]) && 544 (_X != pixelInfo[1] || _Y != pixelInfo[2])) { 545 dhnd.drawPixel(_X, _Y); 546 pixelInfo[3] = _X; 547 pixelInfo[4] = _Y; 548 } 549 } 550 551 /* Drawing line with subpixel endpoints 552 * 553 * (x1, y1), (x2, y2) - fixed point coordinates of the endpoints 554 * with MDP_PREC bits for the fractional part 555 * 556 * pixelInfo - structure which keeps drawing info for avoiding 557 * multiple drawing at the same position on the 558 * screen (required for the XOR mode of drawing) 559 * 560 * pixelInfo[0] - state of the drawing 561 * 0 - no pixel drawn between 562 * moveTo/close of the path 563 * 1 - there are drawn pixels 564 * 565 * pixelInfo[1,2] - first pixel of the path 566 * between moveTo/close of the 567 * path 568 * 569 * pixelInfo[3,4] - last drawn pixel between 570 * moveTo/close of the path 571 * 572 * checkBounds - flag showing necessity of checking the clip 573 * 574 */ 575 public void processFixedLine(int x1, int y1, int x2, int y2, 576 int[] pixelInfo, boolean checkBounds, 577 boolean endSubPath) { 578 579 /* Checking if line is inside a (X,Y),(X+MDP_MULT,Y+MDP_MULT) box */ 580 int c = ((x1 ^ x2) | (y1 ^ y2)); 581 int rx1, ry1, rx2, ry2; 582 if ((c & MDP_W_MASK) == 0) { 583 /* Checking for the segments with integer coordinates having 584 * the same start and end points 585 */ 586 if (c == 0) { 587 PROCESS_POINT(x1 + MDP_HALF_MULT, y1 + MDP_HALF_MULT, 588 checkBounds, pixelInfo); 589 } 590 return; 591 } 592 593 if (x1 == x2 || y1 == y2) { 594 rx1 = x1 + MDP_HALF_MULT; 595 rx2 = x2 + MDP_HALF_MULT; 596 ry1 = y1 + MDP_HALF_MULT; 597 ry2 = y2 + MDP_HALF_MULT; 598 } else { 599 /* Neither dx nor dy can be zero because of the check above */ 600 int dx = x2 - x1; 601 int dy = y2 - y1; 602 603 /* Floor of x1, y1, x2, y2 */ 604 int fx1 = x1 & MDP_W_MASK; 605 int fy1 = y1 & MDP_W_MASK; 606 int fx2 = x2 & MDP_W_MASK; 607 int fy2 = y2 & MDP_W_MASK; 608 609 /* Processing first endpoint */ 610 if (fx1 == x1 || fy1 == y1) { 611 /* Adding MDP_HALF_MULT to the [xy]1 if f[xy]1 == [xy]1 612 * will not affect the result 613 */ 614 rx1 = x1 + MDP_HALF_MULT; 615 ry1 = y1 + MDP_HALF_MULT; 616 } else { 617 /* Boundary at the direction from (x1,y1) to (x2,y2) */ 618 int bx1 = (x1 < x2) ? fx1 + MDP_MULT : fx1; 619 int by1 = (y1 < y2) ? fy1 + MDP_MULT : fy1; 620 621 /* intersection with column bx1 */ 622 int cross = y1 + ((bx1 - x1)*dy)/dx; 623 if (cross >= fy1 && cross <= fy1 + MDP_MULT) { 624 rx1 = bx1; 625 ry1 = cross + MDP_HALF_MULT; 626 } else { 627 /* intersection with row by1 */ 628 cross = x1 + ((by1 - y1)*dx)/dy; 629 rx1 = cross + MDP_HALF_MULT; 630 ry1 = by1; 631 } 632 } 633 634 /* Processing second endpoint */ 635 if (fx2 == x2 || fy2 == y2) { 636 /* Adding MDP_HALF_MULT to the [xy]2 if f[xy]2 == [xy]2 637 * will not affect the result 638 */ 639 rx2 = x2 + MDP_HALF_MULT; 640 ry2 = y2 + MDP_HALF_MULT; 641 } else { 642 /* Boundary at the direction from (x2,y2) to (x1,y1) */ 643 int bx2 = (x1 > x2) ? fx2 + MDP_MULT : fx2; 644 int by2 = (y1 > y2) ? fy2 + MDP_MULT : fy2; 645 646 /* intersection with column bx2 */ 647 int cross = y2 + ((bx2 - x2)*dy)/dx; 648 if (cross >= fy2 && cross <= fy2 + MDP_MULT) { 649 rx2 = bx2; 650 ry2 = cross + MDP_HALF_MULT; 651 } else { 652 /* intersection with row by2 */ 653 cross = x2 + ((by2 - y2)*dx)/dy; 654 rx2 = cross + MDP_HALF_MULT; 655 ry2 = by2; 656 } 657 } 658 } 659 PROCESS_LINE(rx1, ry1, rx2, ry2, checkBounds, pixelInfo); 660 } 661 } 662 663 /* Performing drawing of the monotonic in X and Y quadratic curves with 664 * sizes less than MAX_QUAD_SIZE by using forward differencing method of 665 * calculation. See comments to the DrawMonotonicQuad in the 666 * ProcessGeneralPath.c 667 */ 668 private static void DrawMonotonicQuad(ProcessHandler hnd, 669 float[] coords, 670 boolean checkBounds, 671 int[] pixelInfo) { 672 673 int x0 = (int)(coords[0]*MDP_MULT); 674 int y0 = (int)(coords[1]*MDP_MULT); 675 676 int xe = (int)(coords[4]*MDP_MULT); 677 int ye = (int)(coords[5]*MDP_MULT); 678 679 /* Extracting fractional part of coordinates of first control point */ 680 int px = (x0 & (~MDP_W_MASK)) << DF_QUAD_SHIFT; 681 int py = (y0 & (~MDP_W_MASK)) << DF_QUAD_SHIFT; 682 683 /* Setting default amount of steps */ 684 int count = DF_QUAD_COUNT; 685 686 /* Setting default shift for preparing to the midpoint rounding */ 687 int shift = DF_QUAD_SHIFT; 688 689 int ax = (int)((coords[0] - 2*coords[2] + 690 coords[4])*QUAD_A_MDP_MULT); 691 int ay = (int)((coords[1] - 2*coords[3] + 692 coords[5])*QUAD_A_MDP_MULT); 693 694 int bx = (int)((-2*coords[0] + 2*coords[2])*QUAD_B_MDP_MULT); 695 int by = (int)((-2*coords[1] + 2*coords[3])*QUAD_B_MDP_MULT); 696 697 int ddpx = 2*ax; 698 int ddpy = 2*ay; 699 700 int dpx = ax + bx; 701 int dpy = ay + by; 702 703 int x1, y1; 704 705 int x2 = x0; 706 int y2 = y0; 707 708 int maxDD = Math.max(Math.abs(ddpx),Math.abs(ddpy)); 709 710 int dx = xe - x0; 711 int dy = ye - y0; 712 713 int x0w = x0 & MDP_W_MASK; 714 int y0w = y0 & MDP_W_MASK; 715 716 /* Perform decreasing step in 2 times if slope of the first forward 717 * difference changes too quickly (more than a pixel per step in X or Y 718 * direction). We can perform adjusting of the step size before the 719 * rendering loop because the curvature of the quad curve remains the 720 * same along all the curve 721 */ 722 while (maxDD > DF_QUAD_DEC_BND) { 723 dpx = (dpx<<1) - ax; 724 dpy = (dpy<<1) - ay; 725 count <<= 1; 726 maxDD >>= 2; 727 px <<=2; 728 py <<=2; 729 shift += 2; 730 } 731 732 while(count-- > 1) { 733 px += dpx; 734 py += dpy; 735 736 dpx += ddpx; 737 dpy += ddpy; 738 739 x1 = x2; 740 y1 = y2; 741 742 x2 = x0w + (px >> shift); 743 y2 = y0w + (py >> shift); 744 745 /* Checking that we are not running out of the endpoint and bounding 746 * violating coordinate. The check is pretty simple because the 747 * curve passed to the DrawCubic already split into the 748 * monotonic in X and Y pieces 749 */ 750 751 /* Bounding x2 by xe */ 752 if (((xe-x2)^dx) < 0) { 753 x2 = xe; 754 } 755 756 /* Bounding y2 by ye */ 757 if (((ye-y2)^dy) < 0) { 758 y2 = ye; 759 } 760 761 hnd.processFixedLine(x1, y1, x2, y2, pixelInfo, checkBounds, false); 762 } 763 764 /* We are performing one step less than necessary and use actual 765 * (xe,ye) endpoint of the curve instead of calculated. This prevent us 766 * from running above the curve endpoint due to the accumulated errors 767 * during the iterations. 768 */ 769 770 hnd.processFixedLine(x2, y2, xe, ye, pixelInfo, checkBounds, false); 771 } 772 773 /* 774 * Checking size of the quad curves and split them if necessary. 775 * Calling DrawMonotonicQuad for the curves of the appropriate size. 776 * Note: coords array could be changed 777 */ 778 private static void ProcessMonotonicQuad(ProcessHandler hnd, 779 float[] coords, 780 int[] pixelInfo) { 781 782 float[] coords1 = new float[6]; 783 float tx, ty; 784 float xMin, yMin, xMax, yMax; 785 786 xMin = xMax = coords[0]; 787 yMin = yMax = coords[1]; 788 for (int i = 2; i < 6; i += 2) { 789 xMin = (xMin > coords[i])? coords[i] : xMin; 790 xMax = (xMax < coords[i])? coords[i] : xMax; 791 yMin = (yMin > coords[i + 1])? coords[i + 1] : yMin; 792 yMax = (yMax < coords[i + 1])? coords[i + 1] : yMax; 793 } 794 795 if (hnd.clipMode == PH_MODE_DRAW_CLIP) { 796 797 /* In case of drawing we could just skip curves which are 798 * completely out of bounds 799 */ 800 if (hnd.dhnd.xMaxf < xMin || hnd.dhnd.xMinf > xMax || 801 hnd.dhnd.yMaxf < yMin || hnd.dhnd.yMinf > yMax) { 802 return; 803 } 804 } else { 805 806 /* In case of filling we could skip curves which are above, 807 * below and behind the right boundary of the visible area 808 */ 809 810 if (hnd.dhnd.yMaxf < yMin || hnd.dhnd.yMinf > yMax || 811 hnd.dhnd.xMaxf < xMin) 812 { 813 return; 814 } 815 816 /* We could clamp x coordinates to the corresponding boundary 817 * if the curve is completely behind the left one 818 */ 819 820 if (hnd.dhnd.xMinf > xMax) { 821 coords[0] = coords[2] = coords[4] = hnd.dhnd.xMinf; 822 } 823 } 824 825 if (xMax - xMin > MAX_QUAD_SIZE || yMax - yMin > MAX_QUAD_SIZE) { 826 coords1[4] = coords[4]; 827 coords1[5] = coords[5]; 828 coords1[2] = (coords[2] + coords[4])/2.0f; 829 coords1[3] = (coords[3] + coords[5])/2.0f; 830 coords[2] = (coords[0] + coords[2])/2.0f; 831 coords[3] = (coords[1] + coords[3])/2.0f; 832 coords[4] = coords1[0] = (coords[2] + coords1[2])/2.0f; 833 coords[5] = coords1[1] = (coords[3] + coords1[3])/2.0f; 834 835 ProcessMonotonicQuad(hnd, coords, pixelInfo); 836 837 ProcessMonotonicQuad(hnd, coords1, pixelInfo); 838 } else { 839 DrawMonotonicQuad(hnd, coords, 840 /* Set checkBounds parameter if curve intersects 841 * boundary of the visible area. We know that the 842 * curve is visible, so the check is pretty 843 * simple 844 */ 845 hnd.dhnd.xMinf >= xMin || 846 hnd.dhnd.xMaxf <= xMax || 847 hnd.dhnd.yMinf >= yMin || 848 hnd.dhnd.yMaxf <= yMax, 849 pixelInfo); 850 } 851 } 852 853 /* 854 * Split quadratic curve into monotonic in X and Y parts. Calling 855 * ProcessMonotonicQuad for each monotonic piece of the curve. 856 * Note: coords array could be changed 857 */ 858 private static void ProcessQuad(ProcessHandler hnd, float[] coords, 859 int[] pixelInfo) { 860 /* Temporary array for holding parameters corresponding to the extreme 861 * in X and Y points 862 */ 863 double params[] = new double[2]; 864 int cnt = 0; 865 double param; 866 867 /* Simple check for monotonicity in X before searching for the extreme 868 * points of the X(t) function. We first check if the curve is 869 * monotonic in X by seeing if all of the X coordinates are strongly 870 * ordered. 871 */ 872 if ((coords[0] > coords[2] || coords[2] > coords[4]) && 873 (coords[0] < coords[2] || coords[2] < coords[4])) 874 { 875 /* Searching for extreme points of the X(t) function by solving 876 * dX(t) 877 * ---- = 0 equation 878 * dt 879 */ 880 double ax = coords[0] - 2*coords[2] + coords[4]; 881 if (ax != 0) { 882 /* Calculating root of the following equation 883 * ax*t + bx = 0 884 */ 885 double bx = coords[0] - coords[2]; 886 887 param = bx/ax; 888 if (param < 1.0 && param > 0.0) { 889 params[cnt++] = param; 890 } 891 } 892 } 893 894 /* Simple check for monotonicity in Y before searching for the extreme 895 * points of the Y(t) function. We first check if the curve is 896 * monotonic in Y by seeing if all of the Y coordinates are strongly 897 * ordered. 898 */ 899 if ((coords[1] > coords[3] || coords[3] > coords[5]) && 900 (coords[1] < coords[3] || coords[3] < coords[5])) 901 { 902 /* Searching for extreme points of the Y(t) function by solving 903 * dY(t) 904 * ----- = 0 equation 905 * dt 906 */ 907 double ay = coords[1] - 2*coords[3] + coords[5]; 908 909 if (ay != 0) { 910 /* Calculating root of the following equation 911 * ay*t + by = 0 912 */ 913 double by = coords[1] - coords[3]; 914 915 param = by/ay; 916 if (param < 1.0 && param > 0.0) { 917 if (cnt > 0) { 918 /* Inserting parameter only if it differs from 919 * already stored 920 */ 921 if (params[0] > param) { 922 params[cnt++] = params[0]; 923 params[0] = param; 924 } else if (params[0] < param) { 925 params[cnt++] = param; 926 } 927 } else { 928 params[cnt++] = param; 929 } 930 } 931 } 932 } 933 934 /* Processing obtained monotonic parts */ 935 switch(cnt) { 936 case 0: 937 break; 938 case 1: 939 ProcessFirstMonotonicPartOfQuad(hnd, coords, pixelInfo, 940 (float)params[0]); 941 break; 942 case 2: 943 ProcessFirstMonotonicPartOfQuad(hnd, coords, pixelInfo, 944 (float)params[0]); 945 param = params[1] - params[0]; 946 if (param > 0) { 947 ProcessFirstMonotonicPartOfQuad(hnd, coords, pixelInfo, 948 /* Scale parameter to match with 949 * rest of the curve 950 */ 951 (float)(param/(1.0 - params[0]))); 952 } 953 break; 954 } 955 956 ProcessMonotonicQuad(hnd,coords,pixelInfo); 957 } 958 959 /* 960 * Bite the piece of the quadratic curve from start point till the point 961 * corresponding to the specified parameter then call ProcessQuad for the 962 * bitten part. 963 * Note: coords array will be changed 964 */ 965 private static void ProcessFirstMonotonicPartOfQuad(ProcessHandler hnd, 966 float[] coords, 967 int[] pixelInfo, 968 float t) { 969 float[] coords1 = new float[6]; 970 971 coords1[0] = coords[0]; 972 coords1[1] = coords[1]; 973 coords1[2] = coords[0] + t*(coords[2] - coords[0]); 974 coords1[3] = coords[1] + t*(coords[3] - coords[1]); 975 coords[2] = coords[2] + t*(coords[4] - coords[2]); 976 coords[3] = coords[3] + t*(coords[5] - coords[3]); 977 coords[0] = coords1[4] = coords1[2] + t*(coords[2] - coords1[2]); 978 coords[1] = coords1[5] = coords1[3] + t*(coords[3] - coords1[3]); 979 980 ProcessMonotonicQuad(hnd, coords1, pixelInfo); 981 } 982 983 /* Performing drawing of the monotonic in X and Y cubic curves with sizes 984 * less than MAX_CUB_SIZE by using forward differencing method of 985 * calculation. See comments to the DrawMonotonicCubic in the 986 * ProcessGeneralPath.c 987 */ 988 private static void DrawMonotonicCubic(ProcessHandler hnd, 989 float[] coords, 990 boolean checkBounds, 991 int[] pixelInfo) { 992 int x0 = (int)(coords[0]*MDP_MULT); 993 int y0 = (int)(coords[1]*MDP_MULT); 994 995 int xe = (int)(coords[6]*MDP_MULT); 996 int ye = (int)(coords[7]*MDP_MULT); 997 998 /* Extracting fractional part of coordinates of first control point */ 999 int px = (x0 & (~MDP_W_MASK)) << DF_CUB_SHIFT; 1000 int py = (y0 & (~MDP_W_MASK)) << DF_CUB_SHIFT; 1001 1002 /* Setting default boundary values for checking first and second forward 1003 * difference for the necessity of the restepping. See comments to the 1004 * boundary values in ProcessQuad for more info. 1005 */ 1006 int incStepBnd = DF_CUB_INC_BND; 1007 int decStepBnd = DF_CUB_DEC_BND; 1008 1009 /* Setting default amount of steps */ 1010 int count = DF_CUB_COUNT; 1011 1012 /* Setting default shift for preparing to the midpoint rounding */ 1013 int shift = DF_CUB_SHIFT; 1014 1015 int ax = (int)((-coords[0] + 3*coords[2] - 3*coords[4] + 1016 coords[6])*CUB_A_MDP_MULT); 1017 int ay = (int)((-coords[1] + 3*coords[3] - 3*coords[5] + 1018 coords[7])*CUB_A_MDP_MULT); 1019 1020 int bx = (int)((3*coords[0] - 6*coords[2] + 1021 3*coords[4])*CUB_B_MDP_MULT); 1022 int by = (int)((3*coords[1] - 6*coords[3] + 1023 3*coords[5])*CUB_B_MDP_MULT); 1024 1025 int cx = (int)((-3*coords[0] + 3*coords[2])*(CUB_C_MDP_MULT)); 1026 int cy = (int)((-3*coords[1] + 3*coords[3])*(CUB_C_MDP_MULT)); 1027 1028 int dddpx = 6*ax; 1029 int dddpy = 6*ay; 1030 1031 int ddpx = dddpx + bx; 1032 int ddpy = dddpy + by; 1033 1034 int dpx = ax + (bx>>1) + cx; 1035 int dpy = ay + (by>>1) + cy; 1036 1037 int x1, y1; 1038 1039 int x2 = x0; 1040 int y2 = y0; 1041 1042 /* Calculating whole part of the first point of the curve */ 1043 int x0w = x0 & MDP_W_MASK; 1044 int y0w = y0 & MDP_W_MASK; 1045 1046 int dx = xe - x0; 1047 int dy = ye - y0; 1048 1049 while (count > 0) { 1050 /* Perform decreasing step in 2 times if necessary */ 1051 while (Math.abs(ddpx) > decStepBnd || 1052 Math.abs(ddpy) > decStepBnd) { 1053 ddpx = (ddpx<<1) - dddpx; 1054 ddpy = (ddpy<<1) - dddpy; 1055 dpx = (dpx<<2) - (ddpx>>1); 1056 dpy = (dpy<<2) - (ddpy>>1); 1057 count <<=1; 1058 decStepBnd <<=3; 1059 incStepBnd <<=3; 1060 px <<=3; 1061 py <<=3; 1062 shift += 3; 1063 } 1064 1065 /* Perform increasing step in 2 times if necessary. 1066 * Note: we could do it only in even steps 1067 */ 1068 1069 while ((count & 1) == 0 && shift > DF_CUB_SHIFT && 1070 Math.abs(dpx) <= incStepBnd && 1071 Math.abs(dpy) <= incStepBnd) { 1072 dpx = (dpx>>2) + (ddpx>>3); 1073 dpy = (dpy>>2) + (ddpy>>3); 1074 ddpx = (ddpx + dddpx)>>1; 1075 ddpy = (ddpy + dddpy)>>1; 1076 count >>=1; 1077 decStepBnd >>=3; 1078 incStepBnd >>=3; 1079 px >>=3; 1080 py >>=3; 1081 shift -= 3; 1082 } 1083 1084 count--; 1085 1086 /* Performing one step less than necessary and use actual (xe,ye) 1087 * curve's endpoint instead of calculated. This prevent us from 1088 * running above the curve endpoint due to the accumulated errors 1089 * during the iterations. 1090 */ 1091 if (count > 0) { 1092 px += dpx; 1093 py += dpy; 1094 1095 dpx += ddpx; 1096 dpy += ddpy; 1097 ddpx += dddpx; 1098 ddpy += dddpy; 1099 1100 x1 = x2; 1101 y1 = y2; 1102 1103 x2 = x0w + (px >> shift); 1104 y2 = y0w + (py >> shift); 1105 1106 /* Checking that we are not running out of the endpoint and 1107 * bounding violating coordinate. The check is pretty simple 1108 * because the curve passed to the DrawCubic already split 1109 * into the monotonic in X and Y pieces 1110 */ 1111 1112 /* Bounding x2 by xe */ 1113 if (((xe-x2)^dx) < 0) { 1114 x2 = xe; 1115 } 1116 1117 /* Bounding y2 by ye */ 1118 if (((ye-y2)^dy) < 0) { 1119 y2 = ye; 1120 } 1121 1122 hnd.processFixedLine(x1, y1, x2, y2, pixelInfo, checkBounds, 1123 false); 1124 } else { 1125 hnd.processFixedLine(x2, y2, xe, ye, pixelInfo, checkBounds, 1126 false); 1127 } 1128 } 1129 } 1130 1131 /* 1132 * Checking size of the cubic curves and split them if necessary. 1133 * Calling DrawMonotonicCubic for the curves of the appropriate size. 1134 * Note: coords array could be changed 1135 */ 1136 private static void ProcessMonotonicCubic(ProcessHandler hnd, 1137 float[] coords, 1138 int[] pixelInfo) { 1139 1140 float[] coords1 = new float[8]; 1141 float tx, ty; 1142 float xMin, xMax; 1143 float yMin, yMax; 1144 1145 xMin = xMax = coords[0]; 1146 yMin = yMax = coords[1]; 1147 1148 for (int i = 2; i < 8; i += 2) { 1149 xMin = (xMin > coords[i])? coords[i] : xMin; 1150 xMax = (xMax < coords[i])? coords[i] : xMax; 1151 yMin = (yMin > coords[i + 1])? coords[i + 1] : yMin; 1152 yMax = (yMax < coords[i + 1])? coords[i + 1] : yMax; 1153 } 1154 1155 if (hnd.clipMode == PH_MODE_DRAW_CLIP) { 1156 /* In case of drawing we could just skip curves which are 1157 * completely out of bounds 1158 */ 1159 if (hnd.dhnd.xMaxf < xMin || hnd.dhnd.xMinf > xMax || 1160 hnd.dhnd.yMaxf < yMin || hnd.dhnd.yMinf > yMax) { 1161 return; 1162 } 1163 } else { 1164 1165 /* In case of filling we could skip curves which are above, 1166 * below and behind the right boundary of the visible area 1167 */ 1168 1169 if (hnd.dhnd.yMaxf < yMin || hnd.dhnd.yMinf > yMax || 1170 hnd.dhnd.xMaxf < xMin) 1171 { 1172 return; 1173 } 1174 1175 /* We could clamp x coordinates to the corresponding boundary 1176 * if the curve is completely behind the left one 1177 */ 1178 1179 if (hnd.dhnd.xMinf > xMax) { 1180 coords[0] = coords[2] = coords[4] = coords[6] = 1181 hnd.dhnd.xMinf; 1182 } 1183 } 1184 1185 if (xMax - xMin > MAX_CUB_SIZE || yMax - yMin > MAX_CUB_SIZE) { 1186 coords1[6] = coords[6]; 1187 coords1[7] = coords[7]; 1188 coords1[4] = (coords[4] + coords[6])/2.0f; 1189 coords1[5] = (coords[5] + coords[7])/2.0f; 1190 tx = (coords[2] + coords[4])/2.0f; 1191 ty = (coords[3] + coords[5])/2.0f; 1192 coords1[2] = (tx + coords1[4])/2.0f; 1193 coords1[3] = (ty + coords1[5])/2.0f; 1194 coords[2] = (coords[0] + coords[2])/2.0f; 1195 coords[3] = (coords[1] + coords[3])/2.0f; 1196 coords[4] = (coords[2] + tx)/2.0f; 1197 coords[5] = (coords[3] + ty)/2.0f; 1198 coords[6]=coords1[0]=(coords[4] + coords1[2])/2.0f; 1199 coords[7]=coords1[1]=(coords[5] + coords1[3])/2.0f; 1200 1201 ProcessMonotonicCubic(hnd, coords, pixelInfo); 1202 1203 ProcessMonotonicCubic(hnd, coords1, pixelInfo); 1204 } else { 1205 DrawMonotonicCubic(hnd, coords, 1206 /* Set checkBounds parameter if curve intersects 1207 * boundary of the visible area. We know that 1208 * the curve is visible, so the check is pretty 1209 * simple 1210 */ 1211 hnd.dhnd.xMinf > xMin || 1212 hnd.dhnd.xMaxf < xMax || 1213 hnd.dhnd.yMinf > yMin || 1214 hnd.dhnd.yMaxf < yMax, 1215 pixelInfo); 1216 } 1217 } 1218 1219 /* 1220 * Split cubic curve into monotonic in X and Y parts. Calling 1221 * ProcessMonotonicCubic for each monotonic piece of the curve. 1222 * 1223 * Note: coords array could be changed 1224 */ 1225 private static void ProcessCubic(ProcessHandler hnd, 1226 float[] coords, 1227 int[] pixelInfo) { 1228 /* Temporary array for holding parameters corresponding to the extreme 1229 * in X and Y points 1230 */ 1231 double params[] = new double[4]; 1232 double eqn[] = new double[3]; 1233 double res[] = new double[2]; 1234 int cnt = 0; 1235 1236 /* Simple check for monotonicity in X before searching for the extreme 1237 * points of the X(t) function. We first check if the curve is 1238 * monotonic in X by seeing if all of the X coordinates are strongly 1239 * ordered. 1240 */ 1241 if ((coords[0] > coords[2] || coords[2] > coords[4] || 1242 coords[4] > coords[6]) && 1243 (coords[0] < coords[2] || coords[2] < coords[4] || 1244 coords[4] < coords[6])) 1245 { 1246 /* Searching for extreme points of the X(t) function by solving 1247 * dX(t) 1248 * ---- = 0 equation 1249 * dt 1250 */ 1251 eqn[2] = -coords[0] + 3*coords[2] - 3*coords[4] + coords[6]; 1252 eqn[1] = 2*(coords[0] - 2*coords[2] + coords[4]); 1253 eqn[0] = -coords[0] + coords[2]; 1254 1255 int nr = QuadCurve2D.solveQuadratic(eqn, res); 1256 1257 /* Following code also correctly works in degenerate case of 1258 * the quadratic equation (nr = -1) because we do not need 1259 * splitting in this case. 1260 */ 1261 for (int i = 0; i < nr; i++) { 1262 if (res[i] > 0 && res[i] < 1) { 1263 params[cnt++] = res[i]; 1264 } 1265 } 1266 } 1267 1268 /* Simple check for monotonicity in Y before searching for the extreme 1269 * points of the Y(t) function. We first check if the curve is 1270 * monotonic in Y by seeing if all of the Y coordinates are strongly 1271 * ordered. 1272 */ 1273 if ((coords[1] > coords[3] || coords[3] > coords[5] || 1274 coords[5] > coords[7]) && 1275 (coords[1] < coords[3] || coords[3] < coords[5] || 1276 coords[5] < coords[7])) 1277 { 1278 /* Searching for extreme points of the Y(t) function by solving 1279 * dY(t) 1280 * ----- = 0 equation 1281 * dt 1282 */ 1283 eqn[2] = -coords[1] + 3*coords[3] - 3*coords[5] + coords[7]; 1284 eqn[1] = 2*(coords[1] - 2*coords[3] + coords[5]); 1285 eqn[0] = -coords[1] + coords[3]; 1286 1287 int nr = QuadCurve2D.solveQuadratic(eqn, res); 1288 1289 /* Following code also correctly works in degenerate case of 1290 * the quadratic equation (nr = -1) because we do not need 1291 * splitting in this case. 1292 */ 1293 for (int i = 0; i < nr; i++) { 1294 if (res[i] > 0 && res[i] < 1) { 1295 params[cnt++] = res[i]; 1296 } 1297 } 1298 } 1299 1300 if (cnt > 0) { 1301 /* Sorting parameter values corresponding to the extreme points 1302 * of the curve 1303 */ 1304 Arrays.sort(params, 0, cnt); 1305 1306 /* Processing obtained monotonic parts */ 1307 ProcessFirstMonotonicPartOfCubic(hnd, coords, pixelInfo, 1308 (float)params[0]); 1309 for (int i = 1; i < cnt; i++) { 1310 double param = params[i] - params[i-1]; 1311 if (param > 0) { 1312 ProcessFirstMonotonicPartOfCubic(hnd, coords, pixelInfo, 1313 /* Scale parameter to match with rest of the curve */ 1314 (float)(param/(1.0 - params[i - 1]))); 1315 } 1316 } 1317 } 1318 1319 ProcessMonotonicCubic(hnd,coords,pixelInfo); 1320 } 1321 1322 /* 1323 * Bite the piece of the cubic curve from start point till the point 1324 * corresponding to the specified parameter then call ProcessCubic for the 1325 * bitten part. 1326 * Note: coords array will be changed 1327 */ 1328 private static void ProcessFirstMonotonicPartOfCubic(ProcessHandler hnd, 1329 float[] coords, 1330 int[] pixelInfo, 1331 float t) 1332 { 1333 float[] coords1 = new float[8]; 1334 float tx, ty; 1335 1336 coords1[0] = coords[0]; 1337 coords1[1] = coords[1]; 1338 tx = coords[2] + t*(coords[4] - coords[2]); 1339 ty = coords[3] + t*(coords[5] - coords[3]); 1340 coords1[2] = coords[0] + t*(coords[2] - coords[0]); 1341 coords1[3] = coords[1] + t*(coords[3] - coords[1]); 1342 coords1[4] = coords1[2] + t*(tx - coords1[2]); 1343 coords1[5] = coords1[3] + t*(ty - coords1[3]); 1344 coords[4] = coords[4] + t*(coords[6] - coords[4]); 1345 coords[5] = coords[5] + t*(coords[7] - coords[5]); 1346 coords[2] = tx + t*(coords[4] - tx); 1347 coords[3] = ty + t*(coords[5] - ty); 1348 coords[0]=coords1[6]=coords1[4] + t*(coords[2] - coords1[4]); 1349 coords[1]=coords1[7]=coords1[5] + t*(coords[3] - coords1[5]); 1350 1351 ProcessMonotonicCubic(hnd, coords1, pixelInfo); 1352 } 1353 1354 /* Note: 1355 * For more easy reading of the code below each java version of the macros 1356 * from the ProcessPath.c preceded by the commented origin call 1357 * containing verbose names of the parameters 1358 */ 1359 private static void ProcessLine(ProcessHandler hnd, float x1, float y1, 1360 float x2, float y2, int[] pixelInfo) { 1361 float xMin, yMin, xMax, yMax; 1362 int X1, Y1, X2, Y2, X3, Y3, res; 1363 boolean clipped = false; 1364 float x3,y3; 1365 float c[] = new float[]{x1, y1, x2, y2, 0, 0}; 1366 1367 boolean lastClipped; 1368 1369 xMin = hnd.dhnd.xMinf; 1370 yMin = hnd.dhnd.yMinf; 1371 xMax = hnd.dhnd.xMaxf; 1372 yMax = hnd.dhnd.yMaxf; 1373 1374 // 1375 // TESTANDCLIP(yMin, yMax, y1, x1, y2, x2, res); 1376 // 1377 res = TESTANDCLIP(yMin, yMax, c, 1, 0, 3, 2); 1378 if (res == CRES_INVISIBLE) return; 1379 clipped = IS_CLIPPED(res); 1380 // 1381 // TESTANDCLIP(yMin, yMax, y2, x2, y1, x1, res); 1382 // 1383 res = TESTANDCLIP(yMin, yMax, c, 3, 2, 1, 0); 1384 if (res == CRES_INVISIBLE) return; 1385 lastClipped = IS_CLIPPED(res); 1386 clipped = clipped || lastClipped; 1387 1388 if (hnd.clipMode == PH_MODE_DRAW_CLIP) { 1389 // 1390 // TESTANDCLIP(xMin, xMax, x1, y1, x2, y2, res); 1391 // 1392 res = TESTANDCLIP(xMin, xMax, c, 0, 1, 2, 3); 1393 if (res == CRES_INVISIBLE) return; 1394 clipped = clipped || IS_CLIPPED(res); 1395 // 1396 // TESTANDCLIP(xMin, xMax, x2, y2, x1, y1, res); 1397 // 1398 res = TESTANDCLIP(xMin, xMax, c, 2, 3, 0, 1); 1399 if (res == CRES_INVISIBLE) return; 1400 lastClipped = lastClipped || IS_CLIPPED(res); 1401 clipped = clipped || lastClipped; 1402 X1 = (int)(c[0]*MDP_MULT); 1403 Y1 = (int)(c[1]*MDP_MULT); 1404 X2 = (int)(c[2]*MDP_MULT); 1405 Y2 = (int)(c[3]*MDP_MULT); 1406 1407 hnd.processFixedLine(X1, Y1, X2, Y2, pixelInfo, 1408 clipped, /* enable boundary checking in 1409 case of clipping to avoid 1410 entering out of bounds which 1411 could happens during rounding 1412 */ 1413 lastClipped /* Notify pProcessFixedLine 1414 that 1415 this is the end of the 1416 subpath (because of exiting 1417 out of boundaries) 1418 */ 1419 ); 1420 } else { 1421 /* Clamping starting from first vertex of the the processed 1422 * segment 1423 * 1424 * CLIPCLAMP(xMin, xMax, x1, y1, x2, y2, x3, y3, res); 1425 */ 1426 res = CLIPCLAMP(xMin, xMax, c, 0, 1, 2, 3, 4, 5); 1427 X1 = (int)(c[0]*MDP_MULT); 1428 Y1 = (int)(c[1]*MDP_MULT); 1429 1430 /* Clamping only by left boundary */ 1431 if (res == CRES_MIN_CLIPPED) { 1432 X3 = (int)(c[4]*MDP_MULT); 1433 Y3 = (int)(c[5]*MDP_MULT); 1434 hnd.processFixedLine(X3, Y3, X1, Y1, pixelInfo, 1435 false, lastClipped); 1436 1437 } else if (res == CRES_INVISIBLE) { 1438 return; 1439 } 1440 1441 /* Clamping starting from last vertex of the the processed 1442 * segment 1443 * 1444 * CLIPCLAMP(xMin, xMax, x2, y2, x1, y1, x3, y3, res); 1445 */ 1446 res = CLIPCLAMP(xMin, xMax, c, 2, 3, 0, 1, 4, 5); 1447 1448 /* Checking if there was a clip by right boundary */ 1449 lastClipped = lastClipped || (res == CRES_MAX_CLIPPED); 1450 1451 X2 = (int)(c[2]*MDP_MULT); 1452 Y2 = (int)(c[3]*MDP_MULT); 1453 hnd.processFixedLine(X1, Y1, X2, Y2, pixelInfo, 1454 false, lastClipped); 1455 1456 /* Clamping only by left boundary */ 1457 if (res == CRES_MIN_CLIPPED) { 1458 X3 = (int)(c[4]*MDP_MULT); 1459 Y3 = (int)(c[5]*MDP_MULT); 1460 hnd.processFixedLine(X2, Y2, X3, Y3, pixelInfo, 1461 false, lastClipped); 1462 } 1463 } 1464 } 1465 1466 private static boolean doProcessPath(ProcessHandler hnd, 1467 Path2D.Float p2df, 1468 float transXf, float transYf) { 1469 float coords[] = new float[8]; 1470 float tCoords[] = new float[8]; 1471 float closeCoord[] = new float[] {0.0f, 0.0f}; 1472 float firstCoord[] = new float[2]; 1473 int pixelInfo[] = new int[5]; 1474 boolean subpathStarted = false; 1475 boolean skip = false; 1476 float lastX, lastY; 1477 pixelInfo[0] = 0; 1478 1479 /* Adjusting boundaries to the capabilities of the 1480 * ProcessPath code 1481 */ 1482 hnd.dhnd.adjustBounds(LOWER_OUT_BND, LOWER_OUT_BND, 1483 UPPER_OUT_BND, UPPER_OUT_BND); 1484 1485 /* Adding support of the KEY_STROKE_CONTROL rendering hint. 1486 * Now we are supporting two modes: "pixels at centers" and 1487 * "pixels at corners". 1488 * First one is disabled by default but could be enabled by setting 1489 * VALUE_STROKE_PURE to the rendering hint. It means that pixel at the 1490 * screen (x,y) has (x + 0.5, y + 0.5) float coordinates. 1491 * 1492 * Second one is enabled by default and means straightforward mapping 1493 * (x,y) --> (x,y) 1494 */ 1495 if (hnd.dhnd.strokeControl == SunHints.INTVAL_STROKE_PURE) { 1496 closeCoord[0] = -0.5f; 1497 closeCoord[1] = -0.5f; 1498 transXf -= 0.5; 1499 transYf -= 0.5; 1500 } 1501 1502 PathIterator pi = p2df.getPathIterator(null); 1503 1504 while (!pi.isDone()) { 1505 switch (pi.currentSegment(coords)) { 1506 case PathIterator.SEG_MOVETO: 1507 /* Performing closing of the unclosed segments */ 1508 if (subpathStarted && !skip) { 1509 if (hnd.clipMode == PH_MODE_FILL_CLIP) { 1510 if (tCoords[0] != closeCoord[0] || 1511 tCoords[1] != closeCoord[1]) 1512 { 1513 ProcessLine(hnd, tCoords[0], tCoords[1], 1514 closeCoord[0], closeCoord[1], 1515 pixelInfo); 1516 } 1517 } 1518 hnd.processEndSubPath(); 1519 } 1520 1521 tCoords[0] = coords[0] + transXf; 1522 tCoords[1] = coords[1] + transYf; 1523 1524 /* Checking SEG_MOVETO coordinates if they are out of the 1525 * [LOWER_BND, UPPER_BND] range. This check also handles 1526 * NaN and Infinity values. Skipping next path segment in 1527 * case of invalid data. 1528 */ 1529 1530 if (tCoords[0] < UPPER_BND && 1531 tCoords[0] > LOWER_BND && 1532 tCoords[1] < UPPER_BND && 1533 tCoords[1] > LOWER_BND) 1534 { 1535 subpathStarted = true; 1536 skip = false; 1537 closeCoord[0] = tCoords[0]; 1538 closeCoord[1] = tCoords[1]; 1539 } else { 1540 skip = true; 1541 } 1542 pixelInfo[0] = 0; 1543 break; 1544 case PathIterator.SEG_LINETO: 1545 lastX = tCoords[2] = coords[0] + transXf; 1546 lastY = tCoords[3] = coords[1] + transYf; 1547 1548 /* Checking SEG_LINETO coordinates if they are out of the 1549 * [LOWER_BND, UPPER_BND] range. This check also handles 1550 * NaN and Infinity values. Ignoring current path segment 1551 * in case of invalid data. If segment is skipped its 1552 * endpoint (if valid) is used to begin new subpath. 1553 */ 1554 1555 if (lastX < UPPER_BND && 1556 lastX > LOWER_BND && 1557 lastY < UPPER_BND && 1558 lastY > LOWER_BND) 1559 { 1560 if (skip) { 1561 tCoords[0] = closeCoord[0] = lastX; 1562 tCoords[1] = closeCoord[1] = lastY; 1563 subpathStarted = true; 1564 skip = false; 1565 } else { 1566 ProcessLine(hnd, tCoords[0], tCoords[1], 1567 tCoords[2], tCoords[3], pixelInfo); 1568 tCoords[0] = lastX; 1569 tCoords[1] = lastY; 1570 } 1571 } 1572 break; 1573 case PathIterator.SEG_QUADTO: 1574 tCoords[2] = coords[0] + transXf; 1575 tCoords[3] = coords[1] + transYf; 1576 lastX = tCoords[4] = coords[2] + transXf; 1577 lastY = tCoords[5] = coords[3] + transYf; 1578 1579 /* Checking SEG_QUADTO coordinates if they are out of the 1580 * [LOWER_BND, UPPER_BND] range. This check also handles 1581 * NaN and Infinity values. Ignoring current path segment 1582 * in case of invalid endpoints's data. Equivalent to 1583 * the SEG_LINETO if endpoint coordinates are valid but 1584 * there are invalid data among other coordinates 1585 */ 1586 1587 if (lastX < UPPER_BND && 1588 lastX > LOWER_BND && 1589 lastY < UPPER_BND && 1590 lastY > LOWER_BND) 1591 { 1592 if (skip) { 1593 tCoords[0] = closeCoord[0] = lastX; 1594 tCoords[1] = closeCoord[1] = lastY; 1595 subpathStarted = true; 1596 skip = false; 1597 } else { 1598 if (tCoords[2] < UPPER_BND && 1599 tCoords[2] > LOWER_BND && 1600 tCoords[3] < UPPER_BND && 1601 tCoords[3] > LOWER_BND) 1602 { 1603 ProcessQuad(hnd, tCoords, pixelInfo); 1604 } else { 1605 ProcessLine(hnd, tCoords[0], tCoords[1], 1606 tCoords[4], tCoords[5], 1607 pixelInfo); 1608 } 1609 tCoords[0] = lastX; 1610 tCoords[1] = lastY; 1611 } 1612 } 1613 break; 1614 case PathIterator.SEG_CUBICTO: 1615 tCoords[2] = coords[0] + transXf; 1616 tCoords[3] = coords[1] + transYf; 1617 tCoords[4] = coords[2] + transXf; 1618 tCoords[5] = coords[3] + transYf; 1619 lastX = tCoords[6] = coords[4] + transXf; 1620 lastY = tCoords[7] = coords[5] + transYf; 1621 1622 /* Checking SEG_CUBICTO coordinates if they are out of the 1623 * [LOWER_BND, UPPER_BND] range. This check also handles 1624 * NaN and Infinity values. Ignoring current path segment 1625 * in case of invalid endpoints's data. Equivalent to 1626 * the SEG_LINETO if endpoint coordinates are valid but 1627 * there are invalid data among other coordinates 1628 */ 1629 1630 if (lastX < UPPER_BND && 1631 lastX > LOWER_BND && 1632 lastY < UPPER_BND && 1633 lastY > LOWER_BND) 1634 { 1635 if (skip) { 1636 tCoords[0] = closeCoord[0] = tCoords[6]; 1637 tCoords[1] = closeCoord[1] = tCoords[7]; 1638 subpathStarted = true; 1639 skip = false; 1640 } else { 1641 if (tCoords[2] < UPPER_BND && 1642 tCoords[2] > LOWER_BND && 1643 tCoords[3] < UPPER_BND && 1644 tCoords[3] > LOWER_BND && 1645 tCoords[4] < UPPER_BND && 1646 tCoords[4] > LOWER_BND && 1647 tCoords[5] < UPPER_BND && 1648 tCoords[5] > LOWER_BND) 1649 { 1650 ProcessCubic(hnd, tCoords, pixelInfo); 1651 } else { 1652 ProcessLine(hnd, tCoords[0], tCoords[1], 1653 tCoords[6], tCoords[7], 1654 pixelInfo); 1655 } 1656 tCoords[0] = lastX; 1657 tCoords[1] = lastY; 1658 } 1659 } 1660 break; 1661 case PathIterator.SEG_CLOSE: 1662 if (subpathStarted && !skip) { 1663 skip = false; 1664 if (tCoords[0] != closeCoord[0] || 1665 tCoords[1] != closeCoord[1]) 1666 { 1667 ProcessLine(hnd, tCoords[0], tCoords[1], 1668 closeCoord[0], closeCoord[1], 1669 pixelInfo); 1670 1671 /* Storing last path's point for using in following 1672 * segments without initial moveTo 1673 */ 1674 tCoords[0] = closeCoord[0]; 1675 tCoords[1] = closeCoord[1]; 1676 } 1677 hnd.processEndSubPath(); 1678 } 1679 break; 1680 } 1681 pi.next(); 1682 } 1683 1684 /* Performing closing of the unclosed segments */ 1685 if (subpathStarted & !skip) { 1686 if (hnd.clipMode == PH_MODE_FILL_CLIP) { 1687 if (tCoords[0] != closeCoord[0] || 1688 tCoords[1] != closeCoord[1]) 1689 { 1690 ProcessLine(hnd, tCoords[0], tCoords[1], 1691 closeCoord[0], closeCoord[1], 1692 pixelInfo); 1693 } 1694 } 1695 hnd.processEndSubPath(); 1696 } 1697 return true; 1698 } 1699 1700 private static class Point { 1701 public int x; 1702 public int y; 1703 public boolean lastPoint; 1704 public Point prev; 1705 public Point next; 1706 public Point nextByY; 1707 public Edge edge; 1708 public Point(int x, int y, boolean lastPoint) { 1709 this.x = x; 1710 this.y = y; 1711 this.lastPoint = lastPoint; 1712 } 1713 }; 1714 1715 private static class Edge { 1716 int x; 1717 int dx; 1718 Point p; 1719 int dir; 1720 Edge prev; 1721 Edge next; 1722 1723 public Edge(Point p, int x, int dx, int dir) { 1724 this.p = p; 1725 this.x = x; 1726 this.dx = dx; 1727 this.dir = dir; 1728 } 1729 }; 1730 1731 /* Size of the default buffer in the FillData structure. This buffer is 1732 * replaced with heap allocated in case of large paths. 1733 */ 1734 private static final int DF_MAX_POINT = 256; 1735 1736 /* Following class accumulates points of the non-continuous flattened 1737 * general path during iteration through the origin path's segments . The 1738 * end of the each subpath is marked as lastPoint flag set at the last 1739 * point 1740 */ 1741 private static class FillData { 1742 List<Point> plgPnts; 1743 public int plgYMin; 1744 public int plgYMax; 1745 1746 public FillData() { 1747 plgPnts = new Vector<Point>(DF_MAX_POINT); 1748 } 1749 1750 public void addPoint(int x, int y, boolean lastPoint) { 1751 if (plgPnts.size() == 0) { 1752 plgYMin = plgYMax = y; 1753 } else { 1754 plgYMin = (plgYMin > y)?y:plgYMin; 1755 plgYMax = (plgYMax < y)?y:plgYMax; 1756 } 1757 1758 plgPnts.add(new Point(x, y, lastPoint)); 1759 } 1760 1761 public boolean isEmpty() { 1762 return plgPnts.size() == 0; 1763 } 1764 1765 public boolean isEnded() { 1766 return plgPnts.get(plgPnts.size() - 1).lastPoint; 1767 } 1768 1769 public boolean setEnded() { 1770 return plgPnts.get(plgPnts.size() - 1).lastPoint = true; 1771 } 1772 } 1773 1774 private static class ActiveEdgeList { 1775 Edge head; 1776 1777 public boolean isEmpty() { 1778 return (head == null); 1779 } 1780 1781 public void insert(Point pnt, int cy) { 1782 Point np = pnt.next; 1783 int X1 = pnt.x, Y1 = pnt.y; 1784 int X2 = np.x, Y2 = np.y; 1785 Edge ne; 1786 if (Y1 == Y2) { 1787 /* Skipping horizontal segments */ 1788 return; 1789 } else { 1790 int dX = X2 - X1; 1791 int dY = Y2 - Y1; 1792 int stepx, x0, dy, dir; 1793 1794 if (Y1 < Y2) { 1795 x0 = X1; 1796 dy = cy - Y1; 1797 dir = -1; 1798 } else { // (Y1 > Y2) 1799 x0 = X2; 1800 dy = cy - Y2; 1801 dir = 1; 1802 } 1803 1804 /* We need to worry only about dX because dY is in denominator 1805 * and abs(dy) < MDP_MULT (cy is a first scanline of the scan 1806 * converted segment and we subtract y coordinate of the 1807 * nearest segment's end from it to obtain dy) 1808 */ 1809 if (dX > CALC_UBND || dX < CALC_LBND) { 1810 stepx = (int)((((double)dX)*MDP_MULT)/dY); 1811 x0 = x0 + (int)((((double)dX)*dy)/dY); 1812 } else { 1813 stepx = (dX<<MDP_PREC)/dY; 1814 x0 += (dX*dy)/dY; 1815 } 1816 1817 ne = new Edge(pnt, x0, stepx, dir); 1818 } 1819 1820 ne.next = head; 1821 ne.prev = null; 1822 if (head != null) { 1823 head.prev = ne; 1824 } 1825 head = pnt.edge = ne; 1826 } 1827 1828 public void delete(Edge e) { 1829 Edge prevp = e.prev; 1830 Edge nextp = e.next; 1831 if (prevp != null) { 1832 prevp.next = nextp; 1833 } else { 1834 head = nextp; 1835 } 1836 if (nextp != null) { 1837 nextp.prev = prevp; 1838 } 1839 } 1840 1841 /** 1842 * Bubble sorting in the ascending order of the linked list. This 1843 * implementation stops processing the list if there were no changes 1844 * during the previous pass. 1845 * 1846 * We could not use O(N) Radix sort here because in most cases list of 1847 * edges almost sorted. So, bubble sort (O(N^2)) is working much 1848 * better. Note, in case of array of edges Shell sort is more 1849 * efficient. 1850 */ 1851 public void sort() { 1852 Edge p, q, r, s = null, temp; 1853 boolean wasSwap = true; 1854 1855 // r precedes p and s points to the node up to which 1856 // comparisons are to be made 1857 while (s != head.next && wasSwap) { 1858 r = p = head; 1859 q = p.next; 1860 wasSwap = false; 1861 while (p != s) { 1862 if (p.x >= q.x) { 1863 wasSwap = true; 1864 if (p == head) { 1865 temp = q.next; 1866 q.next = p; 1867 p.next = temp; 1868 head = q; 1869 r = q; 1870 } else { 1871 temp = q.next; 1872 q.next = p; 1873 p.next = temp; 1874 r.next = q; 1875 r = q; 1876 } 1877 } else { 1878 r = p; 1879 p = p.next; 1880 } 1881 q = p.next; 1882 if (q == s) s = p; 1883 } 1884 } 1885 1886 // correction of the back links in the double linked edge list 1887 p = head; 1888 q = null; 1889 while (p != null) { 1890 p.prev = q; 1891 q = p; 1892 p = p.next; 1893 } 1894 } 1895 } 1896 1897 private static void FillPolygon(FillProcessHandler hnd, 1898 int fillRule) { 1899 int k, y, n; 1900 boolean drawing; 1901 Edge active; 1902 int rightBnd = hnd.dhnd.xMax - 1; 1903 FillData fd = hnd.fd; 1904 int yMin = fd.plgYMin; 1905 int yMax = fd.plgYMax; 1906 int hashSize = ((yMax - yMin)>>MDP_PREC) + 4; 1907 1908 /* Because of support of the KEY_STROKE_CONTROL hint we are performing 1909 * shift of the coordinates at the higher level 1910 */ 1911 int hashOffset = ((yMin - 1) & MDP_W_MASK); 1912 1913 /* Winding counter */ 1914 int counter; 1915 1916 /* Calculating mask to be applied to the winding counter */ 1917 int counterMask = 1918 (fillRule == PathIterator.WIND_NON_ZERO)? -1:1; 1919 1920 int pntOffset; 1921 List<Point> pnts = fd.plgPnts; 1922 n = pnts.size(); 1923 1924 if (n <=1) return; 1925 1926 Point[] yHash = new Point[hashSize]; 1927 1928 /* Creating double linked list (prev, next links) describing path order 1929 * and hash table with points which fall between scanlines. nextByY 1930 * link is used for the points which are between same scanlines. 1931 * Scanlines are passed through the centers of the pixels. 1932 */ 1933 Point curpt = pnts.get(0); 1934 curpt.prev = null; 1935 for (int i = 0; i < n - 1; i++) { 1936 curpt = pnts.get(i); 1937 Point nextpt = pnts.get(i + 1); 1938 int curHashInd = (curpt.y - hashOffset - 1) >> MDP_PREC; 1939 curpt.nextByY = yHash[curHashInd]; 1940 yHash[curHashInd] = curpt; 1941 curpt.next = nextpt; 1942 nextpt.prev = curpt; 1943 } 1944 1945 Point ept = pnts.get(n - 1); 1946 int curHashInd = (ept.y - hashOffset - 1) >> MDP_PREC; 1947 ept.nextByY = yHash[curHashInd]; 1948 yHash[curHashInd] = ept; 1949 1950 ActiveEdgeList activeList = new ActiveEdgeList(); 1951 1952 for (y=hashOffset + MDP_MULT,k = 0; 1953 y<=yMax && k < hashSize; y += MDP_MULT, k++) 1954 { 1955 for(Point pt = yHash[k];pt != null; pt=pt.nextByY) { 1956 /* pt.y should be inside hashed interval 1957 * assert(y-MDP_MULT <= pt.y && pt.y < y); 1958 */ 1959 if (pt.prev != null && !pt.prev.lastPoint) { 1960 if (pt.prev.edge != null && pt.prev.y <= y) { 1961 activeList.delete(pt.prev.edge); 1962 pt.prev.edge = null; 1963 } else if (pt.prev.y > y) { 1964 activeList.insert(pt.prev, y); 1965 } 1966 } 1967 1968 if (!pt.lastPoint && pt.next != null) { 1969 if (pt.edge != null && pt.next.y <= y) { 1970 activeList.delete(pt.edge); 1971 pt.edge = null; 1972 } else if (pt.next.y > y) { 1973 activeList.insert(pt, y); 1974 } 1975 } 1976 } 1977 1978 if (activeList.isEmpty()) continue; 1979 1980 activeList.sort(); 1981 1982 counter = 0; 1983 drawing = false; 1984 int xl, xr; 1985 xl = xr = hnd.dhnd.xMin; 1986 Edge curEdge = activeList.head; 1987 while (curEdge != null) { 1988 counter += curEdge.dir; 1989 if ((counter & counterMask) != 0 && !drawing) { 1990 xl = (curEdge.x + MDP_MULT - 1)>>MDP_PREC; 1991 drawing = true; 1992 } 1993 1994 if ((counter & counterMask) == 0 && drawing) { 1995 xr = (curEdge.x - 1) >> MDP_PREC; 1996 if (xl <= xr) { 1997 hnd.dhnd.drawScanline(xl, xr, y >> MDP_PREC); 1998 } 1999 drawing = false; 2000 } 2001 2002 curEdge.x += curEdge.dx; 2003 curEdge = curEdge.next; 2004 } 2005 2006 /* Performing drawing till the right boundary (for correct 2007 * rendering shapes clipped at the right side) 2008 */ 2009 if (drawing && xl <= rightBnd) { 2010 2011 /* Support of the strokeHint was added into the 2012 * draw and fill methods of the sun.java2d.pipe.LoopPipe 2013 */ 2014 hnd.dhnd.drawScanline(xl, rightBnd, y >> MDP_PREC); 2015 } 2016 } 2017 } 2018 2019 private static class FillProcessHandler extends ProcessHandler { 2020 2021 FillData fd; 2022 2023 /* Note: For more easy reading of the code below each java version of 2024 * the macros from the ProcessPath.c preceded by the commented 2025 * origin call containing verbose names of the parameters 2026 */ 2027 public void processFixedLine(int x1, int y1, int x2, int y2, 2028 int[] pixelInfo, boolean checkBounds, 2029 boolean endSubPath) 2030 { 2031 int outXMin, outXMax, outYMin, outYMax; 2032 int res; 2033 2034 /* There is no need to round line coordinates to the forward 2035 * differencing precision anymore. Such a rounding was used for 2036 * preventing the curve go out the endpoint (this sometimes does 2037 * not help). The problem was fixed in the forward differencing 2038 * loops. 2039 */ 2040 if (checkBounds) { 2041 boolean lastClipped; 2042 2043 /* This function is used only for filling shapes, so there is no 2044 * check for the type of clipping 2045 */ 2046 int c[] = new int[]{x1, y1, x2, y2, 0, 0}; 2047 outXMin = (int)(dhnd.xMinf * MDP_MULT); 2048 outXMax = (int)(dhnd.xMaxf * MDP_MULT); 2049 outYMin = (int)(dhnd.yMinf * MDP_MULT); 2050 outYMax = (int)(dhnd.yMaxf * MDP_MULT); 2051 2052 /* 2053 * TESTANDCLIP(outYMin, outYMax, y1, x1, y2, x2, res); 2054 */ 2055 res = TESTANDCLIP(outYMin, outYMax, c, 1, 0, 3, 2); 2056 if (res == CRES_INVISIBLE) return; 2057 2058 /* 2059 * TESTANDCLIP(outYMin, outYMax, y2, x2, y1, x1, res); 2060 */ 2061 res = TESTANDCLIP(outYMin, outYMax, c, 3, 2, 1, 0); 2062 if (res == CRES_INVISIBLE) return; 2063 lastClipped = IS_CLIPPED(res); 2064 2065 /* Clamping starting from first vertex of the the processed 2066 * segment 2067 * 2068 * CLIPCLAMP(outXMin, outXMax, x1, y1, x2, y2, x3, y3, res); 2069 */ 2070 res = CLIPCLAMP(outXMin, outXMax, c, 0, 1, 2, 3, 4, 5); 2071 2072 /* Clamping only by left boundary */ 2073 if (res == CRES_MIN_CLIPPED) { 2074 processFixedLine(c[4], c[5], c[0], c[1], pixelInfo, 2075 false, lastClipped); 2076 2077 } else if (res == CRES_INVISIBLE) { 2078 return; 2079 } 2080 2081 /* Clamping starting from last vertex of the the processed 2082 * segment 2083 * 2084 * CLIPCLAMP(outXMin, outXMax, x2, y2, x1, y1, x3, y3, res); 2085 */ 2086 res = CLIPCLAMP(outXMin, outXMax, c, 2, 3, 0, 1, 4, 5); 2087 2088 /* Checking if there was a clip by right boundary */ 2089 lastClipped = lastClipped || (res == CRES_MAX_CLIPPED); 2090 2091 processFixedLine(c[0], c[1], c[2], c[3], pixelInfo, 2092 false, lastClipped); 2093 2094 /* Clamping only by left boundary */ 2095 if (res == CRES_MIN_CLIPPED) { 2096 processFixedLine(c[2], c[3], c[4], c[5], pixelInfo, 2097 false, lastClipped); 2098 } 2099 2100 return; 2101 } 2102 2103 /* Adding first point of the line only in case of empty or just 2104 * finished path 2105 */ 2106 if (fd.isEmpty() || fd.isEnded()) { 2107 fd.addPoint(x1, y1, false); 2108 } 2109 2110 fd.addPoint(x2, y2, false); 2111 2112 if (endSubPath) { 2113 fd.setEnded(); 2114 } 2115 } 2116 2117 FillProcessHandler(DrawHandler dhnd) { 2118 super(dhnd, PH_MODE_FILL_CLIP); 2119 this.fd = new FillData(); 2120 } 2121 2122 public void processEndSubPath() { 2123 if (!fd.isEmpty()) { 2124 fd.setEnded(); 2125 } 2126 } 2127 } 2128} 2129