1 /*
2 * Copyright (c) 2010, 2017, 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 RectTileManager tileManager;
57 XRDrawLine lineGen;
58 GrowableRectArray rectBuffer;
59
60 public XRRenderer(RectTileManager 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 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 }