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