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; 405 * that essentially calculates pow(x, gamma) and the other calculates 406 * pow(x, 1/gamma). These values are replicated in all three dimensions, so 407 * given a single 3D texture coordinate (typically this will be a triplet 408 * in the form (r,g,b)), the 3D texture lookup will return an RGB triplet: 409 * 410 * (pow(r,g), pow(y,g), pow(z,g) 411 * 412 * where g is either gamma or 1/gamma, depending on the table. 413 */ 414 static jboolean 415 OGLTR_UpdateLCDTextContrast(jint contrast) 416 { 417 double gamma = ((double)contrast) / 100.0; 418 double ig = gamma; 419 double g = 1.0 / ig; 420 GLfloat lut[LUT_EDGE][LUT_EDGE][LUT_EDGE][3]; 421 GLfloat invlut[LUT_EDGE][LUT_EDGE][LUT_EDGE][3]; 422 int min = 0; 423 int max = LUT_EDGE - 1; 424 int x, y, z; 425 426 J2dTraceLn1(J2D_TRACE_INFO, 427 "OGLTR_UpdateLCDTextContrast: contrast=%d", contrast); 428 429 for (z = min; z <= max; z++) { 430 double zval = ((double)z) / max; 431 GLfloat gz = (GLfloat)pow(zval, g); 432 GLfloat igz = (GLfloat)pow(zval, ig); 433 434 for (y = min; y <= max; y++) { 435 double yval = ((double)y) / max; 436 GLfloat gy = (GLfloat)pow(yval, g); 437 GLfloat igy = (GLfloat)pow(yval, ig); 438 439 for (x = min; x <= max; x++) { 440 double xval = ((double)x) / max; 441 GLfloat gx = (GLfloat)pow(xval, g); 442 GLfloat igx = (GLfloat)pow(xval, ig); 443 444 lut[z][y][x][0] = gx; 445 lut[z][y][x][1] = gy; 446 lut[z][y][x][2] = gz; 447 448 invlut[z][y][x][0] = igx; 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 | 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 "uniform vec3 gamma;" 305 "uniform vec3 invgamma;" 306 "" 307 "void main(void)" 308 "{" 309 // load the RGB value from the glyph image at the current texcoord 310 " vec3 glyph_clr = vec3(texture2D(glyph_tex, gl_TexCoord[0].st));" 311 " if (glyph_clr == vec3(0.0)) {" 312 // zero coverage, so skip this fragment 313 " discard;" 314 " }" 315 // load the RGB value from the corresponding destination pixel 316 " vec3 dst_clr = vec3(texture2D(dst_tex, gl_TexCoord[1].st));" 317 // gamma adjust the dest color using the invgamma LUT 318 " vec3 dst_adj = pow(dst_clr.rgb, invgamma);" 319 // linearly interpolate the three color values 320 " vec3 result = mix(dst_adj, src_adj, glyph_clr);" 321 // gamma re-adjust the resulting color (alpha is always set to 1.0) 322 " gl_FragColor = vec4(pow(result.rgb, gamma), 1.0);" 323 "}"; 324 325 /** 326 * Compiles and links the LCD text shader program. If successful, this 327 * function returns a handle to the newly created shader program; otherwise 328 * returns 0. 329 */ 330 static GLhandleARB 331 OGLTR_CreateLCDTextProgram() 332 { 333 GLhandleARB lcdTextProgram; 334 GLint loc; 335 336 J2dTraceLn(J2D_TRACE_INFO, "OGLTR_CreateLCDTextProgram"); 337 338 lcdTextProgram = OGLContext_CreateFragmentProgram(lcdTextShaderSource); 339 if (lcdTextProgram == 0) { 340 J2dRlsTraceLn(J2D_TRACE_ERROR, 341 "OGLTR_CreateLCDTextProgram: error creating program"); 342 return 0; 407 * that essentially calculates pow(x, gamma) and the other calculates 408 * pow(x, 1/gamma). These values are replicated in all three dimensions, so 409 * given a single 3D texture coordinate (typically this will be a triplet 410 * in the form (r,g,b)), the 3D texture lookup will return an RGB triplet: 411 * 412 * (pow(r,g), pow(y,g), pow(z,g) 413 * 414 * where g is either gamma or 1/gamma, depending on the table. 415 */ 416 static jboolean 417 OGLTR_UpdateLCDTextContrast(jint contrast) 418 { 419 double gamma = ((double)contrast) / 100.0; 420 double ig = gamma; 421 double g = 1.0 / ig; 422 GLfloat lut[LUT_EDGE][LUT_EDGE][LUT_EDGE][3]; 423 GLfloat invlut[LUT_EDGE][LUT_EDGE][LUT_EDGE][3]; 424 int min = 0; 425 int max = LUT_EDGE - 1; 426 int x, y, z; 427 GLint loc; 428 429 J2dTraceLn1(J2D_TRACE_INFO, 430 "OGLTR_UpdateLCDTextContrast: contrast=%d", contrast); 431 432 loc = j2d_glGetUniformLocationARB(lcdTextProgram, "gamma"); 433 j2d_glUniform3fARB(loc, g, g, g); 434 435 loc = j2d_glGetUniformLocationARB(lcdTextProgram, "invgamma"); 436 j2d_glUniform3fARB(loc, ig, ig, ig); 437 438 for (z = min; z <= max; z++) { 439 double zval = ((double)z) / max; 440 GLfloat gz = (GLfloat)pow(zval, g); 441 GLfloat igz = (GLfloat)pow(zval, ig); 442 443 for (y = min; y <= max; y++) { 444 double yval = ((double)y) / max; 445 GLfloat gy = (GLfloat)pow(yval, g); 446 GLfloat igy = (GLfloat)pow(yval, ig); 447 448 for (x = min; x <= max; x++) { 449 double xval = ((double)x) / max; 450 GLfloat gx = (GLfloat)pow(xval, g); 451 GLfloat igx = (GLfloat)pow(xval, ig); 452 453 lut[z][y][x][0] = gx; 454 lut[z][y][x][1] = gy; 455 lut[z][y][x][2] = gz; 456 457 invlut[z][y][x][0] = igx; 470 invGammaLutTextureID = OGLTR_InitGammaLutTexture(); 471 } 472 OGLTR_UpdateGammaLutTexture(invGammaLutTextureID, 473 (GLfloat *)invlut, LUT_EDGE); 474 475 return JNI_TRUE; 476 } 477 478 /** 479 * Updates the current gamma-adjusted source color ("src_adj") of the LCD 480 * text shader program. Note that we could calculate this value in the 481 * shader (e.g. just as we do for "dst_adj"), but would be unnecessary work 482 * (and a measurable performance hit, maybe around 5%) since this value is 483 * constant over the entire glyph list. So instead we just calculate the 484 * gamma-adjusted value once and update the uniform parameter of the LCD 485 * shader as needed. 486 */ 487 static jboolean 488 OGLTR_UpdateLCDTextColor(jint contrast) 489 { 490 double invgamma = ((double)contrast) / 100; 491 GLfloat radj, gadj, badj; 492 GLfloat clr[4]; 493 GLint loc; 494 495 J2dTraceLn1(J2D_TRACE_INFO, 496 "OGLTR_UpdateLCDTextColor: contrast=%d", contrast); 497 498 /* 499 * Note: Ideally we would update the "src_adj" uniform parameter only 500 * when there is a change in the source color. Fortunately, the cost 501 * of querying the current OpenGL color state and updating the uniform 502 * value is quite small, and in the common case we only need to do this 503 * once per GlyphList, so we gain little from trying to optimize too 504 * eagerly here. 505 */ 506 507 // get the current OpenGL primary color state 508 j2d_glGetFloatv(GL_CURRENT_COLOR, clr); 509 510 // gamma adjust the primary color 511 radj = (GLfloat)pow(clr[0], invgamma); 512 gadj = (GLfloat)pow(clr[1], invgamma); 513 badj = (GLfloat)pow(clr[2], invgamma); 514 515 // update the "src_adj" parameter of the shader program with this value 516 loc = j2d_glGetUniformLocationARB(lcdTextProgram, "src_adj"); 517 j2d_glUniform3fARB(loc, radj, gadj, badj); 518 519 return JNI_TRUE; 520 } 521 522 /** 523 * Enables the LCD text shader and updates any related state, such as the 524 * gamma lookup table textures. 525 */ 526 static jboolean 527 OGLTR_EnableLCDGlyphModeState(GLuint glyphTextureID, jint contrast) 528 { 529 // bind the texture containing glyph data to texture unit 0 530 j2d_glActiveTextureARB(GL_TEXTURE0_ARB); 531 j2d_glBindTexture(GL_TEXTURE_2D, glyphTextureID); 532 533 // bind the texture tile containing destination data to texture unit 1 |