273 * The "uniform" variables at the top are initialized once the program is |
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 |
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()). |
275 * changes, we will modify the "src_adj" value in OGLTR_UpdateLCDTextColor()). |
276 * |
276 * |
277 * The "main" function is executed for each "fragment" (or pixel) in the |
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 |
278 * glyph image. The pow() routine operates on vectors, gives precise results, |
279 * slow and it only operates on scalar values, not vectors as we require. |
279 * and provides acceptable level of performance, so we use it to perform |
280 * So instead we build two 3D textures containing gamma (and inverse gamma) |
280 * the gamma adjustment. |
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 * |
281 * |
285 * The variables involved in the equation can be expressed as follows: |
282 * The variables involved in the equation can be expressed as follows: |
286 * |
283 * |
287 * Cs = Color component of the source (foreground color) [0.0, 1.0] |
284 * 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] |
285 * Cd = Color component of the destination (background color) [0.0, 1.0] |
310 // zero coverage, so skip this fragment |
307 // zero coverage, so skip this fragment |
311 " discard;" |
308 " discard;" |
312 " }" |
309 " }" |
313 // load the RGB value from the corresponding destination pixel |
310 // load the RGB value from the corresponding destination pixel |
314 " vec3 dst_clr = vec3(texture2D(dst_tex, gl_TexCoord[1].st));" |
311 " vec3 dst_clr = vec3(texture2D(dst_tex, gl_TexCoord[1].st));" |
315 // gamma adjust the dest color using the invgamma LUT |
312 // gamma adjust the dest color |
316 " vec3 dst_adj = vec3(texture3D(invgamma_tex, dst_clr.stp));" |
313 " vec3 dst_adj = pow(dst_clr.rgb, gamma);" |
317 // linearly interpolate the three color values |
314 // linearly interpolate the three color values |
318 " vec3 result = mix(dst_adj, src_adj, glyph_clr);" |
315 " vec3 result = mix(dst_adj, src_adj, glyph_clr);" |
319 // gamma re-adjust the resulting color (alpha is always set to 1.0) |
316 // 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);" |
317 " gl_FragColor = vec4(pow(result.rgb, invgamma), 1.0);" |
321 "}"; |
318 "}"; |
322 |
319 |
323 /** |
320 /** |
324 * Compiles and links the LCD text shader program. If successful, this |
321 * Compiles and links the LCD text shader program. If successful, this |
325 * function returns a handle to the newly created shader program; otherwise |
322 * function returns a handle to the newly created shader program; otherwise |
346 // set the "uniform" values |
343 // set the "uniform" values |
347 loc = j2d_glGetUniformLocationARB(lcdTextProgram, "glyph_tex"); |
344 loc = j2d_glGetUniformLocationARB(lcdTextProgram, "glyph_tex"); |
348 j2d_glUniform1iARB(loc, 0); // texture unit 0 |
345 j2d_glUniform1iARB(loc, 0); // texture unit 0 |
349 loc = j2d_glGetUniformLocationARB(lcdTextProgram, "dst_tex"); |
346 loc = j2d_glGetUniformLocationARB(lcdTextProgram, "dst_tex"); |
350 j2d_glUniform1iARB(loc, 1); // texture unit 1 |
347 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 |
348 |
356 // "unuse" the program object; it will be re-bound later as needed |
349 // "unuse" the program object; it will be re-bound later as needed |
357 j2d_glUseProgramObjectARB(0); |
350 j2d_glUseProgramObjectARB(0); |
358 |
351 |
359 return lcdTextProgram; |
352 return lcdTextProgram; |
360 } |
353 } |
361 |
354 |
362 /** |
355 /** |
363 * Initializes a 3D texture object for use as a three-dimensional gamma |
356 * (Re)Initializes the gamma related uniforms. |
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 * |
357 * |
403 * The given contrast value is an int in the range [100, 250] which we will |
358 * 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 |
359 * then scale to fit in the range [1.0, 2.5]. |
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 */ |
360 */ |
414 static jboolean |
361 static jboolean |
415 OGLTR_UpdateLCDTextContrast(jint contrast) |
362 OGLTR_UpdateLCDTextContrast(jint contrast) |
416 { |
363 { |
417 double gamma = ((double)contrast) / 100.0; |
364 double g = ((double)contrast) / 100.0; |
418 double ig = gamma; |
365 double ig = 1.0 / g; |
419 double g = 1.0 / ig; |
366 GLint loc; |
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 |
367 |
426 J2dTraceLn1(J2D_TRACE_INFO, |
368 J2dTraceLn1(J2D_TRACE_INFO, |
427 "OGLTR_UpdateLCDTextContrast: contrast=%d", contrast); |
369 "OGLTR_UpdateLCDTextContrast: contrast=%d", contrast); |
428 |
370 |
429 for (z = min; z <= max; z++) { |
371 loc = j2d_glGetUniformLocationARB(lcdTextProgram, "gamma"); |
430 double zval = ((double)z) / max; |
372 j2d_glUniform3fARB(loc, g, g, g); |
431 GLfloat gz = (GLfloat)pow(zval, g); |
373 |
432 GLfloat igz = (GLfloat)pow(zval, ig); |
374 loc = j2d_glGetUniformLocationARB(lcdTextProgram, "invgamma"); |
433 |
375 j2d_glUniform3fARB(loc, ig, ig, ig); |
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 |
376 |
466 return JNI_TRUE; |
377 return JNI_TRUE; |
467 } |
378 } |
468 |
379 |
469 /** |
380 /** |