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