1 /* 2 * Copyright (c) 2010, 2011, 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 java.awt.image.*; 31 import sun.awt.*; 32 import sun.java2d.InvalidPipeException; 33 import sun.java2d.SunGraphics2D; 34 import sun.java2d.SurfaceData; 35 import sun.java2d.SurfaceDataProxy; 36 import sun.java2d.jules.*; 37 import sun.java2d.loops.*; 38 import sun.java2d.pipe.*; 39 import sun.java2d.x11.*; 40 import sun.font.FontManagerNativeLibrary; 41 42 public abstract class XRSurfaceData extends XSurfaceData { 43 X11ComponentPeer peer; 44 XRGraphicsConfig graphicsConfig; 45 XRBackend renderQueue; 46 47 private RenderLoops solidloops; 48 49 protected int depth; 50 51 private static native void initIDs(); 52 53 protected native void XRInitSurface(int depth, int width, int height, 54 long drawable, int pictFormat); 55 56 native void initXRPicture(long xsdo, int pictForm); 57 58 native void freeXSDOPicture(long xsdo); 59 60 public static final String DESC_BYTE_A8_X11 = "Byte A8 Pixmap"; 61 public static final String DESC_INT_RGB_X11 = "Integer RGB Pixmap"; 62 public static final String DESC_INT_ARGB_X11 = "Integer ARGB-Pre Pixmap"; 63 64 public static final SurfaceType 65 ByteA8X11 = SurfaceType.ByteGray.deriveSubType(DESC_BYTE_A8_X11); 66 public static final SurfaceType 67 IntRgbX11 = SurfaceType.IntRgb.deriveSubType(DESC_INT_RGB_X11); 68 public static final SurfaceType 69 IntArgbPreX11 = SurfaceType.IntArgbPre.deriveSubType(DESC_INT_ARGB_X11); 70 71 public Raster getRaster(int x, int y, int w, int h) { 72 throw new InternalError("not implemented yet"); 73 } 74 75 protected XRRenderer xrpipe; 76 protected PixelToShapeConverter xrtxpipe; 77 protected TextPipe xrtextpipe; 78 protected XRDrawImage xrDrawImage; 79 80 protected ShapeDrawPipe aaShapePipe; 81 protected PixelToShapeConverter aaPixelToShapeConv; 82 83 public static void initXRSurfaceData() { 84 if (!isX11SurfaceDataInitialized()) { 85 FontManagerNativeLibrary.load(); 86 initIDs(); 87 XRPMBlitLoops.register(); 88 XRMaskFill.register(); 89 XRMaskBlit.register(); 90 91 setX11SurfaceDataInitialized(); 92 } 93 } 94 95 /** 96 * Synchronized accessor method for isDrawableValid. 97 */ 98 protected boolean isXRDrawableValid() { 99 try { 100 SunToolkit.awtLock(); 101 return isDrawableValid(); 102 } finally { 103 SunToolkit.awtUnlock(); 104 } 105 } 106 107 @Override 108 public SurfaceDataProxy makeProxyFor(SurfaceData srcData) { 109 return XRSurfaceDataProxy.createProxy(srcData, graphicsConfig); 110 } 111 112 public void validatePipe(SunGraphics2D sg2d) { 113 TextPipe textpipe; 114 boolean validated = false; 115 116 /* 117 * The textpipe for now can't handle TexturePaint when extra-alpha is 118 * specified nore XOR mode 119 */ 120 if (sg2d.compositeState < SunGraphics2D.COMP_XOR && 121 (sg2d.paintState < SunGraphics2D.PAINT_TEXTURE || 122 sg2d.composite == null || 123 !(sg2d.composite instanceof AlphaComposite) || 124 ((AlphaComposite) sg2d.composite).getAlpha() == 1.0f)) 125 { 126 textpipe = xrtextpipe; 127 } else { 128 super.validatePipe(sg2d); 129 textpipe = sg2d.textpipe; 130 validated = true; 131 } 132 133 PixelToShapeConverter txPipe = null; 134 XRRenderer nonTxPipe = null; 135 136 /* 137 * TODO: Can we rely on the GC for ARGB32 surfaces? 138 */ 139 if (sg2d.antialiasHint != SunHints.INTVAL_ANTIALIAS_ON) { 140 if (sg2d.paintState <= SunGraphics2D.PAINT_ALPHACOLOR) { 141 if (sg2d.compositeState <= SunGraphics2D.COMP_XOR) { 142 txPipe = xrtxpipe; 143 nonTxPipe = xrpipe; 144 } 145 } else if (sg2d.compositeState <= SunGraphics2D.COMP_ALPHA) { 146 if (XRPaints.isValid(sg2d)) { 147 txPipe = xrtxpipe; 148 nonTxPipe = xrpipe; 149 } 150 // custom paints handled by super.validatePipe() below 151 } 152 } 153 154 if (sg2d.antialiasHint == SunHints.INTVAL_ANTIALIAS_ON && 155 JulesPathBuf.isCairoAvailable()) 156 { 157 sg2d.shapepipe = aaShapePipe; 158 sg2d.drawpipe = aaPixelToShapeConv; 159 sg2d.fillpipe = aaPixelToShapeConv; 160 } else { 161 if (txPipe != null) { 162 if (sg2d.transformState >= SunGraphics2D.TRANSFORM_TRANSLATESCALE) { 163 sg2d.drawpipe = txPipe; 164 sg2d.fillpipe = txPipe; 165 } else if (sg2d.strokeState != SunGraphics2D.STROKE_THIN) { 166 sg2d.drawpipe = txPipe; 167 sg2d.fillpipe = nonTxPipe; 168 } else { 169 sg2d.drawpipe = nonTxPipe; 170 sg2d.fillpipe = nonTxPipe; 171 } 172 sg2d.shapepipe = nonTxPipe; 173 } else { 174 if (!validated) { 175 super.validatePipe(sg2d); 176 } 177 } 178 } 179 180 // install the text pipe based on our earlier decision 181 sg2d.textpipe = textpipe; 182 183 // always override the image pipe with the specialized XRender pipe 184 sg2d.imagepipe = xrDrawImage; 185 } 186 187 protected MaskFill getMaskFill(SunGraphics2D sg2d) { 188 if (sg2d.paintState > SunGraphics2D.PAINT_ALPHACOLOR && 189 !XRPaints.isValid(sg2d)) 190 { 191 return null; 192 } 193 return super.getMaskFill(sg2d); 194 } 195 196 public RenderLoops getRenderLoops(SunGraphics2D sg2d) { 197 if (sg2d.paintState <= SunGraphics2D.PAINT_ALPHACOLOR && 198 sg2d.compositeState <= SunGraphics2D.COMP_ALPHA) 199 { 200 return solidloops; 201 } 202 203 return super.getRenderLoops(sg2d); 204 } 205 206 public GraphicsConfiguration getDeviceConfiguration() { 207 return graphicsConfig; 208 } 209 210 /** 211 * Method for instantiating a Window SurfaceData 212 */ 213 public static XRWindowSurfaceData createData(X11ComponentPeer peer) { 214 XRGraphicsConfig gc = getGC(peer); 215 return new XRWindowSurfaceData(peer, gc, gc.getSurfaceType()); 216 } 217 218 /** 219 * Method for instantiating a Pixmap SurfaceData (offscreen). 220 * If the surface * is opaque a 24-bit/RGB surface is chosen, 221 * otherwise a 32-bit ARGB surface. 222 */ 223 public static XRPixmapSurfaceData createData(XRGraphicsConfig gc, 224 int width, int height, 225 ColorModel cm, Image image, 226 long drawable, 227 int transparency) { 228 int depth = transparency > Transparency.OPAQUE ? 32 : 24; 229 if (depth == 24) { 230 cm = new DirectColorModel(depth, 231 0x00FF0000, 0x0000FF00, 0x000000FF); 232 } else { 233 cm = new DirectColorModel(depth, 0x00FF0000, 0x0000FF00, 234 0x000000FF, 0xFF000000); 235 } 236 237 return new XRPixmapSurfaceData 238 (gc, width, height, image, getSurfaceType(gc, transparency), 239 cm, drawable, transparency, 240 XRUtils.getPictureFormatForTransparency(transparency), depth); 241 } 242 243 protected XRSurfaceData(X11ComponentPeer peer, XRGraphicsConfig gc, 244 SurfaceType sType, ColorModel cm, int depth, int transparency) 245 { 246 super(sType, cm); 247 this.peer = peer; 248 this.graphicsConfig = gc; 249 this.solidloops = graphicsConfig.getSolidLoops(sType); 250 this.depth = depth; 251 initOps(peer, graphicsConfig, depth); 252 253 setBlitProxyKey(gc.getProxyKey()); 254 } 255 256 protected XRSurfaceData(XRBackend renderQueue) { 257 super(XRSurfaceData.IntRgbX11, 258 new DirectColorModel(24, 0x00FF0000, 0x0000FF00, 0x000000FF)); 259 this.renderQueue = renderQueue; 260 } 261 262 /** 263 * Inits the XRender-data-structures which belong to the XRSurfaceData. 264 * 265 * @param pictureFormat 266 */ 267 public void initXRender(int pictureFormat) { 268 try { 269 SunToolkit.awtLock(); 270 initXRPicture(getNativeOps(), pictureFormat); 271 renderQueue = XRCompositeManager.getInstance(this).getBackend(); 272 maskBuffer = XRCompositeManager.getInstance(this); 273 } catch (Throwable ex) { 274 ex.printStackTrace(); 275 } finally { 276 SunToolkit.awtUnlock(); 277 } 278 } 279 280 public static XRGraphicsConfig getGC(X11ComponentPeer peer) { 281 if (peer != null) { 282 return (XRGraphicsConfig) peer.getGraphicsConfiguration(); 283 } else { 284 GraphicsEnvironment env = 285 GraphicsEnvironment.getLocalGraphicsEnvironment(); 286 GraphicsDevice gd = env.getDefaultScreenDevice(); 287 return (XRGraphicsConfig) gd.getDefaultConfiguration(); 288 } 289 } 290 291 /** 292 * Returns a boolean indicating whether or not a copyArea from the given 293 * rectangle source coordinates might be incomplete and result in X11 294 * GraphicsExposure events being generated from XCopyArea. This method 295 * allows the SurfaceData copyArea method to determine if it needs to set 296 * the GraphicsExposures attribute of the X11 GC to True or False to receive 297 * or avoid the events. 298 * 299 * @return true if there is any chance that an XCopyArea from the given 300 * source coordinates could produce any X11 Exposure events. 301 */ 302 public abstract boolean canSourceSendExposures(int x, int y, int w, int h); 303 304 /** 305 * CopyArea is implemented using the "old" X11 GC, therefor clip and 306 * needExposures have to be validated against that GC. Pictures and GCs 307 * don't share state. 308 */ 309 public void validateCopyAreaGC(Region gcClip, boolean needExposures) { 310 if (validatedGCClip != gcClip) { 311 if (gcClip != null) 312 renderQueue.setGCClipRectangles(xgc, gcClip); 313 validatedGCClip = gcClip; 314 } 315 316 if (validatedExposures != needExposures) { 317 validatedExposures = needExposures; 318 renderQueue.setGCExposures(xgc, needExposures); 319 } 320 321 if (validatedXorComp != null) { 322 renderQueue.setGCMode(xgc, true); 323 renderQueue.setGCForeground(xgc, validatedGCForegroundPixel); 324 validatedXorComp = null; 325 } 326 } 327 328 public boolean copyArea(SunGraphics2D sg2d, int x, int y, int w, int h, 329 int dx, int dy) { 330 if (xrpipe == null) { 331 if (!isXRDrawableValid()) { 332 return true; 333 } 334 makePipes(); 335 } 336 CompositeType comptype = sg2d.imageComp; 337 if (sg2d.transformState < SunGraphics2D.TRANSFORM_TRANSLATESCALE && 338 (CompositeType.SrcOverNoEa.equals(comptype) || 339 CompositeType.SrcNoEa.equals(comptype))) 340 { 341 x += sg2d.transX; 342 y += sg2d.transY; 343 try { 344 SunToolkit.awtLock(); 345 boolean needExposures = canSourceSendExposures(x, y, w, h); 346 validateCopyAreaGC(sg2d.getCompClip(), needExposures); 347 renderQueue.copyArea(xid, xid, xgc, x, y, w, h, x + dx, y + dy); 348 } finally { 349 SunToolkit.awtUnlock(); 350 } 351 return true; 352 } 353 return false; 354 } 355 356 /** 357 * Returns the XRender SurfaceType which is able to fullfill the specified 358 * transparency requirement. 359 */ 360 public static SurfaceType getSurfaceType(XRGraphicsConfig gc, 361 int transparency) { 362 SurfaceType sType = null; 363 364 switch (transparency) { 365 case Transparency.OPAQUE: 366 sType = XRSurfaceData.IntRgbX11; 367 break; 368 369 case Transparency.BITMASK: 370 case Transparency.TRANSLUCENT: 371 sType = XRSurfaceData.IntArgbPreX11; 372 break; 373 } 374 375 return sType; 376 } 377 378 public void invalidate() { 379 if (isValid()) { 380 setInvalid(); 381 super.invalidate(); 382 } 383 } 384 385 private long xgc; // GC is still used for copyArea 386 private int validatedGCForegroundPixel = 0; 387 private XORComposite validatedXorComp; 388 private int xid; 389 public int picture; 390 public XRCompositeManager maskBuffer; 391 392 private Region validatedClip; 393 private Region validatedGCClip; 394 private boolean validatedExposures = true; 395 396 boolean transformInUse = false; 397 AffineTransform validatedSourceTransform = new AffineTransform(); 398 int validatedRepeat = XRUtils.RepeatNone; 399 int validatedFilter = XRUtils.FAST; 400 401 /** 402 * Validates an XRSurfaceData when used as source. Note that the clip is 403 * applied when used as source as well as destination. 404 */ 405 void validateAsSource(AffineTransform sxForm, int repeat, int filter) { 406 407 if (validatedClip != null) { 408 validatedClip = null; 409 renderQueue.setClipRectangles(picture, null); 410 } 411 412 if (validatedRepeat != repeat && repeat != -1) { 413 validatedRepeat = repeat; 414 renderQueue.setPictureRepeat(picture, repeat); 415 } 416 417 if (sxForm == null) { 418 if (transformInUse) { 419 validatedSourceTransform.setToIdentity(); 420 renderQueue.setPictureTransform(picture, 421 validatedSourceTransform); 422 transformInUse = false; 423 } 424 } else if (!transformInUse || 425 (transformInUse && !sxForm.equals(validatedSourceTransform))) { 426 validatedSourceTransform.setTransform(sxForm.getScaleX(), 427 sxForm.getShearY(), 428 sxForm.getShearX(), 429 sxForm.getScaleY(), 430 sxForm.getTranslateX(), 431 sxForm.getTranslateY()); 432 renderQueue.setPictureTransform(picture, validatedSourceTransform); 433 transformInUse = true; 434 } 435 436 if (filter != validatedFilter && filter != -1) { 437 renderQueue.setFilter(picture, filter); 438 validatedFilter = filter; 439 } 440 } 441 442 /** 443 * Validates the Surface when used as destination. 444 */ 445 public void validateAsDestination(SunGraphics2D sg2d, Region clip) { 446 if (!isValid()) { 447 throw new InvalidPipeException("bounds changed"); 448 } 449 450 boolean updateGCClip = false; 451 if (clip != validatedClip) { 452 renderQueue.setClipRectangles(picture, clip); 453 validatedClip = clip; 454 updateGCClip = true; 455 } 456 457 if (sg2d != null && sg2d.compositeState == SunGraphics2D.COMP_XOR) { 458 if (validatedXorComp != sg2d.getComposite()) { 459 validatedXorComp = (XORComposite) sg2d.getComposite(); 460 int xorpixelmod = validatedXorComp.getXorPixel(); 461 renderQueue.setGCMode(xgc, false); 462 463 // validate pixel 464 int pixel = sg2d.pixel; 465 if (validatedGCForegroundPixel != pixel) { 466 renderQueue.setGCForeground(xgc, pixel ^ xorpixelmod); 467 validatedGCForegroundPixel = pixel; 468 } 469 } 470 471 if (updateGCClip) { 472 renderQueue.setGCClipRectangles(xgc, clip); 473 } 474 } 475 } 476 477 public synchronized void makePipes() { /* 478 * TODO: Why is this synchronized, 479 * but access not? 480 */ 481 if (xrpipe == null) { 482 try { 483 SunToolkit.awtLock(); 484 xgc = XCreateGC(getNativeOps()); 485 486 xrpipe = new XRRenderer(maskBuffer.getMaskBuffer()); 487 xrtxpipe = new PixelToShapeConverter(xrpipe); 488 xrtextpipe = maskBuffer.getTextRenderer(); 489 xrDrawImage = new XRDrawImage(); 490 491 if (JulesPathBuf.isCairoAvailable()) { 492 aaShapePipe = 493 new JulesShapePipe(XRCompositeManager.getInstance(this)); 494 aaPixelToShapeConv = new PixelToShapeConverter(aaShapePipe); 495 } 496 } finally { 497 SunToolkit.awtUnlock(); 498 } 499 } 500 } 501 502 public static class XRWindowSurfaceData extends XRSurfaceData { 503 public XRWindowSurfaceData(X11ComponentPeer peer, 504 XRGraphicsConfig gc, SurfaceType sType) { 505 super(peer, gc, sType, peer.getColorModel(), 506 peer.getColorModel().getPixelSize(), Transparency.OPAQUE); 507 508 if (isXRDrawableValid()) { 509 initXRender(XRUtils. 510 getPictureFormatForTransparency(Transparency.OPAQUE)); 511 makePipes(); 512 } 513 } 514 515 public SurfaceData getReplacement() { 516 return peer.getSurfaceData(); 517 } 518 519 public Rectangle getBounds() { 520 Rectangle r = peer.getBounds(); 521 r.x = r.y = 0; 522 return r; 523 } 524 525 @Override 526 public boolean canSourceSendExposures(int x, int y, int w, int h) { 527 return true; 528 } 529 530 /** 531 * Returns destination Component associated with this SurfaceData. 532 */ 533 public Object getDestination() { 534 return peer.getTarget(); 535 } 536 537 public void invalidate() { 538 try { 539 SunToolkit.awtLock(); 540 freeXSDOPicture(getNativeOps()); 541 }finally { 542 SunToolkit.awtUnlock(); 543 } 544 545 super.invalidate(); 546 } 547 } 548 549 public static class XRInternalSurfaceData extends XRSurfaceData { 550 public XRInternalSurfaceData(XRBackend renderQueue, int pictXid, 551 AffineTransform transform) { 552 super(renderQueue); 553 this.picture = pictXid; 554 this.validatedSourceTransform = transform; 555 556 if (validatedSourceTransform != null) { 557 transformInUse = true; 558 } 559 } 560 561 public boolean canSourceSendExposures(int x, int y, int w, int h) { 562 return false; 563 } 564 565 public Rectangle getBounds() { 566 return null; 567 } 568 569 public Object getDestination() { 570 return null; 571 } 572 573 public SurfaceData getReplacement() { 574 return null; 575 } 576 } 577 578 public static class XRPixmapSurfaceData extends XRSurfaceData { 579 Image offscreenImage; 580 int width; 581 int height; 582 int transparency; 583 584 public XRPixmapSurfaceData(XRGraphicsConfig gc, int width, int height, 585 Image image, SurfaceType sType, 586 ColorModel cm, long drawable, 587 int transparency, int pictFormat, 588 int depth) { 589 super(null, gc, sType, cm, depth, transparency); 590 this.width = width; 591 this.height = height; 592 offscreenImage = image; 593 this.transparency = transparency; 594 initSurface(depth, width, height, drawable, pictFormat); 595 596 initXRender(pictFormat); 597 makePipes(); 598 } 599 600 public void initSurface(int depth, int width, int height, 601 long drawable, int pictFormat) { 602 try { 603 SunToolkit.awtLock(); 604 XRInitSurface(depth, width, height, drawable, pictFormat); 605 } finally { 606 SunToolkit.awtUnlock(); 607 } 608 } 609 610 public SurfaceData getReplacement() { 611 return restoreContents(offscreenImage); 612 } 613 614 /** 615 * Need this since the surface data is created with the color model of 616 * the target GC, which is always opaque. But in SunGraphics2D.blitSD we 617 * choose loops based on the transparency on the source SD, so it could 618 * choose wrong loop (blit instead of blitbg, for example). 619 */ 620 public int getTransparency() { 621 return transparency; 622 } 623 624 public Rectangle getBounds() { 625 return new Rectangle(width, height); 626 } 627 628 @Override 629 public boolean canSourceSendExposures(int x, int y, int w, int h) { 630 return (x < 0 || y < 0 || (x + w) > width || (y + h) > height); 631 } 632 633 public void flush() { 634 /* 635 * We need to invalidate the surface before disposing the native 636 * Drawable and Picture. This way if an application tries to render 637 * to an already flushed XRSurfaceData, we will notice in the 638 * validate() method above that it has been invalidated, and we will 639 * avoid using those native resources that have already been 640 * disposed. 641 */ 642 invalidate(); 643 flushNativeSurface(); 644 } 645 646 /** 647 * Returns destination Image associated with this SurfaceData. 648 */ 649 public Object getDestination() { 650 return offscreenImage; 651 } 652 } 653 654 public long getGC() { 655 return xgc; 656 } 657 658 public static class LazyPipe extends ValidatePipe { 659 public boolean validate(SunGraphics2D sg2d) { 660 XRSurfaceData xsd = (XRSurfaceData) sg2d.surfaceData; 661 if (!xsd.isXRDrawableValid()) { 662 return false; 663 } 664 xsd.makePipes(); 665 return super.validate(sg2d); 666 } 667 } 668 669 public int getPicture() { 670 return picture; 671 } 672 673 public int getXid() { 674 return xid; 675 } 676 677 public XRGraphicsConfig getGraphicsConfig() { 678 return graphicsConfig; 679 } 680 }