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
OGLTR_InitGlyphCache(jboolean lcdCache)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
OGLTR_AddToGlyphCache(GlyphInfo * glyph,GLenum pixelFormat)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
OGLTR_CreateLCDTextProgram()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
OGLTR_UpdateLCDTextContrast(jint contrast)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
OGLTR_UpdateLCDTextColor(jint contrast)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
OGLTR_EnableLCDGlyphModeState(GLuint glyphTextureID,GLuint dstTextureID,jint contrast)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
OGLTR_EnableGlyphVertexCache(OGLContext * oglc)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
OGLTR_DisableGlyphVertexCache(OGLContext * oglc)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
OGLTR_DisableGlyphModeState()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
OGLTR_DrawGrayscaleGlyphViaCache(OGLContext * oglc,GlyphInfo * ginfo,jint x,jint y)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
OGLTR_UpdateCachedDestination(OGLSDOps * dstOps,GlyphInfo * ginfo,jint gx1,jint gy1,jint gx2,jint gy2,jint glyphIndex,jint totalGlyphs)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
OGLTR_DrawLCDGlyphViaCache(OGLContext * oglc,OGLSDOps * dstOps,GlyphInfo * ginfo,jint x,jint y,jint glyphIndex,jint totalGlyphs,jboolean rgbOrder,jint contrast,GLuint dstTextureID)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
OGLTR_DrawGrayscaleGlyphNoCache(OGLContext * oglc,GlyphInfo * ginfo,jint x,jint y)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
OGLTR_DrawLCDGlyphNoCache(OGLContext * oglc,OGLSDOps * dstOps,GlyphInfo * ginfo,jint x,jint y,jint rowBytesOffset,jboolean rgbOrder,jint contrast,GLuint dstTextureID)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
OGLTR_DrawGlyphList(JNIEnv * env,OGLContext * oglc,OGLSDOps * dstOps,jint totalGlyphs,jboolean usePositions,jboolean subPixPos,jboolean rgbOrder,jint lcdContrast,jfloat glyphListOrigX,jfloat glyphListOrigY,unsigned char * images,unsigned char * positions)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     if (OGLC_IS_CAP_PRESENT(oglc, CAPS_EXT_TEXBARRIER) &&
1023         dstOps->textureTarget == GL_TEXTURE_2D)
1024     {
1025         dstTextureID = dstOps->textureID;
1026     }
1027 
1028     for (glyphCounter = 0; glyphCounter < totalGlyphs; glyphCounter++) {
1029         jint x, y;
1030         jfloat glyphx, glyphy;
1031         jboolean grayscale, ok;
1032         GlyphInfo *ginfo = (GlyphInfo *)jlong_to_ptr(NEXT_LONG(images));
1033 
1034         if (ginfo == NULL) {
1035             // this shouldn't happen, but if it does we'll just break out...
1036             J2dRlsTraceLn(J2D_TRACE_ERROR,
1037                           "OGLTR_DrawGlyphList: glyph info is null");
1038             break;
1039         }
1040 
1041         grayscale = (ginfo->rowBytes == ginfo->width);
1042 
1043         if (usePositions) {
1044             jfloat posx = NEXT_FLOAT(positions);
1045             jfloat posy = NEXT_FLOAT(positions);
1046             glyphx = glyphListOrigX + posx + ginfo->topLeftX;
1047             glyphy = glyphListOrigY + posy + ginfo->topLeftY;
1048             FLOOR_ASSIGN(x, glyphx);
1049             FLOOR_ASSIGN(y, glyphy);
1050         } else {
1051             glyphx = glyphListOrigX + ginfo->topLeftX;
1052             glyphy = glyphListOrigY + ginfo->topLeftY;
1053             FLOOR_ASSIGN(x, glyphx);
1054             FLOOR_ASSIGN(y, glyphy);
1055             glyphListOrigX += ginfo->advanceX;
1056             glyphListOrigY += ginfo->advanceY;
1057         }
1058 
1059         if (ginfo->image == NULL) {
1060             continue;
1061         }
1062 
1063         if (grayscale) {
1064             // grayscale or monochrome glyph data
1065             if (ginfo->width <= OGLTR_CACHE_CELL_WIDTH &&
1066                 ginfo->height <= OGLTR_CACHE_CELL_HEIGHT)
1067             {
1068                 ok = OGLTR_DrawGrayscaleGlyphViaCache(oglc, ginfo, x, y);
1069             } else {
1070                 ok = OGLTR_DrawGrayscaleGlyphNoCache(oglc, ginfo, x, y);
1071             }
1072         } else {
1073             // LCD-optimized glyph data
1074             jint rowBytesOffset = 0;
1075 
1076             if (subPixPos) {
1077                 jint frac = (jint)((glyphx - x) * 3);
1078                 if (frac != 0) {
1079                     rowBytesOffset = 3 - frac;
1080                     x += 1;
1081                 }
1082             }
1083 
1084             if (rowBytesOffset == 0 &&
1085                 ginfo->width <= OGLTR_CACHE_CELL_WIDTH &&
1086                 ginfo->height <= OGLTR_CACHE_CELL_HEIGHT)
1087             {
1088                 ok = OGLTR_DrawLCDGlyphViaCache(oglc, dstOps,
1089                                                 ginfo, x, y,
1090                                                 glyphCounter, totalGlyphs,
1091                                                 rgbOrder, lcdContrast,
1092                                                 dstTextureID);
1093             } else {
1094                 ok = OGLTR_DrawLCDGlyphNoCache(oglc, dstOps,
1095                                                ginfo, x, y,
1096                                                rowBytesOffset,
1097                                                rgbOrder, lcdContrast,
1098                                                dstTextureID);
1099             }
1100         }
1101 
1102         if (!ok) {
1103             break;
1104         }
1105     }
1106 
1107     OGLTR_DisableGlyphModeState();
1108 }
1109 
1110 JNIEXPORT void JNICALL
Java_sun_java2d_opengl_OGLTextRenderer_drawGlyphList(JNIEnv * env,jobject self,jint numGlyphs,jboolean usePositions,jboolean subPixPos,jboolean rgbOrder,jint lcdContrast,jfloat glyphListOrigX,jfloat glyphListOrigY,jlongArray imgArray,jfloatArray posArray)1111 Java_sun_java2d_opengl_OGLTextRenderer_drawGlyphList
1112     (JNIEnv *env, jobject self,
1113      jint numGlyphs, jboolean usePositions,
1114      jboolean subPixPos, jboolean rgbOrder, jint lcdContrast,
1115      jfloat glyphListOrigX, jfloat glyphListOrigY,
1116      jlongArray imgArray, jfloatArray posArray)
1117 {
1118     unsigned char *images;
1119 
1120     J2dTraceLn(J2D_TRACE_INFO, "OGLTextRenderer_drawGlyphList");
1121 
1122     images = (unsigned char *)
1123         (*env)->GetPrimitiveArrayCritical(env, imgArray, NULL);
1124     if (images != NULL) {
1125         OGLContext *oglc = OGLRenderQueue_GetCurrentContext();
1126         OGLSDOps *dstOps = OGLRenderQueue_GetCurrentDestination();
1127 
1128         if (usePositions) {
1129             unsigned char *positions = (unsigned char *)
1130                 (*env)->GetPrimitiveArrayCritical(env, posArray, NULL);
1131             if (positions != NULL) {
1132                 OGLTR_DrawGlyphList(env, oglc, dstOps,
1133                                     numGlyphs, usePositions,
1134                                     subPixPos, rgbOrder, lcdContrast,
1135                                     glyphListOrigX, glyphListOrigY,
1136                                     images, positions);
1137                 (*env)->ReleasePrimitiveArrayCritical(env, posArray,
1138                                                       positions, JNI_ABORT);
1139             }
1140         } else {
1141             OGLTR_DrawGlyphList(env, oglc, dstOps,
1142                                 numGlyphs, usePositions,
1143                                 subPixPos, rgbOrder, lcdContrast,
1144                                 glyphListOrigX, glyphListOrigY,
1145                                 images, NULL);
1146         }
1147 
1148         // 6358147: reset current state, and ensure rendering is
1149         // flushed to dest
1150         if (oglc != NULL) {
1151             RESET_PREVIOUS_OP();
1152             j2d_glFlush();
1153         }
1154 
1155         (*env)->ReleasePrimitiveArrayCritical(env, imgArray,
1156                                               images, JNI_ABORT);
1157     }
1158 }
1159 
1160 #endif /* !HEADLESS */
1161