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