< prev index next >

src/java.desktop/share/native/common/java2d/opengl/OGLTextRenderer.c

Print this page




 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;
 341     }
 342 
 343     // "use" the program object temporarily so that we can set the uniforms
 344     j2d_glUseProgramObjectARB(lcdTextProgram);
 345 
 346     // set the "uniform" values
 347     loc = j2d_glGetUniformLocationARB(lcdTextProgram, "glyph_tex");
 348     j2d_glUniform1iARB(loc, 0); // texture unit 0
 349     loc = j2d_glGetUniformLocationARB(lcdTextProgram, "dst_tex");
 350     j2d_glUniform1iARB(loc, 1); // texture unit 1
 351     loc = j2d_glGetUniformLocationARB(lcdTextProgram, "invgamma_tex");
 352     j2d_glUniform1iARB(loc, 2); // texture unit 2
 353     loc = j2d_glGetUniformLocationARB(lcdTextProgram, "gamma_tex");
 354     j2d_glUniform1iARB(loc, 3); // texture unit 3
 355 
 356     // "unuse" the program object; it will be re-bound later as needed
 357     j2d_glUseProgramObjectARB(0);
 358 
 359     return lcdTextProgram;
 360 }
 361 
 362 /**
 363  * Initializes a 3D texture object for use as a three-dimensional gamma
 364  * lookup table.  Note that the wrap mode is initialized to GL_LINEAR so
 365  * that the table will interpolate adjacent values when the index falls
 366  * somewhere in between.
 367  */
 368 static GLuint
 369 OGLTR_InitGammaLutTexture()
 370 {
 371     GLuint lutTextureID;
 372 
 373     j2d_glGenTextures(1, &lutTextureID);
 374     j2d_glBindTexture(GL_TEXTURE_3D, lutTextureID);
 375     j2d_glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
 376     j2d_glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
 377     j2d_glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
 378     j2d_glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
 379     j2d_glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
 380 
 381     return lutTextureID;
 382 }
 383 
 384 /**
 385  * Updates the lookup table in the given texture object with the float
 386  * values in the given system memory buffer.  Note that we could use
 387  * glTexSubImage3D() when updating the texture after its first
 388  * initialization, but since we're updating the entire table (with
 389  * power-of-two dimensions) and this is a relatively rare event, we'll
 390  * just stick with glTexImage3D().
 391  */
 392 static void
 393 OGLTR_UpdateGammaLutTexture(GLuint texID, GLfloat *lut, jint size)
 394 {
 395     j2d_glBindTexture(GL_TEXTURE_3D, texID);
 396     j2d_glTexImage3D(GL_TEXTURE_3D, 0, GL_RGB8,
 397                      size, size, size, 0, GL_RGB, GL_FLOAT, lut);
 398 }
 399 
 400 /**
 401  * (Re)Initializes the gamma lookup table textures.
 402  *
 403  * The given contrast value is an int in the range [100, 250] which we will
 404  * then scale to fit in the range [1.0, 2.5].  We create two LUTs, one
 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;
 449                 invlut[z][y][x][1] = igy;
 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;




 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, gamma);"
 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, invgamma), 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 g = ((double)contrast) / 100.0;
 365     double ig = 1.0 / g;
 366     GLint loc;





 367 
 368     J2dTraceLn1(J2D_TRACE_INFO,
 369                 "OGLTR_UpdateLCDTextContrast: contrast=%d", contrast);
 370     
 371     loc = j2d_glGetUniformLocationARB(lcdTextProgram, "gamma");
 372     j2d_glUniform3fARB(loc, g, g, g);







 373 
 374     loc = j2d_glGetUniformLocationARB(lcdTextProgram, "invgamma");
 375     j2d_glUniform3fARB(loc, ig, ig, ig);
























 376 
 377     return JNI_TRUE;
 378 }
 379 
 380 /**
 381  * Updates the current gamma-adjusted source color ("src_adj") of the LCD
 382  * text shader program.  Note that we could calculate this value in the
 383  * shader (e.g. just as we do for "dst_adj"), but would be unnecessary work
 384  * (and a measurable performance hit, maybe around 5%) since this value is
 385  * constant over the entire glyph list.  So instead we just calculate the
 386  * gamma-adjusted value once and update the uniform parameter of the LCD
 387  * shader as needed.
 388  */
 389 static jboolean
 390 OGLTR_UpdateLCDTextColor(jint contrast)
 391 {
 392     double gamma = ((double)contrast) / 100.0;
 393     GLfloat radj, gadj, badj;
 394     GLfloat clr[4];
 395     GLint loc;


< prev index next >