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