1 /*
   2  * Copyright (c) 2005, 2008, 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 
  26 package sun.java2d.pipe;
  27 
  28 import java.awt.BasicStroke;
  29 import java.awt.Polygon;
  30 import java.awt.Shape;
  31 import java.awt.geom.AffineTransform;
  32 import java.awt.geom.Arc2D;
  33 import java.awt.geom.Ellipse2D;
  34 import java.awt.geom.Path2D;
  35 import java.awt.geom.IllegalPathStateException;
  36 import java.awt.geom.PathIterator;
  37 import java.awt.geom.Rectangle2D;
  38 import java.awt.geom.RoundRectangle2D;
  39 import sun.java2d.SunGraphics2D;
  40 import sun.java2d.loops.ProcessPath;
  41 import static sun.java2d.pipe.BufferedOpCodes.*;
  42 
  43 /**
  44  * Base class for enqueuing rendering operations in a single-threaded
  45  * rendering environment.  Instead of each operation being rendered
  46  * immediately by the underlying graphics library, the operation will be
  47  * added to the provided RenderQueue, which will be processed at a later
  48  * time by a single thread.
  49  *
  50  * This class provides implementations of drawLine(), drawRect(), drawPoly(),
  51  * fillRect(), draw(Shape), and fill(Shape), which are useful for a
  52  * hardware-accelerated renderer.  The other draw*() and fill*() methods
  53  * simply delegate to draw(Shape) and fill(Shape), respectively.
  54  */
  55 public abstract class BufferedRenderPipe
  56     implements PixelDrawPipe, PixelFillPipe, ShapeDrawPipe, ParallelogramPipe
  57 {
  58     ParallelogramPipe aapgrampipe = new AAParallelogramPipe();
  59 
  60     static final int BYTES_PER_POLY_POINT = 8;
  61     static final int BYTES_PER_SCANLINE = 12;
  62     static final int BYTES_PER_SPAN = 16;
  63 
  64     protected RenderQueue rq;
  65     protected RenderBuffer buf;
  66     private BufferedDrawHandler drawHandler;
  67 
  68     public BufferedRenderPipe(RenderQueue rq) {
  69         this.rq = rq;
  70         this.buf = rq.getBuffer();
  71         this.drawHandler = new BufferedDrawHandler();
  72     }
  73 
  74     public ParallelogramPipe getAAParallelogramPipe() {
  75         return aapgrampipe;
  76     }
  77 
  78     /**
  79      * Validates the state in the provided SunGraphics2D object and sets up
  80      * any special resources for this operation (e.g. enabling gradient
  81      * shading).
  82      */
  83     protected abstract void validateContext(SunGraphics2D sg2d);
  84     protected abstract void validateContextAA(SunGraphics2D sg2d);
  85 
  86     public void drawLine(SunGraphics2D sg2d,
  87                          int x1, int y1, int x2, int y2)
  88     {
  89         int transx = sg2d.transX;
  90         int transy = sg2d.transY;
  91         rq.lock();
  92         try {
  93             validateContext(sg2d);
  94             rq.ensureCapacity(20);
  95             buf.putInt(DRAW_LINE);
  96             buf.putInt(x1 + transx);
  97             buf.putInt(y1 + transy);
  98             buf.putInt(x2 + transx);
  99             buf.putInt(y2 + transy);
 100         } finally {
 101             rq.unlock();
 102         }
 103     }
 104 
 105     public void drawRect(SunGraphics2D sg2d,
 106                          int x, int y, int width, int height)
 107     {
 108         rq.lock();
 109         try {
 110             validateContext(sg2d);
 111             rq.ensureCapacity(20);
 112             buf.putInt(DRAW_RECT);
 113             buf.putInt(x + sg2d.transX);
 114             buf.putInt(y + sg2d.transY);
 115             buf.putInt(width);
 116             buf.putInt(height);
 117         } finally {
 118             rq.unlock();
 119         }
 120     }
 121 
 122     public void fillRect(SunGraphics2D sg2d,
 123                          int x, int y, int width, int height)
 124     {
 125         rq.lock();
 126         try {
 127             validateContext(sg2d);
 128             rq.ensureCapacity(20);
 129             buf.putInt(FILL_RECT);
 130             buf.putInt(x + sg2d.transX);
 131             buf.putInt(y + sg2d.transY);
 132             buf.putInt(width);
 133             buf.putInt(height);
 134         } finally {
 135             rq.unlock();
 136         }
 137     }
 138 
 139     public void drawRoundRect(SunGraphics2D sg2d,
 140                               int x, int y, int width, int height,
 141                               int arcWidth, int arcHeight)
 142     {
 143         draw(sg2d, new RoundRectangle2D.Float(x, y, width, height,
 144                                               arcWidth, arcHeight));
 145     }
 146 
 147     public void fillRoundRect(SunGraphics2D sg2d,
 148                               int x, int y, int width, int height,
 149                               int arcWidth, int arcHeight)
 150     {
 151         fill(sg2d, new RoundRectangle2D.Float(x, y, width, height,
 152                                               arcWidth, arcHeight));
 153     }
 154 
 155     public void drawOval(SunGraphics2D sg2d,
 156                          int x, int y, int width, int height)
 157     {
 158         draw(sg2d, new Ellipse2D.Float(x, y, width, height));
 159     }
 160 
 161     public void fillOval(SunGraphics2D sg2d,
 162                          int x, int y, int width, int height)
 163     {
 164         fill(sg2d, new Ellipse2D.Float(x, y, width, height));
 165     }
 166 
 167     public void drawArc(SunGraphics2D sg2d,
 168                         int x, int y, int width, int height,
 169                         int startAngle, int arcAngle)
 170     {
 171         draw(sg2d, new Arc2D.Float(x, y, width, height,
 172                                    startAngle, arcAngle,
 173                                    Arc2D.OPEN));
 174     }
 175 
 176     public void fillArc(SunGraphics2D sg2d,
 177                         int x, int y, int width, int height,
 178                         int startAngle, int arcAngle)
 179     {
 180         fill(sg2d, new Arc2D.Float(x, y, width, height,
 181                                    startAngle, arcAngle,
 182                                    Arc2D.PIE));
 183     }
 184 
 185     protected void drawPoly(final SunGraphics2D sg2d,
 186                             final int[] xPoints, final int[] yPoints,
 187                             final int nPoints, final boolean isClosed)
 188     {
 189         if (xPoints == null || yPoints == null) {
 190             throw new NullPointerException("coordinate array");
 191         }
 192         if (xPoints.length < nPoints || yPoints.length < nPoints) {
 193             throw new ArrayIndexOutOfBoundsException("coordinate array");
 194         }
 195 
 196         if (nPoints < 2) {
 197             // render nothing
 198             return;
 199         } else if (nPoints == 2 && !isClosed) {
 200             // render a simple line
 201             drawLine(sg2d, xPoints[0], yPoints[0], xPoints[1], yPoints[1]);
 202             return;
 203         }
 204 
 205         rq.lock();
 206         try {
 207             validateContext(sg2d);
 208 
 209             int pointBytesRequired = nPoints * BYTES_PER_POLY_POINT;
 210             int totalBytesRequired = 20 + pointBytesRequired;
 211 
 212             if (totalBytesRequired <= buf.capacity()) {
 213                 if (totalBytesRequired > buf.remaining()) {
 214                     // process the queue first and then enqueue the points
 215                     rq.flushNow();
 216                 }
 217                 buf.putInt(DRAW_POLY);
 218                 // enqueue parameters
 219                 buf.putInt(nPoints);
 220                 buf.putInt(isClosed ? 1 : 0);
 221                 buf.putInt(sg2d.transX);
 222                 buf.putInt(sg2d.transY);
 223                 // enqueue the points
 224                 buf.put(xPoints, 0, nPoints);
 225                 buf.put(yPoints, 0, nPoints);
 226             } else {
 227                 // queue is too small to accomodate all points; perform the
 228                 // operation directly on the queue flushing thread
 229                 rq.flushAndInvokeNow(new Runnable() {
 230                     public void run() {
 231                         drawPoly(xPoints, yPoints,
 232                                  nPoints, isClosed,
 233                                  sg2d.transX, sg2d.transY);
 234                     }
 235                 });
 236             }
 237         } finally {
 238             rq.unlock();
 239         }
 240     }
 241 
 242     protected abstract void drawPoly(int[] xPoints, int[] yPoints,
 243                                      int nPoints, boolean isClosed,
 244                                      int transX, int transY);
 245 
 246     public void drawPolyline(SunGraphics2D sg2d,
 247                              int[] xPoints, int[] yPoints,
 248                              int nPoints)
 249     {
 250         drawPoly(sg2d, xPoints, yPoints, nPoints, false);
 251     }
 252 
 253     public void drawPolygon(SunGraphics2D sg2d,
 254                             int[] xPoints, int[] yPoints,
 255                             int nPoints)
 256     {
 257         drawPoly(sg2d, xPoints, yPoints, nPoints, true);
 258     }
 259 
 260     public void fillPolygon(SunGraphics2D sg2d,
 261                             int[] xPoints, int[] yPoints,
 262                             int nPoints)
 263     {
 264         fill(sg2d, new Polygon(xPoints, yPoints, nPoints));
 265     }
 266 
 267     private class BufferedDrawHandler
 268         extends ProcessPath.DrawHandler
 269     {
 270         BufferedDrawHandler() {
 271             // these are bogus values; the caller will use validate()
 272             // to ensure that they are set properly prior to each usage
 273             super(0, 0, 0, 0);
 274         }
 275 
 276         /**
 277          * This method needs to be called prior to each draw/fillPath()
 278          * operation to ensure the clip bounds are up to date.
 279          */
 280         void validate(SunGraphics2D sg2d) {
 281             Region clip = sg2d.getCompClip();
 282             setBounds(clip.getLoX(), clip.getLoY(),
 283                       clip.getHiX(), clip.getHiY(),
 284                       sg2d.strokeHint);
 285         }
 286 
 287         /**
 288          * drawPath() support...
 289          */
 290 
 291         public void drawLine(int x1, int y1, int x2, int y2) {
 292             // assert rq.lock.isHeldByCurrentThread();
 293             rq.ensureCapacity(20);
 294             buf.putInt(DRAW_LINE);
 295             buf.putInt(x1);
 296             buf.putInt(y1);
 297             buf.putInt(x2);
 298             buf.putInt(y2);
 299         }
 300 
 301         public void drawPixel(int x, int y) {
 302             // assert rq.lock.isHeldByCurrentThread();
 303             rq.ensureCapacity(12);
 304             buf.putInt(DRAW_PIXEL);
 305             buf.putInt(x);
 306             buf.putInt(y);
 307         }
 308 
 309         /**
 310          * fillPath() support...
 311          */
 312 
 313         private int scanlineCount;
 314         private int scanlineCountIndex;
 315         private int remainingScanlines;
 316 
 317         private void resetFillPath() {
 318             buf.putInt(DRAW_SCANLINES);
 319             scanlineCountIndex = buf.position();
 320             buf.putInt(0);
 321             scanlineCount = 0;
 322             remainingScanlines = buf.remaining() / BYTES_PER_SCANLINE;
 323         }
 324 
 325         private void updateScanlineCount() {
 326             buf.putInt(scanlineCountIndex, scanlineCount);
 327         }
 328 
 329         /**
 330          * Called from fillPath() to indicate that we are about to
 331          * start issuing drawScanline() calls.
 332          */
 333         public void startFillPath() {
 334             rq.ensureCapacity(20); // to ensure room for at least a scanline
 335             resetFillPath();
 336         }
 337 
 338         public void drawScanline(int x1, int x2, int y) {
 339             if (remainingScanlines == 0) {
 340                 updateScanlineCount();
 341                 rq.flushNow();
 342                 resetFillPath();
 343             }
 344             buf.putInt(x1);
 345             buf.putInt(x2);
 346             buf.putInt(y);
 347             scanlineCount++;
 348             remainingScanlines--;
 349         }
 350 
 351         /**
 352          * Called from fillPath() to indicate that we are done
 353          * issuing drawScanline() calls.
 354          */
 355         public void endFillPath() {
 356             updateScanlineCount();
 357         }
 358     }
 359 
 360     protected void drawPath(SunGraphics2D sg2d,
 361                             Path2D.Float p2df, int transx, int transy)
 362     {
 363         rq.lock();
 364         try {
 365             validateContext(sg2d);
 366             drawHandler.validate(sg2d);
 367             ProcessPath.drawPath(drawHandler, p2df, transx, transy);
 368         } finally {
 369             rq.unlock();
 370         }
 371     }
 372 
 373     protected void fillPath(SunGraphics2D sg2d,
 374                             Path2D.Float p2df, int transx, int transy)
 375     {
 376         rq.lock();
 377         try {
 378             validateContext(sg2d);
 379             drawHandler.validate(sg2d);
 380             drawHandler.startFillPath();
 381             ProcessPath.fillPath(drawHandler, p2df, transx, transy);
 382             drawHandler.endFillPath();
 383         } finally {
 384             rq.unlock();
 385         }
 386     }
 387 
 388     private native int fillSpans(RenderQueue rq, long buf,
 389                                  int pos, int limit,
 390                                  SpanIterator si, long iterator,
 391                                  int transx, int transy);
 392 
 393     protected void fillSpans(SunGraphics2D sg2d, SpanIterator si,
 394                              int transx, int transy)
 395     {
 396         rq.lock();
 397         try {
 398             validateContext(sg2d);
 399             rq.ensureCapacity(24); // so that we have room for at least a span
 400             int newpos = fillSpans(rq, buf.getAddress(),
 401                                    buf.position(), buf.capacity(),
 402                                    si, si.getNativeIterator(),
 403                                    transx, transy);
 404             buf.position(newpos);
 405         } finally {
 406             rq.unlock();
 407         }
 408     }
 409 
 410     public void fillParallelogram(SunGraphics2D sg2d,
 411                                   double ux1, double uy1,
 412                                   double ux2, double uy2,
 413                                   double x, double y,
 414                                   double dx1, double dy1,
 415                                   double dx2, double dy2)
 416     {
 417         rq.lock();
 418         try {
 419             validateContext(sg2d);
 420             rq.ensureCapacity(28);
 421             buf.putInt(FILL_PARALLELOGRAM);
 422             buf.putFloat((float) x);
 423             buf.putFloat((float) y);
 424             buf.putFloat((float) dx1);
 425             buf.putFloat((float) dy1);
 426             buf.putFloat((float) dx2);
 427             buf.putFloat((float) dy2);
 428         } finally {
 429             rq.unlock();
 430         }
 431     }
 432 
 433     public void drawParallelogram(SunGraphics2D sg2d,
 434                                   double ux1, double uy1,
 435                                   double ux2, double uy2,
 436                                   double x, double y,
 437                                   double dx1, double dy1,
 438                                   double dx2, double dy2,
 439                                   double lw1, double lw2)
 440     {
 441         rq.lock();
 442         try {
 443             validateContext(sg2d);
 444             rq.ensureCapacity(36);
 445             buf.putInt(DRAW_PARALLELOGRAM);
 446             buf.putFloat((float) x);
 447             buf.putFloat((float) y);
 448             buf.putFloat((float) dx1);
 449             buf.putFloat((float) dy1);
 450             buf.putFloat((float) dx2);
 451             buf.putFloat((float) dy2);
 452             buf.putFloat((float) lw1);
 453             buf.putFloat((float) lw2);
 454         } finally {
 455             rq.unlock();
 456         }
 457     }
 458 
 459     private class AAParallelogramPipe implements ParallelogramPipe {
 460         public void fillParallelogram(SunGraphics2D sg2d,
 461                                       double ux1, double uy1,
 462                                       double ux2, double uy2,
 463                                       double x, double y,
 464                                       double dx1, double dy1,
 465                                       double dx2, double dy2)
 466         {
 467             rq.lock();
 468             try {
 469                 validateContextAA(sg2d);
 470                 rq.ensureCapacity(28);
 471                 buf.putInt(FILL_AAPARALLELOGRAM);
 472                 buf.putFloat((float) x);
 473                 buf.putFloat((float) y);
 474                 buf.putFloat((float) dx1);
 475                 buf.putFloat((float) dy1);
 476                 buf.putFloat((float) dx2);
 477                 buf.putFloat((float) dy2);
 478             } finally {
 479                 rq.unlock();
 480             }
 481         }
 482 
 483         public void drawParallelogram(SunGraphics2D sg2d,
 484                                       double ux1, double uy1,
 485                                       double ux2, double uy2,
 486                                       double x, double y,
 487                                       double dx1, double dy1,
 488                                       double dx2, double dy2,
 489                                       double lw1, double lw2)
 490         {
 491             rq.lock();
 492             try {
 493                 validateContextAA(sg2d);
 494                 rq.ensureCapacity(36);
 495                 buf.putInt(DRAW_AAPARALLELOGRAM);
 496                 buf.putFloat((float) x);
 497                 buf.putFloat((float) y);
 498                 buf.putFloat((float) dx1);
 499                 buf.putFloat((float) dy1);
 500                 buf.putFloat((float) dx2);
 501                 buf.putFloat((float) dy2);
 502                 buf.putFloat((float) lw1);
 503                 buf.putFloat((float) lw2);
 504             } finally {
 505                 rq.unlock();
 506             }
 507         }
 508     }
 509 
 510     public void draw(SunGraphics2D sg2d, Shape s) {
 511         if (sg2d.strokeState == sg2d.STROKE_THIN) {
 512             if (s instanceof Polygon) {
 513                 if (sg2d.transformState < sg2d.TRANSFORM_TRANSLATESCALE) {
 514                     Polygon p = (Polygon)s;
 515                     drawPolygon(sg2d, p.xpoints, p.ypoints, p.npoints);
 516                     return;
 517                 }
 518             }
 519             Path2D.Float p2df;
 520             int transx, transy;
 521             if (sg2d.transformState <= sg2d.TRANSFORM_INT_TRANSLATE) {
 522                 if (s instanceof Path2D.Float) {
 523                     p2df = (Path2D.Float)s;
 524                 } else {
 525                     p2df = new Path2D.Float(s);
 526                 }
 527                 transx = sg2d.transX;
 528                 transy = sg2d.transY;
 529             } else {
 530                 p2df = new Path2D.Float(s, sg2d.transform);
 531                 transx = 0;
 532                 transy = 0;
 533             }
 534             drawPath(sg2d, p2df, transx, transy);
 535         } else if (sg2d.strokeState < sg2d.STROKE_CUSTOM) {
 536             ShapeSpanIterator si = LoopPipe.getStrokeSpans(sg2d, s);
 537             try {
 538                 fillSpans(sg2d, si, 0, 0);
 539             } finally {
 540                 si.dispose();
 541             }
 542         } else {
 543             fill(sg2d, sg2d.stroke.createStrokedShape(s));
 544         }
 545     }
 546 
 547     public void fill(SunGraphics2D sg2d, Shape s) {
 548         int transx, transy;
 549 
 550         if (sg2d.strokeState == sg2d.STROKE_THIN) {
 551             // Here we are able to use fillPath() for
 552             // high-quality fills.
 553             Path2D.Float p2df;
 554             if (sg2d.transformState <= sg2d.TRANSFORM_INT_TRANSLATE) {
 555                 if (s instanceof Path2D.Float) {
 556                     p2df = (Path2D.Float)s;
 557                 } else {
 558                     p2df = new Path2D.Float(s);
 559                 }
 560                 transx = sg2d.transX;
 561                 transy = sg2d.transY;
 562             } else {
 563                 p2df = new Path2D.Float(s, sg2d.transform);
 564                 transx = 0;
 565                 transy = 0;
 566             }
 567             fillPath(sg2d, p2df, transx, transy);
 568             return;
 569         }
 570 
 571         AffineTransform at;
 572         if (sg2d.transformState <= sg2d.TRANSFORM_INT_TRANSLATE) {
 573             // Transform (translation) will be done by FillSpans (we could
 574             // delegate to fillPolygon() here, but most hardware accelerated
 575             // libraries cannot handle non-convex polygons, so we will use
 576             // the FillSpans approach by default)
 577             at = null;
 578             transx = sg2d.transX;
 579             transy = sg2d.transY;
 580         } else {
 581             // Transform will be done by the PathIterator
 582             at = sg2d.transform;
 583             transx = transy = 0;
 584         }
 585 
 586         ShapeSpanIterator ssi = LoopPipe.getFillSSI(sg2d);
 587         try {
 588             // Subtract transx/y from the SSI clip to match the
 589             // (potentially untranslated) geometry fed to it
 590             Region clip = sg2d.getCompClip();
 591             ssi.setOutputAreaXYXY(clip.getLoX() - transx,
 592                                   clip.getLoY() - transy,
 593                                   clip.getHiX() - transx,
 594                                   clip.getHiY() - transy);
 595             ssi.appendPath(s.getPathIterator(at));
 596             fillSpans(sg2d, ssi, transx, transy);
 597         } finally {
 598             ssi.dispose();
 599         }
 600     }
 601 }