1 /* 2 * Copyright (c) 2003, 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.opengl; 27 28 import java.awt.AlphaComposite; 29 import java.awt.GraphicsEnvironment; 30 import java.awt.Rectangle; 31 import java.awt.Transparency; 32 import java.awt.image.ColorModel; 33 import java.awt.image.Raster; 34 import sun.awt.SunHints; 35 import sun.awt.image.PixelConverter; 36 import sun.java2d.pipe.hw.AccelSurface; 37 import sun.java2d.SunGraphics2D; 38 import sun.java2d.SurfaceData; 39 import sun.java2d.SurfaceDataProxy; 40 import sun.java2d.loops.CompositeType; 41 import sun.java2d.loops.GraphicsPrimitive; 42 import sun.java2d.loops.MaskFill; 43 import sun.java2d.loops.SurfaceType; 44 import sun.java2d.pipe.ParallelogramPipe; 45 import sun.java2d.pipe.PixelToParallelogramConverter; 46 import sun.java2d.pipe.RenderBuffer; 47 import sun.java2d.pipe.TextPipe; 48 import static sun.java2d.pipe.BufferedOpCodes.*; 49 import static sun.java2d.opengl.OGLContext.OGLContextCaps.*; 50 51 /** 52 * This class describes an OpenGL "surface", that is, a region of pixels 53 * managed via OpenGL. An OGLSurfaceData can be tagged with one of three 54 * different SurfaceType objects for the purpose of registering loops, etc. 55 * This diagram shows the hierarchy of OGL SurfaceTypes: 56 * 57 * Any 58 * / \ 59 * OpenGLSurface OpenGLTexture 60 * | 61 * OpenGLSurfaceRTT 62 * 63 * OpenGLSurface 64 * This kind of surface can be rendered to using OpenGL APIs. It is also 65 * possible to copy an OpenGLSurface to another OpenGLSurface (or to itself). 66 * This is typically accomplished by calling MakeContextCurrent(dstSD, srcSD) 67 * and then calling glCopyPixels() (although there are other techniques to 68 * achieve the same goal). 69 * 70 * OpenGLTexture 71 * This kind of surface cannot be rendered to using OpenGL (in the same sense 72 * as in OpenGLSurface). However, it is possible to upload a region of pixels 73 * to an OpenGLTexture object via glTexSubImage2D(). One can also copy a 74 * surface of type OpenGLTexture to an OpenGLSurface by binding the texture 75 * to a quad and then rendering it to the destination surface (this process 76 * is known as "texture mapping"). 77 * 78 * OpenGLSurfaceRTT 79 * This kind of surface can be thought of as a sort of hybrid between 80 * OpenGLSurface and OpenGLTexture, in that one can render to this kind of 81 * surface as if it were of type OpenGLSurface, but the process of copying 82 * this kind of surface to another is more like an OpenGLTexture. (Note that 83 * "RTT" stands for "render-to-texture".) 84 * 85 * In addition to these SurfaceType variants, we have also defined some 86 * constants that describe in more detail the type of underlying OpenGL 87 * surface. This table helps explain the relationships between those 88 * "type" constants and their corresponding SurfaceType: 89 * 90 * OGL Type Corresponding SurfaceType 91 * -------- ------------------------- 92 * WINDOW OpenGLSurface 93 * PBUFFER OpenGLSurface 94 * TEXTURE OpenGLTexture 95 * FLIP_BACKBUFFER OpenGLSurface 96 * FBOBJECT OpenGLSurfaceRTT 97 */ 98 public abstract class OGLSurfaceData extends SurfaceData 99 implements AccelSurface { 100 101 /** 102 * OGL-specific surface types 103 * 104 * @see sun.java2d.pipe.hw.AccelSurface 105 */ 106 public static final int PBUFFER = RT_PLAIN; 107 public static final int FBOBJECT = RT_TEXTURE; 108 109 /** 110 * Pixel formats 111 */ 112 public static final int PF_INT_ARGB = 0; 113 public static final int PF_INT_ARGB_PRE = 1; 114 public static final int PF_INT_RGB = 2; 115 public static final int PF_INT_RGBX = 3; 116 public static final int PF_INT_BGR = 4; 117 public static final int PF_INT_BGRX = 5; 118 public static final int PF_USHORT_565_RGB = 6; 119 public static final int PF_USHORT_555_RGB = 7; 120 public static final int PF_USHORT_555_RGBX = 8; 121 public static final int PF_BYTE_GRAY = 9; 122 public static final int PF_USHORT_GRAY = 10; 123 public static final int PF_3BYTE_BGR = 11; 124 125 /** 126 * SurfaceTypes 127 */ 128 private static final String DESC_OPENGL_SURFACE = "OpenGL Surface"; 129 private static final String DESC_OPENGL_SURFACE_RTT = 130 "OpenGL Surface (render-to-texture)"; 131 private static final String DESC_OPENGL_TEXTURE = "OpenGL Texture"; 132 133 static final SurfaceType OpenGLSurface = 134 SurfaceType.Any.deriveSubType(DESC_OPENGL_SURFACE, 135 PixelConverter.ArgbPre.instance); 136 static final SurfaceType OpenGLSurfaceRTT = 137 OpenGLSurface.deriveSubType(DESC_OPENGL_SURFACE_RTT); 138 static final SurfaceType OpenGLTexture = 139 SurfaceType.Any.deriveSubType(DESC_OPENGL_TEXTURE); 140 141 /** This will be true if the fbobject system property has been enabled. */ 142 private static boolean isFBObjectEnabled; 143 144 /** This will be true if the lcdshader system property has been enabled.*/ 145 private static boolean isLCDShaderEnabled; 146 147 /** This will be true if the biopshader system property has been enabled.*/ 148 private static boolean isBIOpShaderEnabled; 149 150 /** This will be true if the gradshader system property has been enabled.*/ 151 private static boolean isGradShaderEnabled; 152 153 private OGLGraphicsConfig graphicsConfig; 154 protected int type; 155 // these fields are set from the native code when the surface is 156 // initialized 157 private int nativeWidth, nativeHeight; 158 159 protected static OGLRenderer oglRenderPipe; 160 protected static PixelToParallelogramConverter oglTxRenderPipe; 161 protected static ParallelogramPipe oglAAPgramPipe; 162 protected static OGLTextRenderer oglTextPipe; 163 protected static OGLDrawImage oglImagePipe; 164 165 protected native boolean initTexture(long pData, 166 boolean isOpaque, boolean texNonPow2, 167 boolean texRect, 168 int width, int height); 169 protected native boolean initFBObject(long pData, 170 boolean isOpaque, boolean texNonPow2, 171 boolean texRect, 172 int width, int height); 173 protected native boolean initFlipBackbuffer(long pData); 174 protected abstract boolean initPbuffer(long pData, long pConfigInfo, 175 boolean isOpaque, 176 int width, int height); 177 178 private native int getTextureTarget(long pData); 179 private native int getTextureID(long pData); 180 181 static { 182 if (!GraphicsEnvironment.isHeadless()) { 183 // fbobject currently enabled by default; use "false" to disable 184 String fbo = (String)java.security.AccessController.doPrivileged( 185 new sun.security.action.GetPropertyAction( 186 "sun.java2d.opengl.fbobject")); 187 isFBObjectEnabled = !"false".equals(fbo); 188 189 // lcdshader currently enabled by default; use "false" to disable 190 String lcd = (String)java.security.AccessController.doPrivileged( 191 new sun.security.action.GetPropertyAction( 192 "sun.java2d.opengl.lcdshader")); 193 isLCDShaderEnabled = !"false".equals(lcd); 194 195 // biopshader currently enabled by default; use "false" to disable 196 String biop = (String)java.security.AccessController.doPrivileged( 197 new sun.security.action.GetPropertyAction( 198 "sun.java2d.opengl.biopshader")); 199 isBIOpShaderEnabled = !"false".equals(biop); 200 201 // gradshader currently enabled by default; use "false" to disable 202 String grad = (String)java.security.AccessController.doPrivileged( 203 new sun.security.action.GetPropertyAction( 204 "sun.java2d.opengl.gradshader")); 205 isGradShaderEnabled = !"false".equals(grad); 206 207 OGLRenderQueue rq = OGLRenderQueue.getInstance(); 208 oglImagePipe = new OGLDrawImage(); 209 oglTextPipe = new OGLTextRenderer(rq); 210 oglRenderPipe = new OGLRenderer(rq); 211 if (GraphicsPrimitive.tracingEnabled()) { 212 oglTextPipe = oglTextPipe.traceWrap(); 213 //The wrapped oglRenderPipe will wrap the AA pipe as well... 214 //oglAAPgramPipe = oglRenderPipe.traceWrap(); 215 } 216 oglAAPgramPipe = oglRenderPipe.getAAParallelogramPipe(); 217 oglTxRenderPipe = 218 new PixelToParallelogramConverter(oglRenderPipe, 219 oglRenderPipe, 220 1.0, 0.25, true); 221 222 OGLBlitLoops.register(); 223 OGLMaskFill.register(); 224 OGLMaskBlit.register(); 225 } 226 } 227 228 protected OGLSurfaceData(OGLGraphicsConfig gc, 229 ColorModel cm, int type) 230 { 231 super(getCustomSurfaceType(type), cm); 232 this.graphicsConfig = gc; 233 this.type = type; 234 setBlitProxyKey(gc.getProxyKey()); 235 } 236 237 @Override 238 public SurfaceDataProxy makeProxyFor(SurfaceData srcData) { 239 return OGLSurfaceDataProxy.createProxy(srcData, graphicsConfig); 240 } 241 242 /** 243 * Returns the appropriate SurfaceType corresponding to the given OpenGL 244 * surface type constant (e.g. TEXTURE -> OpenGLTexture). 245 */ 246 private static SurfaceType getCustomSurfaceType(int oglType) { 247 switch (oglType) { 248 case TEXTURE: 249 return OpenGLTexture; 250 case FBOBJECT: 251 return OpenGLSurfaceRTT; 252 case PBUFFER: 253 default: 254 return OpenGLSurface; 255 } 256 } 257 258 /** 259 * Note: This should only be called from the QFT under the AWT lock. 260 * This method is kept separate from the initSurface() method below just 261 * to keep the code a bit cleaner. 262 */ 263 private void initSurfaceNow(int width, int height) { 264 boolean isOpaque = (getTransparency() == Transparency.OPAQUE); 265 boolean success = false; 266 267 switch (type) { 268 case PBUFFER: 269 success = initPbuffer(getNativeOps(), 270 graphicsConfig.getNativeConfigInfo(), 271 isOpaque, 272 width, height); 273 break; 274 275 case TEXTURE: 276 success = initTexture(getNativeOps(), 277 isOpaque, isTexNonPow2Available(), 278 isTexRectAvailable(), 279 width, height); 280 break; 281 282 case FBOBJECT: 283 success = initFBObject(getNativeOps(), 284 isOpaque, isTexNonPow2Available(), 285 isTexRectAvailable(), 286 width, height); 287 break; 288 289 case FLIP_BACKBUFFER: 290 success = initFlipBackbuffer(getNativeOps()); 291 break; 292 293 default: 294 break; 295 } 296 297 if (!success) { 298 throw new OutOfMemoryError("can't create offscreen surface"); 299 } 300 } 301 302 /** 303 * Initializes the appropriate OpenGL offscreen surface based on the value 304 * of the type parameter. If the surface creation fails for any reason, 305 * an OutOfMemoryError will be thrown. 306 */ 307 protected void initSurface(final int width, final int height) { 308 OGLRenderQueue rq = OGLRenderQueue.getInstance(); 309 rq.lock(); 310 try { 311 switch (type) { 312 case TEXTURE: 313 case PBUFFER: 314 case FBOBJECT: 315 // need to make sure the context is current before 316 // creating the texture (or pbuffer, or fbobject) 317 OGLContext.setScratchSurface(graphicsConfig); 318 break; 319 default: 320 break; 321 } 322 rq.flushAndInvokeNow(new Runnable() { 323 public void run() { 324 initSurfaceNow(width, height); 325 } 326 }); 327 } finally { 328 rq.unlock(); 329 } 330 } 331 332 /** 333 * Returns the OGLContext for the GraphicsConfig associated with this 334 * surface. 335 */ 336 public final OGLContext getContext() { 337 return graphicsConfig.getContext(); 338 } 339 340 /** 341 * Returns the OGLGraphicsConfig associated with this surface. 342 */ 343 final OGLGraphicsConfig getOGLGraphicsConfig() { 344 return graphicsConfig; 345 } 346 347 /** 348 * Returns one of the surface type constants defined above. 349 */ 350 public final int getType() { 351 return type; 352 } 353 354 /** 355 * If this surface is backed by a texture object, returns the target 356 * for that texture (either GL_TEXTURE_2D or GL_TEXTURE_RECTANGLE_ARB). 357 * Otherwise, this method will return zero. 358 */ 359 public final int getTextureTarget() { 360 return getTextureTarget(getNativeOps()); 361 } 362 363 /** 364 * If this surface is backed by a texture object, returns the texture ID 365 * for that texture. 366 * Otherwise, this method will return zero. 367 */ 368 public final int getTextureID() { 369 return getTextureID(getNativeOps()); 370 } 371 372 /** 373 * Returns native resource of specified {@code resType} associated with 374 * this surface. 375 * 376 * Specifically, for {@code OGLSurfaceData} this method returns the 377 * the following: 378 * <pre> 379 * TEXTURE - texture id 380 * </pre> 381 * 382 * Note: the resource returned by this method is only valid on the rendering 383 * thread. 384 * 385 * @return native resource of specified type or 0L if 386 * such resource doesn't exist or can not be retrieved. 387 * @see sun.java2d.pipe.hw.AccelSurface#getNativeResource 388 */ 389 public long getNativeResource(int resType) { 390 if (resType == TEXTURE) { 391 return getTextureID(); 392 } 393 return 0L; 394 } 395 396 public Raster getRaster(int x, int y, int w, int h) { 397 throw new InternalError("not implemented yet"); 398 } 399 400 /** 401 * For now, we can only render LCD text if: 402 * - the fragment shader extension is available, and 403 * - blending is disabled, and 404 * - the source color is opaque 405 * - and the destination is opaque 406 * 407 * Eventually, we could enhance the native OGL text rendering code 408 * and remove the above restrictions, but that would require significantly 409 * more code just to support a few uncommon cases. 410 */ 411 public boolean canRenderLCDText(SunGraphics2D sg2d) { 412 return 413 graphicsConfig.isCapPresent(CAPS_EXT_LCD_SHADER) && 414 sg2d.compositeState <= SunGraphics2D.COMP_ISCOPY && 415 sg2d.paintState <= SunGraphics2D.PAINT_OPAQUECOLOR && 416 sg2d.surfaceData.getTransparency() == Transparency.OPAQUE; 417 } 418 419 public void validatePipe(SunGraphics2D sg2d) { 420 TextPipe textpipe; 421 boolean validated = false; 422 423 // OGLTextRenderer handles both AA and non-AA text, but 424 // only works with the following modes: 425 // (Note: For LCD text we only enter this code path if 426 // canRenderLCDText() has already validated that the mode is 427 // CompositeType.SrcNoEa (opaque color), which will be subsumed 428 // by the CompositeType.SrcNoEa (any color) test below.) 429 430 if (/* CompositeType.SrcNoEa (any color) */ 431 (sg2d.compositeState <= SunGraphics2D.COMP_ISCOPY && 432 sg2d.paintState <= SunGraphics2D.PAINT_ALPHACOLOR) || 433 434 /* CompositeType.SrcOver (any color) */ 435 (sg2d.compositeState == SunGraphics2D.COMP_ALPHA && 436 sg2d.paintState <= SunGraphics2D.PAINT_ALPHACOLOR && 437 (((AlphaComposite)sg2d.composite).getRule() == 438 AlphaComposite.SRC_OVER)) || 439 440 /* CompositeType.Xor (any color) */ 441 (sg2d.compositeState == SunGraphics2D.COMP_XOR && 442 sg2d.paintState <= SunGraphics2D.PAINT_ALPHACOLOR)) 443 { 444 textpipe = oglTextPipe; 445 } else { 446 // do this to initialize textpipe correctly; we will attempt 447 // to override the non-text pipes below 448 super.validatePipe(sg2d); 449 textpipe = sg2d.textpipe; 450 validated = true; 451 } 452 453 PixelToParallelogramConverter txPipe = null; 454 OGLRenderer nonTxPipe = null; 455 456 if (sg2d.antialiasHint != SunHints.INTVAL_ANTIALIAS_ON) { 457 if (sg2d.paintState <= SunGraphics2D.PAINT_ALPHACOLOR) { 458 if (sg2d.compositeState <= SunGraphics2D.COMP_XOR) { 459 txPipe = oglTxRenderPipe; 460 nonTxPipe = oglRenderPipe; 461 } 462 } else if (sg2d.compositeState <= SunGraphics2D.COMP_ALPHA) { 463 if (OGLPaints.isValid(sg2d)) { 464 txPipe = oglTxRenderPipe; 465 nonTxPipe = oglRenderPipe; 466 } 467 // custom paints handled by super.validatePipe() below 468 } 469 } else { 470 if (sg2d.paintState <= SunGraphics2D.PAINT_ALPHACOLOR) { 471 if (graphicsConfig.isCapPresent(CAPS_PS30) && 472 (sg2d.imageComp == CompositeType.SrcOverNoEa || 473 sg2d.imageComp == CompositeType.SrcOver)) 474 { 475 if (!validated) { 476 super.validatePipe(sg2d); 477 validated = true; 478 } 479 PixelToParallelogramConverter aaConverter = 480 new PixelToParallelogramConverter(sg2d.shapepipe, 481 oglAAPgramPipe, 482 1.0/8.0, 0.499, 483 false); 484 sg2d.drawpipe = aaConverter; 485 sg2d.fillpipe = aaConverter; 486 sg2d.shapepipe = aaConverter; 487 } else if (sg2d.compositeState == SunGraphics2D.COMP_XOR) { 488 // install the solid pipes when AA and XOR are both enabled 489 txPipe = oglTxRenderPipe; 490 nonTxPipe = oglRenderPipe; 491 } 492 } 493 // other cases handled by super.validatePipe() below 494 } 495 496 if (txPipe != null) { 497 if (sg2d.transformState >= SunGraphics2D.TRANSFORM_TRANSLATESCALE) { 498 sg2d.drawpipe = txPipe; 499 sg2d.fillpipe = txPipe; 500 } else if (sg2d.strokeState != SunGraphics2D.STROKE_THIN) { 501 sg2d.drawpipe = txPipe; 502 sg2d.fillpipe = nonTxPipe; 503 } else { 504 sg2d.drawpipe = nonTxPipe; 505 sg2d.fillpipe = nonTxPipe; 506 } 507 // Note that we use the transforming pipe here because it 508 // will examine the shape and possibly perform an optimized 509 // operation if it can be simplified. The simplifications 510 // will be valid for all STROKE and TRANSFORM types. 511 sg2d.shapepipe = txPipe; 512 } else { 513 if (!validated) { 514 super.validatePipe(sg2d); 515 } 516 } 517 518 // install the text pipe based on our earlier decision 519 sg2d.textpipe = textpipe; 520 521 // always override the image pipe with the specialized OGL pipe 522 sg2d.imagepipe = oglImagePipe; 523 } 524 525 @Override 526 protected MaskFill getMaskFill(SunGraphics2D sg2d) { 527 if (sg2d.paintState > SunGraphics2D.PAINT_ALPHACOLOR) { 528 /* 529 * We can only accelerate non-Color MaskFill operations if 530 * all of the following conditions hold true: 531 * - there is an implementation for the given paintState 532 * - the current Paint can be accelerated for this destination 533 * - multitexturing is available (since we need to modulate 534 * the alpha mask texture with the paint texture) 535 * 536 * In all other cases, we return null, in which case the 537 * validation code will choose a more general software-based loop. 538 */ 539 if (!OGLPaints.isValid(sg2d) || 540 !graphicsConfig.isCapPresent(CAPS_MULTITEXTURE)) 541 { 542 return null; 543 } 544 } 545 return super.getMaskFill(sg2d); 546 } 547 548 public boolean copyArea(SunGraphics2D sg2d, 549 int x, int y, int w, int h, int dx, int dy) 550 { 551 if (sg2d.transformState < SunGraphics2D.TRANSFORM_TRANSLATESCALE && 552 sg2d.compositeState < SunGraphics2D.COMP_XOR) 553 { 554 x += sg2d.transX; 555 y += sg2d.transY; 556 557 oglRenderPipe.copyArea(sg2d, x, y, w, h, dx, dy); 558 559 return true; 560 } 561 return false; 562 } 563 564 public void flush() { 565 invalidate(); 566 OGLRenderQueue rq = OGLRenderQueue.getInstance(); 567 rq.lock(); 568 try { 569 // make sure we have a current context before 570 // disposing the native resources (e.g. texture object) 571 OGLContext.setScratchSurface(graphicsConfig); 572 573 RenderBuffer buf = rq.getBuffer(); 574 rq.ensureCapacityAndAlignment(12, 4); 575 buf.putInt(FLUSH_SURFACE); 576 buf.putLong(getNativeOps()); 577 578 // this call is expected to complete synchronously, so flush now 579 rq.flushNow(); 580 } finally { 581 rq.unlock(); 582 } 583 } 584 585 /** 586 * Disposes the native resources associated with the given OGLSurfaceData 587 * (referenced by the pData parameter). This method is invoked from 588 * the native Dispose() method from the Disposer thread when the 589 * Java-level OGLSurfaceData object is about to go away. Note that we 590 * also pass a reference to the native GLX/WGLGraphicsConfigInfo 591 * (pConfigInfo) for the purposes of making a context current. 592 */ 593 static void dispose(long pData, long pConfigInfo) { 594 OGLRenderQueue rq = OGLRenderQueue.getInstance(); 595 rq.lock(); 596 try { 597 // make sure we have a current context before 598 // disposing the native resources (e.g. texture object) 599 OGLContext.setScratchSurface(pConfigInfo); 600 601 RenderBuffer buf = rq.getBuffer(); 602 rq.ensureCapacityAndAlignment(12, 4); 603 buf.putInt(DISPOSE_SURFACE); 604 buf.putLong(pData); 605 606 // this call is expected to complete synchronously, so flush now 607 rq.flushNow(); 608 } finally { 609 rq.unlock(); 610 } 611 } 612 613 static void swapBuffers(long window) { 614 OGLRenderQueue rq = OGLRenderQueue.getInstance(); 615 rq.lock(); 616 try { 617 RenderBuffer buf = rq.getBuffer(); 618 rq.ensureCapacityAndAlignment(12, 4); 619 buf.putInt(SWAP_BUFFERS); 620 buf.putLong(window); 621 rq.flushNow(); 622 } finally { 623 rq.unlock(); 624 } 625 } 626 627 /** 628 * Returns true if OpenGL textures can have non-power-of-two dimensions 629 * when using the basic GL_TEXTURE_2D target. 630 */ 631 boolean isTexNonPow2Available() { 632 return graphicsConfig.isCapPresent(CAPS_TEXNONPOW2); 633 } 634 635 /** 636 * Returns true if OpenGL textures can have non-power-of-two dimensions 637 * when using the GL_TEXTURE_RECTANGLE_ARB target (only available when the 638 * GL_ARB_texture_rectangle extension is present). 639 */ 640 boolean isTexRectAvailable() { 641 return graphicsConfig.isCapPresent(CAPS_EXT_TEXRECT); 642 } 643 644 public Rectangle getNativeBounds() { 645 OGLRenderQueue rq = OGLRenderQueue.getInstance(); 646 rq.lock(); 647 try { 648 return new Rectangle(nativeWidth, nativeHeight); 649 } finally { 650 rq.unlock(); 651 } 652 } 653 654 /** 655 * Returns true if the surface is an on-screen window surface or 656 * a FBO texture attached to an on-screen CALayer. 657 * 658 * Needed by Mac OS X port. 659 */ 660 boolean isOnScreen() { 661 return getType() == WINDOW; 662 } 663 }