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