blob: b9c60046070544fbc180a469adb91314efdb29ea [file] [log] [blame]
/*
* Copyright (c) 2004, 2008, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#ifndef HEADLESS
#include <stdlib.h>
#include <string.h>
#include "sun_java2d_SunGraphics2D.h"
#include "jlong.h"
#include "jni_util.h"
#include "OGLContext.h"
#include "OGLRenderQueue.h"
#include "OGLSurfaceData.h"
#include "GraphicsPrimitiveMgr.h"
#include "Region.h"
/**
* The following methods are implemented in the windowing system (i.e. GLX
* and WGL) source files.
*/
extern jboolean OGLSD_InitOGLWindow(JNIEnv *env, OGLSDOps *oglsdo);
extern OGLContext *OGLSD_MakeOGLContextCurrent(JNIEnv *env,
OGLSDOps *srcOps,
OGLSDOps *dstOps);
/**
* This table contains the standard blending rules (or Porter-Duff compositing
* factors) used in glBlendFunc(), indexed by the rule constants from the
* AlphaComposite class.
*/
OGLBlendRule StdBlendRules[] = {
{ GL_ZERO, GL_ZERO }, /* 0 - Nothing */
{ GL_ZERO, GL_ZERO }, /* 1 - RULE_Clear */
{ GL_ONE, GL_ZERO }, /* 2 - RULE_Src */
{ GL_ONE, GL_ONE_MINUS_SRC_ALPHA }, /* 3 - RULE_SrcOver */
{ GL_ONE_MINUS_DST_ALPHA, GL_ONE }, /* 4 - RULE_DstOver */
{ GL_DST_ALPHA, GL_ZERO }, /* 5 - RULE_SrcIn */
{ GL_ZERO, GL_SRC_ALPHA }, /* 6 - RULE_DstIn */
{ GL_ONE_MINUS_DST_ALPHA, GL_ZERO }, /* 7 - RULE_SrcOut */
{ GL_ZERO, GL_ONE_MINUS_SRC_ALPHA }, /* 8 - RULE_DstOut */
{ GL_ZERO, GL_ONE }, /* 9 - RULE_Dst */
{ GL_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA }, /*10 - RULE_SrcAtop */
{ GL_ONE_MINUS_DST_ALPHA, GL_SRC_ALPHA }, /*11 - RULE_DstAtop */
{ GL_ONE_MINUS_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA }, /*12 - RULE_AlphaXor*/
};
/** Evaluates to "front" or "back", depending on the value of buf. */
#define OGLC_ACTIVE_BUFFER_NAME(buf) \
(buf == GL_FRONT || buf == GL_COLOR_ATTACHMENT0_EXT) ? "front" : "back"
/**
* Initializes the viewport and projection matrix, effectively positioning
* the origin at the top-left corner of the surface. This allows Java 2D
* coordinates to be passed directly to OpenGL, which is typically based on
* a bottom-right coordinate system. This method also sets the appropriate
* read and draw buffers.
*/
static void
OGLContext_SetViewport(OGLSDOps *srcOps, OGLSDOps *dstOps)
{
jint width = dstOps->width;
jint height = dstOps->height;
J2dTraceLn4(J2D_TRACE_INFO,
"OGLContext_SetViewport: w=%d h=%d read=%s draw=%s",
width, height,
OGLC_ACTIVE_BUFFER_NAME(srcOps->activeBuffer),
OGLC_ACTIVE_BUFFER_NAME(dstOps->activeBuffer));
// set the viewport and projection matrix
j2d_glViewport(dstOps->xOffset, dstOps->yOffset,
(GLsizei)width, (GLsizei)height);
j2d_glMatrixMode(GL_PROJECTION);
j2d_glLoadIdentity();
j2d_glOrtho(0.0, (GLdouble)width, (GLdouble)height, 0.0, -1.0, 1.0);
// set the active read and draw buffers
j2d_glReadBuffer(srcOps->activeBuffer);
j2d_glDrawBuffer(dstOps->activeBuffer);
// set the color mask to enable alpha channel only when necessary
j2d_glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, (GLboolean)!dstOps->isOpaque);
}
/**
* Initializes the alpha channel of the current surface so that it contains
* fully opaque alpha values.
*/
static void
OGLContext_InitAlphaChannel()
{
GLboolean scissorEnabled;
J2dTraceLn(J2D_TRACE_INFO, "OGLContext_InitAlphaChannel");
// it is possible for the scissor test to be enabled at this point;
// if it is, disable it temporarily since it can affect the glClear() op
scissorEnabled = j2d_glIsEnabled(GL_SCISSOR_TEST);
if (scissorEnabled) {
j2d_glDisable(GL_SCISSOR_TEST);
}
// set the color mask so that we only affect the alpha channel
j2d_glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE);
// clear the color buffer so that the alpha channel is fully opaque
j2d_glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
j2d_glClear(GL_COLOR_BUFFER_BIT);
// restore the color mask (as it was set in OGLContext_SetViewport())
j2d_glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE);
// re-enable scissor test, only if it was enabled earlier
if (scissorEnabled) {
j2d_glEnable(GL_SCISSOR_TEST);
}
}
/**
* Fetches the OGLContext associated with the given destination surface,
* makes the context current for those surfaces, updates the destination
* viewport, and then returns a pointer to the OGLContext.
*/
OGLContext *
OGLContext_SetSurfaces(JNIEnv *env, jlong pSrc, jlong pDst)
{
OGLSDOps *srcOps = (OGLSDOps *)jlong_to_ptr(pSrc);
OGLSDOps *dstOps = (OGLSDOps *)jlong_to_ptr(pDst);
OGLContext *oglc = NULL;
J2dTraceLn(J2D_TRACE_INFO, "OGLContext_SetSurfaces");
if (srcOps == NULL || dstOps == NULL) {
J2dRlsTraceLn(J2D_TRACE_ERROR,
"OGLContext_SetSurfaces: ops are null");
return NULL;
}
J2dTraceLn2(J2D_TRACE_VERBOSE, " srctype=%d dsttype=%d",
srcOps->drawableType, dstOps->drawableType);
if (dstOps->drawableType == OGLSD_TEXTURE) {
J2dRlsTraceLn(J2D_TRACE_ERROR,
"OGLContext_SetSurfaces: texture cannot be used as destination");
return NULL;
}
if (dstOps->drawableType == OGLSD_UNDEFINED) {
// initialize the surface as an OGLSD_WINDOW
if (!OGLSD_InitOGLWindow(env, dstOps)) {
J2dRlsTraceLn(J2D_TRACE_ERROR,
"OGLContext_SetSurfaces: could not init OGL window");
return NULL;
}
}
// make the context current
oglc = OGLSD_MakeOGLContextCurrent(env, srcOps, dstOps);
if (oglc == NULL) {
J2dRlsTraceLn(J2D_TRACE_ERROR,
"OGLContext_SetSurfaces: could not make context current");
return NULL;
}
// update the viewport
OGLContext_SetViewport(srcOps, dstOps);
// perform additional one-time initialization, if necessary
if (dstOps->needsInit) {
if (dstOps->isOpaque) {
// in this case we are treating the destination as opaque, but
// to do so, first we need to ensure that the alpha channel
// is filled with fully opaque values (see 6319663)
OGLContext_InitAlphaChannel();
}
dstOps->needsInit = JNI_FALSE;
}
return oglc;
}
/**
* Resets the current clip state (disables both scissor and depth tests).
*/
void
OGLContext_ResetClip(OGLContext *oglc)
{
J2dTraceLn(J2D_TRACE_INFO, "OGLContext_ResetClip");
RETURN_IF_NULL(oglc);
CHECK_PREVIOUS_OP(OGL_STATE_CHANGE);
j2d_glDisable(GL_SCISSOR_TEST);
j2d_glDisable(GL_DEPTH_TEST);
}
/**
* Sets the OpenGL scissor bounds to the provided rectangular clip bounds.
*/
void
OGLContext_SetRectClip(OGLContext *oglc, OGLSDOps *dstOps,
jint x1, jint y1, jint x2, jint y2)
{
jint width = x2 - x1;
jint height = y2 - y1;
J2dTraceLn4(J2D_TRACE_INFO,
"OGLContext_SetRectClip: x=%d y=%d w=%d h=%d",
x1, y1, width, height);
RETURN_IF_NULL(dstOps);
RETURN_IF_NULL(oglc);
CHECK_PREVIOUS_OP(OGL_STATE_CHANGE);
if ((width < 0) || (height < 0)) {
// use an empty scissor rectangle when the region is empty
width = 0;
height = 0;
}
j2d_glDisable(GL_DEPTH_TEST);
j2d_glEnable(GL_SCISSOR_TEST);
// the scissor rectangle is specified using the lower-left
// origin of the clip region (in the framebuffer's coordinate
// space), so we must account for the x/y offsets of the
// destination surface
j2d_glScissor(dstOps->xOffset + x1,
dstOps->yOffset + dstOps->height - (y1 + height),
width, height);
}
/**
* Sets up a complex (shape) clip using the OpenGL depth buffer. This
* method prepares the depth buffer so that the clip Region spans can
* be "rendered" into it. The depth buffer is first cleared, then the
* depth func is setup so that when we render the clip spans,
* nothing is rendered into the color buffer, but for each pixel that would
* be rendered, a non-zero value is placed into that location in the depth
* buffer. With depth test enabled, pixels will only be rendered into the
* color buffer if the corresponding value at that (x,y) location in the
* depth buffer differs from the incoming depth value.
*/
void
OGLContext_BeginShapeClip(OGLContext *oglc)
{
J2dTraceLn(J2D_TRACE_INFO, "OGLContext_BeginShapeClip");
RETURN_IF_NULL(oglc);
RESET_PREVIOUS_OP();
j2d_glDisable(GL_SCISSOR_TEST);
// enable depth test and clear depth buffer so that depth values are at
// their maximum; also set the depth func to GL_ALWAYS so that the
// depth values of the clip spans are forced into the depth buffer
j2d_glEnable(GL_DEPTH_TEST);
j2d_glClearDepth(1.0);
j2d_glClear(GL_DEPTH_BUFFER_BIT);
j2d_glDepthFunc(GL_ALWAYS);
// disable writes into the color buffer while we set up the clip
j2d_glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
// save current transform
j2d_glMatrixMode(GL_MODELVIEW);
j2d_glPushMatrix();
// use identity transform plus slight translation in the z-axis when
// setting the clip spans; this will push the clip spans (which would
// normally be at z=0) to the z=1 plane to give them some depth
j2d_glLoadIdentity();
j2d_glTranslatef(0.0f, 0.0f, 1.0f);
}
/**
* Finishes setting up the shape clip by resetting the depth func
* so that future rendering operations will once again be written into the
* color buffer (while respecting the clip set up in the depth buffer).
*/
void
OGLContext_EndShapeClip(OGLContext *oglc, OGLSDOps *dstOps)
{
J2dTraceLn(J2D_TRACE_INFO, "OGLContext_EndShapeClip");
RETURN_IF_NULL(dstOps);
RETURN_IF_NULL(oglc);
RESET_PREVIOUS_OP();
// restore transform
j2d_glPopMatrix();
// re-enable writes into the color buffer
j2d_glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, (GLboolean)!dstOps->isOpaque);
// enable the depth test so that only fragments within the clip region
// (i.e. those fragments whose z-values are >= the values currently
// stored in the depth buffer) are rendered
j2d_glDepthFunc(GL_GEQUAL);
}
/**
* Initializes the OpenGL state responsible for applying extra alpha. This
* step is only necessary for any operation that uses glDrawPixels() or
* glCopyPixels() with a non-1.0f extra alpha value. Since the source is
* always premultiplied, we apply the extra alpha value to both alpha and
* color components using GL_*_SCALE.
*/
void
OGLContext_SetExtraAlpha(jfloat ea)
{
J2dTraceLn1(J2D_TRACE_INFO, "OGLContext_SetExtraAlpha: ea=%f", ea);
j2d_glPixelTransferf(GL_ALPHA_SCALE, ea);
j2d_glPixelTransferf(GL_RED_SCALE, ea);
j2d_glPixelTransferf(GL_GREEN_SCALE, ea);
j2d_glPixelTransferf(GL_BLUE_SCALE, ea);
}
/**
* Resets all OpenGL compositing state (disables blending and logic
* operations).
*/
void
OGLContext_ResetComposite(OGLContext *oglc)
{
J2dTraceLn(J2D_TRACE_INFO, "OGLContext_ResetComposite");
RETURN_IF_NULL(oglc);
CHECK_PREVIOUS_OP(OGL_STATE_CHANGE);
// disable blending and XOR mode
if (oglc->compState == sun_java2d_SunGraphics2D_COMP_ALPHA) {
j2d_glDisable(GL_BLEND);
} else if (oglc->compState == sun_java2d_SunGraphics2D_COMP_XOR) {
j2d_glDisable(GL_COLOR_LOGIC_OP);
j2d_glDisable(GL_ALPHA_TEST);
}
// set state to default values
oglc->compState = sun_java2d_SunGraphics2D_COMP_ISCOPY;
oglc->extraAlpha = 1.0f;
}
/**
* Initializes the OpenGL blending state. XOR mode is disabled and the
* appropriate blend functions are setup based on the AlphaComposite rule
* constant.
*/
void
OGLContext_SetAlphaComposite(OGLContext *oglc,
jint rule, jfloat extraAlpha, jint flags)
{
J2dTraceLn1(J2D_TRACE_INFO,
"OGLContext_SetAlphaComposite: flags=%d", flags);
RETURN_IF_NULL(oglc);
CHECK_PREVIOUS_OP(OGL_STATE_CHANGE);
// disable XOR mode
if (oglc->compState == sun_java2d_SunGraphics2D_COMP_XOR) {
j2d_glDisable(GL_COLOR_LOGIC_OP);
j2d_glDisable(GL_ALPHA_TEST);
}
// we can safely disable blending when:
// - comp is SrcNoEa or SrcOverNoEa, and
// - the source is opaque
// (turning off blending can have a large positive impact on
// performance)
if ((rule == RULE_Src || rule == RULE_SrcOver) &&
(extraAlpha == 1.0f) &&
(flags & OGLC_SRC_IS_OPAQUE))
{
J2dTraceLn1(J2D_TRACE_VERBOSE,
" disabling alpha comp: rule=%d ea=1.0 src=opq", rule);
j2d_glDisable(GL_BLEND);
} else {
J2dTraceLn2(J2D_TRACE_VERBOSE,
" enabling alpha comp: rule=%d ea=%f", rule, extraAlpha);
j2d_glEnable(GL_BLEND);
j2d_glBlendFunc(StdBlendRules[rule].src, StdBlendRules[rule].dst);
}
// update state
oglc->compState = sun_java2d_SunGraphics2D_COMP_ALPHA;
oglc->extraAlpha = extraAlpha;
}
/**
* Initializes the OpenGL logic op state to XOR mode. Blending is disabled
* before enabling logic op mode. The XOR pixel value will be applied
* later in the OGLContext_SetColor() method.
*/
void
OGLContext_SetXorComposite(OGLContext *oglc, jint xorPixel)
{
J2dTraceLn1(J2D_TRACE_INFO,
"OGLContext_SetXorComposite: xorPixel=%08x", xorPixel);
RETURN_IF_NULL(oglc);
CHECK_PREVIOUS_OP(OGL_STATE_CHANGE);
// disable blending mode
if (oglc->compState == sun_java2d_SunGraphics2D_COMP_ALPHA) {
j2d_glDisable(GL_BLEND);
}
// enable XOR mode
j2d_glEnable(GL_COLOR_LOGIC_OP);
j2d_glLogicOp(GL_XOR);
// set up the alpha test so that we discard transparent fragments (this
// is primarily useful for rendering text in XOR mode)
j2d_glEnable(GL_ALPHA_TEST);
j2d_glAlphaFunc(GL_NOTEQUAL, 0.0f);
// update state
oglc->compState = sun_java2d_SunGraphics2D_COMP_XOR;
oglc->xorPixel = xorPixel;
oglc->extraAlpha = 1.0f;
}
/**
* Resets the OpenGL transform state back to the identity matrix.
*/
void
OGLContext_ResetTransform(OGLContext *oglc)
{
J2dTraceLn(J2D_TRACE_INFO, "OGLContext_ResetTransform");
RETURN_IF_NULL(oglc);
CHECK_PREVIOUS_OP(OGL_STATE_CHANGE);
j2d_glMatrixMode(GL_MODELVIEW);
j2d_glLoadIdentity();
}
/**
* Initializes the OpenGL transform state by setting the modelview transform
* using the given matrix parameters.
*
* REMIND: it may be worthwhile to add serial id to AffineTransform, so we
* could do a quick check to see if the xform has changed since
* last time... a simple object compare won't suffice...
*/
void
OGLContext_SetTransform(OGLContext *oglc,
jdouble m00, jdouble m10,
jdouble m01, jdouble m11,
jdouble m02, jdouble m12)
{
J2dTraceLn(J2D_TRACE_INFO, "OGLContext_SetTransform");
RETURN_IF_NULL(oglc);
CHECK_PREVIOUS_OP(OGL_STATE_CHANGE);
if (oglc->xformMatrix == NULL) {
size_t arrsize = 16 * sizeof(GLdouble);
oglc->xformMatrix = (GLdouble *)malloc(arrsize);
memset(oglc->xformMatrix, 0, arrsize);
oglc->xformMatrix[10] = 1.0;
oglc->xformMatrix[15] = 1.0;
}
// copy values from AffineTransform object into native matrix array
oglc->xformMatrix[0] = m00;
oglc->xformMatrix[1] = m10;
oglc->xformMatrix[4] = m01;
oglc->xformMatrix[5] = m11;
oglc->xformMatrix[12] = m02;
oglc->xformMatrix[13] = m12;
J2dTraceLn3(J2D_TRACE_VERBOSE, " [%lf %lf %lf]",
oglc->xformMatrix[0], oglc->xformMatrix[4],
oglc->xformMatrix[12]);
J2dTraceLn3(J2D_TRACE_VERBOSE, " [%lf %lf %lf]",
oglc->xformMatrix[1], oglc->xformMatrix[5],
oglc->xformMatrix[13]);
j2d_glMatrixMode(GL_MODELVIEW);
j2d_glLoadMatrixd(oglc->xformMatrix);
}
/**
* Creates a 2D texture of the given format and dimensions and returns the
* texture object identifier. This method is typically used to create a
* temporary texture for intermediate work, such as in the
* OGLContext_InitBlitTileTexture() method below.
*/
GLuint
OGLContext_CreateBlitTexture(GLenum internalFormat, GLenum pixelFormat,
GLuint width, GLuint height)
{
GLuint texID;
GLint sp, sr, rl, align;
GLclampf priority = 1.0f;
J2dTraceLn(J2D_TRACE_INFO, "OGLContext_CreateBlitTexture");
j2d_glGenTextures(1, &texID);
j2d_glBindTexture(GL_TEXTURE_2D, texID);
j2d_glPrioritizeTextures(1, &texID, &priority);
j2d_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
j2d_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
OGLSD_RESET_TEXTURE_WRAP(GL_TEXTURE_2D);
// save pixel store parameters (since this method could be invoked after
// the caller has already set up its pixel store parameters)
j2d_glGetIntegerv(GL_UNPACK_SKIP_PIXELS, &sp);
j2d_glGetIntegerv(GL_UNPACK_SKIP_ROWS, &sr);
j2d_glGetIntegerv(GL_UNPACK_ROW_LENGTH, &rl);
j2d_glGetIntegerv(GL_UNPACK_ALIGNMENT, &align);
// set pixel store parameters to default values
j2d_glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
j2d_glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
j2d_glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
j2d_glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
j2d_glTexImage2D(GL_TEXTURE_2D, 0, internalFormat,
width, height, 0,
pixelFormat, GL_UNSIGNED_BYTE, NULL);
// restore pixel store parameters
j2d_glPixelStorei(GL_UNPACK_SKIP_PIXELS, sp);
j2d_glPixelStorei(GL_UNPACK_SKIP_ROWS, sr);
j2d_glPixelStorei(GL_UNPACK_ROW_LENGTH, rl);
j2d_glPixelStorei(GL_UNPACK_ALIGNMENT, align);
return texID;
}
/**
* Initializes a small texture tile for use with tiled blit operations (see
* OGLBlitLoops.c and OGLMaskBlit.c for usage examples). The texture ID for
* the tile is stored in the given OGLContext. The tile is initially filled
* with garbage values, but the tile is updated as needed (via
* glTexSubImage2D()) with real RGBA values used in tiled blit situations.
* The internal format for the texture is GL_RGBA8, which should be sufficient
* for storing system memory surfaces of any known format (see PixelFormats
* for a list of compatible surface formats).
*/
jboolean
OGLContext_InitBlitTileTexture(OGLContext *oglc)
{
J2dTraceLn(J2D_TRACE_INFO, "OGLContext_InitBlitTileTexture");
oglc->blitTextureID =
OGLContext_CreateBlitTexture(GL_RGBA8, GL_RGBA,
OGLC_BLIT_TILE_SIZE,
OGLC_BLIT_TILE_SIZE);
return JNI_TRUE;
}
/**
* Destroys the OpenGL resources associated with the given OGLContext.
* It is required that the native context associated with the OGLContext
* be made current prior to calling this method.
*/
void
OGLContext_DestroyContextResources(OGLContext *oglc)
{
J2dTraceLn(J2D_TRACE_INFO, "OGLContext_DestroyContextResources");
if (oglc->xformMatrix != NULL) {
free(oglc->xformMatrix);
}
if (oglc->blitTextureID != 0) {
j2d_glDeleteTextures(1, &oglc->blitTextureID);
}
}
/**
* Returns JNI_TRUE if the given extension name is available for the current
* GraphicsConfig; JNI_FALSE otherwise. An extension is considered available
* if its identifier string is found amongst the space-delimited GL_EXTENSIONS
* string.
*
* Adapted from the OpenGL Red Book, pg. 506.
*/
jboolean
OGLContext_IsExtensionAvailable(const char *extString, char *extName)
{
jboolean ret = JNI_FALSE;
char *p = (char *)extString;
char *end;
if (extString == NULL) {
J2dTraceLn(J2D_TRACE_INFO, "OGLContext_IsExtensionAvailable");
J2dRlsTraceLn(J2D_TRACE_ERROR,
"OGLContext_IsExtensionAvailable: extension string is null");
return JNI_FALSE;
}
end = p + strlen(p);
while (p < end) {
size_t n = strcspn(p, " ");
if ((strlen(extName) == n) && (strncmp(extName, p, n) == 0)) {
ret = JNI_TRUE;
break;
}
p += (n + 1);
}
J2dRlsTraceLn2(J2D_TRACE_INFO,
"OGLContext_IsExtensionAvailable: %s=%s",
extName, ret ? "true" : "false");
return ret;
}
/**
* Returns JNI_TRUE only if all of the following conditions are met:
* - the GL_EXT_framebuffer_object extension is available
* - FBO support has been enabled via the system property
* - we can successfully create an FBO with depth capabilities
*/
static jboolean
OGLContext_IsFBObjectExtensionAvailable(JNIEnv *env,
const char *extString)
{
jboolean isFBObjectEnabled = JNI_FALSE;
GLuint fbobjectID, textureID, depthID;
jint width = 1, height = 1;
J2dTraceLn(J2D_TRACE_INFO, "OGLContext_IsFBObjectExtensionAvailable");
// first see if the fbobject extension is available
if (!OGLContext_IsExtensionAvailable(extString,
"GL_EXT_framebuffer_object"))
{
return JNI_FALSE;
}
// next see if the fbobject system property has been enabled
isFBObjectEnabled =
JNU_GetStaticFieldByName(env, NULL,
"sun/java2d/opengl/OGLSurfaceData",
"isFBObjectEnabled", "Z").z;
if (!isFBObjectEnabled) {
J2dRlsTraceLn(J2D_TRACE_INFO,
"OGLContext_IsFBObjectExtensionAvailable: disabled via flag");
return JNI_FALSE;
}
// finally, create a dummy fbobject with depth capabilities to see
// if this configuration is supported by the drivers/hardware
// (first we initialize a color texture object that will be used to
// construct the dummy fbobject)
j2d_glGenTextures(1, &textureID);
j2d_glBindTexture(GL_TEXTURE_2D, textureID);
j2d_glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB,
width, height, 0,
GL_RGB, GL_UNSIGNED_BYTE, NULL);
j2d_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
j2d_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
// initialize framebuffer object using color texture created above
if (!OGLSD_InitFBObject(&fbobjectID, &depthID,
textureID, GL_TEXTURE_2D,
width, height))
{
J2dRlsTraceLn(J2D_TRACE_INFO,
"OGLContext_IsFBObjectExtensionAvailable: fbobject unsupported");
j2d_glDeleteTextures(1, &textureID);
return JNI_FALSE;
}
// delete the temporary resources
j2d_glDeleteTextures(1, &textureID);
j2d_glDeleteRenderbuffersEXT(1, &depthID);
j2d_glDeleteFramebuffersEXT(1, &fbobjectID);
J2dRlsTraceLn(J2D_TRACE_INFO,
"OGLContext_IsFBObjectExtensionAvailable: fbobject supported");
return JNI_TRUE;
}
/**
* Returns JNI_TRUE only if all of the following conditions are met:
* - the GL_ARB_fragment_shader extension is available
* - the LCD text shader codepath has been enabled via the system property
* - the hardware supports the minimum number of texture units
*/
static jboolean
OGLContext_IsLCDShaderSupportAvailable(JNIEnv *env,
jboolean fragShaderAvailable)
{
jboolean isLCDShaderEnabled = JNI_FALSE;
GLint maxTexUnits;
J2dTraceLn(J2D_TRACE_INFO, "OGLContext_IsLCDShaderSupportAvailable");
// first see if the fragment shader extension is available
if (!fragShaderAvailable) {
return JNI_FALSE;
}
// next see if the lcdshader system property has been enabled
isLCDShaderEnabled =
JNU_GetStaticFieldByName(env, NULL,
"sun/java2d/opengl/OGLSurfaceData",
"isLCDShaderEnabled", "Z").z;
if (!isLCDShaderEnabled) {
J2dRlsTraceLn(J2D_TRACE_INFO,
"OGLContext_IsLCDShaderSupportAvailable: disabled via flag");
return JNI_FALSE;
}
// finally, check to see if the hardware supports the required number
// of texture units
j2d_glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS_ARB, &maxTexUnits);
if (maxTexUnits < 4) {
J2dRlsTraceLn1(J2D_TRACE_INFO,
"OGLContext_IsLCDShaderSupportAvailable: not enough tex units (%d)",
maxTexUnits);
}
J2dRlsTraceLn(J2D_TRACE_INFO,
"OGLContext_IsLCDShaderSupportAvailable: LCD text shader supported");
return JNI_TRUE;
}
/**
* Returns JNI_TRUE only if all of the following conditions are met:
* - the GL_ARB_fragment_shader extension is available
* - the BufferedImageOp shader codepath has been enabled via the
* system property
*/
static jboolean
OGLContext_IsBIOpShaderSupportAvailable(JNIEnv *env,
jboolean fragShaderAvailable)
{
jboolean isBIOpShaderEnabled = JNI_FALSE;
J2dTraceLn(J2D_TRACE_INFO, "OGLContext_IsBIOpShaderSupportAvailable");
// first see if the fragment shader extension is available
if (!fragShaderAvailable) {
return JNI_FALSE;
}
// next see if the biopshader system property has been enabled
isBIOpShaderEnabled =
JNU_GetStaticFieldByName(env, NULL,
"sun/java2d/opengl/OGLSurfaceData",
"isBIOpShaderEnabled", "Z").z;
if (!isBIOpShaderEnabled) {
J2dRlsTraceLn(J2D_TRACE_INFO,
"OGLContext_IsBIOpShaderSupportAvailable: disabled via flag");
return JNI_FALSE;
}
/*
* Note: In theory we should probably do some other checks here, like
* linking a sample shader to see if the hardware truly supports our
* shader programs. However, our current BufferedImageOp shaders were
* designed to support first-generation shader-level hardware, so the
* assumption is that if our shaders work on those GPUs, then they'll
* work on newer ones as well. Also, linking a fragment program can
* cost valuable CPU cycles, which is another reason to avoid these
* checks at startup.
*/
J2dRlsTraceLn(J2D_TRACE_INFO,
"OGLContext_IsBIOpShaderSupportAvailable: BufferedImageOp shader supported");
return JNI_TRUE;
}
/**
* Returns JNI_TRUE only if all of the following conditions are met:
* - the GL_ARB_fragment_shader extension is available
* - the Linear/RadialGradientPaint shader codepath has been enabled via the
* system property
*/
static jboolean
OGLContext_IsGradShaderSupportAvailable(JNIEnv *env,
jboolean fragShaderAvailable)
{
jboolean isGradShaderEnabled = JNI_FALSE;
J2dTraceLn(J2D_TRACE_INFO, "OGLContext_IsGradShaderSupportAvailable");
// first see if the fragment shader extension is available
if (!fragShaderAvailable) {
return JNI_FALSE;
}
// next see if the gradshader system property has been enabled
isGradShaderEnabled =
JNU_GetStaticFieldByName(env, NULL,
"sun/java2d/opengl/OGLSurfaceData",
"isGradShaderEnabled", "Z").z;
if (!isGradShaderEnabled) {
J2dRlsTraceLn(J2D_TRACE_INFO,
"OGLContext_IsGradShaderSupportAvailable: disabled via flag");
return JNI_FALSE;
}
J2dRlsTraceLn(J2D_TRACE_INFO,
"OGLContext_IsGradShaderSupportAvailable: Linear/RadialGradientPaint shader supported");
return JNI_TRUE;
}
/**
* Checks for the presence of the optional extensions used by
* the Java 2D OpenGL pipeline. The given caps bitfield is updated
* to reflect the availability of these extensions.
*/
void
OGLContext_GetExtensionInfo(JNIEnv *env, jint *caps)
{
jint vcap = OGLC_VENDOR_OTHER;
const char *vendor = (char *)j2d_glGetString(GL_VENDOR);
const char *e = (char *)j2d_glGetString(GL_EXTENSIONS);
jboolean fragShaderAvail =
OGLContext_IsExtensionAvailable(e, "GL_ARB_fragment_shader");
J2dTraceLn(J2D_TRACE_INFO, "OGLContext_GetExtensionInfo");
*caps |= CAPS_TEXNONSQUARE;
if (OGLContext_IsExtensionAvailable(e, "GL_ARB_multitexture")) {
*caps |= CAPS_MULTITEXTURE;
}
if (OGLContext_IsExtensionAvailable(e, "GL_ARB_texture_non_power_of_two")){
*caps |= CAPS_TEXNONPOW2;
}
// 6656574: Use of the GL_ARB_texture_rectangle extension by Java 2D
// complicates any third-party libraries that try to interact with
// the OGL pipeline (and we've run into driver bugs in the past related
// to this extension), so for now we will disable its use by default (unless
// forced). We will still make use of the GL_ARB_texture_non_power_of_two
// extension when available, which is the better choice going forward
// anyway.
if (OGLContext_IsExtensionAvailable(e, "GL_ARB_texture_rectangle") &&
getenv("J2D_OGL_TEXRECT") != NULL)
{
*caps |= CAPS_EXT_TEXRECT;
}
if (OGLContext_IsFBObjectExtensionAvailable(env, e)) {
*caps |= CAPS_EXT_FBOBJECT;
}
if (OGLContext_IsLCDShaderSupportAvailable(env, fragShaderAvail)) {
*caps |= CAPS_EXT_LCD_SHADER | CAPS_PS20;
}
if (OGLContext_IsBIOpShaderSupportAvailable(env, fragShaderAvail)) {
*caps |= CAPS_EXT_BIOP_SHADER | CAPS_PS20;
}
if (OGLContext_IsGradShaderSupportAvailable(env, fragShaderAvail)) {
*caps |= CAPS_EXT_GRAD_SHADER | CAPS_PS20;
}
if (OGLContext_IsExtensionAvailable(e, "GL_NV_fragment_program")) {
// this is an Nvidia board, at least PS 2.0, but we can't
// use the "max instructions" heuristic since GeForce FX
// boards report 1024 even though they're only PS 2.0,
// so we'll check the following, which does imply PS 3.0
if (OGLContext_IsExtensionAvailable(e, "GL_NV_fragment_program2")) {
*caps |= CAPS_PS30;
}
} else {
// for all other boards, we look at the "max instructions"
// count reported by the GL_ARB_fragment_program extension
// as a heuristic for detecting PS 3.0 compatible hardware
if (OGLContext_IsExtensionAvailable(e, "GL_ARB_fragment_program")) {
GLint instr;
j2d_glGetProgramivARB(GL_FRAGMENT_PROGRAM_ARB,
GL_MAX_PROGRAM_INSTRUCTIONS_ARB, &instr);
if (instr > 512) {
*caps |= CAPS_PS30;
}
}
}
// stuff vendor descriptor in the upper bits of the caps
if (vendor != NULL) {
if (strncmp(vendor, "ATI", 3) == 0) {
vcap = OGLC_VENDOR_ATI;
} else if (strncmp(vendor, "NVIDIA", 6) == 0) {
vcap = OGLC_VENDOR_NVIDIA;
} else if (strncmp(vendor, "Sun", 3) == 0) {
vcap = OGLC_VENDOR_SUN;
}
// REMIND: new in 7 - check if needs fixing
*caps |= ((vcap & OGLC_VCAP_MASK) << OGLC_VCAP_OFFSET);
}
}
/**
* Returns JNI_TRUE if the given GL_VERSION string meets the minimum
* requirements (>= 1.2); JNI_FALSE otherwise.
*/
jboolean
OGLContext_IsVersionSupported(const unsigned char *versionstr)
{
J2dTraceLn(J2D_TRACE_INFO, "OGLContext_IsVersionSupported");
if (versionstr == NULL) {
J2dRlsTraceLn(J2D_TRACE_ERROR,
"OGLContext_IsVersionSupported: version string is null");
return JNI_FALSE;
}
// note that this check allows for OpenGL 2.x
return ((versionstr[0] == '1' && versionstr[2] >= '2') ||
(versionstr[0] >= '2'));
}
/**
* Compiles and links the given fragment shader program. If
* successful, this function returns a handle to the newly created shader
* program; otherwise returns 0.
*/
GLhandleARB
OGLContext_CreateFragmentProgram(const char *fragmentShaderSource)
{
GLhandleARB fragmentShader, fragmentProgram;
GLint success;
int infoLogLength = 0;
J2dTraceLn(J2D_TRACE_INFO, "OGLContext_CreateFragmentProgram");
// create the shader object and compile the shader source code
fragmentShader = j2d_glCreateShaderObjectARB(GL_FRAGMENT_SHADER_ARB);
j2d_glShaderSourceARB(fragmentShader, 1, &fragmentShaderSource, NULL);
j2d_glCompileShaderARB(fragmentShader);
j2d_glGetObjectParameterivARB(fragmentShader,
GL_OBJECT_COMPILE_STATUS_ARB,
&success);
// print the compiler messages, if necessary
j2d_glGetObjectParameterivARB(fragmentShader,
GL_OBJECT_INFO_LOG_LENGTH_ARB,
&infoLogLength);
if (infoLogLength > 1) {
char infoLog[1024];
j2d_glGetInfoLogARB(fragmentShader, 1024, NULL, infoLog);
J2dRlsTraceLn2(J2D_TRACE_WARNING,
"OGLContext_CreateFragmentProgram: compiler msg (%d):\n%s",
infoLogLength, infoLog);
}
if (!success) {
J2dRlsTraceLn(J2D_TRACE_ERROR,
"OGLContext_CreateFragmentProgram: error compiling shader");
j2d_glDeleteObjectARB(fragmentShader);
return 0;
}
// create the program object and attach it to the shader
fragmentProgram = j2d_glCreateProgramObjectARB();
j2d_glAttachObjectARB(fragmentProgram, fragmentShader);
// it is now safe to delete the shader object
j2d_glDeleteObjectARB(fragmentShader);
// link the program
j2d_glLinkProgramARB(fragmentProgram);
j2d_glGetObjectParameterivARB(fragmentProgram,
GL_OBJECT_LINK_STATUS_ARB,
&success);
// print the linker messages, if necessary
j2d_glGetObjectParameterivARB(fragmentProgram,
GL_OBJECT_INFO_LOG_LENGTH_ARB,
&infoLogLength);
if (infoLogLength > 1) {
char infoLog[1024];
j2d_glGetInfoLogARB(fragmentProgram, 1024, NULL, infoLog);
J2dRlsTraceLn2(J2D_TRACE_WARNING,
"OGLContext_CreateFragmentProgram: linker msg (%d):\n%s",
infoLogLength, infoLog);
}
if (!success) {
J2dRlsTraceLn(J2D_TRACE_ERROR,
"OGLContext_CreateFragmentProgram: error linking shader");
j2d_glDeleteObjectARB(fragmentProgram);
return 0;
}
return fragmentProgram;
}
/*
* Class: sun_java2d_opengl_OGLContext
* Method: getOGLIdString
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_sun_java2d_opengl_OGLContext_getOGLIdString
(JNIEnv *env, jclass oglcc)
{
char *vendor, *renderer, *version;
char *pAdapterId;
jobject ret = NULL;
int len;
J2dTraceLn(J2D_TRACE_INFO, "OGLContext_getOGLIdString");
vendor = (char*)j2d_glGetString(GL_VENDOR);
if (vendor == NULL) {
vendor = "Unknown Vendor";
}
renderer = (char*)j2d_glGetString(GL_RENDERER);
if (renderer == NULL) {
renderer = "Unknown Renderer";
}
version = (char*)j2d_glGetString(GL_VERSION);
if (version == NULL) {
version = "unknown version";
}
// 'vendor renderer (version)0'
len = strlen(vendor) + 1 + strlen(renderer) + 1 + 1+strlen(version)+1 + 1;
pAdapterId = malloc(len);
if (pAdapterId != NULL) {
jio_snprintf(pAdapterId, len, "%s %s (%s)", vendor, renderer, version);
J2dTraceLn1(J2D_TRACE_VERBOSE, " id=%s", pAdapterId);
ret = JNU_NewStringPlatform(env, pAdapterId);
free(pAdapterId);
}
return ret;
}
#endif /* !HEADLESS */