blob: 71feb4b9ba40eec0d7e0ca014a56243f510a6f3c [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2005-2006 Sun Microsystems, Inc. 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. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26package sun.java2d.pipe;
27
28import java.awt.Polygon;
29import java.awt.Shape;
30import java.awt.geom.AffineTransform;
31import java.awt.geom.Arc2D;
32import java.awt.geom.Ellipse2D;
33import java.awt.geom.Path2D;
34import java.awt.geom.IllegalPathStateException;
35import java.awt.geom.PathIterator;
36import java.awt.geom.RoundRectangle2D;
37import sun.java2d.SunGraphics2D;
38import sun.java2d.loops.ProcessPath;
39import static sun.java2d.pipe.BufferedOpCodes.*;
40
41/**
42 * Base class for enqueuing rendering operations in a single-threaded
43 * rendering environment. Instead of each operation being rendered
44 * immediately by the underlying graphics library, the operation will be
45 * added to the provided RenderQueue, which will be processed at a later
46 * time by a single thread.
47 *
48 * This class provides implementations of drawLine(), drawRect(), drawPoly(),
49 * fillRect(), draw(Shape), and fill(Shape), which are useful for a
50 * hardware-accelerated renderer. The other draw*() and fill*() methods
51 * simply delegate to draw(Shape) and fill(Shape), respectively.
52 */
53public abstract class BufferedRenderPipe
54 implements PixelDrawPipe, PixelFillPipe, ShapeDrawPipe
55{
56 static final int BYTES_PER_POLY_POINT = 8;
57 static final int BYTES_PER_SCANLINE = 12;
58 static final int BYTES_PER_SPAN = 16;
59
60 protected RenderQueue rq;
61 protected RenderBuffer buf;
62 private BufferedDrawHandler drawHandler;
63
64 public BufferedRenderPipe(RenderQueue rq) {
65 this.rq = rq;
66 this.buf = rq.getBuffer();
67 this.drawHandler = new BufferedDrawHandler();
68 }
69
70 /**
71 * Validates the state in the provided SunGraphics2D object and sets up
72 * any special resources for this operation (e.g. enabling gradient
73 * shading).
74 */
75 protected abstract void validateContext(SunGraphics2D sg2d);
76
77 public void drawLine(SunGraphics2D sg2d,
78 int x1, int y1, int x2, int y2)
79 {
80 int transx = sg2d.transX;
81 int transy = sg2d.transY;
82 rq.lock();
83 try {
84 validateContext(sg2d);
85 rq.ensureCapacity(20);
86 buf.putInt(DRAW_LINE);
87 buf.putInt(x1 + transx);
88 buf.putInt(y1 + transy);
89 buf.putInt(x2 + transx);
90 buf.putInt(y2 + transy);
91 } finally {
92 rq.unlock();
93 }
94 }
95
96 public void drawRect(SunGraphics2D sg2d,
97 int x, int y, int width, int height)
98 {
99 rq.lock();
100 try {
101 validateContext(sg2d);
102 rq.ensureCapacity(20);
103 buf.putInt(DRAW_RECT);
104 buf.putInt(x + sg2d.transX);
105 buf.putInt(y + sg2d.transY);
106 buf.putInt(width);
107 buf.putInt(height);
108 } finally {
109 rq.unlock();
110 }
111 }
112
113 public void fillRect(SunGraphics2D sg2d,
114 int x, int y, int width, int height)
115 {
116 rq.lock();
117 try {
118 validateContext(sg2d);
119 rq.ensureCapacity(20);
120 buf.putInt(FILL_RECT);
121 buf.putInt(x + sg2d.transX);
122 buf.putInt(y + sg2d.transY);
123 buf.putInt(width);
124 buf.putInt(height);
125 } finally {
126 rq.unlock();
127 }
128 }
129
130 public void drawRoundRect(SunGraphics2D sg2d,
131 int x, int y, int width, int height,
132 int arcWidth, int arcHeight)
133 {
134 draw(sg2d, new RoundRectangle2D.Float(x, y, width, height,
135 arcWidth, arcHeight));
136 }
137
138 public void fillRoundRect(SunGraphics2D sg2d,
139 int x, int y, int width, int height,
140 int arcWidth, int arcHeight)
141 {
142 fill(sg2d, new RoundRectangle2D.Float(x, y, width, height,
143 arcWidth, arcHeight));
144 }
145
146 public void drawOval(SunGraphics2D sg2d,
147 int x, int y, int width, int height)
148 {
149 draw(sg2d, new Ellipse2D.Float(x, y, width, height));
150 }
151
152 public void fillOval(SunGraphics2D sg2d,
153 int x, int y, int width, int height)
154 {
155 fill(sg2d, new Ellipse2D.Float(x, y, width, height));
156 }
157
158 public void drawArc(SunGraphics2D sg2d,
159 int x, int y, int width, int height,
160 int startAngle, int arcAngle)
161 {
162 draw(sg2d, new Arc2D.Float(x, y, width, height,
163 startAngle, arcAngle,
164 Arc2D.OPEN));
165 }
166
167 public void fillArc(SunGraphics2D sg2d,
168 int x, int y, int width, int height,
169 int startAngle, int arcAngle)
170 {
171 fill(sg2d, new Arc2D.Float(x, y, width, height,
172 startAngle, arcAngle,
173 Arc2D.PIE));
174 }
175
176 protected void drawPoly(final SunGraphics2D sg2d,
177 final int[] xPoints, final int[] yPoints,
178 final int nPoints, final boolean isClosed)
179 {
180 if (xPoints == null || yPoints == null) {
181 throw new NullPointerException("coordinate array");
182 }
183 if (xPoints.length < nPoints || yPoints.length < nPoints) {
184 throw new ArrayIndexOutOfBoundsException("coordinate array");
185 }
186
187 if (nPoints < 2) {
188 // render nothing
189 return;
190 } else if (nPoints == 2 && !isClosed) {
191 // render a simple line
192 drawLine(sg2d, xPoints[0], yPoints[0], xPoints[1], yPoints[1]);
193 return;
194 }
195
196 rq.lock();
197 try {
198 validateContext(sg2d);
199
200 int pointBytesRequired = nPoints * BYTES_PER_POLY_POINT;
201 int totalBytesRequired = 20 + pointBytesRequired;
202
203 if (totalBytesRequired <= buf.capacity()) {
204 if (totalBytesRequired > buf.remaining()) {
205 // process the queue first and then enqueue the points
206 rq.flushNow();
207 }
208 buf.putInt(DRAW_POLY);
209 // enqueue parameters
210 buf.putInt(nPoints);
211 buf.putInt(isClosed ? 1 : 0);
212 buf.putInt(sg2d.transX);
213 buf.putInt(sg2d.transY);
214 // enqueue the points
215 buf.put(xPoints, 0, nPoints);
216 buf.put(yPoints, 0, nPoints);
217 } else {
218 // queue is too small to accomodate all points; perform the
219 // operation directly on the queue flushing thread
220 rq.flushAndInvokeNow(new Runnable() {
221 public void run() {
222 drawPoly(xPoints, yPoints,
223 nPoints, isClosed,
224 sg2d.transX, sg2d.transY);
225 }
226 });
227 }
228 } finally {
229 rq.unlock();
230 }
231 }
232
233 protected abstract void drawPoly(int[] xPoints, int[] yPoints,
234 int nPoints, boolean isClosed,
235 int transX, int transY);
236
237 public void drawPolyline(SunGraphics2D sg2d,
238 int[] xPoints, int[] yPoints,
239 int nPoints)
240 {
241 drawPoly(sg2d, xPoints, yPoints, nPoints, false);
242 }
243
244 public void drawPolygon(SunGraphics2D sg2d,
245 int[] xPoints, int[] yPoints,
246 int nPoints)
247 {
248 drawPoly(sg2d, xPoints, yPoints, nPoints, true);
249 }
250
251 public void fillPolygon(SunGraphics2D sg2d,
252 int[] xPoints, int[] yPoints,
253 int nPoints)
254 {
255 fill(sg2d, new Polygon(xPoints, yPoints, nPoints));
256 }
257
258 private class BufferedDrawHandler
259 extends ProcessPath.DrawHandler
260 {
261 BufferedDrawHandler() {
262 // these are bogus values; the caller will use validate()
263 // to ensure that they are set properly prior to each usage
264 super(0, 0, 0, 0);
265 }
266
267 /**
268 * This method needs to be called prior to each draw/fillPath()
269 * operation to ensure the clip bounds are up to date.
270 */
271 void validate(SunGraphics2D sg2d) {
272 Region clip = sg2d.getCompClip();
273 setBounds(clip.getLoX(), clip.getLoY(),
274 clip.getHiX(), clip.getHiY(),
275 sg2d.strokeHint);
276 }
277
278 /**
279 * drawPath() support...
280 */
281
282 public void drawLine(int x1, int y1, int x2, int y2) {
283 // assert rq.lock.isHeldByCurrentThread();
284 rq.ensureCapacity(20);
285 buf.putInt(DRAW_LINE);
286 buf.putInt(x1);
287 buf.putInt(y1);
288 buf.putInt(x2);
289 buf.putInt(y2);
290 }
291
292 public void drawPixel(int x, int y) {
293 // assert rq.lock.isHeldByCurrentThread();
294 rq.ensureCapacity(12);
295 buf.putInt(DRAW_PIXEL);
296 buf.putInt(x);
297 buf.putInt(y);
298 }
299
300 /**
301 * fillPath() support...
302 */
303
304 private int scanlineCount;
305 private int scanlineCountIndex;
306 private int remainingScanlines;
307
308 private void resetFillPath() {
309 buf.putInt(DRAW_SCANLINES);
310 scanlineCountIndex = buf.position();
311 buf.putInt(0);
312 scanlineCount = 0;
313 remainingScanlines = buf.remaining() / BYTES_PER_SCANLINE;
314 }
315
316 private void updateScanlineCount() {
317 buf.putInt(scanlineCountIndex, scanlineCount);
318 }
319
320 /**
321 * Called from fillPath() to indicate that we are about to
322 * start issuing drawScanline() calls.
323 */
324 public void startFillPath() {
325 rq.ensureCapacity(20); // to ensure room for at least a scanline
326 resetFillPath();
327 }
328
329 public void drawScanline(int x1, int x2, int y) {
330 if (remainingScanlines == 0) {
331 updateScanlineCount();
332 rq.flushNow();
333 resetFillPath();
334 }
335 buf.putInt(x1);
336 buf.putInt(x2);
337 buf.putInt(y);
338 scanlineCount++;
339 remainingScanlines--;
340 }
341
342 /**
343 * Called from fillPath() to indicate that we are done
344 * issuing drawScanline() calls.
345 */
346 public void endFillPath() {
347 updateScanlineCount();
348 }
349 }
350
351 protected void drawPath(SunGraphics2D sg2d,
352 Path2D.Float p2df, int transx, int transy)
353 {
354 rq.lock();
355 try {
356 validateContext(sg2d);
357 drawHandler.validate(sg2d);
358 ProcessPath.drawPath(drawHandler, p2df, transx, transy);
359 } finally {
360 rq.unlock();
361 }
362 }
363
364 protected void fillPath(SunGraphics2D sg2d,
365 Path2D.Float p2df, int transx, int transy)
366 {
367 rq.lock();
368 try {
369 validateContext(sg2d);
370 drawHandler.validate(sg2d);
371 drawHandler.startFillPath();
372 ProcessPath.fillPath(drawHandler, p2df, transx, transy);
373 drawHandler.endFillPath();
374 } finally {
375 rq.unlock();
376 }
377 }
378
379 private native int fillSpans(RenderQueue rq, long buf,
380 int pos, int limit,
381 SpanIterator si, long iterator,
382 int transx, int transy);
383
384 protected void fillSpans(SunGraphics2D sg2d, SpanIterator si,
385 int transx, int transy)
386 {
387 rq.lock();
388 try {
389 validateContext(sg2d);
390 rq.ensureCapacity(24); // so that we have room for at least a span
391 int newpos = fillSpans(rq, buf.getAddress(),
392 buf.position(), buf.capacity(),
393 si, si.getNativeIterator(),
394 transx, transy);
395 buf.position(newpos);
396 } finally {
397 rq.unlock();
398 }
399 }
400
401 public void draw(SunGraphics2D sg2d, Shape s) {
402 if (sg2d.strokeState == sg2d.STROKE_THIN) {
403 if (s instanceof Polygon) {
404 if (sg2d.transformState < sg2d.TRANSFORM_TRANSLATESCALE) {
405 Polygon p = (Polygon)s;
406 drawPolygon(sg2d, p.xpoints, p.ypoints, p.npoints);
407 return;
408 }
409 }
410 Path2D.Float p2df;
411 int transx, transy;
412 if (sg2d.transformState <= sg2d.TRANSFORM_INT_TRANSLATE) {
413 if (s instanceof Path2D.Float) {
414 p2df = (Path2D.Float)s;
415 } else {
416 p2df = new Path2D.Float(s);
417 }
418 transx = sg2d.transX;
419 transy = sg2d.transY;
420 } else {
421 p2df = new Path2D.Float(s, sg2d.transform);
422 transx = 0;
423 transy = 0;
424 }
425 drawPath(sg2d, p2df, transx, transy);
426 } else if (sg2d.strokeState < sg2d.STROKE_CUSTOM) {
427 ShapeSpanIterator si = LoopPipe.getStrokeSpans(sg2d, s);
428 try {
429 fillSpans(sg2d, si, 0, 0);
430 } finally {
431 si.dispose();
432 }
433 } else {
434 fill(sg2d, sg2d.stroke.createStrokedShape(s));
435 }
436 }
437
438 public void fill(SunGraphics2D sg2d, Shape s) {
439 int transx, transy;
440
441 if (sg2d.strokeState == sg2d.STROKE_THIN) {
442 // Here we are able to use fillPath() for
443 // high-quality fills.
444 Path2D.Float p2df;
445 if (sg2d.transformState <= sg2d.TRANSFORM_INT_TRANSLATE) {
446 if (s instanceof Path2D.Float) {
447 p2df = (Path2D.Float)s;
448 } else {
449 p2df = new Path2D.Float(s);
450 }
451 transx = sg2d.transX;
452 transy = sg2d.transY;
453 } else {
454 p2df = new Path2D.Float(s, sg2d.transform);
455 transx = 0;
456 transy = 0;
457 }
458 fillPath(sg2d, p2df, transx, transy);
459 return;
460 }
461
462 AffineTransform at;
463 if (sg2d.transformState <= sg2d.TRANSFORM_INT_TRANSLATE) {
464 // Transform (translation) will be done by FillSpans (we could
465 // delegate to fillPolygon() here, but most hardware accelerated
466 // libraries cannot handle non-convex polygons, so we will use
467 // the FillSpans approach by default)
468 at = null;
469 transx = sg2d.transX;
470 transy = sg2d.transY;
471 } else {
472 // Transform will be done by the PathIterator
473 at = sg2d.transform;
474 transx = transy = 0;
475 }
476
477 ShapeSpanIterator ssi = LoopPipe.getFillSSI(sg2d);
478 try {
479 // Subtract transx/y from the SSI clip to match the
480 // (potentially untranslated) geometry fed to it
481 Region clip = sg2d.getCompClip();
482 ssi.setOutputAreaXYXY(clip.getLoX() - transx,
483 clip.getLoY() - transy,
484 clip.getHiX() - transx,
485 clip.getHiY() - transy);
486 ssi.appendPath(s.getPathIterator(at));
487 fillSpans(sg2d, ssi, transx, transy);
488 } finally {
489 ssi.dispose();
490 }
491 }
492}