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