1 /*
2  * Copyright (c) 2010, 2018, 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 
XRRenderer(MaskTileManager tileManager)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      */
validateSurface(SunGraphics2D sg2d)72     private 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 
drawLine(SunGraphics2D sg2d, int x1, int y1, int x2, int y2)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 
drawRect(SunGraphics2D sg2d, int x, int y, int width, int height)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 
drawPolyline(SunGraphics2D sg2d, int[] xpoints, int[] ypoints, int npoints)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 
drawPolygon(SunGraphics2D sg2d, int[] xpoints, int[] ypoints, int npoints)121     public void drawPolygon(SunGraphics2D sg2d,
122                             int[] xpoints, int[] ypoints, int npoints) {
123         draw(sg2d, new Polygon(xpoints, ypoints, npoints));
124     }
125 
fillRect(SunGraphics2D sg2d, int x, int y, int width, int height)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 
fillPolygon(SunGraphics2D sg2d, int[] xpoints, int[] ypoints, int npoints)165     public void fillPolygon(SunGraphics2D sg2d,
166                             int[] xpoints, int[] ypoints, int npoints) {
167         fill(sg2d, new Polygon(xpoints, ypoints, npoints));
168     }
169 
drawRoundRect(SunGraphics2D sg2d, int x, int y, int width, int height, int arcWidth, int arcHeight)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 
fillRoundRect(SunGraphics2D sg2d, int x, int y, int width, int height, int arcWidth, int arcHeight)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 
drawOval(SunGraphics2D sg2d, int x, int y, int width, int height)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 
fillOval(SunGraphics2D sg2d, int x, int y, int width, int height)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 
drawArc(SunGraphics2D sg2d, int x, int y, int width, int height, int startAngle, int arcAngle)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 
fillArc(SunGraphics2D sg2d, int x, int y, int width, int height, int startAngle, int arcAngle)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 
XRDrawHandler()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          */
validate(SunGraphics2D sg2d)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 
drawLine(int x1, int y1, int x2, int y2)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 
drawPixel(int x, int y)250         public void drawPixel(int x, int y) {
251             rectBuffer.pushRectValues(x, y, 1, 1);
252         }
253 
drawScanline(int x1, int x2, int y)254         public void drawScanline(int x1, int x2, int y) {
255             rectBuffer.pushRectValues(x1, y, x2 - x1 + 1, 1);
256         }
257     }
258 
drawPath(SunGraphics2D sg2d, Path2D.Float p2df, int transx, int transy)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 
fillPath(SunGraphics2D sg2d, Path2D.Float p2df, int transx, int transy)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 
fillSpans(SunGraphics2D sg2d, SpanIterator si, int transx, int transy)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 
draw(SunGraphics2D sg2d, Shape s)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 
fill(SunGraphics2D sg2d, Shape s)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 }
385