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