1 /*
   2  * Copyright (c) 2010, 2013, 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.xr;
  27 
  28 import java.awt.*;
  29 import java.awt.geom.*;
  30 import sun.awt.SunToolkit;
  31 import sun.java2d.InvalidPipeException;
  32 import sun.java2d.SunGraphics2D;
  33 import sun.java2d.loops.*;
  34 import sun.java2d.pipe.Region;
  35 import sun.java2d.pipe.PixelDrawPipe;
  36 import sun.java2d.pipe.PixelFillPipe;
  37 import sun.java2d.pipe.ShapeDrawPipe;
  38 import sun.java2d.pipe.SpanIterator;
  39 import sun.java2d.pipe.ShapeSpanIterator;
  40 import sun.java2d.pipe.LoopPipe;
  41 
  42 import static sun.java2d.xr.XRUtils.clampToShort;
  43 import static sun.java2d.xr.XRUtils.clampToUShort;
  44 
  45 /**
  46  * XRender provides only accalerated rectangles. To emulate higher "order"
  47  *  geometry we have to pass everything else to DoPath/FillSpans.
  48  *
  49  * TODO: DrawRect could be instrified
  50  *
  51  * @author Clemens Eisserer
  52  */
  53 
  54 public class XRRenderer implements PixelDrawPipe, PixelFillPipe, ShapeDrawPipe {
  55     XRDrawHandler drawHandler;
  56     MaskTileManager tileManager;
  57     XRDrawLine lineGen;
  58     GrowableRectArray rectBuffer;
  59 
  60     public XRRenderer(MaskTileManager tileManager) {
  61         this.tileManager = tileManager;
  62         this.rectBuffer = tileManager.getMainTile().getRects();
  63 
  64         this.drawHandler = new XRDrawHandler();
  65         this.lineGen = new XRDrawLine();
  66     }
  67 
  68     /**
  69      * Common validate method, used by all XRRender functions to validate the
  70      * destination context.
  71      */
  72     private final void validateSurface(SunGraphics2D sg2d) {
  73         XRSurfaceData xrsd;
  74         try {
  75             xrsd = (XRSurfaceData) sg2d.surfaceData;
  76         } catch (ClassCastException e) {
  77             throw new InvalidPipeException("wrong surface data type: " + sg2d.surfaceData);
  78         }
  79         xrsd.validateAsDestination(sg2d, sg2d.getCompClip());
  80         xrsd.maskBuffer.validateCompositeState(sg2d.composite, sg2d.transform,
  81                                                sg2d.paint, sg2d);
  82     }
  83 
  84     public void drawLine(SunGraphics2D sg2d, int x1, int y1, int x2, int y2) {
  85         Region compClip = sg2d.getCompClip();
  86         int transX1 = Region.clipAdd(x1, sg2d.transX);
  87         int transY1 = Region.clipAdd(y1, sg2d.transY);
  88         int transX2 = Region.clipAdd(x2, sg2d.transX);
  89         int transY2 = Region.clipAdd(y2, sg2d.transY);
  90 
  91         SunToolkit.awtLock();
  92         try {
  93             validateSurface(sg2d);
  94             lineGen.rasterizeLine(rectBuffer, transX1, transY1,
  95                     transX2, transY2, compClip.getLoX(), compClip.getLoY(),
  96                     compClip.getHiX(), compClip.getHiY(), true, true);
  97             tileManager.fillMask((XRSurfaceData) sg2d.surfaceData);
  98         } finally {
  99             SunToolkit.awtUnlock();
 100         }
 101     }
 102 
 103     public void drawRect(SunGraphics2D sg2d,
 104                          int x, int y, int width, int height) {
 105         draw(sg2d, new Rectangle2D.Float(x, y, width, height));
 106     }
 107 
 108     public void drawPolyline(SunGraphics2D sg2d,
 109                              int xpoints[], int ypoints[], int npoints) {
 110         Path2D.Float p2d = new Path2D.Float();
 111         if (npoints > 1) {
 112             p2d.moveTo(xpoints[0], ypoints[0]);
 113             for (int i = 1; i < npoints; i++) {
 114                 p2d.lineTo(xpoints[i], ypoints[i]);
 115             }
 116         }
 117 
 118         draw(sg2d, p2d);
 119     }
 120 
 121     public void drawPolygon(SunGraphics2D sg2d,
 122                             int xpoints[], int ypoints[], int npoints) {
 123         draw(sg2d, new Polygon(xpoints, ypoints, npoints));
 124     }
 125 
 126     public void fillRect(SunGraphics2D sg2d, int x, int y, int width, int height) {
 127         x = Region.clipAdd(x, sg2d.transX);
 128         y = Region.clipAdd(y, sg2d.transY);
 129 
 130         /*
 131          * Limit x/y to signed short, width/height to unsigned short,
 132          * to match the X11 coordinate limits for rectangles.
 133          * Correct width/height in case x/y have been modified by clipping.
 134          */
 135         if (x > Short.MAX_VALUE || y > Short.MAX_VALUE) {
 136             return;
 137         }
 138 
 139         int x2 = Region.dimAdd(x, width);
 140         int y2 = Region.dimAdd(y, height);
 141 
 142         if (x2 < Short.MIN_VALUE || y2 < Short.MIN_VALUE) {
 143             return;
 144         }
 145 
 146         x = clampToShort(x);
 147         y = clampToShort(y);
 148         width = clampToUShort(x2 - x);
 149         height = clampToUShort(y2 - y);
 150 
 151         if (width == 0 || height == 0) {
 152             return;
 153         }
 154 
 155         SunToolkit.awtLock();
 156         try {
 157             validateSurface(sg2d);
 158             rectBuffer.pushRectValues(x, y, width, height);
 159             tileManager.fillMask((XRSurfaceData) sg2d.surfaceData);
 160         } finally {
 161             SunToolkit.awtUnlock();
 162         }
 163     }
 164 
 165     public void fillPolygon(SunGraphics2D sg2d,
 166                             int xpoints[], int ypoints[], int npoints) {
 167         fill(sg2d, new Polygon(xpoints, ypoints, npoints));
 168     }
 169 
 170     public void drawRoundRect(SunGraphics2D sg2d,
 171                               int x, int y, int width, int height,
 172                               int arcWidth, int arcHeight) {
 173         draw(sg2d, new RoundRectangle2D.Float(x, y, width, height,
 174                                               arcWidth, arcHeight));
 175     }
 176 
 177     public void fillRoundRect(SunGraphics2D sg2d, int x, int y,
 178                               int width, int height,
 179                               int arcWidth, int arcHeight) {
 180         fill(sg2d, new RoundRectangle2D.Float(x, y, width, height,
 181                                               arcWidth, arcHeight));
 182     }
 183 
 184     public void drawOval(SunGraphics2D sg2d,
 185                          int x, int y, int width, int height) {
 186         draw(sg2d, new Ellipse2D.Float(x, y, width, height));
 187     }
 188 
 189     public void fillOval(SunGraphics2D sg2d,
 190                          int x, int y, int width, int height) {
 191         fill(sg2d, new Ellipse2D.Float(x, y, width, height));
 192     }
 193 
 194     public void drawArc(SunGraphics2D sg2d,
 195                        int x, int y, int width, int height,
 196                         int startAngle, int arcAngle) {
 197         draw(sg2d, new Arc2D.Float(x, y, width, height,
 198                                    startAngle, arcAngle, Arc2D.OPEN));
 199     }
 200 
 201     public void fillArc(SunGraphics2D sg2d,
 202                          int x, int y, int width, int height,
 203                          int startAngle, int arcAngle) {
 204         fill(sg2d, new Arc2D.Float(x, y, width, height,
 205              startAngle, arcAngle, Arc2D.PIE));
 206     }
 207 
 208     private class XRDrawHandler extends ProcessPath.DrawHandler {
 209         DirtyRegion region;
 210 
 211         XRDrawHandler() {
 212             // these are bogus values; the caller will use validate()
 213             // to ensure that they are set properly prior to each usage
 214             super(0, 0, 0, 0);
 215             this.region = new DirtyRegion();
 216         }
 217 
 218         /**
 219          * This method needs to be called prior to each draw/fillPath()
 220          * operation to ensure the clip bounds are up to date.
 221          */
 222         void validate(SunGraphics2D sg2d) {
 223             Region clip = sg2d.getCompClip();
 224             setBounds(clip.getLoX(), clip.getLoY(),
 225                       clip.getHiX(), clip.getHiY(), sg2d.strokeHint);
 226             validateSurface(sg2d);
 227         }
 228 
 229         public void drawLine(int x1, int y1, int x2, int y2) {
 230             region.setDirtyLineRegion(x1, y1, x2, y2);
 231             int xDiff = region.x2 - region.x;
 232             int yDiff = region.y2 - region.y;
 233 
 234             if (xDiff == 0 || yDiff == 0) {
 235                 // horizontal / diagonal lines can be represented by a single
 236                 // rectangle
 237                 rectBuffer.pushRectValues(region.x, region.y, region.x2 - region.x
 238                         + 1, region.y2 - region.y + 1);
 239             } else if (xDiff == 1 && yDiff == 1) {
 240                 // fast path for pattern commonly generated by
 241                 // ProcessPath.DrawHandler
 242                 rectBuffer.pushRectValues(x1, y1, 1, 1);
 243                 rectBuffer.pushRectValues(x2, y2, 1, 1);
 244             } else {
 245                 lineGen.rasterizeLine(rectBuffer, x1, y1, x2, y2, 0, 0,
 246                                       0, 0, false, false);
 247             }
 248         }
 249 
 250         public void drawPixel(int x, int y) {
 251             rectBuffer.pushRectValues(x, y, 1, 1);
 252         }
 253 
 254         public void drawScanline(int x1, int x2, int y) {
 255             rectBuffer.pushRectValues(x1, y, x2 - x1 + 1, 1);
 256         }
 257     }
 258 
 259     protected void drawPath(SunGraphics2D sg2d, Path2D.Float p2df,
 260                             int transx, int transy) {
 261         SunToolkit.awtLock();
 262         try {
 263             validateSurface(sg2d);
 264             drawHandler.validate(sg2d);
 265             ProcessPath.drawPath(drawHandler, p2df, transx, transy);
 266             tileManager.fillMask(((XRSurfaceData) sg2d.surfaceData));
 267         } finally {
 268             SunToolkit.awtUnlock();
 269         }
 270     }
 271 
 272     protected void fillPath(SunGraphics2D sg2d, Path2D.Float p2df,
 273                             int transx, int transy) {
 274         SunToolkit.awtLock();
 275         try {
 276             validateSurface(sg2d);
 277             drawHandler.validate(sg2d);
 278             ProcessPath.fillPath(drawHandler, p2df, transx, transy);
 279             tileManager.fillMask(((XRSurfaceData) sg2d.surfaceData));
 280         } finally {
 281             SunToolkit.awtUnlock();
 282         }
 283     }
 284 
 285     protected void fillSpans(SunGraphics2D sg2d, SpanIterator si,
 286                              int transx, int transy) {
 287         SunToolkit.awtLock();
 288         try {
 289             validateSurface(sg2d);
 290             int[] spanBox = new int[4];
 291             while (si.nextSpan(spanBox)) {
 292                 rectBuffer.pushRectValues(spanBox[0] + transx,
 293                                     spanBox[1] + transy,
 294                                     spanBox[2] - spanBox[0],
 295                                     spanBox[3] - spanBox[1]);
 296             }
 297             tileManager.fillMask(((XRSurfaceData) sg2d.surfaceData));
 298         } finally {
 299             SunToolkit.awtUnlock();
 300         }
 301     }
 302 
 303     public void draw(SunGraphics2D sg2d, Shape s) {
 304         if (sg2d.strokeState == SunGraphics2D.STROKE_THIN) {
 305             Path2D.Float p2df;
 306             int transx, transy;
 307             if (sg2d.transformState <= SunGraphics2D.TRANSFORM_INT_TRANSLATE) {
 308                 if (s instanceof Path2D.Float) {
 309                     p2df = (Path2D.Float) s;
 310                 } else {
 311                     p2df = new Path2D.Float(s);
 312                 }
 313                 transx = sg2d.transX;
 314                 transy = sg2d.transY;
 315             } else {
 316                 p2df = new Path2D.Float(s, sg2d.transform);
 317                 transx = 0;
 318                 transy = 0;
 319             }
 320             drawPath(sg2d, p2df, transx, transy);
 321         } else if (sg2d.strokeState < SunGraphics2D.STROKE_CUSTOM) {
 322             ShapeSpanIterator si = LoopPipe.getStrokeSpans(sg2d, s);
 323             try {
 324                 fillSpans(sg2d, si, 0, 0);
 325             } finally {
 326                 si.dispose();
 327             }
 328         } else {
 329             fill(sg2d, sg2d.stroke.createStrokedShape(s));
 330         }
 331     }
 332 
 333     public void fill(SunGraphics2D sg2d, Shape s) {
 334         int transx, transy;
 335 
 336         if (sg2d.strokeState == SunGraphics2D.STROKE_THIN) {
 337             // Here we are able to use fillPath() for
 338             // high-quality fills.
 339             Path2D.Float p2df;
 340             if (sg2d.transformState <= SunGraphics2D.TRANSFORM_INT_TRANSLATE) {
 341                 if (s instanceof Path2D.Float) {
 342                     p2df = (Path2D.Float) s;
 343                 } else {
 344                     p2df = new Path2D.Float(s);
 345                 }
 346                 transx = sg2d.transX;
 347                 transy = sg2d.transY;
 348             } else {
 349                 p2df = new Path2D.Float(s, sg2d.transform);
 350                 transx = 0;
 351                 transy = 0;
 352             }
 353             fillPath(sg2d, p2df, transx, transy);
 354             return;
 355         }
 356 
 357         AffineTransform at;
 358         if (sg2d.transformState <= SunGraphics2D.TRANSFORM_INT_TRANSLATE) {
 359             // Transform (translation) will be done by FillSpans
 360             at = null;
 361             transx = sg2d.transX;
 362             transy = sg2d.transY;
 363         } else {
 364             // Transform will be done by the PathIterator
 365             at = sg2d.transform;
 366             transx = transy = 0;
 367         }
 368 
 369         ShapeSpanIterator ssi = LoopPipe.getFillSSI(sg2d);
 370         try {
 371             // Subtract transx/y from the SSI clip to match the
 372             // (potentially untranslated) geometry fed to it
 373             Region clip = sg2d.getCompClip();
 374             ssi.setOutputAreaXYXY(clip.getLoX() - transx,
 375                                   clip.getLoY() - transy,
 376                                   clip.getHiX() - transx,
 377                                   clip.getHiY() - transy);
 378             ssi.appendPath(s.getPathIterator(at));
 379             fillSpans(sg2d, ssi, transx, transy);
 380         } finally {
 381             ssi.dispose();
 382         }
 383     }
 384 }