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