< prev index next >

src/java.desktop/share/classes/sun/java2d/marlin/TransformingPathConsumer2D.java

Print this page




  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 
  26 package sun.java2d.marlin;
  27 
  28 import sun.awt.geom.PathConsumer2D;
  29 import java.awt.geom.AffineTransform;
  30 import java.awt.geom.Path2D;


  31 
  32 final class TransformingPathConsumer2D {
  33 
  34     TransformingPathConsumer2D() {
  35         // used by RendererContext
  36     }
  37 
  38     // recycled PathConsumer2D instance from wrapPath2d()
  39     private final Path2DWrapper        wp_Path2DWrapper        = new Path2DWrapper();
  40 
  41     PathConsumer2D wrapPath2d(Path2D.Float p2d)
  42     {
  43         return wp_Path2DWrapper.init(p2d);
  44     }

  45 
  46     // recycled PathConsumer2D instances from deltaTransformConsumer()
  47     private final DeltaScaleFilter     dt_DeltaScaleFilter     = new DeltaScaleFilter();
  48     private final DeltaTransformFilter dt_DeltaTransformFilter = new DeltaTransformFilter();
  49 













































  50     PathConsumer2D deltaTransformConsumer(PathConsumer2D out,
  51                                           AffineTransform at)
  52     {
  53         if (at == null) {
  54             return out;
  55         }
  56         float mxx = (float) at.getScaleX();
  57         float mxy = (float) at.getShearX();
  58         float myx = (float) at.getShearY();
  59         float myy = (float) at.getScaleY();
  60 
  61         if (mxy == 0.0f && myx == 0.0f) {
  62             if (mxx == 1.0f && myy == 1.0f) {
  63                 return out;
  64             } else {





  65                 return dt_DeltaScaleFilter.init(out, mxx, myy);
  66             }
  67         } else {




  68             return dt_DeltaTransformFilter.init(out, mxx, mxy, myx, myy);
  69         }
  70     }
  71 
  72     // recycled PathConsumer2D instances from inverseDeltaTransformConsumer()
  73     private final DeltaScaleFilter     iv_DeltaScaleFilter     = new DeltaScaleFilter();
  74     private final DeltaTransformFilter iv_DeltaTransformFilter = new DeltaTransformFilter();
































































  75 
  76     PathConsumer2D inverseDeltaTransformConsumer(PathConsumer2D out,
  77                                                  AffineTransform at)
  78     {
  79         if (at == null) {
  80             return out;
  81         }
  82         float mxx = (float) at.getScaleX();
  83         float mxy = (float) at.getShearX();
  84         float myx = (float) at.getShearY();
  85         float myy = (float) at.getScaleY();
  86 
  87         if (mxy == 0.0f && myx == 0.0f) {
  88             if (mxx == 1.0f && myy == 1.0f) {
  89                 return out;
  90             } else {
  91                 return iv_DeltaScaleFilter.init(out, 1.0f/mxx, 1.0f/myy);
  92             }
  93         } else {
  94             float det = mxx * myy - mxy * myx;
  95             return iv_DeltaTransformFilter.init(out,
  96                                                 myy / det,
  97                                                -mxy / det,
  98                                                -myx / det,
  99                                                 mxx / det);
 100         }
 101     }
 102 
 103 
 104     static final class DeltaScaleFilter implements PathConsumer2D {
 105         private PathConsumer2D out;
 106         private float sx, sy;
 107 
 108         DeltaScaleFilter() {}
 109 
 110         DeltaScaleFilter init(PathConsumer2D out,
 111                               float mxx, float myy)
 112         {
 113             this.out = out;
 114             sx = mxx;
 115             sy = myy;
 116             return this; // fluent API
 117         }
 118 
 119         @Override
 120         public void moveTo(float x0, float y0) {
 121             out.moveTo(x0 * sx, y0 * sy);
 122         }
 123 


 251 
 252         @Override
 253         public void closePath() {
 254             p2d.closePath();
 255         }
 256 
 257         @Override
 258         public void pathDone() {}
 259 
 260         @Override
 261         public void curveTo(float x1, float y1,
 262                             float x2, float y2,
 263                             float x3, float y3)
 264         {
 265             p2d.curveTo(x1, y1, x2, y2, x3, y3);
 266         }
 267 
 268         @Override
 269         public void quadTo(float x1, float y1, float x2, float y2) {
 270             p2d.quadTo(x1, y1, x2, y2);







































































































































































































































































































































































































































 271         }
 272 
 273         @Override
 274         public long getNativeConsumer() {
 275             throw new InternalError("Not using a native peer");
 276         }
 277     }
 278 }


  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 
  26 package sun.java2d.marlin;
  27 
  28 import sun.awt.geom.PathConsumer2D;
  29 import java.awt.geom.AffineTransform;
  30 import java.awt.geom.Path2D;
  31 import sun.java2d.marlin.Helpers.IndexStack;
  32 import sun.java2d.marlin.Helpers.PolyStack;
  33 
  34 final class TransformingPathConsumer2D {
  35 
  36     private final RendererContext rdrCtx;


  37 
  38     // recycled ClosedPathDetector instance from detectClosedPath()
  39     private final ClosedPathDetector   cpDetector;
  40 
  41     // recycled PathClipFilter instance from pathClipper()
  42     private final PathClipFilter       pathClipper;
  43 
  44     // recycled PathConsumer2D instance from wrapPath2D()
  45     private final Path2DWrapper        wp_Path2DWrapper        = new Path2DWrapper();
  46 
  47     // recycled PathConsumer2D instances from deltaTransformConsumer()
  48     private final DeltaScaleFilter     dt_DeltaScaleFilter     = new DeltaScaleFilter();
  49     private final DeltaTransformFilter dt_DeltaTransformFilter = new DeltaTransformFilter();
  50 
  51     // recycled PathConsumer2D instances from inverseDeltaTransformConsumer()
  52     private final DeltaScaleFilter     iv_DeltaScaleFilter     = new DeltaScaleFilter();
  53     private final DeltaTransformFilter iv_DeltaTransformFilter = new DeltaTransformFilter();
  54 
  55     // recycled PathTracer instances from tracer...() methods
  56     private final PathTracer tracerInput      = new PathTracer("[Input]");
  57     private final PathTracer tracerCPDetector = new PathTracer("ClosedPathDetector");
  58     private final PathTracer tracerFiller     = new PathTracer("Filler");
  59     private final PathTracer tracerStroker    = new PathTracer("Stroker");
  60 
  61     TransformingPathConsumer2D(final RendererContext rdrCtx) {
  62         // used by RendererContext
  63         this.rdrCtx = rdrCtx;
  64         this.cpDetector = new ClosedPathDetector(rdrCtx);
  65         this.pathClipper = new PathClipFilter(rdrCtx);
  66     }
  67 
  68     PathConsumer2D wrapPath2D(Path2D.Float p2d) {
  69         return wp_Path2DWrapper.init(p2d);
  70     }
  71 
  72     PathConsumer2D traceInput(PathConsumer2D out) {
  73         return tracerInput.init(out);
  74     }
  75 
  76     PathConsumer2D traceClosedPathDetector(PathConsumer2D out) {
  77         return tracerCPDetector.init(out);
  78     }
  79 
  80     PathConsumer2D traceFiller(PathConsumer2D out) {
  81         return tracerFiller.init(out);
  82     }
  83 
  84     PathConsumer2D traceStroker(PathConsumer2D out) {
  85         return tracerStroker.init(out);
  86     }
  87 
  88     PathConsumer2D detectClosedPath(PathConsumer2D out) {
  89         return cpDetector.init(out);
  90     }
  91 
  92     PathConsumer2D pathClipper(PathConsumer2D out) {
  93         return pathClipper.init(out);
  94     }
  95 
  96     PathConsumer2D deltaTransformConsumer(PathConsumer2D out,
  97                                           AffineTransform at)
  98     {
  99         if (at == null) {
 100             return out;
 101         }
 102         final float mxx = (float) at.getScaleX();
 103         final float mxy = (float) at.getShearX();
 104         final float myx = (float) at.getShearY();
 105         final float myy = (float) at.getScaleY();
 106 
 107         if (mxy == 0.0f && myx == 0.0f) {
 108             if (mxx == 1.0f && myy == 1.0f) {
 109                 return out;
 110             } else {
 111                 // Scale only
 112                 if (rdrCtx.doClip) {
 113                     // adjust clip rectangle (ymin, ymax, xmin, xmax):
 114                     adjustClipScale(rdrCtx.clipRect, mxx, myy);
 115                 }
 116                 return dt_DeltaScaleFilter.init(out, mxx, myy);
 117             }
 118         } else {
 119             if (rdrCtx.doClip) {
 120                 // adjust clip rectangle (ymin, ymax, xmin, xmax):
 121                 adjustClipInverseDelta(rdrCtx.clipRect, mxx, mxy, myx, myy);
 122             }
 123             return dt_DeltaTransformFilter.init(out, mxx, mxy, myx, myy);
 124         }
 125     }
 126 
 127     private static void adjustClipOffset(final float[] clipRect) {
 128         clipRect[0] += Renderer.RDR_OFFSET_Y;
 129         clipRect[1] += Renderer.RDR_OFFSET_Y;
 130         clipRect[2] += Renderer.RDR_OFFSET_X;
 131         clipRect[3] += Renderer.RDR_OFFSET_X;
 132     }
 133 
 134     private static void adjustClipScale(final float[] clipRect,
 135                                         final float mxx, final float myy)
 136     {
 137         adjustClipOffset(clipRect);
 138 
 139         // Adjust the clipping rectangle (iv_DeltaScaleFilter):
 140         clipRect[0] /= myy;
 141         clipRect[1] /= myy;
 142         clipRect[2] /= mxx;
 143         clipRect[3] /= mxx;
 144     }
 145 
 146     private static void adjustClipInverseDelta(final float[] clipRect,
 147                                                final float mxx, final float mxy,
 148                                                final float myx, final float myy)
 149     {
 150         adjustClipOffset(clipRect);
 151 
 152         // Adjust the clipping rectangle (iv_DeltaTransformFilter):
 153         final float det = mxx * myy - mxy * myx;
 154         final float imxx =  myy / det;
 155         final float imxy = -mxy / det;
 156         final float imyx = -myx / det;
 157         final float imyy =  mxx / det;
 158 
 159         float xmin, xmax, ymin, ymax;
 160         float x, y;
 161         // xmin, ymin:
 162         x = clipRect[2] * imxx + clipRect[0] * imxy;
 163         y = clipRect[2] * imyx + clipRect[0] * imyy;
 164 
 165         xmin = xmax = x;
 166         ymin = ymax = y;
 167 
 168         // xmax, ymin:
 169         x = clipRect[3] * imxx + clipRect[0] * imxy;
 170         y = clipRect[3] * imyx + clipRect[0] * imyy;
 171 
 172         if (x < xmin) { xmin = x; } else if (x > xmax) { xmax = x; }
 173         if (y < ymin) { ymin = y; } else if (y > ymax) { ymax = y; }
 174 
 175         // xmin, ymax:
 176         x = clipRect[2] * imxx + clipRect[1] * imxy;
 177         y = clipRect[2] * imyx + clipRect[1] * imyy;
 178 
 179         if (x < xmin) { xmin = x; } else if (x > xmax) { xmax = x; }
 180         if (y < ymin) { ymin = y; } else if (y > ymax) { ymax = y; }
 181 
 182         // xmax, ymax:
 183         x = clipRect[3] * imxx + clipRect[1] * imxy;
 184         y = clipRect[3] * imyx + clipRect[1] * imyy;
 185 
 186         if (x < xmin) { xmin = x; } else if (x > xmax) { xmax = x; }
 187         if (y < ymin) { ymin = y; } else if (y > ymax) { ymax = y; }
 188 
 189         clipRect[0] = ymin;
 190         clipRect[1] = ymax;
 191         clipRect[2] = xmin;
 192         clipRect[3] = xmax;
 193     }
 194 
 195     PathConsumer2D inverseDeltaTransformConsumer(PathConsumer2D out,
 196                                                  AffineTransform at)
 197     {
 198         if (at == null) {
 199             return out;
 200         }
 201         float mxx = (float) at.getScaleX();
 202         float mxy = (float) at.getShearX();
 203         float myx = (float) at.getShearY();
 204         float myy = (float) at.getScaleY();
 205 
 206         if (mxy == 0.0f && myx == 0.0f) {
 207             if (mxx == 1.0f && myy == 1.0f) {
 208                 return out;
 209             } else {
 210                 return iv_DeltaScaleFilter.init(out, 1.0f/mxx, 1.0f/myy);
 211             }
 212         } else {
 213             final float det = mxx * myy - mxy * myx;
 214             return iv_DeltaTransformFilter.init(out,
 215                                                 myy / det,
 216                                                -mxy / det,
 217                                                -myx / det,
 218                                                 mxx / det);
 219         }
 220     }
 221 

 222     static final class DeltaScaleFilter implements PathConsumer2D {
 223         private PathConsumer2D out;
 224         private float sx, sy;
 225 
 226         DeltaScaleFilter() {}
 227 
 228         DeltaScaleFilter init(PathConsumer2D out,
 229                               float mxx, float myy)
 230         {
 231             this.out = out;
 232             sx = mxx;
 233             sy = myy;
 234             return this; // fluent API
 235         }
 236 
 237         @Override
 238         public void moveTo(float x0, float y0) {
 239             out.moveTo(x0 * sx, y0 * sy);
 240         }
 241 


 369 
 370         @Override
 371         public void closePath() {
 372             p2d.closePath();
 373         }
 374 
 375         @Override
 376         public void pathDone() {}
 377 
 378         @Override
 379         public void curveTo(float x1, float y1,
 380                             float x2, float y2,
 381                             float x3, float y3)
 382         {
 383             p2d.curveTo(x1, y1, x2, y2, x3, y3);
 384         }
 385 
 386         @Override
 387         public void quadTo(float x1, float y1, float x2, float y2) {
 388             p2d.quadTo(x1, y1, x2, y2);
 389         }
 390 
 391         @Override
 392         public long getNativeConsumer() {
 393             throw new InternalError("Not using a native peer");
 394         }
 395     }
 396 
 397     static final class ClosedPathDetector implements PathConsumer2D {
 398 
 399         private final RendererContext rdrCtx;
 400         private final PolyStack stack;
 401 
 402         private PathConsumer2D out;
 403 
 404         ClosedPathDetector(final RendererContext rdrCtx) {
 405             this.rdrCtx = rdrCtx;
 406             this.stack = (rdrCtx.stats != null) ?
 407                 new PolyStack(rdrCtx,
 408                         rdrCtx.stats.stat_cpd_polystack_types,
 409                         rdrCtx.stats.stat_cpd_polystack_curves,
 410                         rdrCtx.stats.hist_cpd_polystack_curves,
 411                         rdrCtx.stats.stat_array_cpd_polystack_curves,
 412                         rdrCtx.stats.stat_array_cpd_polystack_types)
 413                 : new PolyStack(rdrCtx);
 414         }
 415 
 416         ClosedPathDetector init(PathConsumer2D out) {
 417             this.out = out;
 418             return this; // fluent API
 419         }
 420 
 421         /**
 422          * Disposes this instance:
 423          * clean up before reusing this instance
 424          */
 425         void dispose() {
 426             stack.dispose();
 427         }
 428 
 429         @Override
 430         public void pathDone() {
 431             // previous path is not closed:
 432             finish(false);
 433             out.pathDone();
 434 
 435             // TODO: fix possible leak if exception happened
 436             // Dispose this instance:
 437             dispose();
 438         }
 439 
 440         @Override
 441         public void closePath() {
 442             // path is closed
 443             finish(true);
 444             out.closePath();
 445         }
 446 
 447         @Override
 448         public void moveTo(float x0, float y0) {
 449             // previous path is not closed:
 450             finish(false);
 451             out.moveTo(x0, y0);
 452         }
 453 
 454         private void finish(final boolean closed) {
 455             rdrCtx.closedPath = closed;
 456             stack.pullAll(out);
 457         }
 458 
 459         @Override
 460         public void lineTo(float x1, float y1) {
 461             stack.pushLine(x1, y1);
 462         }
 463 
 464         @Override
 465         public void curveTo(float x3, float y3,
 466                             float x2, float y2,
 467                             float x1, float y1)
 468         {
 469             stack.pushCubic(x1, y1, x2, y2, x3, y3);
 470         }
 471 
 472         @Override
 473         public void quadTo(float x2, float y2, float x1, float y1) {
 474             stack.pushQuad(x1, y1, x2, y2);
 475         }
 476 
 477         @Override
 478         public long getNativeConsumer() {
 479             throw new InternalError("Not using a native peer");
 480         }
 481     }
 482 
 483     static final class PathClipFilter implements PathConsumer2D {
 484 
 485         private PathConsumer2D out;
 486 
 487         // Bounds of the drawing region, at pixel precision.
 488         private final float[] clipRect;
 489 
 490         private final float[] corners = new float[8];
 491         private boolean init_corners = false;
 492 
 493         private final IndexStack stack;
 494 
 495         // the current outcode of the current sub path
 496         private int cOutCode = 0;
 497 
 498         // the cumulated (and) outcode of the complete path
 499         private int gOutCode = MarlinConst.OUTCODE_MASK_T_B_L_R;
 500 
 501         private boolean outside = false;
 502 
 503         // The current point OUTSIDE
 504         private float cx0, cy0;
 505 
 506         PathClipFilter(final RendererContext rdrCtx) {
 507             this.clipRect = rdrCtx.clipRect;
 508             this.stack = (rdrCtx.stats != null) ?
 509                 new IndexStack(rdrCtx,
 510                         rdrCtx.stats.stat_pcf_idxstack_indices,
 511                         rdrCtx.stats.hist_pcf_idxstack_indices,
 512                         rdrCtx.stats.stat_array_pcf_idxstack_indices)
 513                 : new IndexStack(rdrCtx);
 514         }
 515 
 516         PathClipFilter init(final PathConsumer2D out) {
 517             this.out = out;
 518 
 519             // Adjust the clipping rectangle with the renderer offsets
 520             final float rdrOffX = Renderer.RDR_OFFSET_X;
 521             final float rdrOffY = Renderer.RDR_OFFSET_Y;
 522 
 523             // add a small rounding error:
 524             final float margin = 1e-3f;
 525 
 526             final float[] _clipRect = this.clipRect;
 527             _clipRect[0] -= margin - rdrOffY;
 528             _clipRect[1] += margin + rdrOffY;
 529             _clipRect[2] -= margin - rdrOffX;
 530             _clipRect[3] += margin + rdrOffX;
 531 
 532             this.init_corners = true;
 533             this.gOutCode = MarlinConst.OUTCODE_MASK_T_B_L_R;
 534 
 535             return this; // fluent API
 536         }
 537 
 538         /**
 539          * Disposes this instance:
 540          * clean up before reusing this instance
 541          */
 542         void dispose() {
 543             stack.dispose();
 544         }
 545 
 546         private void finishPath() {
 547             if (outside) {
 548                 // criteria: inside or totally outside ?
 549                 if (gOutCode == 0) {
 550                     finish();
 551                 } else {
 552                     this.outside = false;
 553                     stack.reset();
 554                 }
 555             }
 556         }
 557 
 558         private void finish() {
 559             this.outside = false;
 560 
 561             if (!stack.isEmpty()) {
 562                 if (init_corners) {
 563                     init_corners = false;
 564 
 565                     final float[] _corners = corners;
 566                     final float[] _clipRect = clipRect;
 567                     // Top Left (0):
 568                     _corners[0] = _clipRect[2];
 569                     _corners[1] = _clipRect[0];
 570                     // Bottom Left (1):
 571                     _corners[2] = _clipRect[2];
 572                     _corners[3] = _clipRect[1];
 573                     // Top right (2):
 574                     _corners[4] = _clipRect[3];
 575                     _corners[5] = _clipRect[0];
 576                     // Bottom Right (3):
 577                     _corners[6] = _clipRect[3];
 578                     _corners[7] = _clipRect[1];
 579                 }
 580                 stack.pullAll(corners, out);
 581             }
 582             out.lineTo(cx0, cy0);
 583         }
 584 
 585         @Override
 586         public void pathDone() {
 587             finishPath();
 588 
 589             out.pathDone();
 590 
 591             // TODO: fix possible leak if exception happened
 592             // Dispose this instance:
 593             dispose();
 594         }
 595 
 596         @Override
 597         public void closePath() {
 598             finishPath();
 599 
 600             out.closePath();
 601         }
 602 
 603         @Override
 604         public void moveTo(final float x0, final float y0) {
 605             finishPath();
 606 
 607             final int outcode = Helpers.outcode(x0, y0, clipRect);
 608             this.cOutCode = outcode;
 609             this.outside = false;
 610             out.moveTo(x0, y0);
 611         }
 612 
 613         @Override
 614         public void lineTo(final float xe, final float ye) {
 615             final int outcode0 = this.cOutCode;
 616             final int outcode1 = Helpers.outcode(xe, ye, clipRect);
 617             this.cOutCode = outcode1;
 618 
 619             final int sideCode = (outcode0 & outcode1);
 620 
 621             // basic rejection criteria:
 622             if (sideCode == 0) {
 623                 this.gOutCode = 0;
 624             } else {
 625                 this.gOutCode &= sideCode;
 626                 // keep last point coordinate before entering the clip again:
 627                 this.outside = true;
 628                 this.cx0 = xe;
 629                 this.cy0 = ye;
 630 
 631                 clip(sideCode, outcode0, outcode1);
 632                 return;
 633             }
 634             if (outside) {
 635                 finish();
 636             }
 637             // clipping disabled:
 638             out.lineTo(xe, ye);
 639         }
 640 
 641         private void clip(final int sideCode,
 642                           final int outcode0,
 643                           final int outcode1)
 644         {
 645             // corner or cross-boundary on left or right side:
 646             if ((outcode0 != outcode1)
 647                     && ((sideCode & MarlinConst.OUTCODE_MASK_L_R) != 0))
 648             {
 649                 // combine outcodes:
 650                 final int mergeCode = (outcode0 | outcode1);
 651                 final int tbCode = mergeCode & MarlinConst.OUTCODE_MASK_T_B;
 652                 final int lrCode = mergeCode & MarlinConst.OUTCODE_MASK_L_R;
 653                 final int off = (lrCode == MarlinConst.OUTCODE_LEFT) ? 0 : 2;
 654 
 655                 // add corners to outside stack:
 656                 switch (tbCode) {
 657                     case MarlinConst.OUTCODE_TOP:
 658 // System.out.println("TOP "+ ((off == 0) ? "LEFT" : "RIGHT"));
 659                         stack.push(off); // top
 660                         return;
 661                     case MarlinConst.OUTCODE_BOTTOM:
 662 // System.out.println("BOTTOM "+ ((off == 0) ? "LEFT" : "RIGHT"));
 663                         stack.push(off + 1); // bottom
 664                         return;
 665                     default:
 666                         // both TOP / BOTTOM:
 667                         if ((outcode0 & MarlinConst.OUTCODE_TOP) != 0) {
 668 // System.out.println("TOP + BOTTOM "+ ((off == 0) ? "LEFT" : "RIGHT"));
 669                             // top to bottom
 670                             stack.push(off); // top
 671                             stack.push(off + 1); // bottom
 672                         } else {
 673 // System.out.println("BOTTOM + TOP "+ ((off == 0) ? "LEFT" : "RIGHT"));
 674                             // bottom to top
 675                             stack.push(off + 1); // bottom
 676                             stack.push(off); // top
 677                         }
 678                 }
 679             }
 680         }
 681 
 682         @Override
 683         public void curveTo(final float x1, final float y1,
 684                             final float x2, final float y2,
 685                             final float xe, final float ye)
 686         {
 687             final int outcode0 = this.cOutCode;
 688             final int outcode3 = Helpers.outcode(xe, ye, clipRect);
 689             this.cOutCode = outcode3;
 690 
 691             int sideCode = outcode0 & outcode3;
 692 
 693             if (sideCode == 0) {
 694                 this.gOutCode = 0;
 695             } else {
 696                 sideCode &= Helpers.outcode(x1, y1, clipRect);
 697                 sideCode &= Helpers.outcode(x2, y2, clipRect);
 698                 this.gOutCode &= sideCode;
 699 
 700                 // basic rejection criteria:
 701                 if (sideCode != 0) {
 702                     // keep last point coordinate before entering the clip again:
 703                     this.outside = true;
 704                     this.cx0 = xe;
 705                     this.cy0 = ye;
 706 
 707                     clip(sideCode, outcode0, outcode3);
 708                     return;
 709                 }
 710             }
 711             if (outside) {
 712                 finish();
 713             }
 714             // clipping disabled:
 715             out.curveTo(x1, y1, x2, y2, xe, ye);
 716         }
 717 
 718         @Override
 719         public void quadTo(final float x1, final float y1,
 720                            final float xe, final float ye)
 721         {
 722             final int outcode0 = this.cOutCode;
 723             final int outcode2 = Helpers.outcode(xe, ye, clipRect);
 724             this.cOutCode = outcode2;
 725 
 726             int sideCode = outcode0 & outcode2;
 727 
 728             if (sideCode == 0) {
 729                 this.gOutCode = 0;
 730             } else {
 731                 sideCode &= Helpers.outcode(x1, y1, clipRect);
 732                 this.gOutCode &= sideCode;
 733 
 734                 // basic rejection criteria:
 735                 if (sideCode != 0) {
 736                     // keep last point coordinate before entering the clip again:
 737                     this.outside = true;
 738                     this.cx0 = xe;
 739                     this.cy0 = ye;
 740 
 741                     clip(sideCode, outcode0, outcode2);
 742                     return;
 743                 }
 744             }
 745             if (outside) {
 746                 finish();
 747             }
 748             // clipping disabled:
 749             out.quadTo(x1, y1, xe, ye);
 750         }
 751 
 752         @Override
 753         public long getNativeConsumer() {
 754             throw new InternalError("Not using a native peer");
 755         }
 756     }
 757 
 758     static final class PathTracer implements PathConsumer2D {
 759         private final String prefix;
 760         private PathConsumer2D out;
 761 
 762         PathTracer(String name) {
 763             this.prefix = name + ": ";
 764         }
 765 
 766         PathTracer init(PathConsumer2D out) {
 767             this.out = out;
 768             return this; // fluent API
 769         }
 770 
 771         @Override
 772         public void moveTo(float x0, float y0) {
 773             log("moveTo (" + x0 + ", " + y0 + ')');
 774             out.moveTo(x0, y0);
 775         }
 776 
 777         @Override
 778         public void lineTo(float x1, float y1) {
 779             log("lineTo (" + x1 + ", " + y1 + ')');
 780             out.lineTo(x1, y1);
 781         }
 782 
 783         @Override
 784         public void curveTo(float x1, float y1,
 785                             float x2, float y2,
 786                             float x3, float y3)
 787         {
 788             log("curveTo P1(" + x1 + ", " + y1 + ") P2(" + x2 + ", " + y2  + ") P3(" + x3 + ", " + y3 + ')');
 789             out.curveTo(x1, y1, x2, y2, x3, y3);
 790         }
 791 
 792         @Override
 793         public void quadTo(float x1, float y1, float x2, float y2) {
 794             log("quadTo P1(" + x1 + ", " + y1 + ") P2(" + x2 + ", " + y2  + ')');
 795             out.quadTo(x1, y1, x2, y2);
 796         }
 797 
 798         @Override
 799         public void closePath() {
 800             log("closePath");
 801             out.closePath();
 802         }
 803 
 804         @Override
 805         public void pathDone() {
 806             log("pathDone");
 807             out.pathDone();
 808         }
 809 
 810         private void log(final String message) {
 811             System.out.println(prefix + message);
 812         }
 813 
 814         @Override
 815         public long getNativeConsumer() {
 816             throw new InternalError("Not using a native peer");
 817         }
 818     }
 819 }
< prev index next >