| 1 | /* |
| 2 | * Copyright (c) 2004, 2013, 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 | #ifndef HEADLESS |
| 27 | |
| 28 | #include <stdlib.h> |
| 29 | #include <string.h> |
| 30 | |
| 31 | #include "sun_java2d_SunGraphics2D.h" |
| 32 | |
| 33 | #include "jlong.h" |
| 34 | #include "jni_util.h" |
| 35 | #include "OGLContext.h" |
| 36 | #include "OGLRenderQueue.h" |
| 37 | #include "OGLSurfaceData.h" |
| 38 | #include "GraphicsPrimitiveMgr.h" |
| 39 | #include "Region.h" |
| 40 | |
| 41 | #include "jvm.h" |
| 42 | |
| 43 | /** |
| 44 | * The following methods are implemented in the windowing system (i.e. GLX |
| 45 | * and WGL) source files. |
| 46 | */ |
| 47 | extern jboolean OGLSD_InitOGLWindow(JNIEnv *env, OGLSDOps *oglsdo); |
| 48 | extern OGLContext *OGLSD_MakeOGLContextCurrent(JNIEnv *env, |
| 49 | OGLSDOps *srcOps, |
| 50 | OGLSDOps *dstOps); |
| 51 | |
| 52 | /** |
| 53 | * This table contains the standard blending rules (or Porter-Duff compositing |
| 54 | * factors) used in glBlendFunc(), indexed by the rule constants from the |
| 55 | * AlphaComposite class. |
| 56 | */ |
| 57 | OGLBlendRule StdBlendRules[] = { |
| 58 | { GL_ZERO, GL_ZERO }, /* 0 - Nothing */ |
| 59 | { GL_ZERO, GL_ZERO }, /* 1 - RULE_Clear */ |
| 60 | { GL_ONE, GL_ZERO }, /* 2 - RULE_Src */ |
| 61 | { GL_ONE, GL_ONE_MINUS_SRC_ALPHA }, /* 3 - RULE_SrcOver */ |
| 62 | { GL_ONE_MINUS_DST_ALPHA, GL_ONE }, /* 4 - RULE_DstOver */ |
| 63 | { GL_DST_ALPHA, GL_ZERO }, /* 5 - RULE_SrcIn */ |
| 64 | { GL_ZERO, GL_SRC_ALPHA }, /* 6 - RULE_DstIn */ |
| 65 | { GL_ONE_MINUS_DST_ALPHA, GL_ZERO }, /* 7 - RULE_SrcOut */ |
| 66 | { GL_ZERO, GL_ONE_MINUS_SRC_ALPHA }, /* 8 - RULE_DstOut */ |
| 67 | { GL_ZERO, GL_ONE }, /* 9 - RULE_Dst */ |
| 68 | { GL_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA }, /*10 - RULE_SrcAtop */ |
| 69 | { GL_ONE_MINUS_DST_ALPHA, GL_SRC_ALPHA }, /*11 - RULE_DstAtop */ |
| 70 | { GL_ONE_MINUS_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA }, /*12 - RULE_AlphaXor*/ |
| 71 | }; |
| 72 | |
| 73 | /** Evaluates to "front" or "back", depending on the value of buf. */ |
| 74 | #define OGLC_ACTIVE_BUFFER_NAME(buf) \ |
| 75 | (buf == GL_FRONT || buf == GL_COLOR_ATTACHMENT0_EXT) ? "front" : "back" |
| 76 | |
| 77 | /** |
| 78 | * Initializes the viewport and projection matrix, effectively positioning |
| 79 | * the origin at the top-left corner of the surface. This allows Java 2D |
| 80 | * coordinates to be passed directly to OpenGL, which is typically based on |
| 81 | * a bottom-right coordinate system. This method also sets the appropriate |
| 82 | * read and draw buffers. |
| 83 | */ |
| 84 | static void |
| 85 | OGLContext_SetViewport(OGLSDOps *srcOps, OGLSDOps *dstOps) |
| 86 | { |
| 87 | jint width = dstOps->width; |
| 88 | jint height = dstOps->height; |
| 89 | |
| 90 | J2dTraceLn4(J2D_TRACE_INFO, |
| 91 | "OGLContext_SetViewport: w=%d h=%d read=%s draw=%s" , |
| 92 | width, height, |
| 93 | OGLC_ACTIVE_BUFFER_NAME(srcOps->activeBuffer), |
| 94 | OGLC_ACTIVE_BUFFER_NAME(dstOps->activeBuffer)); |
| 95 | |
| 96 | // set the viewport and projection matrix |
| 97 | j2d_glViewport(dstOps->xOffset, dstOps->yOffset, |
| 98 | (GLsizei)width, (GLsizei)height); |
| 99 | j2d_glMatrixMode(GL_PROJECTION); |
| 100 | j2d_glLoadIdentity(); |
| 101 | j2d_glOrtho(0.0, (GLdouble)width, (GLdouble)height, 0.0, -1.0, 1.0); |
| 102 | |
| 103 | // set the active read and draw buffers |
| 104 | j2d_glReadBuffer(srcOps->activeBuffer); |
| 105 | j2d_glDrawBuffer(dstOps->activeBuffer); |
| 106 | |
| 107 | // set the color mask to enable alpha channel only when necessary |
| 108 | j2d_glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, (GLboolean)!dstOps->isOpaque); |
| 109 | } |
| 110 | |
| 111 | /** |
| 112 | * Initializes the alpha channel of the current surface so that it contains |
| 113 | * fully opaque alpha values. |
| 114 | */ |
| 115 | static void |
| 116 | OGLContext_InitAlphaChannel() |
| 117 | { |
| 118 | GLboolean scissorEnabled; |
| 119 | |
| 120 | J2dTraceLn(J2D_TRACE_INFO, "OGLContext_InitAlphaChannel" ); |
| 121 | |
| 122 | // it is possible for the scissor test to be enabled at this point; |
| 123 | // if it is, disable it temporarily since it can affect the glClear() op |
| 124 | scissorEnabled = j2d_glIsEnabled(GL_SCISSOR_TEST); |
| 125 | if (scissorEnabled) { |
| 126 | j2d_glDisable(GL_SCISSOR_TEST); |
| 127 | } |
| 128 | |
| 129 | // set the color mask so that we only affect the alpha channel |
| 130 | j2d_glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE); |
| 131 | |
| 132 | // clear the color buffer so that the alpha channel is fully opaque |
| 133 | j2d_glClearColor(0.0f, 0.0f, 0.0f, 1.0f); |
| 134 | j2d_glClear(GL_COLOR_BUFFER_BIT); |
| 135 | |
| 136 | // restore the color mask (as it was set in OGLContext_SetViewport()) |
| 137 | j2d_glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE); |
| 138 | |
| 139 | // re-enable scissor test, only if it was enabled earlier |
| 140 | if (scissorEnabled) { |
| 141 | j2d_glEnable(GL_SCISSOR_TEST); |
| 142 | } |
| 143 | } |
| 144 | |
| 145 | /** |
| 146 | * Fetches the OGLContext associated with the given destination surface, |
| 147 | * makes the context current for those surfaces, updates the destination |
| 148 | * viewport, and then returns a pointer to the OGLContext. |
| 149 | */ |
| 150 | OGLContext * |
| 151 | OGLContext_SetSurfaces(JNIEnv *env, jlong pSrc, jlong pDst) |
| 152 | { |
| 153 | OGLSDOps *srcOps = (OGLSDOps *)jlong_to_ptr(pSrc); |
| 154 | OGLSDOps *dstOps = (OGLSDOps *)jlong_to_ptr(pDst); |
| 155 | OGLContext *oglc = NULL; |
| 156 | |
| 157 | J2dTraceLn(J2D_TRACE_INFO, "OGLContext_SetSurfaces" ); |
| 158 | |
| 159 | if (srcOps == NULL || dstOps == NULL) { |
| 160 | J2dRlsTraceLn(J2D_TRACE_ERROR, |
| 161 | "OGLContext_SetSurfaces: ops are null" ); |
| 162 | return NULL; |
| 163 | } |
| 164 | |
| 165 | J2dTraceLn2(J2D_TRACE_VERBOSE, " srctype=%d dsttype=%d" , |
| 166 | srcOps->drawableType, dstOps->drawableType); |
| 167 | |
| 168 | if (dstOps->drawableType == OGLSD_TEXTURE) { |
| 169 | J2dRlsTraceLn(J2D_TRACE_ERROR, |
| 170 | "OGLContext_SetSurfaces: texture cannot be used as destination" ); |
| 171 | return NULL; |
| 172 | } |
| 173 | |
| 174 | if (dstOps->drawableType == OGLSD_UNDEFINED) { |
| 175 | // initialize the surface as an OGLSD_WINDOW |
| 176 | if (!OGLSD_InitOGLWindow(env, dstOps)) { |
| 177 | J2dRlsTraceLn(J2D_TRACE_ERROR, |
| 178 | "OGLContext_SetSurfaces: could not init OGL window" ); |
| 179 | return NULL; |
| 180 | } |
| 181 | } |
| 182 | |
| 183 | // make the context current |
| 184 | oglc = OGLSD_MakeOGLContextCurrent(env, srcOps, dstOps); |
| 185 | if (oglc == NULL) { |
| 186 | J2dRlsTraceLn(J2D_TRACE_ERROR, |
| 187 | "OGLContext_SetSurfaces: could not make context current" ); |
| 188 | return NULL; |
| 189 | } |
| 190 | |
| 191 | // update the viewport |
| 192 | OGLContext_SetViewport(srcOps, dstOps); |
| 193 | |
| 194 | // perform additional one-time initialization, if necessary |
| 195 | if (dstOps->needsInit) { |
| 196 | if (dstOps->isOpaque) { |
| 197 | // in this case we are treating the destination as opaque, but |
| 198 | // to do so, first we need to ensure that the alpha channel |
| 199 | // is filled with fully opaque values (see 6319663) |
| 200 | OGLContext_InitAlphaChannel(); |
| 201 | } |
| 202 | dstOps->needsInit = JNI_FALSE; |
| 203 | } |
| 204 | |
| 205 | return oglc; |
| 206 | } |
| 207 | |
| 208 | /** |
| 209 | * Resets the current clip state (disables both scissor and depth tests). |
| 210 | */ |
| 211 | void |
| 212 | OGLContext_ResetClip(OGLContext *oglc) |
| 213 | { |
| 214 | J2dTraceLn(J2D_TRACE_INFO, "OGLContext_ResetClip" ); |
| 215 | |
| 216 | RETURN_IF_NULL(oglc); |
| 217 | CHECK_PREVIOUS_OP(OGL_STATE_CHANGE); |
| 218 | |
| 219 | j2d_glDisable(GL_SCISSOR_TEST); |
| 220 | j2d_glDisable(GL_DEPTH_TEST); |
| 221 | } |
| 222 | |
| 223 | /** |
| 224 | * Sets the OpenGL scissor bounds to the provided rectangular clip bounds. |
| 225 | */ |
| 226 | void |
| 227 | OGLContext_SetRectClip(OGLContext *oglc, OGLSDOps *dstOps, |
| 228 | jint x1, jint y1, jint x2, jint y2) |
| 229 | { |
| 230 | jint width = x2 - x1; |
| 231 | jint height = y2 - y1; |
| 232 | |
| 233 | J2dTraceLn4(J2D_TRACE_INFO, |
| 234 | "OGLContext_SetRectClip: x=%d y=%d w=%d h=%d" , |
| 235 | x1, y1, width, height); |
| 236 | |
| 237 | RETURN_IF_NULL(dstOps); |
| 238 | RETURN_IF_NULL(oglc); |
| 239 | CHECK_PREVIOUS_OP(OGL_STATE_CHANGE); |
| 240 | |
| 241 | if ((width < 0) || (height < 0)) { |
| 242 | // use an empty scissor rectangle when the region is empty |
| 243 | width = 0; |
| 244 | height = 0; |
| 245 | } |
| 246 | |
| 247 | j2d_glDisable(GL_DEPTH_TEST); |
| 248 | j2d_glEnable(GL_SCISSOR_TEST); |
| 249 | |
| 250 | // the scissor rectangle is specified using the lower-left |
| 251 | // origin of the clip region (in the framebuffer's coordinate |
| 252 | // space), so we must account for the x/y offsets of the |
| 253 | // destination surface |
| 254 | j2d_glScissor(dstOps->xOffset + x1, |
| 255 | dstOps->yOffset + dstOps->height - (y1 + height), |
| 256 | width, height); |
| 257 | } |
| 258 | |
| 259 | /** |
| 260 | * Sets up a complex (shape) clip using the OpenGL depth buffer. This |
| 261 | * method prepares the depth buffer so that the clip Region spans can |
| 262 | * be "rendered" into it. The depth buffer is first cleared, then the |
| 263 | * depth func is setup so that when we render the clip spans, |
| 264 | * nothing is rendered into the color buffer, but for each pixel that would |
| 265 | * be rendered, a non-zero value is placed into that location in the depth |
| 266 | * buffer. With depth test enabled, pixels will only be rendered into the |
| 267 | * color buffer if the corresponding value at that (x,y) location in the |
| 268 | * depth buffer differs from the incoming depth value. |
| 269 | */ |
| 270 | void |
| 271 | OGLContext_BeginShapeClip(OGLContext *oglc) |
| 272 | { |
| 273 | J2dTraceLn(J2D_TRACE_INFO, "OGLContext_BeginShapeClip" ); |
| 274 | |
| 275 | RETURN_IF_NULL(oglc); |
| 276 | RESET_PREVIOUS_OP(); |
| 277 | |
| 278 | j2d_glDisable(GL_SCISSOR_TEST); |
| 279 | |
| 280 | // enable depth test and clear depth buffer so that depth values are at |
| 281 | // their maximum; also set the depth func to GL_ALWAYS so that the |
| 282 | // depth values of the clip spans are forced into the depth buffer |
| 283 | j2d_glEnable(GL_DEPTH_TEST); |
| 284 | j2d_glClearDepth(1.0); |
| 285 | j2d_glClear(GL_DEPTH_BUFFER_BIT); |
| 286 | j2d_glDepthFunc(GL_ALWAYS); |
| 287 | |
| 288 | // disable writes into the color buffer while we set up the clip |
| 289 | j2d_glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); |
| 290 | |
| 291 | // save current transform |
| 292 | j2d_glMatrixMode(GL_MODELVIEW); |
| 293 | j2d_glPushMatrix(); |
| 294 | |
| 295 | // use identity transform plus slight translation in the z-axis when |
| 296 | // setting the clip spans; this will push the clip spans (which would |
| 297 | // normally be at z=0) to the z=1 plane to give them some depth |
| 298 | j2d_glLoadIdentity(); |
| 299 | j2d_glTranslatef(0.0f, 0.0f, 1.0f); |
| 300 | } |
| 301 | |
| 302 | /** |
| 303 | * Finishes setting up the shape clip by resetting the depth func |
| 304 | * so that future rendering operations will once again be written into the |
| 305 | * color buffer (while respecting the clip set up in the depth buffer). |
| 306 | */ |
| 307 | void |
| 308 | OGLContext_EndShapeClip(OGLContext *oglc, OGLSDOps *dstOps) |
| 309 | { |
| 310 | J2dTraceLn(J2D_TRACE_INFO, "OGLContext_EndShapeClip" ); |
| 311 | |
| 312 | RETURN_IF_NULL(dstOps); |
| 313 | RETURN_IF_NULL(oglc); |
| 314 | RESET_PREVIOUS_OP(); |
| 315 | |
| 316 | // restore transform |
| 317 | j2d_glPopMatrix(); |
| 318 | |
| 319 | // re-enable writes into the color buffer |
| 320 | j2d_glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, (GLboolean)!dstOps->isOpaque); |
| 321 | |
| 322 | // enable the depth test so that only fragments within the clip region |
| 323 | // (i.e. those fragments whose z-values are >= the values currently |
| 324 | // stored in the depth buffer) are rendered |
| 325 | j2d_glDepthFunc(GL_GEQUAL); |
| 326 | } |
| 327 | |
| 328 | /** |
| 329 | * Initializes the OpenGL state responsible for applying extra alpha. This |
| 330 | * step is only necessary for any operation that uses glDrawPixels() or |
| 331 | * glCopyPixels() with a non-1.0f extra alpha value. Since the source is |
| 332 | * always premultiplied, we apply the extra alpha value to both alpha and |
| 333 | * color components using GL_*_SCALE. |
| 334 | */ |
| 335 | void |
| 336 | OGLContext_SetExtraAlpha(jfloat ea) |
| 337 | { |
| 338 | J2dTraceLn1(J2D_TRACE_INFO, "OGLContext_SetExtraAlpha: ea=%f" , ea); |
| 339 | |
| 340 | j2d_glPixelTransferf(GL_ALPHA_SCALE, ea); |
| 341 | j2d_glPixelTransferf(GL_RED_SCALE, ea); |
| 342 | j2d_glPixelTransferf(GL_GREEN_SCALE, ea); |
| 343 | j2d_glPixelTransferf(GL_BLUE_SCALE, ea); |
| 344 | } |
| 345 | |
| 346 | /** |
| 347 | * Resets all OpenGL compositing state (disables blending and logic |
| 348 | * operations). |
| 349 | */ |
| 350 | void |
| 351 | OGLContext_ResetComposite(OGLContext *oglc) |
| 352 | { |
| 353 | J2dTraceLn(J2D_TRACE_INFO, "OGLContext_ResetComposite" ); |
| 354 | |
| 355 | RETURN_IF_NULL(oglc); |
| 356 | CHECK_PREVIOUS_OP(OGL_STATE_CHANGE); |
| 357 | |
| 358 | // disable blending and XOR mode |
| 359 | if (oglc->compState == sun_java2d_SunGraphics2D_COMP_ALPHA) { |
| 360 | j2d_glDisable(GL_BLEND); |
| 361 | } else if (oglc->compState == sun_java2d_SunGraphics2D_COMP_XOR) { |
| 362 | j2d_glDisable(GL_COLOR_LOGIC_OP); |
| 363 | j2d_glDisable(GL_ALPHA_TEST); |
| 364 | } |
| 365 | |
| 366 | // set state to default values |
| 367 | oglc->compState = sun_java2d_SunGraphics2D_COMP_ISCOPY; |
| 368 | oglc->extraAlpha = 1.0f; |
| 369 | } |
| 370 | |
| 371 | /** |
| 372 | * Initializes the OpenGL blending state. XOR mode is disabled and the |
| 373 | * appropriate blend functions are setup based on the AlphaComposite rule |
| 374 | * constant. |
| 375 | */ |
| 376 | void |
| 377 | OGLContext_SetAlphaComposite(OGLContext *oglc, |
| 378 | jint rule, jfloat extraAlpha, jint flags) |
| 379 | { |
| 380 | J2dTraceLn1(J2D_TRACE_INFO, |
| 381 | "OGLContext_SetAlphaComposite: flags=%d" , flags); |
| 382 | |
| 383 | RETURN_IF_NULL(oglc); |
| 384 | CHECK_PREVIOUS_OP(OGL_STATE_CHANGE); |
| 385 | |
| 386 | // disable XOR mode |
| 387 | if (oglc->compState == sun_java2d_SunGraphics2D_COMP_XOR) { |
| 388 | j2d_glDisable(GL_COLOR_LOGIC_OP); |
| 389 | j2d_glDisable(GL_ALPHA_TEST); |
| 390 | } |
| 391 | |
| 392 | // we can safely disable blending when: |
| 393 | // - comp is SrcNoEa or SrcOverNoEa, and |
| 394 | // - the source is opaque |
| 395 | // (turning off blending can have a large positive impact on |
| 396 | // performance) |
| 397 | if ((rule == RULE_Src || rule == RULE_SrcOver) && |
| 398 | (extraAlpha == 1.0f) && |
| 399 | (flags & OGLC_SRC_IS_OPAQUE)) |
| 400 | { |
| 401 | J2dTraceLn1(J2D_TRACE_VERBOSE, |
| 402 | " disabling alpha comp: rule=%d ea=1.0 src=opq" , rule); |
| 403 | j2d_glDisable(GL_BLEND); |
| 404 | } else { |
| 405 | J2dTraceLn2(J2D_TRACE_VERBOSE, |
| 406 | " enabling alpha comp: rule=%d ea=%f" , rule, extraAlpha); |
| 407 | j2d_glEnable(GL_BLEND); |
| 408 | j2d_glBlendFunc(StdBlendRules[rule].src, StdBlendRules[rule].dst); |
| 409 | } |
| 410 | |
| 411 | // update state |
| 412 | oglc->compState = sun_java2d_SunGraphics2D_COMP_ALPHA; |
| 413 | oglc->extraAlpha = extraAlpha; |
| 414 | } |
| 415 | |
| 416 | /** |
| 417 | * Initializes the OpenGL logic op state to XOR mode. Blending is disabled |
| 418 | * before enabling logic op mode. The XOR pixel value will be applied |
| 419 | * later in the OGLContext_SetColor() method. |
| 420 | */ |
| 421 | void |
| 422 | OGLContext_SetXorComposite(OGLContext *oglc, jint xorPixel) |
| 423 | { |
| 424 | J2dTraceLn1(J2D_TRACE_INFO, |
| 425 | "OGLContext_SetXorComposite: xorPixel=%08x" , xorPixel); |
| 426 | |
| 427 | RETURN_IF_NULL(oglc); |
| 428 | CHECK_PREVIOUS_OP(OGL_STATE_CHANGE); |
| 429 | |
| 430 | // disable blending mode |
| 431 | if (oglc->compState == sun_java2d_SunGraphics2D_COMP_ALPHA) { |
| 432 | j2d_glDisable(GL_BLEND); |
| 433 | } |
| 434 | |
| 435 | // enable XOR mode |
| 436 | j2d_glEnable(GL_COLOR_LOGIC_OP); |
| 437 | j2d_glLogicOp(GL_XOR); |
| 438 | |
| 439 | // set up the alpha test so that we discard transparent fragments (this |
| 440 | // is primarily useful for rendering text in XOR mode) |
| 441 | j2d_glEnable(GL_ALPHA_TEST); |
| 442 | j2d_glAlphaFunc(GL_NOTEQUAL, 0.0f); |
| 443 | |
| 444 | // update state |
| 445 | oglc->compState = sun_java2d_SunGraphics2D_COMP_XOR; |
| 446 | oglc->xorPixel = xorPixel; |
| 447 | oglc->extraAlpha = 1.0f; |
| 448 | } |
| 449 | |
| 450 | /** |
| 451 | * Resets the OpenGL transform state back to the identity matrix. |
| 452 | */ |
| 453 | void |
| 454 | OGLContext_ResetTransform(OGLContext *oglc) |
| 455 | { |
| 456 | J2dTraceLn(J2D_TRACE_INFO, "OGLContext_ResetTransform" ); |
| 457 | |
| 458 | RETURN_IF_NULL(oglc); |
| 459 | CHECK_PREVIOUS_OP(OGL_STATE_CHANGE); |
| 460 | |
| 461 | j2d_glMatrixMode(GL_MODELVIEW); |
| 462 | j2d_glLoadIdentity(); |
| 463 | } |
| 464 | |
| 465 | /** |
| 466 | * Initializes the OpenGL transform state by setting the modelview transform |
| 467 | * using the given matrix parameters. |
| 468 | * |
| 469 | * REMIND: it may be worthwhile to add serial id to AffineTransform, so we |
| 470 | * could do a quick check to see if the xform has changed since |
| 471 | * last time... a simple object compare won't suffice... |
| 472 | */ |
| 473 | void |
| 474 | OGLContext_SetTransform(OGLContext *oglc, |
| 475 | jdouble m00, jdouble m10, |
| 476 | jdouble m01, jdouble m11, |
| 477 | jdouble m02, jdouble m12) |
| 478 | { |
| 479 | J2dTraceLn(J2D_TRACE_INFO, "OGLContext_SetTransform" ); |
| 480 | |
| 481 | RETURN_IF_NULL(oglc); |
| 482 | CHECK_PREVIOUS_OP(OGL_STATE_CHANGE); |
| 483 | |
| 484 | if (oglc->xformMatrix == NULL) { |
| 485 | size_t arrsize = 16 * sizeof(GLdouble); |
| 486 | oglc->xformMatrix = (GLdouble *)malloc(arrsize); |
| 487 | memset(oglc->xformMatrix, 0, arrsize); |
| 488 | oglc->xformMatrix[10] = 1.0; |
| 489 | oglc->xformMatrix[15] = 1.0; |
| 490 | } |
| 491 | |
| 492 | // copy values from AffineTransform object into native matrix array |
| 493 | oglc->xformMatrix[0] = m00; |
| 494 | oglc->xformMatrix[1] = m10; |
| 495 | oglc->xformMatrix[4] = m01; |
| 496 | oglc->xformMatrix[5] = m11; |
| 497 | oglc->xformMatrix[12] = m02; |
| 498 | oglc->xformMatrix[13] = m12; |
| 499 | |
| 500 | J2dTraceLn3(J2D_TRACE_VERBOSE, " [%lf %lf %lf]" , |
| 501 | oglc->xformMatrix[0], oglc->xformMatrix[4], |
| 502 | oglc->xformMatrix[12]); |
| 503 | J2dTraceLn3(J2D_TRACE_VERBOSE, " [%lf %lf %lf]" , |
| 504 | oglc->xformMatrix[1], oglc->xformMatrix[5], |
| 505 | oglc->xformMatrix[13]); |
| 506 | |
| 507 | j2d_glMatrixMode(GL_MODELVIEW); |
| 508 | j2d_glLoadMatrixd(oglc->xformMatrix); |
| 509 | } |
| 510 | |
| 511 | /** |
| 512 | * Creates a 2D texture of the given format and dimensions and returns the |
| 513 | * texture object identifier. This method is typically used to create a |
| 514 | * temporary texture for intermediate work, such as in the |
| 515 | * OGLContext_InitBlitTileTexture() method below. |
| 516 | */ |
| 517 | GLuint |
| 518 | OGLContext_CreateBlitTexture(GLenum internalFormat, GLenum pixelFormat, |
| 519 | GLuint width, GLuint height) |
| 520 | { |
| 521 | GLuint texID; |
| 522 | GLint sp, sr, rl, align; |
| 523 | GLclampf priority = 1.0f; |
| 524 | |
| 525 | J2dTraceLn(J2D_TRACE_INFO, "OGLContext_CreateBlitTexture" ); |
| 526 | |
| 527 | j2d_glGenTextures(1, &texID); |
| 528 | j2d_glBindTexture(GL_TEXTURE_2D, texID); |
| 529 | j2d_glPrioritizeTextures(1, &texID, &priority); |
| 530 | j2d_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
| 531 | j2d_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
| 532 | OGLSD_RESET_TEXTURE_WRAP(GL_TEXTURE_2D); |
| 533 | |
| 534 | // save pixel store parameters (since this method could be invoked after |
| 535 | // the caller has already set up its pixel store parameters) |
| 536 | j2d_glGetIntegerv(GL_UNPACK_SKIP_PIXELS, &sp); |
| 537 | j2d_glGetIntegerv(GL_UNPACK_SKIP_ROWS, &sr); |
| 538 | j2d_glGetIntegerv(GL_UNPACK_ROW_LENGTH, &rl); |
| 539 | j2d_glGetIntegerv(GL_UNPACK_ALIGNMENT, &align); |
| 540 | |
| 541 | // set pixel store parameters to default values |
| 542 | j2d_glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0); |
| 543 | j2d_glPixelStorei(GL_UNPACK_SKIP_ROWS, 0); |
| 544 | j2d_glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); |
| 545 | j2d_glPixelStorei(GL_UNPACK_ALIGNMENT, 1); |
| 546 | |
| 547 | j2d_glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, |
| 548 | width, height, 0, |
| 549 | pixelFormat, GL_UNSIGNED_BYTE, NULL); |
| 550 | |
| 551 | // restore pixel store parameters |
| 552 | j2d_glPixelStorei(GL_UNPACK_SKIP_PIXELS, sp); |
| 553 | j2d_glPixelStorei(GL_UNPACK_SKIP_ROWS, sr); |
| 554 | j2d_glPixelStorei(GL_UNPACK_ROW_LENGTH, rl); |
| 555 | j2d_glPixelStorei(GL_UNPACK_ALIGNMENT, align); |
| 556 | |
| 557 | return texID; |
| 558 | } |
| 559 | |
| 560 | /** |
| 561 | * Initializes a small texture tile for use with tiled blit operations (see |
| 562 | * OGLBlitLoops.c and OGLMaskBlit.c for usage examples). The texture ID for |
| 563 | * the tile is stored in the given OGLContext. The tile is initially filled |
| 564 | * with garbage values, but the tile is updated as needed (via |
| 565 | * glTexSubImage2D()) with real RGBA values used in tiled blit situations. |
| 566 | * The internal format for the texture is GL_RGBA8, which should be sufficient |
| 567 | * for storing system memory surfaces of any known format (see PixelFormats |
| 568 | * for a list of compatible surface formats). |
| 569 | */ |
| 570 | jboolean |
| 571 | OGLContext_InitBlitTileTexture(OGLContext *oglc) |
| 572 | { |
| 573 | J2dTraceLn(J2D_TRACE_INFO, "OGLContext_InitBlitTileTexture" ); |
| 574 | |
| 575 | oglc->blitTextureID = |
| 576 | OGLContext_CreateBlitTexture(GL_RGBA8, GL_RGBA, |
| 577 | OGLC_BLIT_TILE_SIZE, |
| 578 | OGLC_BLIT_TILE_SIZE); |
| 579 | |
| 580 | return JNI_TRUE; |
| 581 | } |
| 582 | |
| 583 | /** |
| 584 | * Destroys the OpenGL resources associated with the given OGLContext. |
| 585 | * It is required that the native context associated with the OGLContext |
| 586 | * be made current prior to calling this method. |
| 587 | */ |
| 588 | void |
| 589 | OGLContext_DestroyContextResources(OGLContext *oglc) |
| 590 | { |
| 591 | J2dTraceLn(J2D_TRACE_INFO, "OGLContext_DestroyContextResources" ); |
| 592 | |
| 593 | if (oglc->xformMatrix != NULL) { |
| 594 | free(oglc->xformMatrix); |
| 595 | } |
| 596 | |
| 597 | if (oglc->blitTextureID != 0) { |
| 598 | j2d_glDeleteTextures(1, &oglc->blitTextureID); |
| 599 | } |
| 600 | } |
| 601 | |
| 602 | /** |
| 603 | * Returns JNI_TRUE if the given extension name is available for the current |
| 604 | * GraphicsConfig; JNI_FALSE otherwise. An extension is considered available |
| 605 | * if its identifier string is found amongst the space-delimited GL_EXTENSIONS |
| 606 | * string. |
| 607 | * |
| 608 | * Adapted from the OpenGL Red Book, pg. 506. |
| 609 | */ |
| 610 | jboolean |
| 611 | OGLContext_IsExtensionAvailable(const char *extString, char *extName) |
| 612 | { |
| 613 | jboolean ret = JNI_FALSE; |
| 614 | char *p = (char *)extString; |
| 615 | char *end; |
| 616 | |
| 617 | if (extString == NULL) { |
| 618 | J2dTraceLn(J2D_TRACE_INFO, "OGLContext_IsExtensionAvailable" ); |
| 619 | J2dRlsTraceLn(J2D_TRACE_ERROR, |
| 620 | "OGLContext_IsExtensionAvailable: extension string is null" ); |
| 621 | return JNI_FALSE; |
| 622 | } |
| 623 | |
| 624 | end = p + strlen(p); |
| 625 | |
| 626 | while (p < end) { |
| 627 | size_t n = strcspn(p, " " ); |
| 628 | |
| 629 | if ((strlen(extName) == n) && (strncmp(extName, p, n) == 0)) { |
| 630 | ret = JNI_TRUE; |
| 631 | break; |
| 632 | } |
| 633 | |
| 634 | p += (n + 1); |
| 635 | } |
| 636 | |
| 637 | J2dRlsTraceLn2(J2D_TRACE_INFO, |
| 638 | "OGLContext_IsExtensionAvailable: %s=%s" , |
| 639 | extName, ret ? "true" : "false" ); |
| 640 | |
| 641 | return ret; |
| 642 | } |
| 643 | |
| 644 | /** |
| 645 | * Returns JNI_TRUE only if all of the following conditions are met: |
| 646 | * - the GL_EXT_framebuffer_object extension is available |
| 647 | * - FBO support has been enabled via the system property |
| 648 | * - we can successfully create an FBO with depth capabilities |
| 649 | */ |
| 650 | static jboolean |
| 651 | OGLContext_IsFBObjectExtensionAvailable(JNIEnv *env, |
| 652 | const char *extString) |
| 653 | { |
| 654 | jboolean isFBObjectEnabled = JNI_FALSE; |
| 655 | GLuint fbobjectID, textureID, depthID; |
| 656 | jint width = 1, height = 1; |
| 657 | |
| 658 | J2dTraceLn(J2D_TRACE_INFO, "OGLContext_IsFBObjectExtensionAvailable" ); |
| 659 | |
| 660 | // first see if the fbobject extension is available |
| 661 | if (!OGLContext_IsExtensionAvailable(extString, |
| 662 | "GL_EXT_framebuffer_object" )) |
| 663 | { |
| 664 | return JNI_FALSE; |
| 665 | } |
| 666 | |
| 667 | // next see if the depth texture extension is available |
| 668 | if (!OGLContext_IsExtensionAvailable(extString, |
| 669 | "GL_ARB_depth_texture" )) |
| 670 | { |
| 671 | return JNI_FALSE; |
| 672 | } |
| 673 | |
| 674 | // next see if the fbobject system property has been enabled |
| 675 | isFBObjectEnabled = |
| 676 | JNU_GetStaticFieldByName(env, NULL, |
| 677 | "sun/java2d/opengl/OGLSurfaceData" , |
| 678 | "isFBObjectEnabled" , "Z" ).z; |
| 679 | if (!isFBObjectEnabled) { |
| 680 | J2dRlsTraceLn(J2D_TRACE_INFO, |
| 681 | "OGLContext_IsFBObjectExtensionAvailable: disabled via flag" ); |
| 682 | return JNI_FALSE; |
| 683 | } |
| 684 | |
| 685 | // finally, create a dummy fbobject with depth capabilities to see |
| 686 | // if this configuration is supported by the drivers/hardware |
| 687 | // (first we initialize a color texture object that will be used to |
| 688 | // construct the dummy fbobject) |
| 689 | j2d_glGenTextures(1, &textureID); |
| 690 | j2d_glBindTexture(GL_TEXTURE_2D, textureID); |
| 691 | j2d_glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, |
| 692 | width, height, 0, |
| 693 | GL_RGB, GL_UNSIGNED_BYTE, NULL); |
| 694 | j2d_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
| 695 | j2d_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
| 696 | |
| 697 | // initialize framebuffer object using color texture created above |
| 698 | if (!OGLSD_InitFBObject(&fbobjectID, &depthID, |
| 699 | textureID, GL_TEXTURE_2D, |
| 700 | width, height)) |
| 701 | { |
| 702 | J2dRlsTraceLn(J2D_TRACE_INFO, |
| 703 | "OGLContext_IsFBObjectExtensionAvailable: fbobject unsupported" ); |
| 704 | j2d_glDeleteTextures(1, &textureID); |
| 705 | return JNI_FALSE; |
| 706 | } |
| 707 | |
| 708 | // delete the temporary resources |
| 709 | j2d_glDeleteTextures(1, &textureID); |
| 710 | j2d_glDeleteRenderbuffersEXT(1, &depthID); |
| 711 | j2d_glDeleteFramebuffersEXT(1, &fbobjectID); |
| 712 | |
| 713 | J2dRlsTraceLn(J2D_TRACE_INFO, |
| 714 | "OGLContext_IsFBObjectExtensionAvailable: fbobject supported" ); |
| 715 | |
| 716 | return JNI_TRUE; |
| 717 | } |
| 718 | |
| 719 | /** |
| 720 | * Returns JNI_TRUE only if all of the following conditions are met: |
| 721 | * - the GL_ARB_fragment_shader extension is available |
| 722 | * - the LCD text shader codepath has been enabled via the system property |
| 723 | * - the hardware supports the minimum number of texture units |
| 724 | */ |
| 725 | static jboolean |
| 726 | OGLContext_IsLCDShaderSupportAvailable(JNIEnv *env, |
| 727 | jboolean fragShaderAvailable) |
| 728 | { |
| 729 | jboolean isLCDShaderEnabled = JNI_FALSE; |
| 730 | GLint maxTexUnits; |
| 731 | |
| 732 | J2dTraceLn(J2D_TRACE_INFO, "OGLContext_IsLCDShaderSupportAvailable" ); |
| 733 | |
| 734 | // first see if the fragment shader extension is available |
| 735 | if (!fragShaderAvailable) { |
| 736 | return JNI_FALSE; |
| 737 | } |
| 738 | |
| 739 | // next see if the lcdshader system property has been enabled |
| 740 | isLCDShaderEnabled = |
| 741 | JNU_GetStaticFieldByName(env, NULL, |
| 742 | "sun/java2d/opengl/OGLSurfaceData" , |
| 743 | "isLCDShaderEnabled" , "Z" ).z; |
| 744 | if (!isLCDShaderEnabled) { |
| 745 | J2dRlsTraceLn(J2D_TRACE_INFO, |
| 746 | "OGLContext_IsLCDShaderSupportAvailable: disabled via flag" ); |
| 747 | return JNI_FALSE; |
| 748 | } |
| 749 | |
| 750 | // finally, check to see if the hardware supports the required number |
| 751 | // of texture units |
| 752 | j2d_glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS_ARB, &maxTexUnits); |
| 753 | if (maxTexUnits < 2) { |
| 754 | J2dRlsTraceLn1(J2D_TRACE_INFO, |
| 755 | "OGLContext_IsLCDShaderSupportAvailable: not enough tex units (%d)" , |
| 756 | maxTexUnits); |
| 757 | } |
| 758 | |
| 759 | J2dRlsTraceLn(J2D_TRACE_INFO, |
| 760 | "OGLContext_IsLCDShaderSupportAvailable: LCD text shader supported" ); |
| 761 | |
| 762 | return JNI_TRUE; |
| 763 | } |
| 764 | |
| 765 | /** |
| 766 | * Returns JNI_TRUE only if all of the following conditions are met: |
| 767 | * - the GL_ARB_fragment_shader extension is available |
| 768 | * - the BufferedImageOp shader codepath has been enabled via the |
| 769 | * system property |
| 770 | */ |
| 771 | static jboolean |
| 772 | OGLContext_IsBIOpShaderSupportAvailable(JNIEnv *env, |
| 773 | jboolean fragShaderAvailable) |
| 774 | { |
| 775 | jboolean isBIOpShaderEnabled = JNI_FALSE; |
| 776 | |
| 777 | J2dTraceLn(J2D_TRACE_INFO, "OGLContext_IsBIOpShaderSupportAvailable" ); |
| 778 | |
| 779 | // first see if the fragment shader extension is available |
| 780 | if (!fragShaderAvailable) { |
| 781 | return JNI_FALSE; |
| 782 | } |
| 783 | |
| 784 | // next see if the biopshader system property has been enabled |
| 785 | isBIOpShaderEnabled = |
| 786 | JNU_GetStaticFieldByName(env, NULL, |
| 787 | "sun/java2d/opengl/OGLSurfaceData" , |
| 788 | "isBIOpShaderEnabled" , "Z" ).z; |
| 789 | if (!isBIOpShaderEnabled) { |
| 790 | J2dRlsTraceLn(J2D_TRACE_INFO, |
| 791 | "OGLContext_IsBIOpShaderSupportAvailable: disabled via flag" ); |
| 792 | return JNI_FALSE; |
| 793 | } |
| 794 | |
| 795 | /* |
| 796 | * Note: In theory we should probably do some other checks here, like |
| 797 | * linking a sample shader to see if the hardware truly supports our |
| 798 | * shader programs. However, our current BufferedImageOp shaders were |
| 799 | * designed to support first-generation shader-level hardware, so the |
| 800 | * assumption is that if our shaders work on those GPUs, then they'll |
| 801 | * work on newer ones as well. Also, linking a fragment program can |
| 802 | * cost valuable CPU cycles, which is another reason to avoid these |
| 803 | * checks at startup. |
| 804 | */ |
| 805 | |
| 806 | J2dRlsTraceLn(J2D_TRACE_INFO, |
| 807 | "OGLContext_IsBIOpShaderSupportAvailable: BufferedImageOp shader supported" ); |
| 808 | |
| 809 | return JNI_TRUE; |
| 810 | } |
| 811 | |
| 812 | /** |
| 813 | * Returns JNI_TRUE only if all of the following conditions are met: |
| 814 | * - the GL_ARB_fragment_shader extension is available |
| 815 | * - the Linear/RadialGradientPaint shader codepath has been enabled via the |
| 816 | * system property |
| 817 | */ |
| 818 | static jboolean |
| 819 | OGLContext_IsGradShaderSupportAvailable(JNIEnv *env, |
| 820 | jboolean fragShaderAvailable) |
| 821 | { |
| 822 | jboolean isGradShaderEnabled = JNI_FALSE; |
| 823 | |
| 824 | J2dTraceLn(J2D_TRACE_INFO, "OGLContext_IsGradShaderSupportAvailable" ); |
| 825 | |
| 826 | // first see if the fragment shader extension is available |
| 827 | if (!fragShaderAvailable) { |
| 828 | return JNI_FALSE; |
| 829 | } |
| 830 | |
| 831 | // next see if the gradshader system property has been enabled |
| 832 | isGradShaderEnabled = |
| 833 | JNU_GetStaticFieldByName(env, NULL, |
| 834 | "sun/java2d/opengl/OGLSurfaceData" , |
| 835 | "isGradShaderEnabled" , "Z" ).z; |
| 836 | if (!isGradShaderEnabled) { |
| 837 | J2dRlsTraceLn(J2D_TRACE_INFO, |
| 838 | "OGLContext_IsGradShaderSupportAvailable: disabled via flag" ); |
| 839 | return JNI_FALSE; |
| 840 | } |
| 841 | |
| 842 | J2dRlsTraceLn(J2D_TRACE_INFO, |
| 843 | "OGLContext_IsGradShaderSupportAvailable: Linear/RadialGradientPaint shader supported" ); |
| 844 | |
| 845 | return JNI_TRUE; |
| 846 | } |
| 847 | |
| 848 | /** |
| 849 | * Checks for the presence of the optional extensions used by |
| 850 | * the Java 2D OpenGL pipeline. The given caps bitfield is updated |
| 851 | * to reflect the availability of these extensions. |
| 852 | */ |
| 853 | void |
| 854 | OGLContext_GetExtensionInfo(JNIEnv *env, jint *caps) |
| 855 | { |
| 856 | jint vcap = OGLC_VENDOR_OTHER; |
| 857 | const char *vendor = (char *)j2d_glGetString(GL_VENDOR); |
| 858 | const char *e = (char *)j2d_glGetString(GL_EXTENSIONS); |
| 859 | jboolean fragShaderAvail = |
| 860 | OGLContext_IsExtensionAvailable(e, "GL_ARB_fragment_shader" ); |
| 861 | |
| 862 | J2dTraceLn(J2D_TRACE_INFO, "OGLContext_GetExtensionInfo" ); |
| 863 | |
| 864 | *caps |= CAPS_TEXNONSQUARE; |
| 865 | if (OGLContext_IsExtensionAvailable(e, "GL_ARB_multitexture" )) { |
| 866 | *caps |= CAPS_MULTITEXTURE; |
| 867 | } |
| 868 | if (OGLContext_IsExtensionAvailable(e, "GL_ARB_texture_non_power_of_two" )){ |
| 869 | *caps |= CAPS_TEXNONPOW2; |
| 870 | } |
| 871 | // 6656574: Use of the GL_ARB_texture_rectangle extension by Java 2D |
| 872 | // complicates any third-party libraries that try to interact with |
| 873 | // the OGL pipeline (and we've run into driver bugs in the past related |
| 874 | // to this extension), so for now we will disable its use by default (unless |
| 875 | // forced). We will still make use of the GL_ARB_texture_non_power_of_two |
| 876 | // extension when available, which is the better choice going forward |
| 877 | // anyway. |
| 878 | if (OGLContext_IsExtensionAvailable(e, "GL_ARB_texture_rectangle" ) && |
| 879 | getenv("J2D_OGL_TEXRECT" ) != NULL) |
| 880 | { |
| 881 | *caps |= CAPS_EXT_TEXRECT; |
| 882 | } |
| 883 | if (OGLContext_IsFBObjectExtensionAvailable(env, e)) { |
| 884 | *caps |= CAPS_EXT_FBOBJECT; |
| 885 | } |
| 886 | if (OGLContext_IsLCDShaderSupportAvailable(env, fragShaderAvail)) { |
| 887 | *caps |= CAPS_EXT_LCD_SHADER | CAPS_PS20; |
| 888 | } |
| 889 | if (OGLContext_IsBIOpShaderSupportAvailable(env, fragShaderAvail)) { |
| 890 | *caps |= CAPS_EXT_BIOP_SHADER | CAPS_PS20; |
| 891 | } |
| 892 | if (OGLContext_IsGradShaderSupportAvailable(env, fragShaderAvail)) { |
| 893 | *caps |= CAPS_EXT_GRAD_SHADER | CAPS_PS20; |
| 894 | } |
| 895 | if (OGLContext_IsExtensionAvailable(e, "GL_NV_fragment_program" )) { |
| 896 | // this is an Nvidia board, at least PS 2.0, but we can't |
| 897 | // use the "max instructions" heuristic since GeForce FX |
| 898 | // boards report 1024 even though they're only PS 2.0, |
| 899 | // so we'll check the following, which does imply PS 3.0 |
| 900 | if (OGLContext_IsExtensionAvailable(e, "GL_NV_fragment_program2" )) { |
| 901 | *caps |= CAPS_PS30; |
| 902 | } |
| 903 | } else { |
| 904 | // for all other boards, we look at the "max instructions" |
| 905 | // count reported by the GL_ARB_fragment_program extension |
| 906 | // as a heuristic for detecting PS 3.0 compatible hardware |
| 907 | if (OGLContext_IsExtensionAvailable(e, "GL_ARB_fragment_program" )) { |
| 908 | GLint instr; |
| 909 | j2d_glGetProgramivARB(GL_FRAGMENT_PROGRAM_ARB, |
| 910 | GL_MAX_PROGRAM_INSTRUCTIONS_ARB, &instr); |
| 911 | if (instr > 512) { |
| 912 | *caps |= CAPS_PS30; |
| 913 | } |
| 914 | } |
| 915 | } |
| 916 | if (OGLContext_IsExtensionAvailable(e, "GL_NV_texture_barrier" )) { |
| 917 | *caps |= CAPS_EXT_TEXBARRIER; |
| 918 | } |
| 919 | |
| 920 | // stuff vendor descriptor in the upper bits of the caps |
| 921 | if (vendor != NULL) { |
| 922 | if (strncmp(vendor, "ATI" , 3) == 0) { |
| 923 | vcap = OGLC_VENDOR_ATI; |
| 924 | } else if (strncmp(vendor, "NVIDIA" , 6) == 0) { |
| 925 | vcap = OGLC_VENDOR_NVIDIA; |
| 926 | } else if (strncmp(vendor, "Intel" , 5) == 0) { |
| 927 | vcap = OGLC_VENDOR_INTEL; |
| 928 | } |
| 929 | // REMIND: new in 7 - check if needs fixing |
| 930 | *caps |= ((vcap & OGLC_VCAP_MASK) << OGLC_VCAP_OFFSET); |
| 931 | } |
| 932 | |
| 933 | } |
| 934 | |
| 935 | /** |
| 936 | * Returns JNI_TRUE if the given GL_VERSION string meets the minimum |
| 937 | * requirements (>= 1.2); JNI_FALSE otherwise. |
| 938 | */ |
| 939 | jboolean |
| 940 | OGLContext_IsVersionSupported(const unsigned char *versionstr) |
| 941 | { |
| 942 | J2dTraceLn(J2D_TRACE_INFO, "OGLContext_IsVersionSupported" ); |
| 943 | |
| 944 | if (versionstr == NULL) { |
| 945 | J2dRlsTraceLn(J2D_TRACE_ERROR, |
| 946 | "OGLContext_IsVersionSupported: version string is null" ); |
| 947 | return JNI_FALSE; |
| 948 | } |
| 949 | |
| 950 | // note that this check allows for OpenGL 2.x |
| 951 | return ((versionstr[0] == '1' && versionstr[2] >= '2') || |
| 952 | (versionstr[0] >= '2')); |
| 953 | } |
| 954 | |
| 955 | /** |
| 956 | * Compiles and links the given fragment shader program. If |
| 957 | * successful, this function returns a handle to the newly created shader |
| 958 | * program; otherwise returns 0. |
| 959 | */ |
| 960 | GLhandleARB |
| 961 | OGLContext_CreateFragmentProgram(const char *fragmentShaderSource) |
| 962 | { |
| 963 | GLhandleARB fragmentShader, fragmentProgram; |
| 964 | GLint success; |
| 965 | int infoLogLength = 0; |
| 966 | |
| 967 | J2dTraceLn(J2D_TRACE_INFO, "OGLContext_CreateFragmentProgram" ); |
| 968 | |
| 969 | // create the shader object and compile the shader source code |
| 970 | fragmentShader = j2d_glCreateShaderObjectARB(GL_FRAGMENT_SHADER_ARB); |
| 971 | j2d_glShaderSourceARB(fragmentShader, 1, &fragmentShaderSource, NULL); |
| 972 | j2d_glCompileShaderARB(fragmentShader); |
| 973 | j2d_glGetObjectParameterivARB(fragmentShader, |
| 974 | GL_OBJECT_COMPILE_STATUS_ARB, |
| 975 | &success); |
| 976 | |
| 977 | // print the compiler messages, if necessary |
| 978 | j2d_glGetObjectParameterivARB(fragmentShader, |
| 979 | GL_OBJECT_INFO_LOG_LENGTH_ARB, |
| 980 | &infoLogLength); |
| 981 | if (infoLogLength > 1) { |
| 982 | char infoLog[1024]; |
| 983 | j2d_glGetInfoLogARB(fragmentShader, 1024, NULL, infoLog); |
| 984 | J2dRlsTraceLn2(J2D_TRACE_WARNING, |
| 985 | "OGLContext_CreateFragmentProgram: compiler msg (%d):\n%s" , |
| 986 | infoLogLength, infoLog); |
| 987 | } |
| 988 | |
| 989 | if (!success) { |
| 990 | J2dRlsTraceLn(J2D_TRACE_ERROR, |
| 991 | "OGLContext_CreateFragmentProgram: error compiling shader" ); |
| 992 | j2d_glDeleteObjectARB(fragmentShader); |
| 993 | return 0; |
| 994 | } |
| 995 | |
| 996 | // create the program object and attach it to the shader |
| 997 | fragmentProgram = j2d_glCreateProgramObjectARB(); |
| 998 | j2d_glAttachObjectARB(fragmentProgram, fragmentShader); |
| 999 | |
| 1000 | // it is now safe to delete the shader object |
| 1001 | j2d_glDeleteObjectARB(fragmentShader); |
| 1002 | |
| 1003 | // link the program |
| 1004 | j2d_glLinkProgramARB(fragmentProgram); |
| 1005 | j2d_glGetObjectParameterivARB(fragmentProgram, |
| 1006 | GL_OBJECT_LINK_STATUS_ARB, |
| 1007 | &success); |
| 1008 | |
| 1009 | // print the linker messages, if necessary |
| 1010 | j2d_glGetObjectParameterivARB(fragmentProgram, |
| 1011 | GL_OBJECT_INFO_LOG_LENGTH_ARB, |
| 1012 | &infoLogLength); |
| 1013 | if (infoLogLength > 1) { |
| 1014 | char infoLog[1024]; |
| 1015 | j2d_glGetInfoLogARB(fragmentProgram, 1024, NULL, infoLog); |
| 1016 | J2dRlsTraceLn2(J2D_TRACE_WARNING, |
| 1017 | "OGLContext_CreateFragmentProgram: linker msg (%d):\n%s" , |
| 1018 | infoLogLength, infoLog); |
| 1019 | } |
| 1020 | |
| 1021 | if (!success) { |
| 1022 | J2dRlsTraceLn(J2D_TRACE_ERROR, |
| 1023 | "OGLContext_CreateFragmentProgram: error linking shader" ); |
| 1024 | j2d_glDeleteObjectARB(fragmentProgram); |
| 1025 | return 0; |
| 1026 | } |
| 1027 | |
| 1028 | return fragmentProgram; |
| 1029 | } |
| 1030 | |
| 1031 | /* |
| 1032 | * Class: sun_java2d_opengl_OGLContext |
| 1033 | * Method: getOGLIdString |
| 1034 | * Signature: ()Ljava/lang/String; |
| 1035 | */ |
| 1036 | JNIEXPORT jstring JNICALL Java_sun_java2d_opengl_OGLContext_getOGLIdString |
| 1037 | (JNIEnv *env, jclass oglcc) |
| 1038 | { |
| 1039 | char *vendor, *renderer, *version; |
| 1040 | char *pAdapterId; |
| 1041 | jobject ret = NULL; |
| 1042 | int len; |
| 1043 | |
| 1044 | J2dTraceLn(J2D_TRACE_INFO, "OGLContext_getOGLIdString" ); |
| 1045 | |
| 1046 | vendor = (char*)j2d_glGetString(GL_VENDOR); |
| 1047 | if (vendor == NULL) { |
| 1048 | vendor = "Unknown Vendor" ; |
| 1049 | } |
| 1050 | renderer = (char*)j2d_glGetString(GL_RENDERER); |
| 1051 | if (renderer == NULL) { |
| 1052 | renderer = "Unknown Renderer" ; |
| 1053 | } |
| 1054 | version = (char*)j2d_glGetString(GL_VERSION); |
| 1055 | if (version == NULL) { |
| 1056 | version = "unknown version" ; |
| 1057 | } |
| 1058 | |
| 1059 | // 'vendor renderer (version)0' |
| 1060 | len = strlen(vendor) + 1 + strlen(renderer) + 1 + 1+strlen(version)+1 + 1; |
| 1061 | pAdapterId = malloc(len); |
| 1062 | if (pAdapterId != NULL) { |
| 1063 | |
| 1064 | jio_snprintf(pAdapterId, len, "%s %s (%s)" , vendor, renderer, version); |
| 1065 | |
| 1066 | J2dTraceLn1(J2D_TRACE_VERBOSE, " id=%s" , pAdapterId); |
| 1067 | |
| 1068 | ret = JNU_NewStringPlatform(env, pAdapterId); |
| 1069 | |
| 1070 | free(pAdapterId); |
| 1071 | } |
| 1072 | |
| 1073 | return ret; |
| 1074 | } |
| 1075 | |
| 1076 | #endif /* !HEADLESS */ |
| 1077 | |