001: /*
002: * Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025:
026: package sun.java2d.opengl;
027:
028: import java.awt.AlphaComposite;
029: import java.awt.GraphicsEnvironment;
030: import java.awt.Transparency;
031: import java.awt.image.ColorModel;
032: import java.awt.image.Raster;
033: import sun.awt.SunHints;
034: import sun.awt.image.PixelConverter;
035: import sun.java2d.SunGraphics2D;
036: import sun.java2d.SurfaceData;
037: import sun.java2d.SurfaceDataProxy;
038: import sun.java2d.loops.GraphicsPrimitive;
039: import sun.java2d.loops.MaskFill;
040: import sun.java2d.loops.SurfaceType;
041: import sun.java2d.pipe.PixelToShapeConverter;
042: import sun.java2d.pipe.RenderBuffer;
043: import sun.java2d.pipe.RenderQueue;
044: import sun.java2d.pipe.TextPipe;
045: import static sun.java2d.pipe.BufferedOpCodes.*;
046: import static sun.java2d.opengl.OGLContext.*;
047:
048: /**
049: * This class describes an OpenGL "surface", that is, a region of pixels
050: * managed via OpenGL. An OGLSurfaceData can be tagged with one of three
051: * different SurfaceType objects for the purpose of registering loops, etc.
052: * This diagram shows the hierarchy of OGL SurfaceTypes:
053: *
054: * Any
055: * / \
056: * OpenGLSurface OpenGLTexture
057: * |
058: * OpenGLSurfaceRTT
059: *
060: * OpenGLSurface
061: * This kind of surface can be rendered to using OpenGL APIs. It is also
062: * possible to copy an OpenGLSurface to another OpenGLSurface (or to itself).
063: * This is typically accomplished by calling MakeContextCurrent(dstSD, srcSD)
064: * and then calling glCopyPixels() (although there are other techniques to
065: * achieve the same goal).
066: *
067: * OpenGLTexture
068: * This kind of surface cannot be rendered to using OpenGL (in the same sense
069: * as in OpenGLSurface). However, it is possible to upload a region of pixels
070: * to an OpenGLTexture object via glTexSubImage2D(). One can also copy a
071: * surface of type OpenGLTexture to an OpenGLSurface by binding the texture
072: * to a quad and then rendering it to the destination surface (this process
073: * is known as "texture mapping").
074: *
075: * OpenGLSurfaceRTT
076: * This kind of surface can be thought of as a sort of hybrid between
077: * OpenGLSurface and OpenGLTexture, in that one can render to this kind of
078: * surface as if it were of type OpenGLSurface, but the process of copying
079: * this kind of surface to another is more like an OpenGLTexture. (Note that
080: * "RTT" stands for "render-to-texture".)
081: *
082: * In addition to these SurfaceType variants, we have also defined some
083: * constants that describe in more detail the type of underlying OpenGL
084: * surface. This table helps explain the relationships between those
085: * "type" constants and their corresponding SurfaceType:
086: *
087: * OGL Type Corresponding SurfaceType
088: * -------- -------------------------
089: * WINDOW OpenGLSurface
090: * PBUFFER OpenGLSurface
091: * TEXTURE OpenGLTexture
092: * FLIP_BACKBUFFER OpenGLSurface
093: * FBOBJECT OpenGLSurfaceRTT
094: */
095: public abstract class OGLSurfaceData extends SurfaceData {
096:
097: /**
098: * OGL-specific surface types
099: */
100: public static final int UNDEFINED = 0;
101: public static final int WINDOW = 1;
102: public static final int PBUFFER = 2;
103: public static final int TEXTURE = 3;
104: public static final int FLIP_BACKBUFFER = 4;
105: public static final int FBOBJECT = 5;
106:
107: /**
108: * Pixel formats
109: */
110: public static final int PF_INT_ARGB = 0;
111: public static final int PF_INT_ARGB_PRE = 1;
112: public static final int PF_INT_RGB = 2;
113: public static final int PF_INT_RGBX = 3;
114: public static final int PF_INT_BGR = 4;
115: public static final int PF_INT_BGRX = 5;
116: public static final int PF_USHORT_565_RGB = 6;
117: public static final int PF_USHORT_555_RGB = 7;
118: public static final int PF_USHORT_555_RGBX = 8;
119: public static final int PF_BYTE_GRAY = 9;
120: public static final int PF_USHORT_GRAY = 10;
121:
122: /**
123: * SurfaceTypes
124: */
125: private static final String DESC_OPENGL_SURFACE = "OpenGL Surface";
126: private static final String DESC_OPENGL_SURFACE_RTT = "OpenGL Surface (render-to-texture)";
127: private static final String DESC_OPENGL_TEXTURE = "OpenGL Texture";
128:
129: static final SurfaceType OpenGLSurface = SurfaceType.Any
130: .deriveSubType(DESC_OPENGL_SURFACE,
131: PixelConverter.ArgbPre.instance);
132: static final SurfaceType OpenGLSurfaceRTT = OpenGLSurface
133: .deriveSubType(DESC_OPENGL_SURFACE_RTT);
134: static final SurfaceType OpenGLTexture = SurfaceType.Any
135: .deriveSubType(DESC_OPENGL_TEXTURE);
136:
137: /** This will be true if the fbobject system property has been enabled. */
138: private static boolean isFBObjectEnabled;
139:
140: /** This will be true if the lcdshader system property has been enabled.*/
141: private static boolean isLCDShaderEnabled;
142:
143: /** This will be true if the biopshader system property has been enabled.*/
144: private static boolean isBIOpShaderEnabled;
145:
146: /** This will be true if the gradshader system property has been enabled.*/
147: private static boolean isGradShaderEnabled;
148:
149: private OGLGraphicsConfig graphicsConfig;
150: private int textureTarget;
151: protected int type;
152:
153: protected static OGLRenderer oglRenderPipe;
154: protected static PixelToShapeConverter oglTxRenderPipe;
155: protected static OGLTextRenderer oglTextPipe;
156: protected static OGLDrawImage oglImagePipe;
157:
158: protected native boolean initTexture(long pData, boolean isOpaque,
159: boolean texNonPow2, boolean texRect, int width, int height);
160:
161: protected native boolean initFBObject(long pData, boolean isOpaque,
162: boolean texNonPow2, boolean texRect, int width, int height);
163:
164: protected native boolean initFlipBackbuffer(long pData);
165:
166: protected abstract boolean initPbuffer(long pData,
167: long pConfigInfo, boolean isOpaque, int width, int height);
168:
169: private native int getTextureTarget(long pData);
170:
171: static {
172: if (!GraphicsEnvironment.isHeadless()) {
173: // fbobject currently enabled by default; use "false" to disable
174: String fbo = (String) java.security.AccessController
175: .doPrivileged(new sun.security.action.GetPropertyAction(
176: "sun.java2d.opengl.fbobject"));
177: isFBObjectEnabled = !"false".equals(fbo);
178:
179: // lcdshader currently enabled by default; use "false" to disable
180: String lcd = (String) java.security.AccessController
181: .doPrivileged(new sun.security.action.GetPropertyAction(
182: "sun.java2d.opengl.lcdshader"));
183: isLCDShaderEnabled = !"false".equals(lcd);
184:
185: // biopshader currently enabled by default; use "false" to disable
186: String biop = (String) java.security.AccessController
187: .doPrivileged(new sun.security.action.GetPropertyAction(
188: "sun.java2d.opengl.biopshader"));
189: isBIOpShaderEnabled = !"false".equals(biop);
190:
191: // gradshader currently enabled by default; use "false" to disable
192: String grad = (String) java.security.AccessController
193: .doPrivileged(new sun.security.action.GetPropertyAction(
194: "sun.java2d.opengl.gradshader"));
195: isGradShaderEnabled = !"false".equals(grad);
196:
197: OGLRenderQueue rq = OGLRenderQueue.getInstance();
198: oglImagePipe = new OGLDrawImage();
199: oglTextPipe = new OGLTextRenderer(rq);
200: oglRenderPipe = new OGLRenderer(rq);
201: if (GraphicsPrimitive.tracingEnabled()) {
202: oglTextPipe = oglTextPipe.traceWrap();
203: oglRenderPipe = oglRenderPipe.traceWrap();
204: }
205: oglTxRenderPipe = new PixelToShapeConverter(oglRenderPipe);
206:
207: OGLBlitLoops.register();
208: OGLMaskFill.register();
209: OGLMaskBlit.register();
210: }
211: }
212:
213: protected OGLSurfaceData(OGLGraphicsConfig gc, ColorModel cm,
214: int type) {
215: super (getCustomSurfaceType(type), cm);
216: this .graphicsConfig = gc;
217: this .type = type;
218: setBlitProxyKey(gc.getProxyKey());
219: }
220:
221: @Override
222: public SurfaceDataProxy makeProxyFor(SurfaceData srcData) {
223: return OGLSurfaceDataProxy.createProxy(srcData, graphicsConfig);
224: }
225:
226: /**
227: * Returns the appropriate SurfaceType corresponding to the given OpenGL
228: * surface type constant (e.g. TEXTURE -> OpenGLTexture).
229: */
230: private static SurfaceType getCustomSurfaceType(int oglType) {
231: switch (oglType) {
232: case TEXTURE:
233: return OpenGLTexture;
234: case FBOBJECT:
235: return OpenGLSurfaceRTT;
236: case PBUFFER:
237: default:
238: return OpenGLSurface;
239: }
240: }
241:
242: /**
243: * Note: This should only be called from the QFT under the AWT lock.
244: * This method is kept separate from the initSurface() method below just
245: * to keep the code a bit cleaner.
246: */
247: private void initSurfaceNow(int width, int height) {
248: boolean isOpaque = (getTransparency() == Transparency.OPAQUE);
249: boolean success = false;
250:
251: switch (type) {
252: case PBUFFER:
253: success = initPbuffer(getNativeOps(), graphicsConfig
254: .getNativeConfigInfo(), isOpaque, width, height);
255: break;
256:
257: case TEXTURE:
258: success = initTexture(getNativeOps(), isOpaque,
259: isTexNonPow2Available(), isTexRectAvailable(),
260: width, height);
261: break;
262:
263: case FBOBJECT:
264: success = initFBObject(getNativeOps(), isOpaque,
265: isTexNonPow2Available(), isTexRectAvailable(),
266: width, height);
267: break;
268:
269: case FLIP_BACKBUFFER:
270: success = initFlipBackbuffer(getNativeOps());
271: break;
272:
273: default:
274: break;
275: }
276:
277: if (success) {
278: textureTarget = getTextureTarget(getNativeOps());
279: } else {
280: throw new OutOfMemoryError("can't create offscreen surface");
281: }
282: }
283:
284: /**
285: * Initializes the appropriate OpenGL offscreen surface based on the value
286: * of the type parameter. If the surface creation fails for any reason,
287: * an OutOfMemoryError will be thrown.
288: */
289: protected void initSurface(final int width, final int height) {
290: OGLRenderQueue rq = OGLRenderQueue.getInstance();
291: rq.lock();
292: try {
293: switch (type) {
294: case TEXTURE:
295: case PBUFFER:
296: case FBOBJECT:
297: // need to make sure the context is current before
298: // creating the texture (or pbuffer, or fbobject)
299: OGLContext.setScratchSurface(graphicsConfig);
300: break;
301: default:
302: break;
303: }
304: rq.flushAndInvokeNow(new Runnable() {
305: public void run() {
306: initSurfaceNow(width, height);
307: }
308: });
309: } finally {
310: rq.unlock();
311: }
312: }
313:
314: /**
315: * Returns the OGLContext for the GraphicsConfig associated with this
316: * surface.
317: */
318: final OGLContext getContext() {
319: return graphicsConfig.getContext();
320: }
321:
322: /**
323: * Returns the OGLGraphicsConfig associated with this surface.
324: */
325: final OGLGraphicsConfig getOGLGraphicsConfig() {
326: return graphicsConfig;
327: }
328:
329: /**
330: * Returns one of the surface type constants defined above.
331: */
332: final int getType() {
333: return type;
334: }
335:
336: /**
337: * If this surface is backed by a texture object, returns the target
338: * for that texture (either GL_TEXTURE_2D or GL_TEXTURE_RECTANGLE_ARB).
339: * Otherwise, this method will return zero.
340: */
341: final int getTextureTarget() {
342: return textureTarget;
343: }
344:
345: public Raster getRaster(int x, int y, int w, int h) {
346: throw new InternalError("not implemented yet");
347: }
348:
349: /**
350: * For now, we can only render LCD text if:
351: * - the fragment shader extension is available, and
352: * - blending is disabled, and
353: * - the source color is opaque
354: *
355: * Eventually, we could enhance the native OGL text rendering code
356: * and remove the above restrictions, but that would require significantly
357: * more code just to support a few uncommon cases.
358: */
359: public boolean canRenderLCDText(SunGraphics2D sg2d) {
360: return graphicsConfig
361: .isCapPresent(OGLContext.CAPS_EXT_LCD_SHADER)
362: && sg2d.compositeState <= SunGraphics2D.COMP_ISCOPY
363: && sg2d.paintState <= SunGraphics2D.PAINT_OPAQUECOLOR;
364: }
365:
366: public void validatePipe(SunGraphics2D sg2d) {
367: TextPipe textpipe;
368: boolean validated = false;
369:
370: // OGLTextRenderer handles both AA and non-AA text, but
371: // only works with the following modes:
372: // (Note: For LCD text we only enter this code path if
373: // canRenderLCDText() has already validated that the mode is
374: // CompositeType.SrcNoEa (opaque color), which will be subsumed
375: // by the CompositeType.SrcNoEa (any color) test below.)
376:
377: if (/* CompositeType.SrcNoEa (any color) */
378: (sg2d.compositeState <= sg2d.COMP_ISCOPY && sg2d.paintState <= sg2d.PAINT_ALPHACOLOR)
379: ||
380:
381: /* CompositeType.SrcOver (any color) */
382: (sg2d.compositeState == sg2d.COMP_ALPHA
383: && sg2d.paintState <= sg2d.PAINT_ALPHACOLOR && (((AlphaComposite) sg2d.composite)
384: .getRule() == AlphaComposite.SRC_OVER))
385: ||
386:
387: /* CompositeType.Xor (any color) */
388: (sg2d.compositeState == sg2d.COMP_XOR && sg2d.paintState <= sg2d.PAINT_ALPHACOLOR)) {
389: textpipe = oglTextPipe;
390: } else {
391: // do this to initialize textpipe correctly; we will attempt
392: // to override the non-text pipes below
393: super .validatePipe(sg2d);
394: textpipe = sg2d.textpipe;
395: validated = true;
396: }
397:
398: PixelToShapeConverter txPipe = null;
399: OGLRenderer nonTxPipe = null;
400:
401: if (sg2d.antialiasHint != SunHints.INTVAL_ANTIALIAS_ON) {
402: if (sg2d.paintState <= sg2d.PAINT_ALPHACOLOR) {
403: if (sg2d.compositeState <= sg2d.COMP_XOR) {
404: txPipe = oglTxRenderPipe;
405: nonTxPipe = oglRenderPipe;
406: }
407: } else if (sg2d.compositeState <= sg2d.COMP_ALPHA) {
408: if (OGLPaints.isValid(sg2d)) {
409: txPipe = oglTxRenderPipe;
410: nonTxPipe = oglRenderPipe;
411: }
412: // custom paints handled by super.validatePipe() below
413: }
414: } else {
415: if (sg2d.paintState <= sg2d.PAINT_ALPHACOLOR
416: && sg2d.compositeState == sg2d.COMP_XOR) {
417: // install the solid pipes when AA and XOR are both enabled
418: txPipe = oglTxRenderPipe;
419: nonTxPipe = oglRenderPipe;
420: }
421: // other cases handled by super.validatePipe() below
422: }
423:
424: if (txPipe != null) {
425: if (sg2d.transformState >= sg2d.TRANSFORM_TRANSLATESCALE) {
426: sg2d.drawpipe = txPipe;
427: sg2d.fillpipe = txPipe;
428: } else if (sg2d.strokeState != sg2d.STROKE_THIN) {
429: sg2d.drawpipe = txPipe;
430: sg2d.fillpipe = nonTxPipe;
431: } else {
432: sg2d.drawpipe = nonTxPipe;
433: sg2d.fillpipe = nonTxPipe;
434: }
435: sg2d.shapepipe = nonTxPipe;
436: } else {
437: if (!validated) {
438: super .validatePipe(sg2d);
439: }
440: }
441:
442: // install the text pipe based on our earlier decision
443: sg2d.textpipe = textpipe;
444:
445: // always override the image pipe with the specialized OGL pipe
446: sg2d.imagepipe = oglImagePipe;
447: }
448:
449: @Override
450: protected MaskFill getMaskFill(SunGraphics2D sg2d) {
451: if (sg2d.paintState > sg2d.PAINT_ALPHACOLOR) {
452: /*
453: * We can only accelerate non-Color MaskFill operations if
454: * all of the following conditions hold true:
455: * - there is an implementation for the given paintState
456: * - the current Paint can be accelerated for this destination
457: * - multitexturing is available (since we need to modulate
458: * the alpha mask texture with the paint texture)
459: *
460: * In all other cases, we return null, in which case the
461: * validation code will choose a more general software-based loop.
462: */
463: if (!OGLPaints.isValid(sg2d)
464: || !graphicsConfig
465: .isCapPresent(CAPS_EXT_MULTITEXTURE)) {
466: return null;
467: }
468: }
469: return super .getMaskFill(sg2d);
470: }
471:
472: public boolean copyArea(SunGraphics2D sg2d, int x, int y, int w,
473: int h, int dx, int dy) {
474: if (sg2d.transformState < sg2d.TRANSFORM_TRANSLATESCALE
475: && sg2d.compositeState < sg2d.COMP_XOR) {
476: x += sg2d.transX;
477: y += sg2d.transY;
478:
479: oglRenderPipe.copyArea(sg2d, x, y, w, h, dx, dy);
480:
481: return true;
482: }
483: return false;
484: }
485:
486: public void flush() {
487: invalidate();
488: OGLRenderQueue rq = OGLRenderQueue.getInstance();
489: rq.lock();
490: try {
491: // make sure we have a current context before
492: // disposing the native resources (e.g. texture object)
493: OGLContext.setScratchSurface(graphicsConfig);
494:
495: RenderBuffer buf = rq.getBuffer();
496: rq.ensureCapacityAndAlignment(12, 4);
497: buf.putInt(FLUSH_SURFACE);
498: buf.putLong(getNativeOps());
499:
500: // this call is expected to complete synchronously, so flush now
501: rq.flushNow();
502: } finally {
503: rq.unlock();
504: }
505: }
506:
507: /**
508: * Disposes the native resources associated with the given OGLSurfaceData
509: * (referenced by the pData parameter). This method is invoked from
510: * the native Dispose() method from the Disposer thread when the
511: * Java-level OGLSurfaceData object is about to go away. Note that we
512: * also pass a reference to the native GLX/WGLGraphicsConfigInfo
513: * (pConfigInfo) for the purposes of making a context current.
514: */
515: static void dispose(long pData, long pConfigInfo) {
516: OGLRenderQueue rq = OGLRenderQueue.getInstance();
517: rq.lock();
518: try {
519: // make sure we have a current context before
520: // disposing the native resources (e.g. texture object)
521: OGLContext.setScratchSurface(pConfigInfo);
522:
523: RenderBuffer buf = rq.getBuffer();
524: rq.ensureCapacityAndAlignment(12, 4);
525: buf.putInt(DISPOSE_SURFACE);
526: buf.putLong(pData);
527:
528: // this call is expected to complete synchronously, so flush now
529: rq.flushNow();
530: } finally {
531: rq.unlock();
532: }
533: }
534:
535: static void swapBuffers(long window) {
536: OGLRenderQueue rq = OGLRenderQueue.getInstance();
537: rq.lock();
538: try {
539: RenderBuffer buf = rq.getBuffer();
540: rq.ensureCapacityAndAlignment(12, 4);
541: buf.putInt(SWAP_BUFFERS);
542: buf.putLong(window);
543: rq.flushNow();
544: } finally {
545: rq.unlock();
546: }
547: }
548:
549: /**
550: * Returns true if OpenGL textures can have non-power-of-two dimensions
551: * when using the basic GL_TEXTURE_2D target.
552: */
553: boolean isTexNonPow2Available() {
554: return graphicsConfig
555: .isCapPresent(OGLContext.CAPS_EXT_TEXNONPOW2);
556: }
557:
558: /**
559: * Returns true if OpenGL textures can have non-power-of-two dimensions
560: * when using the GL_TEXTURE_RECTANGLE_ARB target (only available when the
561: * GL_ARB_texture_rectangle extension is present).
562: */
563: boolean isTexRectAvailable() {
564: return graphicsConfig.isCapPresent(OGLContext.CAPS_EXT_TEXRECT);
565: }
566: }
|