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