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