1 /*
   2  * Copyright (c) 2003, 2013, 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 <stdlib.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 glyph cache data structure
 206     gcinfo = AccelGlyphCache_Init(OGLTR_CACHE_WIDTH,
 207                                   OGLTR_CACHE_HEIGHT,
 208                                   OGLTR_CACHE_CELL_WIDTH,
 209                                   OGLTR_CACHE_CELL_HEIGHT,
 210                                   OGLVertexCache_FlushVertexCache);
 211     if (gcinfo == NULL) {
 212         J2dRlsTraceLn(J2D_TRACE_ERROR,
 213                       "OGLTR_InitGlyphCache: could not init OGL glyph cache");
 214         return JNI_FALSE;
 215     }
 216 
 217     // init cache texture object
 218     j2d_glGenTextures(1, &gcinfo->cacheID);
 219     j2d_glBindTexture(GL_TEXTURE_2D, gcinfo->cacheID);
 220     j2d_glPrioritizeTextures(1, &gcinfo->cacheID, &priority);
 221     j2d_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
 222     j2d_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
 223 
 224     j2d_glTexImage2D(GL_TEXTURE_2D, 0, internalFormat,
 225                      OGLTR_CACHE_WIDTH, OGLTR_CACHE_HEIGHT, 0,
 226                      pixelFormat, GL_UNSIGNED_BYTE, NULL);
 227 
 228     cacheStatus = (lcdCache ? CACHE_LCD : CACHE_GRAY);
 229     glyphCache = gcinfo;
 230 
 231     return JNI_TRUE;
 232 }
 233 
 234 /**
 235  * Adds the given glyph to the glyph cache (texture and data structure)
 236  * associated with the given OGLContext.
 237  */
 238 static void
 239 OGLTR_AddToGlyphCache(GlyphInfo *glyph, jboolean rgbOrder)
 240 {
 241     GLenum pixelFormat;
 242     CacheCellInfo *ccinfo;
 243 
 244     J2dTraceLn(J2D_TRACE_INFO, "OGLTR_AddToGlyphCache");
 245 
 246     if ((glyphCache == NULL) || (glyph->image == NULL)) {
 247         return;
 248     }
 249 
 250     if (cacheStatus == CACHE_LCD) {
 251         pixelFormat = rgbOrder ? GL_RGB : GL_BGR;
 252     } else {
 253         pixelFormat = GL_LUMINANCE;
 254     }
 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 vec3 gamma;"
 303     "uniform vec3 invgamma;"
 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 = pow(dst_clr.rgb, invgamma);"
 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(pow(result.rgb, gamma), 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     
 352     // "unuse" the program object; it will be re-bound later as needed
 353     j2d_glUseProgramObjectARB(0);
 354 
 355     return lcdTextProgram;
 356 }
 357 
 358 /**
 359  * (Re)Initializes the gamma related uniforms.
 360  *
 361  * The given contrast value is an int in the range [100, 250] which we will
 362  * then scale to fit in the range [1.0, 2.5].  
 363  */
 364 static jboolean
 365 OGLTR_UpdateLCDTextContrast(jint contrast)
 366 {
 367     double gamma = ((double)contrast) / 100.0;
 368     double ig = gamma;
 369     double g = 1.0 / ig;
 370     GLint loc;
 371 
 372     J2dTraceLn1(J2D_TRACE_INFO,
 373                 "OGLTR_UpdateLCDTextContrast: contrast=%d", contrast);
 374     
 375     loc = j2d_glGetUniformLocationARB(lcdTextProgram, "gamma");
 376     j2d_glUniform3fARB(loc, g, g, g);
 377 
 378     loc = j2d_glGetUniformLocationARB(lcdTextProgram, "invgamma");
 379     j2d_glUniform3fARB(loc, ig, ig, ig);
 380 
 381     return JNI_TRUE;
 382 }
 383 
 384 /**
 385  * Updates the current gamma-adjusted source color ("src_adj") of the LCD
 386  * text shader program.  Note that we could calculate this value in the
 387  * shader (e.g. just as we do for "dst_adj"), but would be unnecessary work
 388  * (and a measurable performance hit, maybe around 5%) since this value is
 389  * constant over the entire glyph list.  So instead we just calculate the
 390  * gamma-adjusted value once and update the uniform parameter of the LCD
 391  * shader as needed.
 392  */
 393 static jboolean
 394 OGLTR_UpdateLCDTextColor(jint contrast)
 395 {
 396     double invgamma = ((double)contrast) / 100;
 397     GLfloat radj, gadj, badj;
 398     GLfloat clr[4];
 399     GLint loc;
 400 
 401     J2dTraceLn1(J2D_TRACE_INFO,
 402                 "OGLTR_UpdateLCDTextColor: contrast=%d", contrast);
 403 
 404     /*
 405      * Note: Ideally we would update the "src_adj" uniform parameter only
 406      * when there is a change in the source color.  Fortunately, the cost
 407      * of querying the current OpenGL color state and updating the uniform
 408      * value is quite small, and in the common case we only need to do this
 409      * once per GlyphList, so we gain little from trying to optimize too
 410      * eagerly here.
 411      */
 412 
 413     // get the current OpenGL primary color state
 414     j2d_glGetFloatv(GL_CURRENT_COLOR, clr);
 415 
 416     // gamma adjust the primary color
 417     radj = (GLfloat)pow(clr[0], invgamma);
 418     gadj = (GLfloat)pow(clr[1], invgamma);
 419     badj = (GLfloat)pow(clr[2], invgamma);
 420 
 421     // update the "src_adj" parameter of the shader program with this value
 422     loc = j2d_glGetUniformLocationARB(lcdTextProgram, "src_adj");
 423     j2d_glUniform3fARB(loc, radj, gadj, badj);
 424 
 425     return JNI_TRUE;
 426 }
 427 
 428 /**
 429  * Enables the LCD text shader and updates any related state, such as the
 430  * gamma lookup table textures.
 431  */
 432 static jboolean
 433 OGLTR_EnableLCDGlyphModeState(GLuint glyphTextureID, jint contrast)
 434 {
 435     // bind the texture containing glyph data to texture unit 0
 436     j2d_glActiveTextureARB(GL_TEXTURE0_ARB);
 437     j2d_glBindTexture(GL_TEXTURE_2D, glyphTextureID);
 438 
 439     // bind the texture tile containing destination data to texture unit 1
 440     j2d_glActiveTextureARB(GL_TEXTURE1_ARB);
 441     if (cachedDestTextureID == 0) {
 442         cachedDestTextureID =
 443             OGLContext_CreateBlitTexture(GL_RGB8, GL_RGB,
 444                                          OGLTR_CACHED_DEST_WIDTH,
 445                                          OGLTR_CACHED_DEST_HEIGHT);
 446         if (cachedDestTextureID == 0) {
 447             return JNI_FALSE;
 448         }
 449     }
 450     j2d_glBindTexture(GL_TEXTURE_2D, cachedDestTextureID);
 451 
 452     // note that GL_TEXTURE_2D was already enabled for texture unit 0,
 453     // but we need to explicitly enable it for texture unit 1
 454     j2d_glEnable(GL_TEXTURE_2D);
 455 
 456     // create the LCD text shader, if necessary
 457     if (lcdTextProgram == 0) {
 458         lcdTextProgram = OGLTR_CreateLCDTextProgram();
 459         if (lcdTextProgram == 0) {
 460             return JNI_FALSE;
 461         }
 462     }
 463 
 464     // enable the LCD text shader
 465     j2d_glUseProgramObjectARB(lcdTextProgram);
 466 
 467     // update the current contrast settings, if necessary
 468     if (lastLCDContrast != contrast) {
 469         if (!OGLTR_UpdateLCDTextContrast(contrast)) {
 470             return JNI_FALSE;
 471         }
 472         lastLCDContrast = contrast;
 473     }
 474 
 475     // update the current color settings
 476     if (!OGLTR_UpdateLCDTextColor(contrast)) {
 477         return JNI_FALSE;
 478     }
 479 
 480     // bind the gamma LUT textures
 481     j2d_glActiveTextureARB(GL_TEXTURE2_ARB);
 482     j2d_glBindTexture(GL_TEXTURE_3D, invGammaLutTextureID);
 483     j2d_glEnable(GL_TEXTURE_3D);
 484     j2d_glActiveTextureARB(GL_TEXTURE3_ARB);
 485     j2d_glBindTexture(GL_TEXTURE_3D, gammaLutTextureID);
 486     j2d_glEnable(GL_TEXTURE_3D);
 487 
 488     return JNI_TRUE;
 489 }
 490 
 491 void
 492 OGLTR_EnableGlyphVertexCache(OGLContext *oglc)
 493 {
 494     J2dTraceLn(J2D_TRACE_INFO, "OGLTR_EnableGlyphVertexCache");
 495 
 496     if (!OGLVertexCache_InitVertexCache(oglc)) {
 497         return;
 498     }
 499 
 500     if (glyphCache == NULL) {
 501         if (!OGLTR_InitGlyphCache(JNI_FALSE)) {
 502             return;
 503         }
 504     }
 505 
 506     j2d_glEnable(GL_TEXTURE_2D);
 507     j2d_glBindTexture(GL_TEXTURE_2D, glyphCache->cacheID);
 508     j2d_glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
 509 
 510     // for grayscale/monochrome text, the current OpenGL source color
 511     // is modulated with the glyph image as part of the texture
 512     // application stage, so we use GL_MODULATE here
 513     OGLC_UPDATE_TEXTURE_FUNCTION(oglc, GL_MODULATE);
 514 }
 515 
 516 void
 517 OGLTR_DisableGlyphVertexCache(OGLContext *oglc)
 518 {
 519     J2dTraceLn(J2D_TRACE_INFO, "OGLTR_DisableGlyphVertexCache");
 520 
 521     OGLVertexCache_FlushVertexCache();
 522     OGLVertexCache_RestoreColorState(oglc);
 523 
 524     j2d_glDisable(GL_TEXTURE_2D);
 525     j2d_glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
 526     j2d_glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
 527     j2d_glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
 528     j2d_glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
 529 }
 530 
 531 /**
 532  * Disables any pending state associated with the current "glyph mode".
 533  */
 534 static void
 535 OGLTR_DisableGlyphModeState()
 536 {
 537     switch (glyphMode) {
 538     case MODE_NO_CACHE_LCD:
 539         j2d_glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
 540         j2d_glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
 541         /* FALLTHROUGH */
 542 
 543     case MODE_USE_CACHE_LCD:
 544         j2d_glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
 545         j2d_glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
 546         j2d_glUseProgramObjectARB(0);
 547         j2d_glActiveTextureARB(GL_TEXTURE3_ARB);
 548         j2d_glDisable(GL_TEXTURE_3D);
 549         j2d_glActiveTextureARB(GL_TEXTURE2_ARB);
 550         j2d_glDisable(GL_TEXTURE_3D);
 551         j2d_glActiveTextureARB(GL_TEXTURE1_ARB);
 552         j2d_glDisable(GL_TEXTURE_2D);
 553         j2d_glActiveTextureARB(GL_TEXTURE0_ARB);
 554         break;
 555 
 556     case MODE_NO_CACHE_GRAY:
 557     case MODE_USE_CACHE_GRAY:
 558     case MODE_NOT_INITED:
 559     default:
 560         break;
 561     }
 562 }
 563 
 564 static jboolean
 565 OGLTR_DrawGrayscaleGlyphViaCache(OGLContext *oglc,
 566                                  GlyphInfo *ginfo, jint x, jint y)
 567 {
 568     CacheCellInfo *cell;
 569     jfloat x1, y1, x2, y2;
 570 
 571     if (glyphMode != MODE_USE_CACHE_GRAY) {
 572         OGLTR_DisableGlyphModeState();
 573         CHECK_PREVIOUS_OP(OGL_STATE_GLYPH_OP);
 574         glyphMode = MODE_USE_CACHE_GRAY;
 575     }
 576 
 577     if (ginfo->cellInfo == NULL) {
 578         // attempt to add glyph to accelerated glyph cache
 579         OGLTR_AddToGlyphCache(ginfo, JNI_FALSE);
 580 
 581         if (ginfo->cellInfo == NULL) {
 582             // we'll just no-op in the rare case that the cell is NULL
 583             return JNI_TRUE;
 584         }
 585     }
 586 
 587     cell = (CacheCellInfo *) (ginfo->cellInfo);
 588     cell->timesRendered++;
 589 
 590     x1 = (jfloat)x;
 591     y1 = (jfloat)y;
 592     x2 = x1 + ginfo->width;
 593     y2 = y1 + ginfo->height;
 594 
 595     OGLVertexCache_AddGlyphQuad(oglc,
 596                                 cell->tx1, cell->ty1,
 597                                 cell->tx2, cell->ty2,
 598                                 x1, y1, x2, y2);
 599 
 600     return JNI_TRUE;
 601 }
 602 
 603 /**
 604  * Evaluates to true if the rectangle defined by gx1/gy1/gx2/gy2 is
 605  * inside outerBounds.
 606  */
 607 #define INSIDE(gx1, gy1, gx2, gy2, outerBounds) \
 608     (((gx1) >= outerBounds.x1) && ((gy1) >= outerBounds.y1) && \
 609      ((gx2) <= outerBounds.x2) && ((gy2) <= outerBounds.y2))
 610 
 611 /**
 612  * Evaluates to true if the rectangle defined by gx1/gy1/gx2/gy2 intersects
 613  * the rectangle defined by bounds.
 614  */
 615 #define INTERSECTS(gx1, gy1, gx2, gy2, bounds) \
 616     ((bounds.x2 > (gx1)) && (bounds.y2 > (gy1)) && \
 617      (bounds.x1 < (gx2)) && (bounds.y1 < (gy2)))
 618 
 619 /**
 620  * This method checks to see if the given LCD glyph bounds fall within the
 621  * cached destination texture bounds.  If so, this method can return
 622  * immediately.  If not, this method will copy a chunk of framebuffer data
 623  * into the cached destination texture and then update the current cached
 624  * destination bounds before returning.
 625  */
 626 static void
 627 OGLTR_UpdateCachedDestination(OGLSDOps *dstOps, GlyphInfo *ginfo,
 628                               jint gx1, jint gy1, jint gx2, jint gy2,
 629                               jint glyphIndex, jint totalGlyphs)
 630 {
 631     jint dx1, dy1, dx2, dy2;
 632     jint dx1adj, dy1adj;
 633 
 634     if (isCachedDestValid && INSIDE(gx1, gy1, gx2, gy2, cachedDestBounds)) {
 635         // glyph is already within the cached destination bounds; no need
 636         // to read back the entire destination region again, but we do
 637         // need to see if the current glyph overlaps the previous glyph...
 638 
 639         if (INTERSECTS(gx1, gy1, gx2, gy2, previousGlyphBounds)) {
 640             // the current glyph overlaps the destination region touched
 641             // by the previous glyph, so now we need to read back the part
 642             // of the destination corresponding to the previous glyph
 643             dx1 = previousGlyphBounds.x1;
 644             dy1 = previousGlyphBounds.y1;
 645             dx2 = previousGlyphBounds.x2;
 646             dy2 = previousGlyphBounds.y2;
 647 
 648             // this accounts for lower-left origin of the destination region
 649             dx1adj = dstOps->xOffset + dx1;
 650             dy1adj = dstOps->yOffset + dstOps->height - dy2;
 651 
 652             // copy destination into subregion of cached texture tile:
 653             //   dx1-cachedDestBounds.x1 == +xoffset from left side of texture
 654             //   cachedDestBounds.y2-dy2 == +yoffset from bottom of texture
 655             j2d_glActiveTextureARB(GL_TEXTURE1_ARB);
 656             j2d_glCopyTexSubImage2D(GL_TEXTURE_2D, 0,
 657                                     dx1 - cachedDestBounds.x1,
 658                                     cachedDestBounds.y2 - dy2,
 659                                     dx1adj, dy1adj,
 660                                     dx2-dx1, dy2-dy1);
 661         }
 662     } else {
 663         jint remainingWidth;
 664 
 665         // destination region is not valid, so we need to read back a
 666         // chunk of the destination into our cached texture
 667 
 668         // position the upper-left corner of the destination region on the
 669         // "top" line of glyph list
 670         // REMIND: this isn't ideal; it would be better if we had some idea
 671         //         of the bounding box of the whole glyph list (this is
 672         //         do-able, but would require iterating through the whole
 673         //         list up front, which may present its own problems)
 674         dx1 = gx1;
 675         dy1 = gy1;
 676 
 677         if (ginfo->advanceX > 0) {
 678             // estimate the width based on our current position in the glyph
 679             // list and using the x advance of the current glyph (this is just
 680             // a quick and dirty heuristic; if this is a "thin" glyph image,
 681             // then we're likely to underestimate, and if it's "thick" then we
 682             // may end up reading back more than we need to)
 683             remainingWidth =
 684                 (jint)(ginfo->advanceX * (totalGlyphs - glyphIndex));
 685             if (remainingWidth > OGLTR_CACHED_DEST_WIDTH) {
 686                 remainingWidth = OGLTR_CACHED_DEST_WIDTH;
 687             } else if (remainingWidth < ginfo->width) {
 688                 // in some cases, the x-advance may be slightly smaller
 689                 // than the actual width of the glyph; if so, adjust our
 690                 // estimate so that we can accommodate the entire glyph
 691                 remainingWidth = ginfo->width;
 692             }
 693         } else {
 694             // a negative advance is possible when rendering rotated text,
 695             // in which case it is difficult to estimate an appropriate
 696             // region for readback, so we will pick a region that
 697             // encompasses just the current glyph
 698             remainingWidth = ginfo->width;
 699         }
 700         dx2 = dx1 + remainingWidth;
 701 
 702         // estimate the height (this is another sloppy heuristic; we'll
 703         // make the cached destination region tall enough to encompass most
 704         // glyphs that are small enough to fit in the glyph cache, and then
 705         // we add a little something extra to account for descenders
 706         dy2 = dy1 + OGLTR_CACHE_CELL_HEIGHT + 2;
 707 
 708         // this accounts for lower-left origin of the destination region
 709         dx1adj = dstOps->xOffset + dx1;
 710         dy1adj = dstOps->yOffset + dstOps->height - dy2;
 711 
 712         // copy destination into cached texture tile (the lower-left corner
 713         // of the destination region will be positioned at the lower-left
 714         // corner (0,0) of the texture)
 715         j2d_glActiveTextureARB(GL_TEXTURE1_ARB);
 716         j2d_glCopyTexSubImage2D(GL_TEXTURE_2D, 0,
 717                                 0, 0, dx1adj, dy1adj,
 718                                 dx2-dx1, dy2-dy1);
 719 
 720         // update the cached bounds and mark it valid
 721         cachedDestBounds.x1 = dx1;
 722         cachedDestBounds.y1 = dy1;
 723         cachedDestBounds.x2 = dx2;
 724         cachedDestBounds.y2 = dy2;
 725         isCachedDestValid = JNI_TRUE;
 726     }
 727 
 728     // always update the previous glyph bounds
 729     previousGlyphBounds.x1 = gx1;
 730     previousGlyphBounds.y1 = gy1;
 731     previousGlyphBounds.x2 = gx2;
 732     previousGlyphBounds.y2 = gy2;
 733 }
 734 
 735 static jboolean
 736 OGLTR_DrawLCDGlyphViaCache(OGLContext *oglc, OGLSDOps *dstOps,
 737                            GlyphInfo *ginfo, jint x, jint y,
 738                            jint glyphIndex, jint totalGlyphs,
 739                            jboolean rgbOrder, jint contrast)
 740 {
 741     CacheCellInfo *cell;
 742     jint dx1, dy1, dx2, dy2;
 743     jfloat dtx1, dty1, dtx2, dty2;
 744 
 745     if (glyphMode != MODE_USE_CACHE_LCD) {
 746         OGLTR_DisableGlyphModeState();
 747         CHECK_PREVIOUS_OP(GL_TEXTURE_2D);
 748         j2d_glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
 749 
 750         if (glyphCache == NULL) {
 751             if (!OGLTR_InitGlyphCache(JNI_TRUE)) {
 752                 return JNI_FALSE;
 753             }
 754         }
 755 
 756         if (rgbOrder != lastRGBOrder) {
 757             // need to invalidate the cache in this case; see comments
 758             // for lastRGBOrder above
 759             AccelGlyphCache_Invalidate(glyphCache);
 760             lastRGBOrder = rgbOrder;
 761         }
 762 
 763         if (!OGLTR_EnableLCDGlyphModeState(glyphCache->cacheID, contrast)) {
 764             return JNI_FALSE;
 765         }
 766 
 767         // when a fragment shader is enabled, the texture function state is
 768         // ignored, so the following line is not needed...
 769         // OGLC_UPDATE_TEXTURE_FUNCTION(oglc, GL_MODULATE);
 770 
 771         glyphMode = MODE_USE_CACHE_LCD;
 772     }
 773 
 774     if (ginfo->cellInfo == NULL) {
 775         // rowBytes will always be a multiple of 3, so the following is safe
 776         j2d_glPixelStorei(GL_UNPACK_ROW_LENGTH, ginfo->rowBytes / 3);
 777 
 778         // make sure the glyph cache texture is bound to texture unit 0
 779         j2d_glActiveTextureARB(GL_TEXTURE0_ARB);
 780 
 781         // attempt to add glyph to accelerated glyph cache
 782         OGLTR_AddToGlyphCache(ginfo, rgbOrder);
 783 
 784         if (ginfo->cellInfo == NULL) {
 785             // we'll just no-op in the rare case that the cell is NULL
 786             return JNI_TRUE;
 787         }
 788     }
 789 
 790     cell = (CacheCellInfo *) (ginfo->cellInfo);
 791     cell->timesRendered++;
 792 
 793     // location of the glyph in the destination's coordinate space
 794     dx1 = x;
 795     dy1 = y;
 796     dx2 = dx1 + ginfo->width;
 797     dy2 = dy1 + ginfo->height;
 798 
 799     // copy destination into second cached texture, if necessary
 800     OGLTR_UpdateCachedDestination(dstOps, ginfo,
 801                                   dx1, dy1, dx2, dy2,
 802                                   glyphIndex, totalGlyphs);
 803 
 804     // texture coordinates of the destination tile
 805     dtx1 = ((jfloat)(dx1 - cachedDestBounds.x1)) / OGLTR_CACHED_DEST_WIDTH;
 806     dty1 = ((jfloat)(cachedDestBounds.y2 - dy1)) / OGLTR_CACHED_DEST_HEIGHT;
 807     dtx2 = ((jfloat)(dx2 - cachedDestBounds.x1)) / OGLTR_CACHED_DEST_WIDTH;
 808     dty2 = ((jfloat)(cachedDestBounds.y2 - dy2)) / OGLTR_CACHED_DEST_HEIGHT;
 809 
 810     // render composed texture to the destination surface
 811     j2d_glBegin(GL_QUADS);
 812     j2d_glMultiTexCoord2fARB(GL_TEXTURE0_ARB, cell->tx1, cell->ty1);
 813     j2d_glMultiTexCoord2fARB(GL_TEXTURE1_ARB, dtx1, dty1);
 814     j2d_glVertex2i(dx1, dy1);
 815     j2d_glMultiTexCoord2fARB(GL_TEXTURE0_ARB, cell->tx2, cell->ty1);
 816     j2d_glMultiTexCoord2fARB(GL_TEXTURE1_ARB, dtx2, dty1);
 817     j2d_glVertex2i(dx2, dy1);
 818     j2d_glMultiTexCoord2fARB(GL_TEXTURE0_ARB, cell->tx2, cell->ty2);
 819     j2d_glMultiTexCoord2fARB(GL_TEXTURE1_ARB, dtx2, dty2);
 820     j2d_glVertex2i(dx2, dy2);
 821     j2d_glMultiTexCoord2fARB(GL_TEXTURE0_ARB, cell->tx1, cell->ty2);
 822     j2d_glMultiTexCoord2fARB(GL_TEXTURE1_ARB, dtx1, dty2);
 823     j2d_glVertex2i(dx1, dy2);
 824     j2d_glEnd();
 825 
 826     return JNI_TRUE;
 827 }
 828 
 829 static jboolean
 830 OGLTR_DrawGrayscaleGlyphNoCache(OGLContext *oglc,
 831                                 GlyphInfo *ginfo, jint x, jint y)
 832 {
 833     jint tw, th;
 834     jint sx, sy, sw, sh;
 835     jint x0;
 836     jint w = ginfo->width;
 837     jint h = ginfo->height;
 838 
 839     if (glyphMode != MODE_NO_CACHE_GRAY) {
 840         OGLTR_DisableGlyphModeState();
 841         CHECK_PREVIOUS_OP(OGL_STATE_MASK_OP);
 842         glyphMode = MODE_NO_CACHE_GRAY;
 843     }
 844 
 845     x0 = x;
 846     tw = OGLVC_MASK_CACHE_TILE_WIDTH;
 847     th = OGLVC_MASK_CACHE_TILE_HEIGHT;
 848 
 849     for (sy = 0; sy < h; sy += th, y += th) {
 850         x = x0;
 851         sh = ((sy + th) > h) ? (h - sy) : th;
 852 
 853         for (sx = 0; sx < w; sx += tw, x += tw) {
 854             sw = ((sx + tw) > w) ? (w - sx) : tw;
 855 
 856             OGLVertexCache_AddMaskQuad(oglc,
 857                                        sx, sy, x, y, sw, sh,
 858                                        w, ginfo->image);
 859         }
 860     }
 861 
 862     return JNI_TRUE;
 863 }
 864 
 865 static jboolean
 866 OGLTR_DrawLCDGlyphNoCache(OGLContext *oglc, OGLSDOps *dstOps,
 867                           GlyphInfo *ginfo, jint x, jint y,
 868                           jint rowBytesOffset,
 869                           jboolean rgbOrder, jint contrast)
 870 {
 871     GLfloat tx1, ty1, tx2, ty2;
 872     GLfloat dtx1, dty1, dtx2, dty2;
 873     jint tw, th;
 874     jint sx, sy, sw, sh, dxadj, dyadj;
 875     jint x0;
 876     jint w = ginfo->width;
 877     jint h = ginfo->height;
 878     GLenum pixelFormat = rgbOrder ? GL_RGB : GL_BGR;
 879 
 880     if (glyphMode != MODE_NO_CACHE_LCD) {
 881         OGLTR_DisableGlyphModeState();
 882         CHECK_PREVIOUS_OP(GL_TEXTURE_2D);
 883         j2d_glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
 884 
 885         if (oglc->blitTextureID == 0) {
 886             if (!OGLContext_InitBlitTileTexture(oglc)) {
 887                 return JNI_FALSE;
 888             }
 889         }
 890 
 891         if (!OGLTR_EnableLCDGlyphModeState(oglc->blitTextureID, contrast)) {
 892             return JNI_FALSE;
 893         }
 894 
 895         // when a fragment shader is enabled, the texture function state is
 896         // ignored, so the following line is not needed...
 897         // OGLC_UPDATE_TEXTURE_FUNCTION(oglc, GL_MODULATE);
 898 
 899         glyphMode = MODE_NO_CACHE_LCD;
 900     }
 901 
 902     // rowBytes will always be a multiple of 3, so the following is safe
 903     j2d_glPixelStorei(GL_UNPACK_ROW_LENGTH, ginfo->rowBytes / 3);
 904 
 905     x0 = x;
 906     tx1 = 0.0f;
 907     ty1 = 0.0f;
 908     dtx1 = 0.0f;
 909     dty2 = 0.0f;
 910     tw = OGLTR_NOCACHE_TILE_SIZE;
 911     th = OGLTR_NOCACHE_TILE_SIZE;
 912 
 913     for (sy = 0; sy < h; sy += th, y += th) {
 914         x = x0;
 915         sh = ((sy + th) > h) ? (h - sy) : th;
 916 
 917         for (sx = 0; sx < w; sx += tw, x += tw) {
 918             sw = ((sx + tw) > w) ? (w - sx) : tw;
 919 
 920             // update the source pointer offsets
 921             j2d_glPixelStorei(GL_UNPACK_SKIP_PIXELS, sx);
 922             j2d_glPixelStorei(GL_UNPACK_SKIP_ROWS, sy);
 923 
 924             // copy LCD mask into glyph texture tile
 925             j2d_glActiveTextureARB(GL_TEXTURE0_ARB);
 926             j2d_glTexSubImage2D(GL_TEXTURE_2D, 0,
 927                                 0, 0, sw, sh,
 928                                 pixelFormat, GL_UNSIGNED_BYTE,
 929                                 ginfo->image + rowBytesOffset);
 930 
 931             // update the lower-right glyph texture coordinates
 932             tx2 = ((GLfloat)sw) / OGLC_BLIT_TILE_SIZE;
 933             ty2 = ((GLfloat)sh) / OGLC_BLIT_TILE_SIZE;
 934 
 935             // this accounts for lower-left origin of the destination region
 936             dxadj = dstOps->xOffset + x;
 937             dyadj = dstOps->yOffset + dstOps->height - (y + sh);
 938 
 939             // copy destination into cached texture tile (the lower-left
 940             // corner of the destination region will be positioned at the
 941             // lower-left corner (0,0) of the texture)
 942             j2d_glActiveTextureARB(GL_TEXTURE1_ARB);
 943             j2d_glCopyTexSubImage2D(GL_TEXTURE_2D, 0,
 944                                     0, 0,
 945                                     dxadj, dyadj,
 946                                     sw, sh);
 947 
 948             // update the remaining destination texture coordinates
 949             dtx2 = ((GLfloat)sw) / OGLTR_CACHED_DEST_WIDTH;
 950             dty1 = ((GLfloat)sh) / OGLTR_CACHED_DEST_HEIGHT;
 951 
 952             // render composed texture to the destination surface
 953             j2d_glBegin(GL_QUADS);
 954             j2d_glMultiTexCoord2fARB(GL_TEXTURE0_ARB, tx1, ty1);
 955             j2d_glMultiTexCoord2fARB(GL_TEXTURE1_ARB, dtx1, dty1);
 956             j2d_glVertex2i(x, y);
 957             j2d_glMultiTexCoord2fARB(GL_TEXTURE0_ARB, tx2, ty1);
 958             j2d_glMultiTexCoord2fARB(GL_TEXTURE1_ARB, dtx2, dty1);
 959             j2d_glVertex2i(x + sw, y);
 960             j2d_glMultiTexCoord2fARB(GL_TEXTURE0_ARB, tx2, ty2);
 961             j2d_glMultiTexCoord2fARB(GL_TEXTURE1_ARB, dtx2, dty2);
 962             j2d_glVertex2i(x + sw, y + sh);
 963             j2d_glMultiTexCoord2fARB(GL_TEXTURE0_ARB, tx1, ty2);
 964             j2d_glMultiTexCoord2fARB(GL_TEXTURE1_ARB, dtx1, dty2);
 965             j2d_glVertex2i(x, y + sh);
 966             j2d_glEnd();
 967         }
 968     }
 969 
 970     return JNI_TRUE;
 971 }
 972 
 973 // see DrawGlyphList.c for more on this macro...
 974 #define FLOOR_ASSIGN(l, r) \
 975     if ((r)<0) (l) = ((int)floor(r)); else (l) = ((int)(r))
 976 
 977 void
 978 OGLTR_DrawGlyphList(JNIEnv *env, OGLContext *oglc, OGLSDOps *dstOps,
 979                     jint totalGlyphs, jboolean usePositions,
 980                     jboolean subPixPos, jboolean rgbOrder, jint lcdContrast,
 981                     jfloat glyphListOrigX, jfloat glyphListOrigY,
 982                     unsigned char *images, unsigned char *positions)
 983 {
 984     int glyphCounter;
 985 
 986     J2dTraceLn(J2D_TRACE_INFO, "OGLTR_DrawGlyphList");
 987 
 988     RETURN_IF_NULL(oglc);
 989     RETURN_IF_NULL(dstOps);
 990     RETURN_IF_NULL(images);
 991     if (usePositions) {
 992         RETURN_IF_NULL(positions);
 993     }
 994 
 995     glyphMode = MODE_NOT_INITED;
 996     isCachedDestValid = JNI_FALSE;
 997 
 998     for (glyphCounter = 0; glyphCounter < totalGlyphs; glyphCounter++) {
 999         jint x, y;
1000         jfloat glyphx, glyphy;
1001         jboolean grayscale, ok;
1002         GlyphInfo *ginfo = (GlyphInfo *)jlong_to_ptr(NEXT_LONG(images));
1003 
1004         if (ginfo == NULL) {
1005             // this shouldn't happen, but if it does we'll just break out...
1006             J2dRlsTraceLn(J2D_TRACE_ERROR,
1007                           "OGLTR_DrawGlyphList: glyph info is null");
1008             break;
1009         }
1010 
1011         grayscale = (ginfo->rowBytes == ginfo->width);
1012 
1013         if (usePositions) {
1014             jfloat posx = NEXT_FLOAT(positions);
1015             jfloat posy = NEXT_FLOAT(positions);
1016             glyphx = glyphListOrigX + posx + ginfo->topLeftX;
1017             glyphy = glyphListOrigY + posy + ginfo->topLeftY;
1018             FLOOR_ASSIGN(x, glyphx);
1019             FLOOR_ASSIGN(y, glyphy);
1020         } else {
1021             glyphx = glyphListOrigX + ginfo->topLeftX;
1022             glyphy = glyphListOrigY + ginfo->topLeftY;
1023             FLOOR_ASSIGN(x, glyphx);
1024             FLOOR_ASSIGN(y, glyphy);
1025             glyphListOrigX += ginfo->advanceX;
1026             glyphListOrigY += ginfo->advanceY;
1027         }
1028 
1029         if (ginfo->image == NULL) {
1030             continue;
1031         }
1032 
1033         if (grayscale) {
1034             // grayscale or monochrome glyph data
1035             if (cacheStatus != CACHE_LCD &&
1036                 ginfo->width <= OGLTR_CACHE_CELL_WIDTH &&
1037                 ginfo->height <= OGLTR_CACHE_CELL_HEIGHT)
1038             {
1039                 ok = OGLTR_DrawGrayscaleGlyphViaCache(oglc, ginfo, x, y);
1040             } else {
1041                 ok = OGLTR_DrawGrayscaleGlyphNoCache(oglc, ginfo, x, y);
1042             }
1043         } else {
1044             // LCD-optimized glyph data
1045             jint rowBytesOffset = 0;
1046 
1047             if (subPixPos) {
1048                 jint frac = (jint)((glyphx - x) * 3);
1049                 if (frac != 0) {
1050                     rowBytesOffset = 3 - frac;
1051                     x += 1;
1052                 }
1053             }
1054 
1055             if (rowBytesOffset == 0 &&
1056                 cacheStatus != CACHE_GRAY &&
1057                 ginfo->width <= OGLTR_CACHE_CELL_WIDTH &&
1058                 ginfo->height <= OGLTR_CACHE_CELL_HEIGHT)
1059             {
1060                 ok = OGLTR_DrawLCDGlyphViaCache(oglc, dstOps,
1061                                                 ginfo, x, y,
1062                                                 glyphCounter, totalGlyphs,
1063                                                 rgbOrder, lcdContrast);
1064             } else {
1065                 ok = OGLTR_DrawLCDGlyphNoCache(oglc, dstOps,
1066                                                ginfo, x, y,
1067                                                rowBytesOffset,
1068                                                rgbOrder, lcdContrast);
1069             }
1070         }
1071 
1072         if (!ok) {
1073             break;
1074         }
1075     }
1076 
1077     OGLTR_DisableGlyphModeState();
1078 }
1079 
1080 JNIEXPORT void JNICALL
1081 Java_sun_java2d_opengl_OGLTextRenderer_drawGlyphList
1082     (JNIEnv *env, jobject self,
1083      jint numGlyphs, jboolean usePositions,
1084      jboolean subPixPos, jboolean rgbOrder, jint lcdContrast,
1085      jfloat glyphListOrigX, jfloat glyphListOrigY,
1086      jlongArray imgArray, jfloatArray posArray)
1087 {
1088     unsigned char *images;
1089 
1090     J2dTraceLn(J2D_TRACE_INFO, "OGLTextRenderer_drawGlyphList");
1091 
1092     images = (unsigned char *)
1093         (*env)->GetPrimitiveArrayCritical(env, imgArray, NULL);
1094     if (images != NULL) {
1095         OGLContext *oglc = OGLRenderQueue_GetCurrentContext();
1096         OGLSDOps *dstOps = OGLRenderQueue_GetCurrentDestination();
1097 
1098         if (usePositions) {
1099             unsigned char *positions = (unsigned char *)
1100                 (*env)->GetPrimitiveArrayCritical(env, posArray, NULL);
1101             if (positions != NULL) {
1102                 OGLTR_DrawGlyphList(env, oglc, dstOps,
1103                                     numGlyphs, usePositions,
1104                                     subPixPos, rgbOrder, lcdContrast,
1105                                     glyphListOrigX, glyphListOrigY,
1106                                     images, positions);
1107                 (*env)->ReleasePrimitiveArrayCritical(env, posArray,
1108                                                       positions, JNI_ABORT);
1109             }
1110         } else {
1111             OGLTR_DrawGlyphList(env, oglc, dstOps,
1112                                 numGlyphs, usePositions,
1113                                 subPixPos, rgbOrder, lcdContrast,
1114                                 glyphListOrigX, glyphListOrigY,
1115                                 images, NULL);
1116         }
1117 
1118         // 6358147: reset current state, and ensure rendering is
1119         // flushed to dest
1120         if (oglc != NULL) {
1121             RESET_PREVIOUS_OP();
1122             j2d_glFlush();
1123         }
1124 
1125         (*env)->ReleasePrimitiveArrayCritical(env, imgArray,
1126                                               images, JNI_ABORT);
1127     }
1128 }
1129 
1130 #endif /* !HEADLESS */