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. We have determined that the pow() function can be quite 279 * slow and it only operates on scalar values, not vectors as we require. 280 * So instead we build two 3D textures containing gamma (and inverse gamma) 281 * lookup tables that allow us to approximate a component-wise pow() function 282 * with a single 3D texture lookup. This approach is at least 2x faster 283 * than the equivalent pow() calls. 284 * 285 * The variables involved in the equation can be expressed as follows: 286 * 287 * Cs = Color component of the source (foreground color) [0.0, 1.0] 288 * Cd = Color component of the destination (background color) [0.0, 1.0] 289 * Cr = Color component to be written to the destination [0.0, 1.0] 290 * Ag = Glyph alpha (aka intensity or coverage) [0.0, 1.0] 291 * Ga = Gamma adjustment in the range [1.0, 2.5] 292 * (^ means raised to the power) 293 * 294 * And here is the theoretical equation approximated by this shader: 295 * 296 * Cr = (Ag*(Cs^Ga) + (1-Ag)*(Cd^Ga)) ^ (1/Ga) 297 */ 298 static const char *lcdTextShaderSource = 299 "uniform vec3 src_adj;" 300 "uniform sampler2D glyph_tex;" 301 "uniform sampler2D dst_tex;" 302 "uniform sampler3D invgamma_tex;" 303 "uniform sampler3D gamma_tex;" 304 "" 305 "void main(void)" 306 "{" 307 // load the RGB value from the glyph image at the current texcoord 308 " vec3 glyph_clr = vec3(texture2D(glyph_tex, gl_TexCoord[0].st));" 309 " if (glyph_clr == vec3(0.0)) {" 310 // zero coverage, so skip this fragment 311 " discard;" 312 " }" 313 // load the RGB value from the corresponding destination pixel 314 " vec3 dst_clr = vec3(texture2D(dst_tex, gl_TexCoord[1].st));" 315 // gamma adjust the dest color using the invgamma LUT 316 " vec3 dst_adj = vec3(texture3D(invgamma_tex, dst_clr.stp));" 317 // linearly interpolate the three color values 318 " vec3 result = mix(dst_adj, src_adj, glyph_clr);" 319 // gamma re-adjust the resulting color (alpha is always set to 1.0) 320 " gl_FragColor = vec4(vec3(texture3D(gamma_tex, result.stp)), 1.0);" 321 "}"; 322 323 /** 324 * Compiles and links the LCD text shader program. If successful, this 325 * function returns a handle to the newly created shader program; otherwise 326 * returns 0. 327 */ 328 static GLhandleARB 329 OGLTR_CreateLCDTextProgram() 330 { 331 GLhandleARB lcdTextProgram; 332 GLint loc; 333 334 J2dTraceLn(J2D_TRACE_INFO, "OGLTR_CreateLCDTextProgram"); 335 336 lcdTextProgram = OGLContext_CreateFragmentProgram(lcdTextShaderSource); 337 if (lcdTextProgram == 0) { 338 J2dRlsTraceLn(J2D_TRACE_ERROR, 339 "OGLTR_CreateLCDTextProgram: error creating program"); 340 return 0; 450 invlut[z][y][x][2] = igz; 451 } 452 } 453 } 454 455 if (gammaLutTextureID == 0) { 456 gammaLutTextureID = OGLTR_InitGammaLutTexture(); 457 } 458 OGLTR_UpdateGammaLutTexture(gammaLutTextureID, (GLfloat *)lut, LUT_EDGE); 459 460 if (invGammaLutTextureID == 0) { 461 invGammaLutTextureID = OGLTR_InitGammaLutTexture(); 462 } 463 OGLTR_UpdateGammaLutTexture(invGammaLutTextureID, 464 (GLfloat *)invlut, LUT_EDGE); 465 466 return JNI_TRUE; 467 } 468 469 /** 470 * Updates the current gamma-adjusted source color ("src_adj") of the LCD 471 * text shader program. Note that we could calculate this value in the 472 * shader (e.g. just as we do for "dst_adj"), but would be unnecessary work 473 * (and a measurable performance hit, maybe around 5%) since this value is 474 * constant over the entire glyph list. So instead we just calculate the 475 * gamma-adjusted value once and update the uniform parameter of the LCD 476 * shader as needed. 477 */ 478 static jboolean 479 OGLTR_UpdateLCDTextColor(jint contrast) 480 { 481 double gamma = ((double)contrast) / 100.0; 482 GLfloat radj, gadj, badj; 483 GLfloat clr[4]; 484 GLint loc; 485 486 J2dTraceLn1(J2D_TRACE_INFO, 487 "OGLTR_UpdateLCDTextColor: contrast=%d", contrast); 488 489 /* 490 * Note: Ideally we would update the "src_adj" uniform parameter only 491 * when there is a change in the source color. Fortunately, the cost 492 * of querying the current OpenGL color state and updating the uniform 493 * value is quite small, and in the common case we only need to do this 494 * once per GlyphList, so we gain little from trying to optimize too 495 * eagerly here. 496 */ 497 498 // get the current OpenGL primary color state 499 j2d_glGetFloatv(GL_CURRENT_COLOR, clr); 500 501 // gamma adjust the primary color 502 radj = (GLfloat)pow(clr[0], gamma); 503 gadj = (GLfloat)pow(clr[1], gamma); 504 badj = (GLfloat)pow(clr[2], gamma); 505 506 // update the "src_adj" parameter of the shader program with this value 507 loc = j2d_glGetUniformLocationARB(lcdTextProgram, "src_adj"); 508 j2d_glUniform3fARB(loc, radj, gadj, badj); 509 510 return JNI_TRUE; 511 } 512 513 /** 514 * Enables the LCD text shader and updates any related state, such as the 515 * gamma lookup table textures. 516 */ 517 static jboolean 518 OGLTR_EnableLCDGlyphModeState(GLuint glyphTextureID, jint contrast) 519 { 520 // bind the texture containing glyph data to texture unit 0 521 j2d_glActiveTextureARB(GL_TEXTURE0_ARB); 522 j2d_glBindTexture(GL_TEXTURE_2D, glyphTextureID); 523 524 // bind the texture tile containing destination data to texture unit 1 525 j2d_glActiveTextureARB(GL_TEXTURE1_ARB); 526 if (cachedDestTextureID == 0) { 527 cachedDestTextureID = 528 OGLContext_CreateBlitTexture(GL_RGB8, GL_RGB, | 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_clr" value in OGLTR_UpdateLCDTextColor()). 276 * 277 * The "main" function is executed for each "fragment" (or pixel) in the 278 * glyph image. We have determined that the pow() function can be quite 279 * slow and it only operates on scalar values, not vectors as we require. 280 * So instead we build two 3D textures containing gamma (and inverse gamma) 281 * lookup tables that allow us to approximate a component-wise pow() function 282 * with a single 3D texture lookup. This approach is at least 2x faster 283 * than the equivalent pow() calls. 284 * 285 * The variables involved in the equation can be expressed as follows: 286 * 287 * Cs = Color component of the source (foreground color) [0.0, 1.0] 288 * Cd = Color component of the destination (background color) [0.0, 1.0] 289 * Cr = Color component to be written to the destination [0.0, 1.0] 290 * Ag = Glyph alpha (aka intensity or coverage) [0.0, 1.0] 291 * Ga = Gamma adjustment in the range [1.0, 2.5] 292 * (^ means raised to the power) 293 * 294 * And here is the theoretical equation approximated by this shader: 295 * 296 * Cr = (Ag*(Cs^Ga) + (1-Ag)*(Cd^Ga)) ^ (1/Ga) 297 */ 298 static const char *lcdTextShaderSource = 299 "uniform vec4 src_clr;" 300 "uniform sampler2D glyph_tex;" 301 "uniform sampler2D dst_tex;" 302 "uniform sampler3D invgamma_tex;" 303 "uniform sampler3D gamma_tex;" 304 "" 305 "void main(void)" 306 "{" 307 // load the RGB value from the glyph image at the current texcoord 308 " vec3 glyph_clr = vec3(texture2D(glyph_tex, gl_TexCoord[0].st));" 309 " if (glyph_clr == vec3(0.0)) {" 310 // zero coverage, so skip this fragment 311 " discard;" 312 " }" 313 // load the RGBA value from the corresponding destination pixel 314 " vec4 dst_clr = vec4(texture2D(dst_tex, gl_TexCoord[1].st));" 315 // blend src and dst colors as SrcOverNoEa 316 " vec3 src_comp = vec3(src_clr.rgb + ((1.0 - src_clr.a) * dst_clr.rgb));" 317 // gamma adjust the blended src color using the invgamma LUT 318 " vec3 src_adj = vec3(texture3D(invgamma_tex, src_comp));" 319 // gamma adjust the dest color using the invgamma LUT 320 " vec3 dst_adj = vec3(texture3D(invgamma_tex, dst_clr.rgb));" 321 // linearly interpolate the three color values 322 " vec3 result = mix(dst_adj, src_adj, glyph_clr);" 323 // calculate the resulting alpha 324 " dst_clr.a = src_clr.a + (1.0 - src_clr.a) * dst_clr.a;" 325 // gamma re-adjust the resulting color 326 " gl_FragColor = vec4(vec3(texture3D(gamma_tex, result.rgb)), dst_clr.a);" 327 "}"; 328 329 /** 330 * Compiles and links the LCD text shader program. If successful, this 331 * function returns a handle to the newly created shader program; otherwise 332 * returns 0. 333 */ 334 static GLhandleARB 335 OGLTR_CreateLCDTextProgram() 336 { 337 GLhandleARB lcdTextProgram; 338 GLint loc; 339 340 J2dTraceLn(J2D_TRACE_INFO, "OGLTR_CreateLCDTextProgram"); 341 342 lcdTextProgram = OGLContext_CreateFragmentProgram(lcdTextShaderSource); 343 if (lcdTextProgram == 0) { 344 J2dRlsTraceLn(J2D_TRACE_ERROR, 345 "OGLTR_CreateLCDTextProgram: error creating program"); 346 return 0; 456 invlut[z][y][x][2] = igz; 457 } 458 } 459 } 460 461 if (gammaLutTextureID == 0) { 462 gammaLutTextureID = OGLTR_InitGammaLutTexture(); 463 } 464 OGLTR_UpdateGammaLutTexture(gammaLutTextureID, (GLfloat *)lut, LUT_EDGE); 465 466 if (invGammaLutTextureID == 0) { 467 invGammaLutTextureID = OGLTR_InitGammaLutTexture(); 468 } 469 OGLTR_UpdateGammaLutTexture(invGammaLutTextureID, 470 (GLfloat *)invlut, LUT_EDGE); 471 472 return JNI_TRUE; 473 } 474 475 /** 476 * Updates the current source color ("src_clr") of the LCD text shader program. 477 */ 478 static jboolean 479 OGLTR_UpdateLCDTextColor(jint contrast) 480 { 481 double gamma = 100.0 / ((double)contrast); 482 GLfloat clr[4]; 483 GLint loc; 484 485 J2dTraceLn1(J2D_TRACE_INFO, 486 "OGLTR_UpdateLCDTextColor: contrast=%d", contrast); 487 488 // get the current OpenGL primary color state 489 j2d_glGetFloatv(GL_CURRENT_COLOR, clr); 490 491 // update the "src_clr" parameter of the shader program with this value 492 loc = j2d_glGetUniformLocationARB(lcdTextProgram, "src_clr"); 493 j2d_glUniform4fARB(loc, clr[0], clr[1], clr[2], clr[3]); 494 495 return JNI_TRUE; 496 } 497 498 /** 499 * Enables the LCD text shader and updates any related state, such as the 500 * gamma lookup table textures. 501 */ 502 static jboolean 503 OGLTR_EnableLCDGlyphModeState(GLuint glyphTextureID, jint contrast) 504 { 505 // bind the texture containing glyph data to texture unit 0 506 j2d_glActiveTextureARB(GL_TEXTURE0_ARB); 507 j2d_glBindTexture(GL_TEXTURE_2D, glyphTextureID); 508 509 // bind the texture tile containing destination data to texture unit 1 510 j2d_glActiveTextureARB(GL_TEXTURE1_ARB); 511 if (cachedDestTextureID == 0) { 512 cachedDestTextureID = 513 OGLContext_CreateBlitTexture(GL_RGB8, GL_RGB, |