1 /* 2 * Copyright (c) 2003, 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 <math.h> 30 #include <jlong.h> 31 32 #include "sun_java2d_opengl_OGLTextRenderer.h" 33 34 #include "SurfaceData.h" 35 #include "OGLContext.h" 36 #include "OGLSurfaceData.h" 37 #include "OGLRenderQueue.h" 38 #include "OGLTextRenderer.h" 39 #include "OGLVertexCache.h" 40 #include "AccelGlyphCache.h" 41 #include "fontscalerdefs.h" 42 43 /** 44 * The following constants define the inner and outer bounds of the 45 * accelerated glyph cache. 46 */ 47 #define OGLTR_CACHE_WIDTH 512 48 #define OGLTR_CACHE_HEIGHT 512 49 #define OGLTR_CACHE_CELL_WIDTH 32 50 #define OGLTR_CACHE_CELL_HEIGHT 32 51 52 /** 53 * The current "glyph mode" state. This variable is used to track the 54 * codepath used to render a particular glyph. This variable is reset to 55 * MODE_NOT_INITED at the beginning of every call to OGLTR_DrawGlyphList(). 56 * As each glyph is rendered, the glyphMode variable is updated to reflect 57 * the current mode, so if the current mode is the same as the mode used 58 * to render the previous glyph, we can avoid doing costly setup operations 59 * each time. 60 */ 61 typedef enum { 62 MODE_NOT_INITED, 63 MODE_USE_CACHE_GRAY, 64 MODE_USE_CACHE_LCD, 65 MODE_NO_CACHE_GRAY, 66 MODE_NO_CACHE_LCD 67 } GlyphMode; 68 static GlyphMode glyphMode = MODE_NOT_INITED; 69 70 /** 71 * There are two separate glyph caches: for AA and for LCD. 72 * Once one of them is initialized as either GRAY or LCD, it 73 * stays in that mode for the duration of the application. It should 74 * be safe to use this one glyph cache for all screens in a multimon 75 * environment, since the glyph cache texture is shared between all contexts, 76 * and (in theory) OpenGL drivers should be smart enough to manage that 77 * texture across all screens. 78 */ 79 80 static GlyphCacheInfo *glyphCacheLCD = NULL; 81 static GlyphCacheInfo *glyphCacheAA = NULL; 82 83 /** 84 * The handle to the LCD text fragment program object. 85 */ 86 static GLhandleARB lcdTextProgram = 0; 87 88 /** 89 * This value tracks the previous LCD contrast setting, so if the contrast 90 * value hasn't changed since the last time the gamma uniforms were 91 * updated (not very common), then we can skip updating the unforms. 92 */ 93 static jint lastLCDContrast = -1; 94 95 /** 96 * This value tracks the previous LCD rgbOrder setting, so if the rgbOrder 97 * value has changed since the last time, it indicates that we need to 98 * invalidate the cache, which may already store glyph images in the reverse 99 * order. Note that in most real world applications this value will not 100 * change over the course of the application, but tests like Font2DTest 101 * allow for changing the ordering at runtime, so we need to handle that case. 102 */ 103 static jboolean lastRGBOrder = JNI_TRUE; 104 105 /** 106 * This constant defines the size of the tile to use in the 107 * OGLTR_DrawLCDGlyphNoCache() method. See below for more on why we 108 * restrict this value to a particular size. 109 */ 110 #define OGLTR_NOCACHE_TILE_SIZE 32 111 112 /** 113 * These constants define the size of the "cached destination" texture. 114 * This texture is only used when rendering LCD-optimized text, as that 115 * codepath needs direct access to the destination. There is no way to 116 * access the framebuffer directly from an OpenGL shader, so we need to first 117 * copy the destination region corresponding to a particular glyph into 118 * this cached texture, and then that texture will be accessed inside the 119 * shader. Copying the destination into this cached texture can be a very 120 * expensive operation (accounting for about half the rendering time for 121 * LCD text), so to mitigate this cost we try to bulk read a horizontal 122 * region of the destination at a time. (These values are empirically 123 * derived for the common case where text runs horizontally.) 124 * 125 * Note: It is assumed in various calculations below that: 126 * (OGLTR_CACHED_DEST_WIDTH >= OGLTR_CACHE_CELL_WIDTH) && 127 * (OGLTR_CACHED_DEST_WIDTH >= OGLTR_NOCACHE_TILE_SIZE) && 128 * (OGLTR_CACHED_DEST_HEIGHT >= OGLTR_CACHE_CELL_HEIGHT) && 129 * (OGLTR_CACHED_DEST_HEIGHT >= OGLTR_NOCACHE_TILE_SIZE) 130 */ 131 #define OGLTR_CACHED_DEST_WIDTH 512 132 #define OGLTR_CACHED_DEST_HEIGHT (OGLTR_CACHE_CELL_HEIGHT * 2) 133 134 /** 135 * The handle to the "cached destination" texture object. 136 */ 137 static GLuint cachedDestTextureID = 0; 138 139 /** 140 * The current bounds of the "cached destination" texture, in destination 141 * coordinate space. The width/height of these bounds will not exceed the 142 * OGLTR_CACHED_DEST_WIDTH/HEIGHT values defined above. These bounds are 143 * only considered valid when the isCachedDestValid flag is JNI_TRUE. 144 */ 145 static SurfaceDataBounds cachedDestBounds; 146 147 /** 148 * This flag indicates whether the "cached destination" texture contains 149 * valid data. This flag is reset to JNI_FALSE at the beginning of every 150 * call to OGLTR_DrawGlyphList(). Once we copy valid destination data 151 * into the cached texture, this flag is set to JNI_TRUE. This way, we can 152 * limit the number of times we need to copy destination data, which is a 153 * very costly operation. 154 */ 155 static jboolean isCachedDestValid = JNI_FALSE; 156 157 /** 158 * The bounds of the previously rendered LCD glyph, in destination 159 * coordinate space. We use these bounds to determine whether the glyph 160 * currently being rendered overlaps the previously rendered glyph (i.e. 161 * its bounding box intersects that of the previously rendered glyph). If 162 * so, we need to re-read the destination area associated with that previous 163 * glyph so that we can correctly blend with the actual destination data. 164 */ 165 static SurfaceDataBounds previousGlyphBounds; 166 167 /** 168 * Initializes the one glyph cache (texture and data structure). 169 * If lcdCache is JNI_TRUE, the texture will contain RGB data, 170 * otherwise we will simply store the grayscale/monochrome glyph images 171 * as intensity values (which work well with the GL_MODULATE function). 172 */ 173 static jboolean 174 OGLTR_InitGlyphCache(jboolean lcdCache) 175 { 176 GlyphCacheInfo *gcinfo; 177 GLclampf priority = 1.0f; 178 GLenum internalFormat = lcdCache ? GL_RGB8 : GL_INTENSITY8; 179 GLenum pixelFormat = lcdCache ? GL_RGB : GL_LUMINANCE; 180 181 J2dTraceLn(J2D_TRACE_INFO, "OGLTR_InitGlyphCache"); 182 183 // init glyph cache data structure 184 gcinfo = AccelGlyphCache_Init(OGLTR_CACHE_WIDTH, 185 OGLTR_CACHE_HEIGHT, 186 OGLTR_CACHE_CELL_WIDTH, 187 OGLTR_CACHE_CELL_HEIGHT, 188 OGLVertexCache_FlushVertexCache); 189 if (gcinfo == NULL) { 190 J2dRlsTraceLn(J2D_TRACE_ERROR, 191 "OGLTR_InitGlyphCache: could not init OGL glyph cache"); 192 return JNI_FALSE; 193 } 194 195 // init cache texture object 196 j2d_glGenTextures(1, &gcinfo->cacheID); 197 j2d_glBindTexture(GL_TEXTURE_2D, gcinfo->cacheID); 198 j2d_glPrioritizeTextures(1, &gcinfo->cacheID, &priority); 199 j2d_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 200 j2d_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 201 202 j2d_glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, 203 OGLTR_CACHE_WIDTH, OGLTR_CACHE_HEIGHT, 0, 204 pixelFormat, GL_UNSIGNED_BYTE, NULL); 205 206 if (lcdCache) { 207 glyphCacheLCD = gcinfo; 208 } else { 209 glyphCacheAA = gcinfo; 210 } 211 212 return JNI_TRUE; 213 } 214 215 /** 216 * Adds the given glyph to the glyph cache (texture and data structure) 217 * associated with the given OGLContext. 218 */ 219 static void 220 OGLTR_AddToGlyphCache(GlyphInfo *glyph, GLenum pixelFormat) 221 { 222 CacheCellInfo *ccinfo; 223 GlyphCacheInfo *gcinfo; 224 225 J2dTraceLn(J2D_TRACE_INFO, "OGLTR_AddToGlyphCache"); 226 227 if (pixelFormat == GL_LUMINANCE) { 228 gcinfo = glyphCacheAA; 229 } else { 230 gcinfo = glyphCacheLCD; 231 } 232 233 if ((gcinfo == NULL) || (glyph->image == NULL)) { 234 return; 235 } 236 237 AccelGlyphCache_AddGlyph(gcinfo, glyph); 238 ccinfo = (CacheCellInfo *) glyph->cellInfo; 239 240 if (ccinfo != NULL) { 241 // store glyph image in texture cell 242 j2d_glTexSubImage2D(GL_TEXTURE_2D, 0, 243 ccinfo->x, ccinfo->y, 244 glyph->width, glyph->height, 245 pixelFormat, GL_UNSIGNED_BYTE, glyph->image); 246 } 247 } 248 249 /** 250 * This is the GLSL fragment shader source code for rendering LCD-optimized 251 * text. Do not be frightened; it is much easier to understand than the 252 * equivalent ASM-like fragment program! 253 * 254 * The "uniform" variables at the top are initialized once the program is 255 * linked, and are updated at runtime as needed (e.g. when the source color 256 * changes, we will modify the "src_adj" value in OGLTR_UpdateLCDTextColor()). 257 * 258 * The "main" function is executed for each "fragment" (or pixel) in the 259 * glyph image. The pow() routine operates on vectors, gives precise results, 260 * and provides acceptable level of performance, so we use it to perform 261 * the gamma adjustment. 262 * 263 * The variables involved in the equation can be expressed as follows: 264 * 265 * Cs = Color component of the source (foreground color) [0.0, 1.0] 266 * Cd = Color component of the destination (background color) [0.0, 1.0] 267 * Cr = Color component to be written to the destination [0.0, 1.0] 268 * Ag = Glyph alpha (aka intensity or coverage) [0.0, 1.0] 269 * Ga = Gamma adjustment in the range [1.0, 2.5] 270 * (^ means raised to the power) 271 * 272 * And here is the theoretical equation approximated by this shader: 273 * 274 * Cr = (Ag*(Cs^Ga) + (1-Ag)*(Cd^Ga)) ^ (1/Ga) 275 */ 276 static const char *lcdTextShaderSource = 277 "uniform vec3 src_adj;" 278 "uniform sampler2D glyph_tex;" 279 "uniform sampler2D dst_tex;" 280 "uniform vec3 gamma;" 281 "uniform vec3 invgamma;" 282 "" 283 "void main(void)" 284 "{" 285 // load the RGB value from the glyph image at the current texcoord 286 " vec3 glyph_clr = vec3(texture2D(glyph_tex, gl_TexCoord[0].st));" 287 " if (glyph_clr == vec3(0.0)) {" 288 // zero coverage, so skip this fragment 289 " discard;" 290 " }" 291 // load the RGB value from the corresponding destination pixel 292 " vec3 dst_clr = vec3(texture2D(dst_tex, gl_TexCoord[1].st));" 293 // gamma adjust the dest color 294 " vec3 dst_adj = pow(dst_clr.rgb, gamma);" 295 // linearly interpolate the three color values 296 " vec3 result = mix(dst_adj, src_adj, glyph_clr);" 297 // gamma re-adjust the resulting color (alpha is always set to 1.0) 298 " gl_FragColor = vec4(pow(result.rgb, invgamma), 1.0);" 299 "}"; 300 301 /** 302 * Compiles and links the LCD text shader program. If successful, this 303 * function returns a handle to the newly created shader program; otherwise 304 * returns 0. 305 */ 306 static GLhandleARB 307 OGLTR_CreateLCDTextProgram() 308 { 309 GLhandleARB lcdTextProgram; 310 GLint loc; 311 312 J2dTraceLn(J2D_TRACE_INFO, "OGLTR_CreateLCDTextProgram"); 313 314 lcdTextProgram = OGLContext_CreateFragmentProgram(lcdTextShaderSource); 315 if (lcdTextProgram == 0) { 316 J2dRlsTraceLn(J2D_TRACE_ERROR, 317 "OGLTR_CreateLCDTextProgram: error creating program"); 318 return 0; 319 } 320 321 // "use" the program object temporarily so that we can set the uniforms 322 j2d_glUseProgramObjectARB(lcdTextProgram); 323 324 // set the "uniform" values 325 loc = j2d_glGetUniformLocationARB(lcdTextProgram, "glyph_tex"); 326 j2d_glUniform1iARB(loc, 0); // texture unit 0 327 loc = j2d_glGetUniformLocationARB(lcdTextProgram, "dst_tex"); 328 j2d_glUniform1iARB(loc, 1); // texture unit 1 329 330 // "unuse" the program object; it will be re-bound later as needed 331 j2d_glUseProgramObjectARB(0); 332 333 return lcdTextProgram; 334 } 335 336 /** 337 * (Re)Initializes the gamma related uniforms. 338 * 339 * The given contrast value is an int in the range [100, 250] which we will 340 * then scale to fit in the range [1.0, 2.5]. 341 */ 342 static jboolean 343 OGLTR_UpdateLCDTextContrast(jint contrast) 344 { 345 float g = ((float)contrast) / 100.0f; 346 float ig = 1.0f / g; 347 GLint loc; 348 349 J2dTraceLn1(J2D_TRACE_INFO, 350 "OGLTR_UpdateLCDTextContrast: contrast=%d", contrast); 351 352 loc = j2d_glGetUniformLocationARB(lcdTextProgram, "gamma"); 353 j2d_glUniform3fARB(loc, g, g, g); 354 355 loc = j2d_glGetUniformLocationARB(lcdTextProgram, "invgamma"); 356 j2d_glUniform3fARB(loc, ig, ig, ig); 357 358 return JNI_TRUE; 359 } 360 361 /** 362 * Updates the current gamma-adjusted source color ("src_adj") of the LCD 363 * text shader program. Note that we could calculate this value in the 364 * shader (e.g. just as we do for "dst_adj"), but would be unnecessary work 365 * (and a measurable performance hit, maybe around 5%) since this value is 366 * constant over the entire glyph list. So instead we just calculate the 367 * gamma-adjusted value once and update the uniform parameter of the LCD 368 * shader as needed. 369 */ 370 static jboolean 371 OGLTR_UpdateLCDTextColor(jint contrast) 372 { 373 double gamma = ((double)contrast) / 100.0; 374 GLfloat radj, gadj, badj; 375 GLfloat clr[4]; 376 GLint loc; 377 378 J2dTraceLn1(J2D_TRACE_INFO, 379 "OGLTR_UpdateLCDTextColor: contrast=%d", contrast); 380 381 /* 382 * Note: Ideally we would update the "src_adj" uniform parameter only 383 * when there is a change in the source color. Fortunately, the cost 384 * of querying the current OpenGL color state and updating the uniform 385 * value is quite small, and in the common case we only need to do this 386 * once per GlyphList, so we gain little from trying to optimize too 387 * eagerly here. 388 */ 389 390 // get the current OpenGL primary color state 391 j2d_glGetFloatv(GL_CURRENT_COLOR, clr); 392 393 // gamma adjust the primary color 394 radj = (GLfloat)pow(clr[0], gamma); 395 gadj = (GLfloat)pow(clr[1], gamma); 396 badj = (GLfloat)pow(clr[2], gamma); 397 398 // update the "src_adj" parameter of the shader program with this value 399 loc = j2d_glGetUniformLocationARB(lcdTextProgram, "src_adj"); 400 j2d_glUniform3fARB(loc, radj, gadj, badj); 401 402 return JNI_TRUE; 403 } 404 405 /** 406 * Enables the LCD text shader and updates any related state, such as the 407 * gamma lookup table textures. 408 */ 409 static jboolean 410 OGLTR_EnableLCDGlyphModeState(GLuint glyphTextureID, 411 GLuint dstTextureID, 412 jint contrast) 413 { 414 // bind the texture containing glyph data to texture unit 0 415 j2d_glActiveTextureARB(GL_TEXTURE0_ARB); 416 j2d_glBindTexture(GL_TEXTURE_2D, glyphTextureID); 417 j2d_glEnable(GL_TEXTURE_2D); 418 419 // bind the texture tile containing destination data to texture unit 1 420 j2d_glActiveTextureARB(GL_TEXTURE1_ARB); 421 if (dstTextureID != 0) { 422 j2d_glBindTexture(GL_TEXTURE_2D, dstTextureID); 423 } else { 424 if (cachedDestTextureID == 0) { 425 cachedDestTextureID = 426 OGLContext_CreateBlitTexture(GL_RGB8, GL_RGB, 427 OGLTR_CACHED_DEST_WIDTH, 428 OGLTR_CACHED_DEST_HEIGHT); 429 if (cachedDestTextureID == 0) { 430 return JNI_FALSE; 431 } 432 } 433 j2d_glBindTexture(GL_TEXTURE_2D, cachedDestTextureID); 434 } 435 436 // note that GL_TEXTURE_2D was already enabled for texture unit 0, 437 // but we need to explicitly enable it for texture unit 1 438 j2d_glEnable(GL_TEXTURE_2D); 439 440 // create the LCD text shader, if necessary 441 if (lcdTextProgram == 0) { 442 lcdTextProgram = OGLTR_CreateLCDTextProgram(); 443 if (lcdTextProgram == 0) { 444 return JNI_FALSE; 445 } 446 } 447 448 // enable the LCD text shader 449 j2d_glUseProgramObjectARB(lcdTextProgram); 450 451 // update the current contrast settings, if necessary 452 if (lastLCDContrast != contrast) { 453 if (!OGLTR_UpdateLCDTextContrast(contrast)) { 454 return JNI_FALSE; 455 } 456 lastLCDContrast = contrast; 457 } 458 459 // update the current color settings 460 if (!OGLTR_UpdateLCDTextColor(contrast)) { 461 return JNI_FALSE; 462 } 463 464 return JNI_TRUE; 465 } 466 467 void 468 OGLTR_EnableGlyphVertexCache(OGLContext *oglc) 469 { 470 J2dTraceLn(J2D_TRACE_INFO, "OGLTR_EnableGlyphVertexCache"); 471 472 if (!OGLVertexCache_InitVertexCache(oglc)) { 473 return; 474 } 475 476 if (glyphCacheAA == NULL) { 477 if (!OGLTR_InitGlyphCache(JNI_FALSE)) { 478 return; 479 } 480 } 481 482 j2d_glEnable(GL_TEXTURE_2D); 483 j2d_glBindTexture(GL_TEXTURE_2D, glyphCacheAA->cacheID); 484 j2d_glPixelStorei(GL_UNPACK_ALIGNMENT, 1); 485 486 // for grayscale/monochrome text, the current OpenGL source color 487 // is modulated with the glyph image as part of the texture 488 // application stage, so we use GL_MODULATE here 489 OGLC_UPDATE_TEXTURE_FUNCTION(oglc, GL_MODULATE); 490 } 491 492 void 493 OGLTR_DisableGlyphVertexCache(OGLContext *oglc) 494 { 495 J2dTraceLn(J2D_TRACE_INFO, "OGLTR_DisableGlyphVertexCache"); 496 497 OGLVertexCache_FlushVertexCache(); 498 OGLVertexCache_RestoreColorState(oglc); 499 500 j2d_glDisable(GL_TEXTURE_2D); 501 j2d_glPixelStorei(GL_UNPACK_ALIGNMENT, 4); 502 j2d_glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0); 503 j2d_glPixelStorei(GL_UNPACK_SKIP_ROWS, 0); 504 j2d_glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); 505 } 506 507 /** 508 * Disables any pending state associated with the current "glyph mode". 509 */ 510 static void 511 OGLTR_DisableGlyphModeState() 512 { 513 switch (glyphMode) { 514 case MODE_NO_CACHE_LCD: 515 j2d_glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0); 516 j2d_glPixelStorei(GL_UNPACK_SKIP_ROWS, 0); 517 /* FALLTHROUGH */ 518 519 case MODE_USE_CACHE_LCD: 520 j2d_glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); 521 j2d_glPixelStorei(GL_UNPACK_ALIGNMENT, 4); 522 j2d_glUseProgramObjectARB(0); 523 j2d_glActiveTextureARB(GL_TEXTURE1_ARB); 524 j2d_glDisable(GL_TEXTURE_2D); 525 j2d_glActiveTextureARB(GL_TEXTURE0_ARB); 526 j2d_glDisable(GL_TEXTURE_2D); 527 break; 528 529 case MODE_NO_CACHE_GRAY: 530 case MODE_USE_CACHE_GRAY: 531 case MODE_NOT_INITED: 532 default: 533 break; 534 } 535 } 536 537 static jboolean 538 OGLTR_DrawGrayscaleGlyphViaCache(OGLContext *oglc, 539 GlyphInfo *ginfo, jint x, jint y) 540 { 541 CacheCellInfo *cell; 542 jfloat x1, y1, x2, y2; 543 544 if (glyphMode != MODE_USE_CACHE_GRAY) { 545 OGLTR_DisableGlyphModeState(); 546 CHECK_PREVIOUS_OP(OGL_STATE_GLYPH_OP); 547 glyphMode = MODE_USE_CACHE_GRAY; 548 } 549 550 if (ginfo->cellInfo == NULL) { 551 // attempt to add glyph to accelerated glyph cache 552 OGLTR_AddToGlyphCache(ginfo, GL_LUMINANCE); 553 554 if (ginfo->cellInfo == NULL) { 555 // we'll just no-op in the rare case that the cell is NULL 556 return JNI_TRUE; 557 } 558 } 559 560 cell = (CacheCellInfo *) (ginfo->cellInfo); 561 cell->timesRendered++; 562 563 x1 = (jfloat)x; 564 y1 = (jfloat)y; 565 x2 = x1 + ginfo->width; 566 y2 = y1 + ginfo->height; 567 568 OGLVertexCache_AddGlyphQuad(oglc, 569 cell->tx1, cell->ty1, 570 cell->tx2, cell->ty2, 571 x1, y1, x2, y2); 572 573 return JNI_TRUE; 574 } 575 576 /** 577 * Evaluates to true if the rectangle defined by gx1/gy1/gx2/gy2 is 578 * inside outerBounds. 579 */ 580 #define INSIDE(gx1, gy1, gx2, gy2, outerBounds) \ 581 (((gx1) >= outerBounds.x1) && ((gy1) >= outerBounds.y1) && \ 582 ((gx2) <= outerBounds.x2) && ((gy2) <= outerBounds.y2)) 583 584 /** 585 * Evaluates to true if the rectangle defined by gx1/gy1/gx2/gy2 intersects 586 * the rectangle defined by bounds. 587 */ 588 #define INTERSECTS(gx1, gy1, gx2, gy2, bounds) \ 589 ((bounds.x2 > (gx1)) && (bounds.y2 > (gy1)) && \ 590 (bounds.x1 < (gx2)) && (bounds.y1 < (gy2))) 591 592 /** 593 * This method checks to see if the given LCD glyph bounds fall within the 594 * cached destination texture bounds. If so, this method can return 595 * immediately. If not, this method will copy a chunk of framebuffer data 596 * into the cached destination texture and then update the current cached 597 * destination bounds before returning. 598 */ 599 static void 600 OGLTR_UpdateCachedDestination(OGLSDOps *dstOps, GlyphInfo *ginfo, 601 jint gx1, jint gy1, jint gx2, jint gy2, 602 jint glyphIndex, jint totalGlyphs) 603 { 604 jint dx1, dy1, dx2, dy2; 605 jint dx1adj, dy1adj; 606 607 if (isCachedDestValid && INSIDE(gx1, gy1, gx2, gy2, cachedDestBounds)) { 608 // glyph is already within the cached destination bounds; no need 609 // to read back the entire destination region again, but we do 610 // need to see if the current glyph overlaps the previous glyph... 611 612 if (INTERSECTS(gx1, gy1, gx2, gy2, previousGlyphBounds)) { 613 // the current glyph overlaps the destination region touched 614 // by the previous glyph, so now we need to read back the part 615 // of the destination corresponding to the previous glyph 616 dx1 = previousGlyphBounds.x1; 617 dy1 = previousGlyphBounds.y1; 618 dx2 = previousGlyphBounds.x2; 619 dy2 = previousGlyphBounds.y2; 620 621 // this accounts for lower-left origin of the destination region 622 dx1adj = dstOps->xOffset + dx1; 623 dy1adj = dstOps->yOffset + dstOps->height - dy2; 624 625 // copy destination into subregion of cached texture tile: 626 // dx1-cachedDestBounds.x1 == +xoffset from left side of texture 627 // cachedDestBounds.y2-dy2 == +yoffset from bottom of texture 628 j2d_glActiveTextureARB(GL_TEXTURE1_ARB); 629 j2d_glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 630 dx1 - cachedDestBounds.x1, 631 cachedDestBounds.y2 - dy2, 632 dx1adj, dy1adj, 633 dx2-dx1, dy2-dy1); 634 } 635 } else { 636 jint remainingWidth; 637 638 // destination region is not valid, so we need to read back a 639 // chunk of the destination into our cached texture 640 641 // position the upper-left corner of the destination region on the 642 // "top" line of glyph list 643 // REMIND: this isn't ideal; it would be better if we had some idea 644 // of the bounding box of the whole glyph list (this is 645 // do-able, but would require iterating through the whole 646 // list up front, which may present its own problems) 647 dx1 = gx1; 648 dy1 = gy1; 649 650 if (ginfo->advanceX > 0) { 651 // estimate the width based on our current position in the glyph 652 // list and using the x advance of the current glyph (this is just 653 // a quick and dirty heuristic; if this is a "thin" glyph image, 654 // then we're likely to underestimate, and if it's "thick" then we 655 // may end up reading back more than we need to) 656 remainingWidth = 657 (jint)(ginfo->advanceX * (totalGlyphs - glyphIndex)); 658 if (remainingWidth > OGLTR_CACHED_DEST_WIDTH) { 659 remainingWidth = OGLTR_CACHED_DEST_WIDTH; 660 } else if (remainingWidth < ginfo->width) { 661 // in some cases, the x-advance may be slightly smaller 662 // than the actual width of the glyph; if so, adjust our 663 // estimate so that we can accommodate the entire glyph 664 remainingWidth = ginfo->width; 665 } 666 } else { 667 // a negative advance is possible when rendering rotated text, 668 // in which case it is difficult to estimate an appropriate 669 // region for readback, so we will pick a region that 670 // encompasses just the current glyph 671 remainingWidth = ginfo->width; 672 } 673 dx2 = dx1 + remainingWidth; 674 675 // estimate the height (this is another sloppy heuristic; we'll 676 // make the cached destination region tall enough to encompass most 677 // glyphs that are small enough to fit in the glyph cache, and then 678 // we add a little something extra to account for descenders 679 dy2 = dy1 + OGLTR_CACHE_CELL_HEIGHT + 2; 680 681 // this accounts for lower-left origin of the destination region 682 dx1adj = dstOps->xOffset + dx1; 683 dy1adj = dstOps->yOffset + dstOps->height - dy2; 684 685 // copy destination into cached texture tile (the lower-left corner 686 // of the destination region will be positioned at the lower-left 687 // corner (0,0) of the texture) 688 j2d_glActiveTextureARB(GL_TEXTURE1_ARB); 689 j2d_glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 690 0, 0, dx1adj, dy1adj, 691 dx2-dx1, dy2-dy1); 692 693 // update the cached bounds and mark it valid 694 cachedDestBounds.x1 = dx1; 695 cachedDestBounds.y1 = dy1; 696 cachedDestBounds.x2 = dx2; 697 cachedDestBounds.y2 = dy2; 698 isCachedDestValid = JNI_TRUE; 699 } 700 701 // always update the previous glyph bounds 702 previousGlyphBounds.x1 = gx1; 703 previousGlyphBounds.y1 = gy1; 704 previousGlyphBounds.x2 = gx2; 705 previousGlyphBounds.y2 = gy2; 706 } 707 708 static jboolean 709 OGLTR_DrawLCDGlyphViaCache(OGLContext *oglc, OGLSDOps *dstOps, 710 GlyphInfo *ginfo, jint x, jint y, 711 jint glyphIndex, jint totalGlyphs, 712 jboolean rgbOrder, jint contrast, 713 GLuint dstTextureID) 714 { 715 CacheCellInfo *cell; 716 jint dx1, dy1, dx2, dy2; 717 jfloat dtx1, dty1, dtx2, dty2; 718 719 if (glyphMode != MODE_USE_CACHE_LCD) { 720 OGLTR_DisableGlyphModeState(); 721 CHECK_PREVIOUS_OP(GL_TEXTURE_2D); 722 j2d_glPixelStorei(GL_UNPACK_ALIGNMENT, 1); 723 724 if (glyphCacheLCD == NULL) { 725 if (!OGLTR_InitGlyphCache(JNI_TRUE)) { 726 return JNI_FALSE; 727 } 728 } 729 730 if (rgbOrder != lastRGBOrder) { 731 // need to invalidate the cache in this case; see comments 732 // for lastRGBOrder above 733 AccelGlyphCache_Invalidate(glyphCacheLCD); 734 lastRGBOrder = rgbOrder; 735 } 736 737 if (!OGLTR_EnableLCDGlyphModeState(glyphCacheLCD->cacheID, 738 dstTextureID, contrast)) 739 { 740 return JNI_FALSE; 741 } 742 743 // when a fragment shader is enabled, the texture function state is 744 // ignored, so the following line is not needed... 745 // OGLC_UPDATE_TEXTURE_FUNCTION(oglc, GL_MODULATE); 746 747 glyphMode = MODE_USE_CACHE_LCD; 748 } 749 750 if (ginfo->cellInfo == NULL) { 751 // rowBytes will always be a multiple of 3, so the following is safe 752 j2d_glPixelStorei(GL_UNPACK_ROW_LENGTH, ginfo->rowBytes / 3); 753 754 // make sure the glyph cache texture is bound to texture unit 0 755 j2d_glActiveTextureARB(GL_TEXTURE0_ARB); 756 757 // attempt to add glyph to accelerated glyph cache 758 OGLTR_AddToGlyphCache(ginfo, rgbOrder ? GL_RGB : GL_BGR); 759 760 if (ginfo->cellInfo == NULL) { 761 // we'll just no-op in the rare case that the cell is NULL 762 return JNI_TRUE; 763 } 764 } 765 766 cell = (CacheCellInfo *) (ginfo->cellInfo); 767 cell->timesRendered++; 768 769 // location of the glyph in the destination's coordinate space 770 dx1 = x; 771 dy1 = y; 772 dx2 = dx1 + ginfo->width; 773 dy2 = dy1 + ginfo->height; 774 775 if (dstTextureID == 0) { 776 // copy destination into second cached texture, if necessary 777 OGLTR_UpdateCachedDestination(dstOps, ginfo, 778 dx1, dy1, dx2, dy2, 779 glyphIndex, totalGlyphs); 780 781 // texture coordinates of the destination tile 782 dtx1 = ((jfloat)(dx1 - cachedDestBounds.x1)) / OGLTR_CACHED_DEST_WIDTH; 783 dty1 = ((jfloat)(cachedDestBounds.y2 - dy1)) / OGLTR_CACHED_DEST_HEIGHT; 784 dtx2 = ((jfloat)(dx2 - cachedDestBounds.x1)) / OGLTR_CACHED_DEST_WIDTH; 785 dty2 = ((jfloat)(cachedDestBounds.y2 - dy2)) / OGLTR_CACHED_DEST_HEIGHT; 786 } else { 787 jint gw = ginfo->width; 788 jint gh = ginfo->height; 789 790 // this accounts for lower-left origin of the destination region 791 jint dxadj = dstOps->xOffset + x; 792 jint dyadj = dstOps->yOffset + dstOps->height - (y + gh); 793 794 // update the remaining destination texture coordinates 795 dtx1 =((GLfloat)dxadj) / dstOps->textureWidth; 796 dtx2 = ((GLfloat)dxadj + gw) / dstOps->textureWidth; 797 798 dty1 = ((GLfloat)dyadj + gh) / dstOps->textureHeight; 799 dty2 = ((GLfloat)dyadj) / dstOps->textureHeight; 800 801 j2d_glTextureBarrierNV(); 802 } 803 804 // render composed texture to the destination surface 805 j2d_glBegin(GL_QUADS); 806 j2d_glMultiTexCoord2fARB(GL_TEXTURE0_ARB, cell->tx1, cell->ty1); 807 j2d_glMultiTexCoord2fARB(GL_TEXTURE1_ARB, dtx1, dty1); 808 j2d_glVertex2i(dx1, dy1); 809 j2d_glMultiTexCoord2fARB(GL_TEXTURE0_ARB, cell->tx2, cell->ty1); 810 j2d_glMultiTexCoord2fARB(GL_TEXTURE1_ARB, dtx2, dty1); 811 j2d_glVertex2i(dx2, dy1); 812 j2d_glMultiTexCoord2fARB(GL_TEXTURE0_ARB, cell->tx2, cell->ty2); 813 j2d_glMultiTexCoord2fARB(GL_TEXTURE1_ARB, dtx2, dty2); 814 j2d_glVertex2i(dx2, dy2); 815 j2d_glMultiTexCoord2fARB(GL_TEXTURE0_ARB, cell->tx1, cell->ty2); 816 j2d_glMultiTexCoord2fARB(GL_TEXTURE1_ARB, dtx1, dty2); 817 j2d_glVertex2i(dx1, dy2); 818 j2d_glEnd(); 819 820 return JNI_TRUE; 821 } 822 823 static jboolean 824 OGLTR_DrawGrayscaleGlyphNoCache(OGLContext *oglc, 825 GlyphInfo *ginfo, jint x, jint y) 826 { 827 jint tw, th; 828 jint sx, sy, sw, sh; 829 jint x0; 830 jint w = ginfo->width; 831 jint h = ginfo->height; 832 833 if (glyphMode != MODE_NO_CACHE_GRAY) { 834 OGLTR_DisableGlyphModeState(); 835 CHECK_PREVIOUS_OP(OGL_STATE_MASK_OP); 836 glyphMode = MODE_NO_CACHE_GRAY; 837 } 838 839 x0 = x; 840 tw = OGLVC_MASK_CACHE_TILE_WIDTH; 841 th = OGLVC_MASK_CACHE_TILE_HEIGHT; 842 843 for (sy = 0; sy < h; sy += th, y += th) { 844 x = x0; 845 sh = ((sy + th) > h) ? (h - sy) : th; 846 847 for (sx = 0; sx < w; sx += tw, x += tw) { 848 sw = ((sx + tw) > w) ? (w - sx) : tw; 849 850 OGLVertexCache_AddMaskQuad(oglc, 851 sx, sy, x, y, sw, sh, 852 w, ginfo->image); 853 } 854 } 855 856 return JNI_TRUE; 857 } 858 859 static jboolean 860 OGLTR_DrawLCDGlyphNoCache(OGLContext *oglc, OGLSDOps *dstOps, 861 GlyphInfo *ginfo, jint x, jint y, 862 jint rowBytesOffset, 863 jboolean rgbOrder, jint contrast, 864 GLuint dstTextureID) 865 { 866 GLfloat tx1, ty1, tx2, ty2; 867 GLfloat dtx1, dty1, dtx2, dty2; 868 jint tw, th; 869 jint sx, sy, sw, sh, dxadj, dyadj; 870 jint x0; 871 jint w = ginfo->width; 872 jint h = ginfo->height; 873 GLenum pixelFormat = rgbOrder ? GL_RGB : GL_BGR; 874 875 if (glyphMode != MODE_NO_CACHE_LCD) { 876 OGLTR_DisableGlyphModeState(); 877 CHECK_PREVIOUS_OP(GL_TEXTURE_2D); 878 j2d_glPixelStorei(GL_UNPACK_ALIGNMENT, 1); 879 880 if (oglc->blitTextureID == 0) { 881 if (!OGLContext_InitBlitTileTexture(oglc)) { 882 return JNI_FALSE; 883 } 884 } 885 886 if (!OGLTR_EnableLCDGlyphModeState(oglc->blitTextureID, 887 dstTextureID, contrast)) 888 { 889 return JNI_FALSE; 890 } 891 892 // when a fragment shader is enabled, the texture function state is 893 // ignored, so the following line is not needed... 894 // OGLC_UPDATE_TEXTURE_FUNCTION(oglc, GL_MODULATE); 895 896 glyphMode = MODE_NO_CACHE_LCD; 897 } 898 899 // rowBytes will always be a multiple of 3, so the following is safe 900 j2d_glPixelStorei(GL_UNPACK_ROW_LENGTH, ginfo->rowBytes / 3); 901 902 x0 = x; 903 tx1 = 0.0f; 904 ty1 = 0.0f; 905 dtx1 = 0.0f; 906 dty2 = 0.0f; 907 tw = OGLTR_NOCACHE_TILE_SIZE; 908 th = OGLTR_NOCACHE_TILE_SIZE; 909 910 for (sy = 0; sy < h; sy += th, y += th) { 911 x = x0; 912 sh = ((sy + th) > h) ? (h - sy) : th; 913 914 for (sx = 0; sx < w; sx += tw, x += tw) { 915 sw = ((sx + tw) > w) ? (w - sx) : tw; 916 917 // update the source pointer offsets 918 j2d_glPixelStorei(GL_UNPACK_SKIP_PIXELS, sx); 919 j2d_glPixelStorei(GL_UNPACK_SKIP_ROWS, sy); 920 921 // copy LCD mask into glyph texture tile 922 j2d_glActiveTextureARB(GL_TEXTURE0_ARB); 923 j2d_glTexSubImage2D(GL_TEXTURE_2D, 0, 924 0, 0, sw, sh, 925 pixelFormat, GL_UNSIGNED_BYTE, 926 ginfo->image + rowBytesOffset); 927 928 // update the lower-right glyph texture coordinates 929 tx2 = ((GLfloat)sw) / OGLC_BLIT_TILE_SIZE; 930 ty2 = ((GLfloat)sh) / OGLC_BLIT_TILE_SIZE; 931 932 // this accounts for lower-left origin of the destination region 933 dxadj = dstOps->xOffset + x; 934 dyadj = dstOps->yOffset + dstOps->height - (y + sh); 935 936 if (dstTextureID == 0) { 937 // copy destination into cached texture tile (the lower-left 938 // corner of the destination region will be positioned at the 939 // lower-left corner (0,0) of the texture) 940 j2d_glActiveTextureARB(GL_TEXTURE1_ARB); 941 j2d_glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 942 0, 0, 943 dxadj, dyadj, 944 sw, sh); 945 // update the remaining destination texture coordinates 946 dtx2 = ((GLfloat)sw) / OGLTR_CACHED_DEST_WIDTH; 947 dty1 = ((GLfloat)sh) / OGLTR_CACHED_DEST_HEIGHT; 948 } else { 949 // use the destination texture directly 950 // update the remaining destination texture coordinates 951 dtx1 =((GLfloat)dxadj) / dstOps->textureWidth; 952 dtx2 = ((GLfloat)dxadj + sw) / dstOps->textureWidth; 953 954 dty1 = ((GLfloat)dyadj + sh) / dstOps->textureHeight; 955 dty2 = ((GLfloat)dyadj) / dstOps->textureHeight; 956 957 j2d_glTextureBarrierNV(); 958 } 959 960 // render composed texture to the destination surface 961 j2d_glBegin(GL_QUADS); 962 j2d_glMultiTexCoord2fARB(GL_TEXTURE0_ARB, tx1, ty1); 963 j2d_glMultiTexCoord2fARB(GL_TEXTURE1_ARB, dtx1, dty1); 964 j2d_glVertex2i(x, y); 965 j2d_glMultiTexCoord2fARB(GL_TEXTURE0_ARB, tx2, ty1); 966 j2d_glMultiTexCoord2fARB(GL_TEXTURE1_ARB, dtx2, dty1); 967 j2d_glVertex2i(x + sw, y); 968 j2d_glMultiTexCoord2fARB(GL_TEXTURE0_ARB, tx2, ty2); 969 j2d_glMultiTexCoord2fARB(GL_TEXTURE1_ARB, dtx2, dty2); 970 j2d_glVertex2i(x + sw, y + sh); 971 j2d_glMultiTexCoord2fARB(GL_TEXTURE0_ARB, tx1, ty2); 972 j2d_glMultiTexCoord2fARB(GL_TEXTURE1_ARB, dtx1, dty2); 973 j2d_glVertex2i(x, y + sh); 974 j2d_glEnd(); 975 } 976 } 977 978 return JNI_TRUE; 979 } 980 981 // see DrawGlyphList.c for more on this macro... 982 #define FLOOR_ASSIGN(l, r) \ 983 if ((r)<0) (l) = ((int)floor(r)); else (l) = ((int)(r)) 984 985 void 986 OGLTR_DrawGlyphList(JNIEnv *env, OGLContext *oglc, OGLSDOps *dstOps, 987 jint totalGlyphs, jboolean usePositions, 988 jboolean subPixPos, jboolean rgbOrder, jint lcdContrast, 989 jfloat glyphListOrigX, jfloat glyphListOrigY, 990 unsigned char *images, unsigned char *positions) 991 { 992 int glyphCounter; 993 GLuint dstTextureID = 0; 994 995 J2dTraceLn(J2D_TRACE_INFO, "OGLTR_DrawGlyphList"); 996 997 RETURN_IF_NULL(oglc); 998 RETURN_IF_NULL(dstOps); 999 RETURN_IF_NULL(images); 1000 if (usePositions) { 1001 RETURN_IF_NULL(positions); 1002 } 1003 1004 glyphMode = MODE_NOT_INITED; 1005 isCachedDestValid = JNI_FALSE; 1006 1007 // We have to obtain an information about destination content 1008 // in order to render lcd glyphs. It could be done by copying 1009 // a part of desitination buffer into an intermediate texture 1010 // using glCopyTexSubImage2D(). However, on macosx this path is 1011 // slow, and it dramatically reduces the overall speed of lcd 1012 // text rendering. 1013 // 1014 // In some cases, we can use a texture from the destination 1015 // surface data in oredr to avoid this slow reading routine. 1016 // It requires: 1017 // * An appropriate textureTarget for the destination SD. 1018 // In particular, we need GL_TEXTURE_2D 1019 // * Means to prevent read-after-write problem. 1020 // At the moment, a GL_NV_texture_barrier extension is used 1021 // to achieve this. 1022 if (OGLC_IS_CAP_PRESENT(oglc, CAPS_EXT_TEXBARRIER) && 1023 dstOps->textureTarget == GL_TEXTURE_2D) 1024 { 1025 dstTextureID = dstOps->textureID; 1026 } 1027 1028 for (glyphCounter = 0; glyphCounter < totalGlyphs; glyphCounter++) { 1029 jint x, y; 1030 jfloat glyphx, glyphy; 1031 jboolean grayscale, ok; 1032 GlyphInfo *ginfo = (GlyphInfo *)jlong_to_ptr(NEXT_LONG(images)); 1033 1034 if (ginfo == NULL) { 1035 // this shouldn't happen, but if it does we'll just break out... 1036 J2dRlsTraceLn(J2D_TRACE_ERROR, 1037 "OGLTR_DrawGlyphList: glyph info is null"); 1038 break; 1039 } 1040 1041 grayscale = (ginfo->rowBytes == ginfo->width); 1042 1043 if (usePositions) { 1044 jfloat posx = NEXT_FLOAT(positions); 1045 jfloat posy = NEXT_FLOAT(positions); 1046 glyphx = glyphListOrigX + posx + ginfo->topLeftX; 1047 glyphy = glyphListOrigY + posy + ginfo->topLeftY; 1048 FLOOR_ASSIGN(x, glyphx); 1049 FLOOR_ASSIGN(y, glyphy); 1050 } else { 1051 glyphx = glyphListOrigX + ginfo->topLeftX; 1052 glyphy = glyphListOrigY + ginfo->topLeftY; 1053 FLOOR_ASSIGN(x, glyphx); 1054 FLOOR_ASSIGN(y, glyphy); 1055 glyphListOrigX += ginfo->advanceX; 1056 glyphListOrigY += ginfo->advanceY; 1057 } 1058 1059 if (ginfo->image == NULL) { 1060 continue; 1061 } 1062 1063 if (grayscale) { 1064 // grayscale or monochrome glyph data 1065 if (ginfo->width <= OGLTR_CACHE_CELL_WIDTH && 1066 ginfo->height <= OGLTR_CACHE_CELL_HEIGHT) 1067 { 1068 ok = OGLTR_DrawGrayscaleGlyphViaCache(oglc, ginfo, x, y); 1069 } else { 1070 ok = OGLTR_DrawGrayscaleGlyphNoCache(oglc, ginfo, x, y); 1071 } 1072 } else { 1073 // LCD-optimized glyph data 1074 jint rowBytesOffset = 0; 1075 1076 if (subPixPos) { 1077 jint frac = (jint)((glyphx - x) * 3); 1078 if (frac != 0) { 1079 rowBytesOffset = 3 - frac; 1080 x += 1; 1081 } 1082 } 1083 1084 if (rowBytesOffset == 0 && 1085 ginfo->width <= OGLTR_CACHE_CELL_WIDTH && 1086 ginfo->height <= OGLTR_CACHE_CELL_HEIGHT) 1087 { 1088 ok = OGLTR_DrawLCDGlyphViaCache(oglc, dstOps, 1089 ginfo, x, y, 1090 glyphCounter, totalGlyphs, 1091 rgbOrder, lcdContrast, 1092 dstTextureID); 1093 } else { 1094 ok = OGLTR_DrawLCDGlyphNoCache(oglc, dstOps, 1095 ginfo, x, y, 1096 rowBytesOffset, 1097 rgbOrder, lcdContrast, 1098 dstTextureID); 1099 } 1100 } 1101 1102 if (!ok) { 1103 break; 1104 } 1105 } 1106 1107 OGLTR_DisableGlyphModeState(); 1108 } 1109 1110 JNIEXPORT void JNICALL 1111 Java_sun_java2d_opengl_OGLTextRenderer_drawGlyphList 1112 (JNIEnv *env, jobject self, 1113 jint numGlyphs, jboolean usePositions, 1114 jboolean subPixPos, jboolean rgbOrder, jint lcdContrast, 1115 jfloat glyphListOrigX, jfloat glyphListOrigY, 1116 jlongArray imgArray, jfloatArray posArray) 1117 { 1118 unsigned char *images; 1119 1120 J2dTraceLn(J2D_TRACE_INFO, "OGLTextRenderer_drawGlyphList"); 1121 1122 images = (unsigned char *) 1123 (*env)->GetPrimitiveArrayCritical(env, imgArray, NULL); 1124 if (images != NULL) { 1125 OGLContext *oglc = OGLRenderQueue_GetCurrentContext(); 1126 OGLSDOps *dstOps = OGLRenderQueue_GetCurrentDestination(); 1127 1128 if (usePositions) { 1129 unsigned char *positions = (unsigned char *) 1130 (*env)->GetPrimitiveArrayCritical(env, posArray, NULL); 1131 if (positions != NULL) { 1132 OGLTR_DrawGlyphList(env, oglc, dstOps, 1133 numGlyphs, usePositions, 1134 subPixPos, rgbOrder, lcdContrast, 1135 glyphListOrigX, glyphListOrigY, 1136 images, positions); 1137 (*env)->ReleasePrimitiveArrayCritical(env, posArray, 1138 positions, JNI_ABORT); 1139 } 1140 } else { 1141 OGLTR_DrawGlyphList(env, oglc, dstOps, 1142 numGlyphs, usePositions, 1143 subPixPos, rgbOrder, lcdContrast, 1144 glyphListOrigX, glyphListOrigY, 1145 images, NULL); 1146 } 1147 1148 // 6358147: reset current state, and ensure rendering is 1149 // flushed to dest 1150 if (oglc != NULL) { 1151 RESET_PREVIOUS_OP(); 1152 j2d_glFlush(); 1153 } 1154 1155 (*env)->ReleasePrimitiveArrayCritical(env, imgArray, 1156 images, JNI_ABORT); 1157 } 1158 } 1159 1160 #endif /* !HEADLESS */