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 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 glyph cache data structure 206 gcinfo = AccelGlyphCache_Init(OGLTR_CACHE_WIDTH, 207 OGLTR_CACHE_HEIGHT, 208 OGLTR_CACHE_CELL_WIDTH, 209 OGLTR_CACHE_CELL_HEIGHT, 210 OGLVertexCache_FlushVertexCache); 211 if (gcinfo == NULL) { 212 J2dRlsTraceLn(J2D_TRACE_ERROR, 213 "OGLTR_InitGlyphCache: could not init OGL glyph cache"); 214 return JNI_FALSE; 215 } 216 217 // init cache texture object 218 j2d_glGenTextures(1, &gcinfo->cacheID); 219 j2d_glBindTexture(GL_TEXTURE_2D, gcinfo->cacheID); 220 j2d_glPrioritizeTextures(1, &gcinfo->cacheID, &priority); 221 j2d_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 222 j2d_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 223 224 j2d_glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, 225 OGLTR_CACHE_WIDTH, OGLTR_CACHE_HEIGHT, 0, 226 pixelFormat, GL_UNSIGNED_BYTE, NULL); 227 228 cacheStatus = (lcdCache ? CACHE_LCD : CACHE_GRAY); 229 glyphCache = gcinfo; 230 231 return JNI_TRUE; 232 } 233 234 /** 235 * Adds the given glyph to the glyph cache (texture and data structure) 236 * associated with the given OGLContext. 237 */ 238 static void 239 OGLTR_AddToGlyphCache(GlyphInfo *glyph, jboolean rgbOrder) 240 { 241 GLenum pixelFormat; 242 CacheCellInfo *ccinfo; 243 244 J2dTraceLn(J2D_TRACE_INFO, "OGLTR_AddToGlyphCache"); 245 246 if ((glyphCache == NULL) || (glyph->image == NULL)) { 247 return; 248 } 249 250 if (cacheStatus == CACHE_LCD) { 251 pixelFormat = rgbOrder ? GL_RGB : GL_BGR; 252 } else { 253 pixelFormat = GL_LUMINANCE; 254 } 255 256 AccelGlyphCache_AddGlyph(glyphCache, glyph); 257 ccinfo = (CacheCellInfo *) glyph->cellInfo; 258 259 if (ccinfo != NULL) { 260 // store glyph image in texture cell 261 j2d_glTexSubImage2D(GL_TEXTURE_2D, 0, 262 ccinfo->x, ccinfo->y, 263 glyph->width, glyph->height, 264 pixelFormat, GL_UNSIGNED_BYTE, glyph->image); 265 } 266 } 267 268 /** 269 * This is the GLSL fragment shader source code for rendering LCD-optimized 270 * text. Do not be frightened; it is much easier to understand than the 271 * equivalent ASM-like fragment program! 272 * 273 * The "uniform" variables at the top are initialized once the program is 274 * linked, and are updated at runtime as needed (e.g. when the source color 275 * changes, we will modify the "src_adj" value in OGLTR_UpdateLCDTextColor()). 276 * 277 * The "main" function is executed for each "fragment" (or pixel) in the 278 * glyph image. The pow() routine operates on vectors, gives precise results, 279 * and provides acceptable level of performance, so we use it to perform 280 * the gamma adjustment. 281 * 282 * The variables involved in the equation can be expressed as follows: 283 * 284 * Cs = Color component of the source (foreground color) [0.0, 1.0] 285 * Cd = Color component of the destination (background color) [0.0, 1.0] 286 * Cr = Color component to be written to the destination [0.0, 1.0] 287 * Ag = Glyph alpha (aka intensity or coverage) [0.0, 1.0] 288 * Ga = Gamma adjustment in the range [1.0, 2.5] 289 * (^ means raised to the power) 290 * 291 * And here is the theoretical equation approximated by this shader: 292 * 293 * Cr = (Ag*(Cs^Ga) + (1-Ag)*(Cd^Ga)) ^ (1/Ga) 294 */ 295 static const char *lcdTextShaderSource = 296 "uniform vec3 src_adj;" 297 "uniform sampler2D glyph_tex;" 298 "uniform sampler2D dst_tex;" 299 "uniform vec3 gamma;" 300 "uniform vec3 invgamma;" 301 "" 302 "void main(void)" 303 "{" 304 // load the RGB value from the glyph image at the current texcoord 305 " vec3 glyph_clr = vec3(texture2D(glyph_tex, gl_TexCoord[0].st));" 306 " if (glyph_clr == vec3(0.0)) {" 307 // zero coverage, so skip this fragment 308 " discard;" 309 " }" 310 // load the RGB value from the corresponding destination pixel 311 " vec3 dst_clr = vec3(texture2D(dst_tex, gl_TexCoord[1].st));" 312 // gamma adjust the dest color 313 " vec3 dst_adj = pow(dst_clr.rgb, invgamma);" 314 // linearly interpolate the three color values 315 " vec3 result = mix(dst_adj, src_adj, glyph_clr);" 316 // gamma re-adjust the resulting color (alpha is always set to 1.0) 317 " gl_FragColor = vec4(pow(result.rgb, gamma), 1.0);" 318 "}"; 319 320 /** 321 * Compiles and links the LCD text shader program. If successful, this 322 * function returns a handle to the newly created shader program; otherwise 323 * returns 0. 324 */ 325 static GLhandleARB 326 OGLTR_CreateLCDTextProgram() 327 { 328 GLhandleARB lcdTextProgram; 329 GLint loc; 330 331 J2dTraceLn(J2D_TRACE_INFO, "OGLTR_CreateLCDTextProgram"); 332 333 lcdTextProgram = OGLContext_CreateFragmentProgram(lcdTextShaderSource); 334 if (lcdTextProgram == 0) { 335 J2dRlsTraceLn(J2D_TRACE_ERROR, 336 "OGLTR_CreateLCDTextProgram: error creating program"); 337 return 0; 338 } 339 340 // "use" the program object temporarily so that we can set the uniforms 341 j2d_glUseProgramObjectARB(lcdTextProgram); 342 343 // set the "uniform" values 344 loc = j2d_glGetUniformLocationARB(lcdTextProgram, "glyph_tex"); 345 j2d_glUniform1iARB(loc, 0); // texture unit 0 346 loc = j2d_glGetUniformLocationARB(lcdTextProgram, "dst_tex"); 347 j2d_glUniform1iARB(loc, 1); // texture unit 1 348 349 // "unuse" the program object; it will be re-bound later as needed 350 j2d_glUseProgramObjectARB(0); 351 352 return lcdTextProgram; 353 } 354 355 /** 356 * (Re)Initializes the gamma related uniforms. 357 * 358 * The given contrast value is an int in the range [100, 250] which we will 359 * then scale to fit in the range [1.0, 2.5]. 360 */ 361 static jboolean 362 OGLTR_UpdateLCDTextContrast(jint contrast) 363 { 364 double gamma = ((double)contrast) / 100.0; 365 double ig = gamma; 366 double g = 1.0 / ig; 367 GLint loc; 368 369 J2dTraceLn1(J2D_TRACE_INFO, 370 "OGLTR_UpdateLCDTextContrast: contrast=%d", contrast); 371 372 loc = j2d_glGetUniformLocationARB(lcdTextProgram, "gamma"); 373 j2d_glUniform3fARB(loc, g, g, g); 374 375 loc = j2d_glGetUniformLocationARB(lcdTextProgram, "invgamma"); 376 j2d_glUniform3fARB(loc, ig, ig, ig); 377 378 return JNI_TRUE; 379 } 380 381 /** 382 * Updates the current gamma-adjusted source color ("src_adj") of the LCD 383 * text shader program. Note that we could calculate this value in the 384 * shader (e.g. just as we do for "dst_adj"), but would be unnecessary work 385 * (and a measurable performance hit, maybe around 5%) since this value is 386 * constant over the entire glyph list. So instead we just calculate the 387 * gamma-adjusted value once and update the uniform parameter of the LCD 388 * shader as needed. 389 */ 390 static jboolean 391 OGLTR_UpdateLCDTextColor(jint contrast) 392 { 393 double invgamma = ((double)contrast) / 100; 394 GLfloat radj, gadj, badj; 395 GLfloat clr[4]; 396 GLint loc; 397 398 J2dTraceLn1(J2D_TRACE_INFO, 399 "OGLTR_UpdateLCDTextColor: contrast=%d", contrast); 400 401 /* 402 * Note: Ideally we would update the "src_adj" uniform parameter only 403 * when there is a change in the source color. Fortunately, the cost 404 * of querying the current OpenGL color state and updating the uniform 405 * value is quite small, and in the common case we only need to do this 406 * once per GlyphList, so we gain little from trying to optimize too 407 * eagerly here. 408 */ 409 410 // get the current OpenGL primary color state 411 j2d_glGetFloatv(GL_CURRENT_COLOR, clr); 412 413 // gamma adjust the primary color 414 radj = (GLfloat)pow(clr[0], invgamma); 415 gadj = (GLfloat)pow(clr[1], invgamma); 416 badj = (GLfloat)pow(clr[2], invgamma); 417 418 // update the "src_adj" parameter of the shader program with this value 419 loc = j2d_glGetUniformLocationARB(lcdTextProgram, "src_adj"); 420 j2d_glUniform3fARB(loc, radj, gadj, badj); 421 422 return JNI_TRUE; 423 } 424 425 /** 426 * Enables the LCD text shader and updates any related state, such as the 427 * gamma lookup table textures. 428 */ 429 static jboolean 430 OGLTR_EnableLCDGlyphModeState(GLuint glyphTextureID, jint contrast) 431 { 432 // bind the texture containing glyph data to texture unit 0 433 j2d_glActiveTextureARB(GL_TEXTURE0_ARB); 434 j2d_glBindTexture(GL_TEXTURE_2D, glyphTextureID); 435 436 // bind the texture tile containing destination data to texture unit 1 437 j2d_glActiveTextureARB(GL_TEXTURE1_ARB); 438 if (cachedDestTextureID == 0) { 439 cachedDestTextureID = 440 OGLContext_CreateBlitTexture(GL_RGB8, GL_RGB, 441 OGLTR_CACHED_DEST_WIDTH, 442 OGLTR_CACHED_DEST_HEIGHT); 443 if (cachedDestTextureID == 0) { 444 return JNI_FALSE; 445 } 446 } 447 j2d_glBindTexture(GL_TEXTURE_2D, cachedDestTextureID); 448 449 // note that GL_TEXTURE_2D was already enabled for texture unit 0, 450 // but we need to explicitly enable it for texture unit 1 451 j2d_glEnable(GL_TEXTURE_2D); 452 453 // create the LCD text shader, if necessary 454 if (lcdTextProgram == 0) { 455 lcdTextProgram = OGLTR_CreateLCDTextProgram(); 456 if (lcdTextProgram == 0) { 457 return JNI_FALSE; 458 } 459 } 460 461 // enable the LCD text shader 462 j2d_glUseProgramObjectARB(lcdTextProgram); 463 464 // update the current contrast settings, if necessary 465 if (lastLCDContrast != contrast) { 466 if (!OGLTR_UpdateLCDTextContrast(contrast)) { 467 return JNI_FALSE; 468 } 469 lastLCDContrast = contrast; 470 } 471 472 // update the current color settings 473 if (!OGLTR_UpdateLCDTextColor(contrast)) { 474 return JNI_FALSE; 475 } 476 477 // bind the gamma LUT textures 478 j2d_glActiveTextureARB(GL_TEXTURE2_ARB); 479 j2d_glBindTexture(GL_TEXTURE_3D, invGammaLutTextureID); 480 j2d_glEnable(GL_TEXTURE_3D); 481 j2d_glActiveTextureARB(GL_TEXTURE3_ARB); 482 j2d_glBindTexture(GL_TEXTURE_3D, gammaLutTextureID); 483 j2d_glEnable(GL_TEXTURE_3D); 484 485 return JNI_TRUE; 486 } 487 488 void 489 OGLTR_EnableGlyphVertexCache(OGLContext *oglc) 490 { 491 J2dTraceLn(J2D_TRACE_INFO, "OGLTR_EnableGlyphVertexCache"); 492 493 if (!OGLVertexCache_InitVertexCache(oglc)) { 494 return; 495 } 496 497 if (glyphCache == NULL) { 498 if (!OGLTR_InitGlyphCache(JNI_FALSE)) { 499 return; 500 } 501 } 502 503 j2d_glEnable(GL_TEXTURE_2D); 504 j2d_glBindTexture(GL_TEXTURE_2D, glyphCache->cacheID); 505 j2d_glPixelStorei(GL_UNPACK_ALIGNMENT, 1); 506 507 // for grayscale/monochrome text, the current OpenGL source color 508 // is modulated with the glyph image as part of the texture 509 // application stage, so we use GL_MODULATE here 510 OGLC_UPDATE_TEXTURE_FUNCTION(oglc, GL_MODULATE); 511 } 512 513 void 514 OGLTR_DisableGlyphVertexCache(OGLContext *oglc) 515 { 516 J2dTraceLn(J2D_TRACE_INFO, "OGLTR_DisableGlyphVertexCache"); 517 518 OGLVertexCache_FlushVertexCache(); 519 OGLVertexCache_RestoreColorState(oglc); 520 521 j2d_glDisable(GL_TEXTURE_2D); 522 j2d_glPixelStorei(GL_UNPACK_ALIGNMENT, 4); 523 j2d_glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0); 524 j2d_glPixelStorei(GL_UNPACK_SKIP_ROWS, 0); 525 j2d_glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); 526 } 527 528 /** 529 * Disables any pending state associated with the current "glyph mode". 530 */ 531 static void 532 OGLTR_DisableGlyphModeState() 533 { 534 switch (glyphMode) { 535 case MODE_NO_CACHE_LCD: 536 j2d_glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0); 537 j2d_glPixelStorei(GL_UNPACK_SKIP_ROWS, 0); 538 /* FALLTHROUGH */ 539 540 case MODE_USE_CACHE_LCD: 541 j2d_glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); 542 j2d_glPixelStorei(GL_UNPACK_ALIGNMENT, 4); 543 j2d_glUseProgramObjectARB(0); 544 j2d_glActiveTextureARB(GL_TEXTURE3_ARB); 545 j2d_glDisable(GL_TEXTURE_3D); 546 j2d_glActiveTextureARB(GL_TEXTURE2_ARB); 547 j2d_glDisable(GL_TEXTURE_3D); 548 j2d_glActiveTextureARB(GL_TEXTURE1_ARB); 549 j2d_glDisable(GL_TEXTURE_2D); 550 j2d_glActiveTextureARB(GL_TEXTURE0_ARB); 551 break; 552 553 case MODE_NO_CACHE_GRAY: 554 case MODE_USE_CACHE_GRAY: 555 case MODE_NOT_INITED: 556 default: 557 break; 558 } 559 } 560 561 static jboolean 562 OGLTR_DrawGrayscaleGlyphViaCache(OGLContext *oglc, 563 GlyphInfo *ginfo, jint x, jint y) 564 { 565 CacheCellInfo *cell; 566 jfloat x1, y1, x2, y2; 567 568 if (glyphMode != MODE_USE_CACHE_GRAY) { 569 OGLTR_DisableGlyphModeState(); 570 CHECK_PREVIOUS_OP(OGL_STATE_GLYPH_OP); 571 glyphMode = MODE_USE_CACHE_GRAY; 572 } 573 574 if (ginfo->cellInfo == NULL) { 575 // attempt to add glyph to accelerated glyph cache 576 OGLTR_AddToGlyphCache(ginfo, JNI_FALSE); 577 578 if (ginfo->cellInfo == NULL) { 579 // we'll just no-op in the rare case that the cell is NULL 580 return JNI_TRUE; 581 } 582 } 583 584 cell = (CacheCellInfo *) (ginfo->cellInfo); 585 cell->timesRendered++; 586 587 x1 = (jfloat)x; 588 y1 = (jfloat)y; 589 x2 = x1 + ginfo->width; 590 y2 = y1 + ginfo->height; 591 592 OGLVertexCache_AddGlyphQuad(oglc, 593 cell->tx1, cell->ty1, 594 cell->tx2, cell->ty2, 595 x1, y1, x2, y2); 596 597 return JNI_TRUE; 598 } 599 600 /** 601 * Evaluates to true if the rectangle defined by gx1/gy1/gx2/gy2 is 602 * inside outerBounds. 603 */ 604 #define INSIDE(gx1, gy1, gx2, gy2, outerBounds) \ 605 (((gx1) >= outerBounds.x1) && ((gy1) >= outerBounds.y1) && \ 606 ((gx2) <= outerBounds.x2) && ((gy2) <= outerBounds.y2)) 607 608 /** 609 * Evaluates to true if the rectangle defined by gx1/gy1/gx2/gy2 intersects 610 * the rectangle defined by bounds. 611 */ 612 #define INTERSECTS(gx1, gy1, gx2, gy2, bounds) \ 613 ((bounds.x2 > (gx1)) && (bounds.y2 > (gy1)) && \ 614 (bounds.x1 < (gx2)) && (bounds.y1 < (gy2))) 615 616 /** 617 * This method checks to see if the given LCD glyph bounds fall within the 618 * cached destination texture bounds. If so, this method can return 619 * immediately. If not, this method will copy a chunk of framebuffer data 620 * into the cached destination texture and then update the current cached 621 * destination bounds before returning. 622 */ 623 static void 624 OGLTR_UpdateCachedDestination(OGLSDOps *dstOps, GlyphInfo *ginfo, 625 jint gx1, jint gy1, jint gx2, jint gy2, 626 jint glyphIndex, jint totalGlyphs) 627 { 628 jint dx1, dy1, dx2, dy2; 629 jint dx1adj, dy1adj; 630 631 if (isCachedDestValid && INSIDE(gx1, gy1, gx2, gy2, cachedDestBounds)) { 632 // glyph is already within the cached destination bounds; no need 633 // to read back the entire destination region again, but we do 634 // need to see if the current glyph overlaps the previous glyph... 635 636 if (INTERSECTS(gx1, gy1, gx2, gy2, previousGlyphBounds)) { 637 // the current glyph overlaps the destination region touched 638 // by the previous glyph, so now we need to read back the part 639 // of the destination corresponding to the previous glyph 640 dx1 = previousGlyphBounds.x1; 641 dy1 = previousGlyphBounds.y1; 642 dx2 = previousGlyphBounds.x2; 643 dy2 = previousGlyphBounds.y2; 644 645 // this accounts for lower-left origin of the destination region 646 dx1adj = dstOps->xOffset + dx1; 647 dy1adj = dstOps->yOffset + dstOps->height - dy2; 648 649 // copy destination into subregion of cached texture tile: 650 // dx1-cachedDestBounds.x1 == +xoffset from left side of texture 651 // cachedDestBounds.y2-dy2 == +yoffset from bottom of texture 652 j2d_glActiveTextureARB(GL_TEXTURE1_ARB); 653 j2d_glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 654 dx1 - cachedDestBounds.x1, 655 cachedDestBounds.y2 - dy2, 656 dx1adj, dy1adj, 657 dx2-dx1, dy2-dy1); 658 } 659 } else { 660 jint remainingWidth; 661 662 // destination region is not valid, so we need to read back a 663 // chunk of the destination into our cached texture 664 665 // position the upper-left corner of the destination region on the 666 // "top" line of glyph list 667 // REMIND: this isn't ideal; it would be better if we had some idea 668 // of the bounding box of the whole glyph list (this is 669 // do-able, but would require iterating through the whole 670 // list up front, which may present its own problems) 671 dx1 = gx1; 672 dy1 = gy1; 673 674 if (ginfo->advanceX > 0) { 675 // estimate the width based on our current position in the glyph 676 // list and using the x advance of the current glyph (this is just 677 // a quick and dirty heuristic; if this is a "thin" glyph image, 678 // then we're likely to underestimate, and if it's "thick" then we 679 // may end up reading back more than we need to) 680 remainingWidth = 681 (jint)(ginfo->advanceX * (totalGlyphs - glyphIndex)); 682 if (remainingWidth > OGLTR_CACHED_DEST_WIDTH) { 683 remainingWidth = OGLTR_CACHED_DEST_WIDTH; 684 } else if (remainingWidth < ginfo->width) { 685 // in some cases, the x-advance may be slightly smaller 686 // than the actual width of the glyph; if so, adjust our 687 // estimate so that we can accommodate the entire glyph 688 remainingWidth = ginfo->width; 689 } 690 } else { 691 // a negative advance is possible when rendering rotated text, 692 // in which case it is difficult to estimate an appropriate 693 // region for readback, so we will pick a region that 694 // encompasses just the current glyph 695 remainingWidth = ginfo->width; 696 } 697 dx2 = dx1 + remainingWidth; 698 699 // estimate the height (this is another sloppy heuristic; we'll 700 // make the cached destination region tall enough to encompass most 701 // glyphs that are small enough to fit in the glyph cache, and then 702 // we add a little something extra to account for descenders 703 dy2 = dy1 + OGLTR_CACHE_CELL_HEIGHT + 2; 704 705 // this accounts for lower-left origin of the destination region 706 dx1adj = dstOps->xOffset + dx1; 707 dy1adj = dstOps->yOffset + dstOps->height - dy2; 708 709 // copy destination into cached texture tile (the lower-left corner 710 // of the destination region will be positioned at the lower-left 711 // corner (0,0) of the texture) 712 j2d_glActiveTextureARB(GL_TEXTURE1_ARB); 713 j2d_glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 714 0, 0, dx1adj, dy1adj, 715 dx2-dx1, dy2-dy1); 716 717 // update the cached bounds and mark it valid 718 cachedDestBounds.x1 = dx1; 719 cachedDestBounds.y1 = dy1; 720 cachedDestBounds.x2 = dx2; 721 cachedDestBounds.y2 = dy2; 722 isCachedDestValid = JNI_TRUE; 723 } 724 725 // always update the previous glyph bounds 726 previousGlyphBounds.x1 = gx1; 727 previousGlyphBounds.y1 = gy1; 728 previousGlyphBounds.x2 = gx2; 729 previousGlyphBounds.y2 = gy2; 730 } 731 732 static jboolean 733 OGLTR_DrawLCDGlyphViaCache(OGLContext *oglc, OGLSDOps *dstOps, 734 GlyphInfo *ginfo, jint x, jint y, 735 jint glyphIndex, jint totalGlyphs, 736 jboolean rgbOrder, jint contrast) 737 { 738 CacheCellInfo *cell; 739 jint dx1, dy1, dx2, dy2; 740 jfloat dtx1, dty1, dtx2, dty2; 741 742 if (glyphMode != MODE_USE_CACHE_LCD) { 743 OGLTR_DisableGlyphModeState(); 744 CHECK_PREVIOUS_OP(GL_TEXTURE_2D); 745 j2d_glPixelStorei(GL_UNPACK_ALIGNMENT, 1); 746 747 if (glyphCache == NULL) { 748 if (!OGLTR_InitGlyphCache(JNI_TRUE)) { 749 return JNI_FALSE; 750 } 751 } 752 753 if (rgbOrder != lastRGBOrder) { 754 // need to invalidate the cache in this case; see comments 755 // for lastRGBOrder above 756 AccelGlyphCache_Invalidate(glyphCache); 757 lastRGBOrder = rgbOrder; 758 } 759 760 if (!OGLTR_EnableLCDGlyphModeState(glyphCache->cacheID, contrast)) { 761 return JNI_FALSE; 762 } 763 764 // when a fragment shader is enabled, the texture function state is 765 // ignored, so the following line is not needed... 766 // OGLC_UPDATE_TEXTURE_FUNCTION(oglc, GL_MODULATE); 767 768 glyphMode = MODE_USE_CACHE_LCD; 769 } 770 771 if (ginfo->cellInfo == NULL) { 772 // rowBytes will always be a multiple of 3, so the following is safe 773 j2d_glPixelStorei(GL_UNPACK_ROW_LENGTH, ginfo->rowBytes / 3); 774 775 // make sure the glyph cache texture is bound to texture unit 0 776 j2d_glActiveTextureARB(GL_TEXTURE0_ARB); 777 778 // attempt to add glyph to accelerated glyph cache 779 OGLTR_AddToGlyphCache(ginfo, rgbOrder); 780 781 if (ginfo->cellInfo == NULL) { 782 // we'll just no-op in the rare case that the cell is NULL 783 return JNI_TRUE; 784 } 785 } 786 787 cell = (CacheCellInfo *) (ginfo->cellInfo); 788 cell->timesRendered++; 789 790 // location of the glyph in the destination's coordinate space 791 dx1 = x; 792 dy1 = y; 793 dx2 = dx1 + ginfo->width; 794 dy2 = dy1 + ginfo->height; 795 796 // copy destination into second cached texture, if necessary 797 OGLTR_UpdateCachedDestination(dstOps, ginfo, 798 dx1, dy1, dx2, dy2, 799 glyphIndex, totalGlyphs); 800 801 // texture coordinates of the destination tile 802 dtx1 = ((jfloat)(dx1 - cachedDestBounds.x1)) / OGLTR_CACHED_DEST_WIDTH; 803 dty1 = ((jfloat)(cachedDestBounds.y2 - dy1)) / OGLTR_CACHED_DEST_HEIGHT; 804 dtx2 = ((jfloat)(dx2 - cachedDestBounds.x1)) / OGLTR_CACHED_DEST_WIDTH; 805 dty2 = ((jfloat)(cachedDestBounds.y2 - dy2)) / OGLTR_CACHED_DEST_HEIGHT; 806 807 // render composed texture to the destination surface 808 j2d_glBegin(GL_QUADS); 809 j2d_glMultiTexCoord2fARB(GL_TEXTURE0_ARB, cell->tx1, cell->ty1); 810 j2d_glMultiTexCoord2fARB(GL_TEXTURE1_ARB, dtx1, dty1); 811 j2d_glVertex2i(dx1, dy1); 812 j2d_glMultiTexCoord2fARB(GL_TEXTURE0_ARB, cell->tx2, cell->ty1); 813 j2d_glMultiTexCoord2fARB(GL_TEXTURE1_ARB, dtx2, dty1); 814 j2d_glVertex2i(dx2, dy1); 815 j2d_glMultiTexCoord2fARB(GL_TEXTURE0_ARB, cell->tx2, cell->ty2); 816 j2d_glMultiTexCoord2fARB(GL_TEXTURE1_ARB, dtx2, dty2); 817 j2d_glVertex2i(dx2, dy2); 818 j2d_glMultiTexCoord2fARB(GL_TEXTURE0_ARB, cell->tx1, cell->ty2); 819 j2d_glMultiTexCoord2fARB(GL_TEXTURE1_ARB, dtx1, dty2); 820 j2d_glVertex2i(dx1, dy2); 821 j2d_glEnd(); 822 823 return JNI_TRUE; 824 } 825 826 static jboolean 827 OGLTR_DrawGrayscaleGlyphNoCache(OGLContext *oglc, 828 GlyphInfo *ginfo, jint x, jint y) 829 { 830 jint tw, th; 831 jint sx, sy, sw, sh; 832 jint x0; 833 jint w = ginfo->width; 834 jint h = ginfo->height; 835 836 if (glyphMode != MODE_NO_CACHE_GRAY) { 837 OGLTR_DisableGlyphModeState(); 838 CHECK_PREVIOUS_OP(OGL_STATE_MASK_OP); 839 glyphMode = MODE_NO_CACHE_GRAY; 840 } 841 842 x0 = x; 843 tw = OGLVC_MASK_CACHE_TILE_WIDTH; 844 th = OGLVC_MASK_CACHE_TILE_HEIGHT; 845 846 for (sy = 0; sy < h; sy += th, y += th) { 847 x = x0; 848 sh = ((sy + th) > h) ? (h - sy) : th; 849 850 for (sx = 0; sx < w; sx += tw, x += tw) { 851 sw = ((sx + tw) > w) ? (w - sx) : tw; 852 853 OGLVertexCache_AddMaskQuad(oglc, 854 sx, sy, x, y, sw, sh, 855 w, ginfo->image); 856 } 857 } 858 859 return JNI_TRUE; 860 } 861 862 static jboolean 863 OGLTR_DrawLCDGlyphNoCache(OGLContext *oglc, OGLSDOps *dstOps, 864 GlyphInfo *ginfo, jint x, jint y, 865 jint rowBytesOffset, 866 jboolean rgbOrder, jint contrast) 867 { 868 GLfloat tx1, ty1, tx2, ty2; 869 GLfloat dtx1, dty1, dtx2, dty2; 870 jint tw, th; 871 jint sx, sy, sw, sh, dxadj, dyadj; 872 jint x0; 873 jint w = ginfo->width; 874 jint h = ginfo->height; 875 GLenum pixelFormat = rgbOrder ? GL_RGB : GL_BGR; 876 877 if (glyphMode != MODE_NO_CACHE_LCD) { 878 OGLTR_DisableGlyphModeState(); 879 CHECK_PREVIOUS_OP(GL_TEXTURE_2D); 880 j2d_glPixelStorei(GL_UNPACK_ALIGNMENT, 1); 881 882 if (oglc->blitTextureID == 0) { 883 if (!OGLContext_InitBlitTileTexture(oglc)) { 884 return JNI_FALSE; 885 } 886 } 887 888 if (!OGLTR_EnableLCDGlyphModeState(oglc->blitTextureID, contrast)) { 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 // copy destination into cached texture tile (the lower-left 937 // corner of the destination region will be positioned at the 938 // lower-left corner (0,0) of the texture) 939 j2d_glActiveTextureARB(GL_TEXTURE1_ARB); 940 j2d_glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 941 0, 0, 942 dxadj, dyadj, 943 sw, sh); 944 945 // update the remaining destination texture coordinates 946 dtx2 = ((GLfloat)sw) / OGLTR_CACHED_DEST_WIDTH; 947 dty1 = ((GLfloat)sh) / OGLTR_CACHED_DEST_HEIGHT; 948 949 // render composed texture to the destination surface 950 j2d_glBegin(GL_QUADS); 951 j2d_glMultiTexCoord2fARB(GL_TEXTURE0_ARB, tx1, ty1); 952 j2d_glMultiTexCoord2fARB(GL_TEXTURE1_ARB, dtx1, dty1); 953 j2d_glVertex2i(x, y); 954 j2d_glMultiTexCoord2fARB(GL_TEXTURE0_ARB, tx2, ty1); 955 j2d_glMultiTexCoord2fARB(GL_TEXTURE1_ARB, dtx2, dty1); 956 j2d_glVertex2i(x + sw, y); 957 j2d_glMultiTexCoord2fARB(GL_TEXTURE0_ARB, tx2, ty2); 958 j2d_glMultiTexCoord2fARB(GL_TEXTURE1_ARB, dtx2, dty2); 959 j2d_glVertex2i(x + sw, y + sh); 960 j2d_glMultiTexCoord2fARB(GL_TEXTURE0_ARB, tx1, ty2); 961 j2d_glMultiTexCoord2fARB(GL_TEXTURE1_ARB, dtx1, dty2); 962 j2d_glVertex2i(x, y + sh); 963 j2d_glEnd(); 964 } 965 } 966 967 return JNI_TRUE; 968 } 969 970 // see DrawGlyphList.c for more on this macro... 971 #define FLOOR_ASSIGN(l, r) \ 972 if ((r)<0) (l) = ((int)floor(r)); else (l) = ((int)(r)) 973 974 void 975 OGLTR_DrawGlyphList(JNIEnv *env, OGLContext *oglc, OGLSDOps *dstOps, 976 jint totalGlyphs, jboolean usePositions, 977 jboolean subPixPos, jboolean rgbOrder, jint lcdContrast, 978 jfloat glyphListOrigX, jfloat glyphListOrigY, 979 unsigned char *images, unsigned char *positions) 980 { 981 int glyphCounter; 982 983 J2dTraceLn(J2D_TRACE_INFO, "OGLTR_DrawGlyphList"); 984 985 RETURN_IF_NULL(oglc); 986 RETURN_IF_NULL(dstOps); 987 RETURN_IF_NULL(images); 988 if (usePositions) { 989 RETURN_IF_NULL(positions); 990 } 991 992 glyphMode = MODE_NOT_INITED; 993 isCachedDestValid = JNI_FALSE; 994 995 for (glyphCounter = 0; glyphCounter < totalGlyphs; glyphCounter++) { 996 jint x, y; 997 jfloat glyphx, glyphy; 998 jboolean grayscale, ok; 999 GlyphInfo *ginfo = (GlyphInfo *)jlong_to_ptr(NEXT_LONG(images)); 1000 1001 if (ginfo == NULL) { 1002 // this shouldn't happen, but if it does we'll just break out... 1003 J2dRlsTraceLn(J2D_TRACE_ERROR, 1004 "OGLTR_DrawGlyphList: glyph info is null"); 1005 break; 1006 } 1007 1008 grayscale = (ginfo->rowBytes == ginfo->width); 1009 1010 if (usePositions) { 1011 jfloat posx = NEXT_FLOAT(positions); 1012 jfloat posy = NEXT_FLOAT(positions); 1013 glyphx = glyphListOrigX + posx + ginfo->topLeftX; 1014 glyphy = glyphListOrigY + posy + ginfo->topLeftY; 1015 FLOOR_ASSIGN(x, glyphx); 1016 FLOOR_ASSIGN(y, glyphy); 1017 } else { 1018 glyphx = glyphListOrigX + ginfo->topLeftX; 1019 glyphy = glyphListOrigY + ginfo->topLeftY; 1020 FLOOR_ASSIGN(x, glyphx); 1021 FLOOR_ASSIGN(y, glyphy); 1022 glyphListOrigX += ginfo->advanceX; 1023 glyphListOrigY += ginfo->advanceY; 1024 } 1025 1026 if (ginfo->image == NULL) { 1027 continue; 1028 } 1029 1030 if (grayscale) { 1031 // grayscale or monochrome glyph data 1032 if (cacheStatus != CACHE_LCD && 1033 ginfo->width <= OGLTR_CACHE_CELL_WIDTH && 1034 ginfo->height <= OGLTR_CACHE_CELL_HEIGHT) 1035 { 1036 ok = OGLTR_DrawGrayscaleGlyphViaCache(oglc, ginfo, x, y); 1037 } else { 1038 ok = OGLTR_DrawGrayscaleGlyphNoCache(oglc, ginfo, x, y); 1039 } 1040 } else { 1041 // LCD-optimized glyph data 1042 jint rowBytesOffset = 0; 1043 1044 if (subPixPos) { 1045 jint frac = (jint)((glyphx - x) * 3); 1046 if (frac != 0) { 1047 rowBytesOffset = 3 - frac; 1048 x += 1; 1049 } 1050 } 1051 1052 if (rowBytesOffset == 0 && 1053 cacheStatus != CACHE_GRAY && 1054 ginfo->width <= OGLTR_CACHE_CELL_WIDTH && 1055 ginfo->height <= OGLTR_CACHE_CELL_HEIGHT) 1056 { 1057 ok = OGLTR_DrawLCDGlyphViaCache(oglc, dstOps, 1058 ginfo, x, y, 1059 glyphCounter, totalGlyphs, 1060 rgbOrder, lcdContrast); 1061 } else { 1062 ok = OGLTR_DrawLCDGlyphNoCache(oglc, dstOps, 1063 ginfo, x, y, 1064 rowBytesOffset, 1065 rgbOrder, lcdContrast); 1066 } 1067 } 1068 1069 if (!ok) { 1070 break; 1071 } 1072 } 1073 1074 OGLTR_DisableGlyphModeState(); 1075 } 1076 1077 JNIEXPORT void JNICALL 1078 Java_sun_java2d_opengl_OGLTextRenderer_drawGlyphList 1079 (JNIEnv *env, jobject self, 1080 jint numGlyphs, jboolean usePositions, 1081 jboolean subPixPos, jboolean rgbOrder, jint lcdContrast, 1082 jfloat glyphListOrigX, jfloat glyphListOrigY, 1083 jlongArray imgArray, jfloatArray posArray) 1084 { 1085 unsigned char *images; 1086 1087 J2dTraceLn(J2D_TRACE_INFO, "OGLTextRenderer_drawGlyphList"); 1088 1089 images = (unsigned char *) 1090 (*env)->GetPrimitiveArrayCritical(env, imgArray, NULL); 1091 if (images != NULL) { 1092 OGLContext *oglc = OGLRenderQueue_GetCurrentContext(); 1093 OGLSDOps *dstOps = OGLRenderQueue_GetCurrentDestination(); 1094 1095 if (usePositions) { 1096 unsigned char *positions = (unsigned char *) 1097 (*env)->GetPrimitiveArrayCritical(env, posArray, NULL); 1098 if (positions != NULL) { 1099 OGLTR_DrawGlyphList(env, oglc, dstOps, 1100 numGlyphs, usePositions, 1101 subPixPos, rgbOrder, lcdContrast, 1102 glyphListOrigX, glyphListOrigY, 1103 images, positions); 1104 (*env)->ReleasePrimitiveArrayCritical(env, posArray, 1105 positions, JNI_ABORT); 1106 } 1107 } else { 1108 OGLTR_DrawGlyphList(env, oglc, dstOps, 1109 numGlyphs, usePositions, 1110 subPixPos, rgbOrder, lcdContrast, 1111 glyphListOrigX, glyphListOrigY, 1112 images, NULL); 1113 } 1114 1115 // 6358147: reset current state, and ensure rendering is 1116 // flushed to dest 1117 if (oglc != NULL) { 1118 RESET_PREVIOUS_OP(); 1119 j2d_glFlush(); 1120 } 1121 1122 (*env)->ReleasePrimitiveArrayCritical(env, imgArray, 1123 images, JNI_ABORT); 1124 } 1125 } 1126 1127 #endif /* !HEADLESS */