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