29 #include <math.h>
30 #include <jlong.h>
31
32 #include "sun_java2d_opengl_OGLTextRenderer.h"
33
34 #include "SurfaceData.h"
35 #include "OGLContext.h"
36 #include "OGLSurfaceData.h"
37 #include "OGLRenderQueue.h"
38 #include "OGLTextRenderer.h"
39 #include "OGLVertexCache.h"
40 #include "AccelGlyphCache.h"
41 #include "fontscalerdefs.h"
42
43 /**
44 * The following constants define the inner and outer bounds of the
45 * accelerated glyph cache.
46 */
47 #define OGLTR_CACHE_WIDTH 512
48 #define OGLTR_CACHE_HEIGHT 512
49 #define OGLTR_CACHE_CELL_WIDTH 16
50 #define OGLTR_CACHE_CELL_HEIGHT 16
51
52 /**
53 * The current "glyph mode" state. This variable is used to track the
54 * codepath used to render a particular glyph. This variable is reset to
55 * MODE_NOT_INITED at the beginning of every call to OGLTR_DrawGlyphList().
56 * As each glyph is rendered, the glyphMode variable is updated to reflect
57 * the current mode, so if the current mode is the same as the mode used
58 * to render the previous glyph, we can avoid doing costly setup operations
59 * each time.
60 */
61 typedef enum {
62 MODE_NOT_INITED,
63 MODE_USE_CACHE_GRAY,
64 MODE_USE_CACHE_LCD,
65 MODE_NO_CACHE_GRAY,
66 MODE_NO_CACHE_LCD
67 } GlyphMode;
68 static GlyphMode glyphMode = MODE_NOT_INITED;
69
70 /**
71 * This enum indicates the current state of the hardware glyph cache.
72 * Initially the CacheStatus is set to CACHE_NOT_INITED, and then it is
73 * set to either GRAY or LCD when the glyph cache is initialized.
74 */
75 typedef enum {
76 CACHE_NOT_INITED,
77 CACHE_GRAY,
78 CACHE_LCD
79 } CacheStatus;
80 static CacheStatus cacheStatus = CACHE_NOT_INITED;
81
82 /**
83 * This is the one glyph cache. Once it is initialized as either GRAY or
84 * LCD, it stays in that mode for the duration of the application. It should
85 * be safe to use this one glyph cache for all screens in a multimon
86 * environment, since the glyph cache texture is shared between all contexts,
87 * and (in theory) OpenGL drivers should be smart enough to manage that
88 * texture across all screens.
89 */
90 static GlyphCacheInfo *glyphCache = NULL;
91
92 /**
93 * The handle to the LCD text fragment program object.
94 */
95 static GLhandleARB lcdTextProgram = 0;
96
97 /**
98 * This value tracks the previous LCD contrast setting, so if the contrast
99 * value hasn't changed since the last time the gamma uniforms were
100 * updated (not very common), then we can skip updating the unforms.
101 */
102 static jint lastLCDContrast = -1;
103
104 /**
105 * This value tracks the previous LCD rgbOrder setting, so if the rgbOrder
106 * value has changed since the last time, it indicates that we need to
107 * invalidate the cache, which may already store glyph images in the reverse
108 * order. Note that in most real world applications this value will not
109 * change over the course of the application, but tests like Font2DTest
110 * allow for changing the ordering at runtime, so we need to handle that case.
121 /**
122 * These constants define the size of the "cached destination" texture.
123 * This texture is only used when rendering LCD-optimized text, as that
124 * codepath needs direct access to the destination. There is no way to
125 * access the framebuffer directly from an OpenGL shader, so we need to first
126 * copy the destination region corresponding to a particular glyph into
127 * this cached texture, and then that texture will be accessed inside the
128 * shader. Copying the destination into this cached texture can be a very
129 * expensive operation (accounting for about half the rendering time for
130 * LCD text), so to mitigate this cost we try to bulk read a horizontal
131 * region of the destination at a time. (These values are empirically
132 * derived for the common case where text runs horizontally.)
133 *
134 * Note: It is assumed in various calculations below that:
135 * (OGLTR_CACHED_DEST_WIDTH >= OGLTR_CACHE_CELL_WIDTH) &&
136 * (OGLTR_CACHED_DEST_WIDTH >= OGLTR_NOCACHE_TILE_SIZE) &&
137 * (OGLTR_CACHED_DEST_HEIGHT >= OGLTR_CACHE_CELL_HEIGHT) &&
138 * (OGLTR_CACHED_DEST_HEIGHT >= OGLTR_NOCACHE_TILE_SIZE)
139 */
140 #define OGLTR_CACHED_DEST_WIDTH 512
141 #define OGLTR_CACHED_DEST_HEIGHT 32
142
143 /**
144 * The handle to the "cached destination" texture object.
145 */
146 static GLuint cachedDestTextureID = 0;
147
148 /**
149 * The current bounds of the "cached destination" texture, in destination
150 * coordinate space. The width/height of these bounds will not exceed the
151 * OGLTR_CACHED_DEST_WIDTH/HEIGHT values defined above. These bounds are
152 * only considered valid when the isCachedDestValid flag is JNI_TRUE.
153 */
154 static SurfaceDataBounds cachedDestBounds;
155
156 /**
157 * This flag indicates whether the "cached destination" texture contains
158 * valid data. This flag is reset to JNI_FALSE at the beginning of every
159 * call to OGLTR_DrawGlyphList(). Once we copy valid destination data
160 * into the cached texture, this flag is set to JNI_TRUE. This way, we can
161 * limit the number of times we need to copy destination data, which is a
195 OGLTR_CACHE_CELL_WIDTH,
196 OGLTR_CACHE_CELL_HEIGHT,
197 OGLVertexCache_FlushVertexCache);
198 if (gcinfo == NULL) {
199 J2dRlsTraceLn(J2D_TRACE_ERROR,
200 "OGLTR_InitGlyphCache: could not init OGL glyph cache");
201 return JNI_FALSE;
202 }
203
204 // init cache texture object
205 j2d_glGenTextures(1, &gcinfo->cacheID);
206 j2d_glBindTexture(GL_TEXTURE_2D, gcinfo->cacheID);
207 j2d_glPrioritizeTextures(1, &gcinfo->cacheID, &priority);
208 j2d_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
209 j2d_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
210
211 j2d_glTexImage2D(GL_TEXTURE_2D, 0, internalFormat,
212 OGLTR_CACHE_WIDTH, OGLTR_CACHE_HEIGHT, 0,
213 pixelFormat, GL_UNSIGNED_BYTE, NULL);
214
215 cacheStatus = (lcdCache ? CACHE_LCD : CACHE_GRAY);
216 glyphCache = gcinfo;
217
218 return JNI_TRUE;
219 }
220
221 /**
222 * Adds the given glyph to the glyph cache (texture and data structure)
223 * associated with the given OGLContext.
224 */
225 static void
226 OGLTR_AddToGlyphCache(GlyphInfo *glyph, jboolean rgbOrder)
227 {
228 GLenum pixelFormat;
229 CacheCellInfo *ccinfo;
230
231 J2dTraceLn(J2D_TRACE_INFO, "OGLTR_AddToGlyphCache");
232
233 if ((glyphCache == NULL) || (glyph->image == NULL)) {
234 return;
235 }
236
237 if (cacheStatus == CACHE_LCD) {
238 pixelFormat = rgbOrder ? GL_RGB : GL_BGR;
239 } else {
240 pixelFormat = GL_LUMINANCE;
241 }
242
243 AccelGlyphCache_AddGlyph(glyphCache, glyph);
244 ccinfo = (CacheCellInfo *) glyph->cellInfo;
245
246 if (ccinfo != NULL) {
247 // store glyph image in texture cell
248 j2d_glTexSubImage2D(GL_TEXTURE_2D, 0,
249 ccinfo->x, ccinfo->y,
250 glyph->width, glyph->height,
251 pixelFormat, GL_UNSIGNED_BYTE, glyph->image);
252 }
253 }
254
255 /**
256 * This is the GLSL fragment shader source code for rendering LCD-optimized
257 * text. Do not be frightened; it is much easier to understand than the
258 * equivalent ASM-like fragment program!
259 *
260 * The "uniform" variables at the top are initialized once the program is
261 * linked, and are updated at runtime as needed (e.g. when the source color
262 * changes, we will modify the "src_adj" value in OGLTR_UpdateLCDTextColor()).
263 *
396 // get the current OpenGL primary color state
397 j2d_glGetFloatv(GL_CURRENT_COLOR, clr);
398
399 // gamma adjust the primary color
400 radj = (GLfloat)pow(clr[0], gamma);
401 gadj = (GLfloat)pow(clr[1], gamma);
402 badj = (GLfloat)pow(clr[2], gamma);
403
404 // update the "src_adj" parameter of the shader program with this value
405 loc = j2d_glGetUniformLocationARB(lcdTextProgram, "src_adj");
406 j2d_glUniform3fARB(loc, radj, gadj, badj);
407
408 return JNI_TRUE;
409 }
410
411 /**
412 * Enables the LCD text shader and updates any related state, such as the
413 * gamma lookup table textures.
414 */
415 static jboolean
416 OGLTR_EnableLCDGlyphModeState(GLuint glyphTextureID, jint contrast)
417 {
418 // bind the texture containing glyph data to texture unit 0
419 j2d_glActiveTextureARB(GL_TEXTURE0_ARB);
420 j2d_glBindTexture(GL_TEXTURE_2D, glyphTextureID);
421
422 // bind the texture tile containing destination data to texture unit 1
423 j2d_glActiveTextureARB(GL_TEXTURE1_ARB);
424 if (cachedDestTextureID == 0) {
425 cachedDestTextureID =
426 OGLContext_CreateBlitTexture(GL_RGB8, GL_RGB,
427 OGLTR_CACHED_DEST_WIDTH,
428 OGLTR_CACHED_DEST_HEIGHT);
429 if (cachedDestTextureID == 0) {
430 return JNI_FALSE;
431 }
432 }
433 j2d_glBindTexture(GL_TEXTURE_2D, cachedDestTextureID);
434
435 // note that GL_TEXTURE_2D was already enabled for texture unit 0,
436 // but we need to explicitly enable it for texture unit 1
437 j2d_glEnable(GL_TEXTURE_2D);
438
439 // create the LCD text shader, if necessary
440 if (lcdTextProgram == 0) {
441 lcdTextProgram = OGLTR_CreateLCDTextProgram();
442 if (lcdTextProgram == 0) {
443 return JNI_FALSE;
444 }
445 }
446
447 // enable the LCD text shader
448 j2d_glUseProgramObjectARB(lcdTextProgram);
449
450 // update the current contrast settings, if necessary
451 if (lastLCDContrast != contrast) {
452 if (!OGLTR_UpdateLCDTextContrast(contrast)) {
453 return JNI_FALSE;
455 lastLCDContrast = contrast;
456 }
457
458 // update the current color settings
459 if (!OGLTR_UpdateLCDTextColor(contrast)) {
460 return JNI_FALSE;
461 }
462
463 return JNI_TRUE;
464 }
465
466 void
467 OGLTR_EnableGlyphVertexCache(OGLContext *oglc)
468 {
469 J2dTraceLn(J2D_TRACE_INFO, "OGLTR_EnableGlyphVertexCache");
470
471 if (!OGLVertexCache_InitVertexCache(oglc)) {
472 return;
473 }
474
475 if (glyphCache == NULL) {
476 if (!OGLTR_InitGlyphCache(JNI_FALSE)) {
477 return;
478 }
479 }
480
481 j2d_glEnable(GL_TEXTURE_2D);
482 j2d_glBindTexture(GL_TEXTURE_2D, glyphCache->cacheID);
483 j2d_glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
484
485 // for grayscale/monochrome text, the current OpenGL source color
486 // is modulated with the glyph image as part of the texture
487 // application stage, so we use GL_MODULATE here
488 OGLC_UPDATE_TEXTURE_FUNCTION(oglc, GL_MODULATE);
489 }
490
491 void
492 OGLTR_DisableGlyphVertexCache(OGLContext *oglc)
493 {
494 J2dTraceLn(J2D_TRACE_INFO, "OGLTR_DisableGlyphVertexCache");
495
496 OGLVertexCache_FlushVertexCache();
497 OGLVertexCache_RestoreColorState(oglc);
498
499 j2d_glDisable(GL_TEXTURE_2D);
500 j2d_glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
501 j2d_glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
502 j2d_glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
505
506 /**
507 * Disables any pending state associated with the current "glyph mode".
508 */
509 static void
510 OGLTR_DisableGlyphModeState()
511 {
512 switch (glyphMode) {
513 case MODE_NO_CACHE_LCD:
514 j2d_glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
515 j2d_glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
516 /* FALLTHROUGH */
517
518 case MODE_USE_CACHE_LCD:
519 j2d_glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
520 j2d_glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
521 j2d_glUseProgramObjectARB(0);
522 j2d_glActiveTextureARB(GL_TEXTURE1_ARB);
523 j2d_glDisable(GL_TEXTURE_2D);
524 j2d_glActiveTextureARB(GL_TEXTURE0_ARB);
525 break;
526
527 case MODE_NO_CACHE_GRAY:
528 case MODE_USE_CACHE_GRAY:
529 case MODE_NOT_INITED:
530 default:
531 break;
532 }
533 }
534
535 static jboolean
536 OGLTR_DrawGrayscaleGlyphViaCache(OGLContext *oglc,
537 GlyphInfo *ginfo, jint x, jint y)
538 {
539 CacheCellInfo *cell;
540 jfloat x1, y1, x2, y2;
541
542 if (glyphMode != MODE_USE_CACHE_GRAY) {
543 OGLTR_DisableGlyphModeState();
544 CHECK_PREVIOUS_OP(OGL_STATE_GLYPH_OP);
545 glyphMode = MODE_USE_CACHE_GRAY;
546 }
547
548 if (ginfo->cellInfo == NULL) {
549 // attempt to add glyph to accelerated glyph cache
550 OGLTR_AddToGlyphCache(ginfo, JNI_FALSE);
551
552 if (ginfo->cellInfo == NULL) {
553 // we'll just no-op in the rare case that the cell is NULL
554 return JNI_TRUE;
555 }
556 }
557
558 cell = (CacheCellInfo *) (ginfo->cellInfo);
559 cell->timesRendered++;
560
561 x1 = (jfloat)x;
562 y1 = (jfloat)y;
563 x2 = x1 + ginfo->width;
564 y2 = y1 + ginfo->height;
565
566 OGLVertexCache_AddGlyphQuad(oglc,
567 cell->tx1, cell->ty1,
568 cell->tx2, cell->ty2,
569 x1, y1, x2, y2);
570
690
691 // update the cached bounds and mark it valid
692 cachedDestBounds.x1 = dx1;
693 cachedDestBounds.y1 = dy1;
694 cachedDestBounds.x2 = dx2;
695 cachedDestBounds.y2 = dy2;
696 isCachedDestValid = JNI_TRUE;
697 }
698
699 // always update the previous glyph bounds
700 previousGlyphBounds.x1 = gx1;
701 previousGlyphBounds.y1 = gy1;
702 previousGlyphBounds.x2 = gx2;
703 previousGlyphBounds.y2 = gy2;
704 }
705
706 static jboolean
707 OGLTR_DrawLCDGlyphViaCache(OGLContext *oglc, OGLSDOps *dstOps,
708 GlyphInfo *ginfo, jint x, jint y,
709 jint glyphIndex, jint totalGlyphs,
710 jboolean rgbOrder, jint contrast)
711 {
712 CacheCellInfo *cell;
713 jint dx1, dy1, dx2, dy2;
714 jfloat dtx1, dty1, dtx2, dty2;
715
716 if (glyphMode != MODE_USE_CACHE_LCD) {
717 OGLTR_DisableGlyphModeState();
718 CHECK_PREVIOUS_OP(GL_TEXTURE_2D);
719 j2d_glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
720
721 if (glyphCache == NULL) {
722 if (!OGLTR_InitGlyphCache(JNI_TRUE)) {
723 return JNI_FALSE;
724 }
725 }
726
727 if (rgbOrder != lastRGBOrder) {
728 // need to invalidate the cache in this case; see comments
729 // for lastRGBOrder above
730 AccelGlyphCache_Invalidate(glyphCache);
731 lastRGBOrder = rgbOrder;
732 }
733
734 if (!OGLTR_EnableLCDGlyphModeState(glyphCache->cacheID, contrast)) {
735 return JNI_FALSE;
736 }
737
738 // when a fragment shader is enabled, the texture function state is
739 // ignored, so the following line is not needed...
740 // OGLC_UPDATE_TEXTURE_FUNCTION(oglc, GL_MODULATE);
741
742 glyphMode = MODE_USE_CACHE_LCD;
743 }
744
745 if (ginfo->cellInfo == NULL) {
746 // rowBytes will always be a multiple of 3, so the following is safe
747 j2d_glPixelStorei(GL_UNPACK_ROW_LENGTH, ginfo->rowBytes / 3);
748
749 // make sure the glyph cache texture is bound to texture unit 0
750 j2d_glActiveTextureARB(GL_TEXTURE0_ARB);
751
752 // attempt to add glyph to accelerated glyph cache
753 OGLTR_AddToGlyphCache(ginfo, rgbOrder);
754
755 if (ginfo->cellInfo == NULL) {
756 // we'll just no-op in the rare case that the cell is NULL
757 return JNI_TRUE;
758 }
759 }
760
761 cell = (CacheCellInfo *) (ginfo->cellInfo);
762 cell->timesRendered++;
763
764 // location of the glyph in the destination's coordinate space
765 dx1 = x;
766 dy1 = y;
767 dx2 = dx1 + ginfo->width;
768 dy2 = dy1 + ginfo->height;
769
770 // copy destination into second cached texture, if necessary
771 OGLTR_UpdateCachedDestination(dstOps, ginfo,
772 dx1, dy1, dx2, dy2,
773 glyphIndex, totalGlyphs);
774
775 // texture coordinates of the destination tile
776 dtx1 = ((jfloat)(dx1 - cachedDestBounds.x1)) / OGLTR_CACHED_DEST_WIDTH;
777 dty1 = ((jfloat)(cachedDestBounds.y2 - dy1)) / OGLTR_CACHED_DEST_HEIGHT;
778 dtx2 = ((jfloat)(dx2 - cachedDestBounds.x1)) / OGLTR_CACHED_DEST_WIDTH;
779 dty2 = ((jfloat)(cachedDestBounds.y2 - dy2)) / OGLTR_CACHED_DEST_HEIGHT;
780
781 // render composed texture to the destination surface
782 j2d_glBegin(GL_QUADS);
783 j2d_glMultiTexCoord2fARB(GL_TEXTURE0_ARB, cell->tx1, cell->ty1);
784 j2d_glMultiTexCoord2fARB(GL_TEXTURE1_ARB, dtx1, dty1);
785 j2d_glVertex2i(dx1, dy1);
786 j2d_glMultiTexCoord2fARB(GL_TEXTURE0_ARB, cell->tx2, cell->ty1);
787 j2d_glMultiTexCoord2fARB(GL_TEXTURE1_ARB, dtx2, dty1);
788 j2d_glVertex2i(dx2, dy1);
789 j2d_glMultiTexCoord2fARB(GL_TEXTURE0_ARB, cell->tx2, cell->ty2);
790 j2d_glMultiTexCoord2fARB(GL_TEXTURE1_ARB, dtx2, dty2);
791 j2d_glVertex2i(dx2, dy2);
792 j2d_glMultiTexCoord2fARB(GL_TEXTURE0_ARB, cell->tx1, cell->ty2);
793 j2d_glMultiTexCoord2fARB(GL_TEXTURE1_ARB, dtx1, dty2);
794 j2d_glVertex2i(dx1, dy2);
795 j2d_glEnd();
796
797 return JNI_TRUE;
798 }
799
820 for (sy = 0; sy < h; sy += th, y += th) {
821 x = x0;
822 sh = ((sy + th) > h) ? (h - sy) : th;
823
824 for (sx = 0; sx < w; sx += tw, x += tw) {
825 sw = ((sx + tw) > w) ? (w - sx) : tw;
826
827 OGLVertexCache_AddMaskQuad(oglc,
828 sx, sy, x, y, sw, sh,
829 w, ginfo->image);
830 }
831 }
832
833 return JNI_TRUE;
834 }
835
836 static jboolean
837 OGLTR_DrawLCDGlyphNoCache(OGLContext *oglc, OGLSDOps *dstOps,
838 GlyphInfo *ginfo, jint x, jint y,
839 jint rowBytesOffset,
840 jboolean rgbOrder, jint contrast)
841 {
842 GLfloat tx1, ty1, tx2, ty2;
843 GLfloat dtx1, dty1, dtx2, dty2;
844 jint tw, th;
845 jint sx, sy, sw, sh, dxadj, dyadj;
846 jint x0;
847 jint w = ginfo->width;
848 jint h = ginfo->height;
849 GLenum pixelFormat = rgbOrder ? GL_RGB : GL_BGR;
850
851 if (glyphMode != MODE_NO_CACHE_LCD) {
852 OGLTR_DisableGlyphModeState();
853 CHECK_PREVIOUS_OP(GL_TEXTURE_2D);
854 j2d_glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
855
856 if (oglc->blitTextureID == 0) {
857 if (!OGLContext_InitBlitTileTexture(oglc)) {
858 return JNI_FALSE;
859 }
860 }
861
862 if (!OGLTR_EnableLCDGlyphModeState(oglc->blitTextureID, contrast)) {
863 return JNI_FALSE;
864 }
865
866 // when a fragment shader is enabled, the texture function state is
867 // ignored, so the following line is not needed...
868 // OGLC_UPDATE_TEXTURE_FUNCTION(oglc, GL_MODULATE);
869
870 glyphMode = MODE_NO_CACHE_LCD;
871 }
872
873 // rowBytes will always be a multiple of 3, so the following is safe
874 j2d_glPixelStorei(GL_UNPACK_ROW_LENGTH, ginfo->rowBytes / 3);
875
876 x0 = x;
877 tx1 = 0.0f;
878 ty1 = 0.0f;
879 dtx1 = 0.0f;
880 dty2 = 0.0f;
881 tw = OGLTR_NOCACHE_TILE_SIZE;
882 th = OGLTR_NOCACHE_TILE_SIZE;
890
891 // update the source pointer offsets
892 j2d_glPixelStorei(GL_UNPACK_SKIP_PIXELS, sx);
893 j2d_glPixelStorei(GL_UNPACK_SKIP_ROWS, sy);
894
895 // copy LCD mask into glyph texture tile
896 j2d_glActiveTextureARB(GL_TEXTURE0_ARB);
897 j2d_glTexSubImage2D(GL_TEXTURE_2D, 0,
898 0, 0, sw, sh,
899 pixelFormat, GL_UNSIGNED_BYTE,
900 ginfo->image + rowBytesOffset);
901
902 // update the lower-right glyph texture coordinates
903 tx2 = ((GLfloat)sw) / OGLC_BLIT_TILE_SIZE;
904 ty2 = ((GLfloat)sh) / OGLC_BLIT_TILE_SIZE;
905
906 // this accounts for lower-left origin of the destination region
907 dxadj = dstOps->xOffset + x;
908 dyadj = dstOps->yOffset + dstOps->height - (y + sh);
909
910 // copy destination into cached texture tile (the lower-left
911 // corner of the destination region will be positioned at the
912 // lower-left corner (0,0) of the texture)
913 j2d_glActiveTextureARB(GL_TEXTURE1_ARB);
914 j2d_glCopyTexSubImage2D(GL_TEXTURE_2D, 0,
915 0, 0,
916 dxadj, dyadj,
917 sw, sh);
918
919 // update the remaining destination texture coordinates
920 dtx2 = ((GLfloat)sw) / OGLTR_CACHED_DEST_WIDTH;
921 dty1 = ((GLfloat)sh) / OGLTR_CACHED_DEST_HEIGHT;
922
923 // render composed texture to the destination surface
924 j2d_glBegin(GL_QUADS);
925 j2d_glMultiTexCoord2fARB(GL_TEXTURE0_ARB, tx1, ty1);
926 j2d_glMultiTexCoord2fARB(GL_TEXTURE1_ARB, dtx1, dty1);
927 j2d_glVertex2i(x, y);
928 j2d_glMultiTexCoord2fARB(GL_TEXTURE0_ARB, tx2, ty1);
929 j2d_glMultiTexCoord2fARB(GL_TEXTURE1_ARB, dtx2, dty1);
930 j2d_glVertex2i(x + sw, y);
931 j2d_glMultiTexCoord2fARB(GL_TEXTURE0_ARB, tx2, ty2);
932 j2d_glMultiTexCoord2fARB(GL_TEXTURE1_ARB, dtx2, dty2);
933 j2d_glVertex2i(x + sw, y + sh);
934 j2d_glMultiTexCoord2fARB(GL_TEXTURE0_ARB, tx1, ty2);
935 j2d_glMultiTexCoord2fARB(GL_TEXTURE1_ARB, dtx1, dty2);
936 j2d_glVertex2i(x, y + sh);
937 j2d_glEnd();
938 }
939 }
940
941 return JNI_TRUE;
942 }
943
944 // see DrawGlyphList.c for more on this macro...
945 #define FLOOR_ASSIGN(l, r) \
946 if ((r)<0) (l) = ((int)floor(r)); else (l) = ((int)(r))
947
948 void
949 OGLTR_DrawGlyphList(JNIEnv *env, OGLContext *oglc, OGLSDOps *dstOps,
950 jint totalGlyphs, jboolean usePositions,
951 jboolean subPixPos, jboolean rgbOrder, jint lcdContrast,
952 jfloat glyphListOrigX, jfloat glyphListOrigY,
953 unsigned char *images, unsigned char *positions)
954 {
955 int glyphCounter;
956
957 J2dTraceLn(J2D_TRACE_INFO, "OGLTR_DrawGlyphList");
958
959 RETURN_IF_NULL(oglc);
960 RETURN_IF_NULL(dstOps);
961 RETURN_IF_NULL(images);
962 if (usePositions) {
963 RETURN_IF_NULL(positions);
964 }
965
966 glyphMode = MODE_NOT_INITED;
967 isCachedDestValid = JNI_FALSE;
968
969 for (glyphCounter = 0; glyphCounter < totalGlyphs; glyphCounter++) {
970 jint x, y;
971 jfloat glyphx, glyphy;
972 jboolean grayscale, ok;
973 GlyphInfo *ginfo = (GlyphInfo *)jlong_to_ptr(NEXT_LONG(images));
974
975 if (ginfo == NULL) {
976 // this shouldn't happen, but if it does we'll just break out...
977 J2dRlsTraceLn(J2D_TRACE_ERROR,
978 "OGLTR_DrawGlyphList: glyph info is null");
979 break;
980 }
981
982 grayscale = (ginfo->rowBytes == ginfo->width);
983
984 if (usePositions) {
985 jfloat posx = NEXT_FLOAT(positions);
986 jfloat posy = NEXT_FLOAT(positions);
987 glyphx = glyphListOrigX + posx + ginfo->topLeftX;
988 glyphy = glyphListOrigY + posy + ginfo->topLeftY;
989 FLOOR_ASSIGN(x, glyphx);
990 FLOOR_ASSIGN(y, glyphy);
991 } else {
992 glyphx = glyphListOrigX + ginfo->topLeftX;
993 glyphy = glyphListOrigY + ginfo->topLeftY;
994 FLOOR_ASSIGN(x, glyphx);
995 FLOOR_ASSIGN(y, glyphy);
996 glyphListOrigX += ginfo->advanceX;
997 glyphListOrigY += ginfo->advanceY;
998 }
999
1000 if (ginfo->image == NULL) {
1001 continue;
1002 }
1003
1004 if (grayscale) {
1005 // grayscale or monochrome glyph data
1006 if (cacheStatus != CACHE_LCD &&
1007 ginfo->width <= OGLTR_CACHE_CELL_WIDTH &&
1008 ginfo->height <= OGLTR_CACHE_CELL_HEIGHT)
1009 {
1010 ok = OGLTR_DrawGrayscaleGlyphViaCache(oglc, ginfo, x, y);
1011 } else {
1012 ok = OGLTR_DrawGrayscaleGlyphNoCache(oglc, ginfo, x, y);
1013 }
1014 } else {
1015 // LCD-optimized glyph data
1016 jint rowBytesOffset = 0;
1017
1018 if (subPixPos) {
1019 jint frac = (jint)((glyphx - x) * 3);
1020 if (frac != 0) {
1021 rowBytesOffset = 3 - frac;
1022 x += 1;
1023 }
1024 }
1025
1026 if (rowBytesOffset == 0 &&
1027 cacheStatus != CACHE_GRAY &&
1028 ginfo->width <= OGLTR_CACHE_CELL_WIDTH &&
1029 ginfo->height <= OGLTR_CACHE_CELL_HEIGHT)
1030 {
1031 ok = OGLTR_DrawLCDGlyphViaCache(oglc, dstOps,
1032 ginfo, x, y,
1033 glyphCounter, totalGlyphs,
1034 rgbOrder, lcdContrast);
1035 } else {
1036 ok = OGLTR_DrawLCDGlyphNoCache(oglc, dstOps,
1037 ginfo, x, y,
1038 rowBytesOffset,
1039 rgbOrder, lcdContrast);
1040 }
1041 }
1042
1043 if (!ok) {
1044 break;
1045 }
1046 }
1047
1048 OGLTR_DisableGlyphModeState();
1049 }
1050
1051 JNIEXPORT void JNICALL
1052 Java_sun_java2d_opengl_OGLTextRenderer_drawGlyphList
1053 (JNIEnv *env, jobject self,
1054 jint numGlyphs, jboolean usePositions,
1055 jboolean subPixPos, jboolean rgbOrder, jint lcdContrast,
1056 jfloat glyphListOrigX, jfloat glyphListOrigY,
1057 jlongArray imgArray, jfloatArray posArray)
1058 {
1059 unsigned char *images;
|
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.
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
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 *
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;
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);
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
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
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;
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;
|