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 | |