Path: blob/master/src/java.desktop/share/native/common/java2d/opengl/OGLTextRenderer.c
41159 views
/*1* Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425#ifndef HEADLESS2627#include <stdlib.h>28#include <math.h>29#include <jlong.h>3031#include "sun_java2d_opengl_OGLTextRenderer.h"3233#include "SurfaceData.h"34#include "OGLContext.h"35#include "OGLSurfaceData.h"36#include "OGLRenderQueue.h"37#include "OGLTextRenderer.h"38#include "OGLVertexCache.h"39#include "AccelGlyphCache.h"40#include "fontscalerdefs.h"4142/**43* The following constants define the inner and outer bounds of the44* accelerated glyph cache.45*/46#define OGLTR_CACHE_WIDTH 51247#define OGLTR_CACHE_HEIGHT 51248#define OGLTR_CACHE_CELL_WIDTH 3249#define OGLTR_CACHE_CELL_HEIGHT 325051/**52* The current "glyph mode" state. This variable is used to track the53* codepath used to render a particular glyph. This variable is reset to54* MODE_NOT_INITED at the beginning of every call to OGLTR_DrawGlyphList().55* As each glyph is rendered, the glyphMode variable is updated to reflect56* the current mode, so if the current mode is the same as the mode used57* to render the previous glyph, we can avoid doing costly setup operations58* each time.59*/60typedef enum {61MODE_NOT_INITED,62MODE_USE_CACHE_GRAY,63MODE_USE_CACHE_LCD,64MODE_NO_CACHE_GRAY,65MODE_NO_CACHE_LCD,66MODE_NO_CACHE_COLOR67} GlyphMode;68static GlyphMode glyphMode = MODE_NOT_INITED;6970/**71* There are two separate glyph caches: for AA and for LCD.72* Once one of them is initialized as either GRAY or LCD, it73* stays in that mode for the duration of the application. It should74* be safe to use this one glyph cache for all screens in a multimon75* environment, since the glyph cache texture is shared between all contexts,76* and (in theory) OpenGL drivers should be smart enough to manage that77* texture across all screens.78*/7980static GlyphCacheInfo *glyphCacheLCD = NULL;81static GlyphCacheInfo *glyphCacheAA = NULL;8283/**84* The handle to the LCD text fragment program object.85*/86static GLhandleARB lcdTextProgram = 0;8788/**89* This value tracks the previous LCD contrast setting, so if the contrast90* value hasn't changed since the last time the gamma uniforms were91* updated (not very common), then we can skip updating the unforms.92*/93static jint lastLCDContrast = -1;9495/**96* This value tracks the previous LCD rgbOrder setting, so if the rgbOrder97* value has changed since the last time, it indicates that we need to98* invalidate the cache, which may already store glyph images in the reverse99* order. Note that in most real world applications this value will not100* change over the course of the application, but tests like Font2DTest101* allow for changing the ordering at runtime, so we need to handle that case.102*/103static jboolean lastRGBOrder = JNI_TRUE;104105/**106* This constant defines the size of the tile to use in the107* OGLTR_DrawLCDGlyphNoCache() method. See below for more on why we108* restrict this value to a particular size.109*/110#define OGLTR_NOCACHE_TILE_SIZE 32111112/**113* These constants define the size of the "cached destination" texture.114* This texture is only used when rendering LCD-optimized text, as that115* codepath needs direct access to the destination. There is no way to116* access the framebuffer directly from an OpenGL shader, so we need to first117* copy the destination region corresponding to a particular glyph into118* this cached texture, and then that texture will be accessed inside the119* shader. Copying the destination into this cached texture can be a very120* expensive operation (accounting for about half the rendering time for121* LCD text), so to mitigate this cost we try to bulk read a horizontal122* region of the destination at a time. (These values are empirically123* 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 512132#define OGLTR_CACHED_DEST_HEIGHT (OGLTR_CACHE_CELL_HEIGHT * 2)133134/**135* The handle to the "cached destination" texture object.136*/137static GLuint cachedDestTextureID = 0;138139/**140* The current bounds of the "cached destination" texture, in destination141* coordinate space. The width/height of these bounds will not exceed the142* OGLTR_CACHED_DEST_WIDTH/HEIGHT values defined above. These bounds are143* only considered valid when the isCachedDestValid flag is JNI_TRUE.144*/145static SurfaceDataBounds cachedDestBounds;146147/**148* This flag indicates whether the "cached destination" texture contains149* valid data. This flag is reset to JNI_FALSE at the beginning of every150* call to OGLTR_DrawGlyphList(). Once we copy valid destination data151* into the cached texture, this flag is set to JNI_TRUE. This way, we can152* limit the number of times we need to copy destination data, which is a153* very costly operation.154*/155static jboolean isCachedDestValid = JNI_FALSE;156157/**158* The bounds of the previously rendered LCD glyph, in destination159* coordinate space. We use these bounds to determine whether the glyph160* currently being rendered overlaps the previously rendered glyph (i.e.161* its bounding box intersects that of the previously rendered glyph). If162* so, we need to re-read the destination area associated with that previous163* glyph so that we can correctly blend with the actual destination data.164*/165static SurfaceDataBounds previousGlyphBounds;166167/**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 images171* as intensity values (which work well with the GL_MODULATE function).172*/173static jboolean174OGLTR_InitGlyphCache(jboolean lcdCache)175{176GlyphCacheInfo *gcinfo;177GLclampf priority = 1.0f;178GLenum internalFormat = lcdCache ? GL_RGB8 : GL_INTENSITY8;179GLenum pixelFormat = lcdCache ? GL_RGB : GL_LUMINANCE;180181J2dTraceLn(J2D_TRACE_INFO, "OGLTR_InitGlyphCache");182183// init glyph cache data structure184gcinfo = AccelGlyphCache_Init(OGLTR_CACHE_WIDTH,185OGLTR_CACHE_HEIGHT,186OGLTR_CACHE_CELL_WIDTH,187OGLTR_CACHE_CELL_HEIGHT,188OGLVertexCache_FlushVertexCache);189if (gcinfo == NULL) {190J2dRlsTraceLn(J2D_TRACE_ERROR,191"OGLTR_InitGlyphCache: could not init OGL glyph cache");192return JNI_FALSE;193}194195// init cache texture object196j2d_glGenTextures(1, &gcinfo->cacheID);197j2d_glBindTexture(GL_TEXTURE_2D, gcinfo->cacheID);198j2d_glPrioritizeTextures(1, &gcinfo->cacheID, &priority);199j2d_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);200j2d_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);201202j2d_glTexImage2D(GL_TEXTURE_2D, 0, internalFormat,203OGLTR_CACHE_WIDTH, OGLTR_CACHE_HEIGHT, 0,204pixelFormat, GL_UNSIGNED_BYTE, NULL);205206if (lcdCache) {207glyphCacheLCD = gcinfo;208} else {209glyphCacheAA = gcinfo;210}211212return JNI_TRUE;213}214215/**216* Adds the given glyph to the glyph cache (texture and data structure)217* associated with the given OGLContext.218*/219static void220OGLTR_AddToGlyphCache(GlyphInfo *glyph, GLenum pixelFormat)221{222CacheCellInfo *ccinfo;223GlyphCacheInfo *gcinfo;224225J2dTraceLn(J2D_TRACE_INFO, "OGLTR_AddToGlyphCache");226227if (pixelFormat == GL_LUMINANCE) {228gcinfo = glyphCacheAA;229} else {230gcinfo = glyphCacheLCD;231}232233if ((gcinfo == NULL) || (glyph->image == NULL)) {234return;235}236237AccelGlyphCache_AddGlyph(gcinfo, glyph);238ccinfo = (CacheCellInfo *) glyph->cellInfo;239240if (ccinfo != NULL) {241// store glyph image in texture cell242j2d_glTexSubImage2D(GL_TEXTURE_2D, 0,243ccinfo->x, ccinfo->y,244glyph->width, glyph->height,245pixelFormat, GL_UNSIGNED_BYTE, glyph->image);246}247}248249/**250* This is the GLSL fragment shader source code for rendering LCD-optimized251* text. Do not be frightened; it is much easier to understand than the252* equivalent ASM-like fragment program!253*254* The "uniform" variables at the top are initialized once the program is255* linked, and are updated at runtime as needed (e.g. when the source color256* changes, we will modify the "src_adj" value in OGLTR_UpdateLCDTextColor()).257*258* The "main" function is executed for each "fragment" (or pixel) in the259* glyph image. The pow() routine operates on vectors, gives precise results,260* and provides acceptable level of performance, so we use it to perform261* 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*/276static 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 texcoord286" vec3 glyph_clr = vec3(texture2D(glyph_tex, gl_TexCoord[0].st));"287" if (glyph_clr == vec3(0.0)) {"288// zero coverage, so skip this fragment289" discard;"290" }"291// load the RGB value from the corresponding destination pixel292" vec3 dst_clr = vec3(texture2D(dst_tex, gl_TexCoord[1].st));"293// gamma adjust the dest color294" vec3 dst_adj = pow(dst_clr.rgb, gamma);"295// linearly interpolate the three color values296" 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"}";300301/**302* Compiles and links the LCD text shader program. If successful, this303* function returns a handle to the newly created shader program; otherwise304* returns 0.305*/306static GLhandleARB307OGLTR_CreateLCDTextProgram()308{309GLhandleARB lcdTextProgram;310GLint loc;311312J2dTraceLn(J2D_TRACE_INFO, "OGLTR_CreateLCDTextProgram");313314lcdTextProgram = OGLContext_CreateFragmentProgram(lcdTextShaderSource);315if (lcdTextProgram == 0) {316J2dRlsTraceLn(J2D_TRACE_ERROR,317"OGLTR_CreateLCDTextProgram: error creating program");318return 0;319}320321// "use" the program object temporarily so that we can set the uniforms322j2d_glUseProgramObjectARB(lcdTextProgram);323324// set the "uniform" values325loc = j2d_glGetUniformLocationARB(lcdTextProgram, "glyph_tex");326j2d_glUniform1iARB(loc, 0); // texture unit 0327loc = j2d_glGetUniformLocationARB(lcdTextProgram, "dst_tex");328j2d_glUniform1iARB(loc, 1); // texture unit 1329330// "unuse" the program object; it will be re-bound later as needed331j2d_glUseProgramObjectARB(0);332333return lcdTextProgram;334}335336/**337* (Re)Initializes the gamma related uniforms.338*339* The given contrast value is an int in the range [100, 250] which we will340* then scale to fit in the range [1.0, 2.5].341*/342static jboolean343OGLTR_UpdateLCDTextContrast(jint contrast)344{345double g = ((double)contrast) / 100.0;346double ig = 1.0 / g;347GLint loc;348349J2dTraceLn1(J2D_TRACE_INFO,350"OGLTR_UpdateLCDTextContrast: contrast=%d", contrast);351352loc = j2d_glGetUniformLocationARB(lcdTextProgram, "gamma");353j2d_glUniform3fARB(loc, g, g, g);354355loc = j2d_glGetUniformLocationARB(lcdTextProgram, "invgamma");356j2d_glUniform3fARB(loc, ig, ig, ig);357358return JNI_TRUE;359}360361/**362* Updates the current gamma-adjusted source color ("src_adj") of the LCD363* text shader program. Note that we could calculate this value in the364* shader (e.g. just as we do for "dst_adj"), but would be unnecessary work365* (and a measurable performance hit, maybe around 5%) since this value is366* constant over the entire glyph list. So instead we just calculate the367* gamma-adjusted value once and update the uniform parameter of the LCD368* shader as needed.369*/370static jboolean371OGLTR_UpdateLCDTextColor(jint contrast)372{373double gamma = ((double)contrast) / 100.0;374GLfloat radj, gadj, badj;375GLfloat clr[4];376GLint loc;377378J2dTraceLn1(J2D_TRACE_INFO,379"OGLTR_UpdateLCDTextColor: contrast=%d", contrast);380381/*382* Note: Ideally we would update the "src_adj" uniform parameter only383* when there is a change in the source color. Fortunately, the cost384* of querying the current OpenGL color state and updating the uniform385* value is quite small, and in the common case we only need to do this386* once per GlyphList, so we gain little from trying to optimize too387* eagerly here.388*/389390// get the current OpenGL primary color state391j2d_glGetFloatv(GL_CURRENT_COLOR, clr);392393// gamma adjust the primary color394radj = (GLfloat)pow(clr[0], gamma);395gadj = (GLfloat)pow(clr[1], gamma);396badj = (GLfloat)pow(clr[2], gamma);397398// update the "src_adj" parameter of the shader program with this value399loc = j2d_glGetUniformLocationARB(lcdTextProgram, "src_adj");400j2d_glUniform3fARB(loc, radj, gadj, badj);401402return JNI_TRUE;403}404405/**406* Enables the LCD text shader and updates any related state, such as the407* gamma lookup table textures.408*/409static jboolean410OGLTR_EnableLCDGlyphModeState(GLuint glyphTextureID,411GLuint dstTextureID,412jint contrast)413{414// bind the texture containing glyph data to texture unit 0415j2d_glActiveTextureARB(GL_TEXTURE0_ARB);416j2d_glBindTexture(GL_TEXTURE_2D, glyphTextureID);417j2d_glEnable(GL_TEXTURE_2D);418419// bind the texture tile containing destination data to texture unit 1420j2d_glActiveTextureARB(GL_TEXTURE1_ARB);421if (dstTextureID != 0) {422j2d_glBindTexture(GL_TEXTURE_2D, dstTextureID);423} else {424if (cachedDestTextureID == 0) {425cachedDestTextureID =426OGLContext_CreateBlitTexture(GL_RGB8, GL_RGB,427OGLTR_CACHED_DEST_WIDTH,428OGLTR_CACHED_DEST_HEIGHT);429if (cachedDestTextureID == 0) {430return JNI_FALSE;431}432}433j2d_glBindTexture(GL_TEXTURE_2D, cachedDestTextureID);434}435436// note that GL_TEXTURE_2D was already enabled for texture unit 0,437// but we need to explicitly enable it for texture unit 1438j2d_glEnable(GL_TEXTURE_2D);439440// create the LCD text shader, if necessary441if (lcdTextProgram == 0) {442lcdTextProgram = OGLTR_CreateLCDTextProgram();443if (lcdTextProgram == 0) {444return JNI_FALSE;445}446}447448// enable the LCD text shader449j2d_glUseProgramObjectARB(lcdTextProgram);450451// update the current contrast settings, if necessary452if (lastLCDContrast != contrast) {453if (!OGLTR_UpdateLCDTextContrast(contrast)) {454return JNI_FALSE;455}456lastLCDContrast = contrast;457}458459// update the current color settings460if (!OGLTR_UpdateLCDTextColor(contrast)) {461return JNI_FALSE;462}463464return JNI_TRUE;465}466467void468OGLTR_EnableGlyphVertexCache(OGLContext *oglc)469{470J2dTraceLn(J2D_TRACE_INFO, "OGLTR_EnableGlyphVertexCache");471472if (!OGLVertexCache_InitVertexCache(oglc)) {473return;474}475476if (glyphCacheAA == NULL) {477if (!OGLTR_InitGlyphCache(JNI_FALSE)) {478return;479}480}481482j2d_glEnable(GL_TEXTURE_2D);483j2d_glBindTexture(GL_TEXTURE_2D, glyphCacheAA->cacheID);484j2d_glPixelStorei(GL_UNPACK_ALIGNMENT, 1);485486// for grayscale/monochrome text, the current OpenGL source color487// is modulated with the glyph image as part of the texture488// application stage, so we use GL_MODULATE here489OGLC_UPDATE_TEXTURE_FUNCTION(oglc, GL_MODULATE);490}491492void493OGLTR_DisableGlyphVertexCache(OGLContext *oglc)494{495J2dTraceLn(J2D_TRACE_INFO, "OGLTR_DisableGlyphVertexCache");496497OGLVertexCache_FlushVertexCache();498OGLVertexCache_RestoreColorState(oglc);499500j2d_glDisable(GL_TEXTURE_2D);501j2d_glPixelStorei(GL_UNPACK_ALIGNMENT, 4);502j2d_glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);503j2d_glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);504j2d_glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);505}506507/**508* Disables any pending state associated with the current "glyph mode".509*/510static void511OGLTR_DisableGlyphModeState()512{513switch (glyphMode) {514case MODE_NO_CACHE_LCD:515j2d_glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);516j2d_glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);517/* FALLTHROUGH */518519case MODE_USE_CACHE_LCD:520j2d_glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);521j2d_glPixelStorei(GL_UNPACK_ALIGNMENT, 4);522j2d_glUseProgramObjectARB(0);523j2d_glActiveTextureARB(GL_TEXTURE1_ARB);524j2d_glDisable(GL_TEXTURE_2D);525j2d_glActiveTextureARB(GL_TEXTURE0_ARB);526j2d_glDisable(GL_TEXTURE_2D);527break;528529case MODE_NO_CACHE_COLOR:530case MODE_NO_CACHE_GRAY:531case MODE_USE_CACHE_GRAY:532case MODE_NOT_INITED:533default:534break;535}536}537538static jboolean539OGLTR_DrawGrayscaleGlyphViaCache(OGLContext *oglc,540GlyphInfo *ginfo, jint x, jint y)541{542CacheCellInfo *cell;543jfloat x1, y1, x2, y2;544545if (glyphMode != MODE_USE_CACHE_GRAY) {546OGLTR_DisableGlyphModeState();547CHECK_PREVIOUS_OP(OGL_STATE_GLYPH_OP);548glyphMode = MODE_USE_CACHE_GRAY;549}550551if (ginfo->cellInfo == NULL) {552// attempt to add glyph to accelerated glyph cache553OGLTR_AddToGlyphCache(ginfo, GL_LUMINANCE);554555if (ginfo->cellInfo == NULL) {556// we'll just no-op in the rare case that the cell is NULL557return JNI_TRUE;558}559}560561cell = (CacheCellInfo *) (ginfo->cellInfo);562cell->timesRendered++;563564x1 = (jfloat)x;565y1 = (jfloat)y;566x2 = x1 + ginfo->width;567y2 = y1 + ginfo->height;568569OGLVertexCache_AddGlyphQuad(oglc,570cell->tx1, cell->ty1,571cell->tx2, cell->ty2,572x1, y1, x2, y2);573574return JNI_TRUE;575}576577/**578* Evaluates to true if the rectangle defined by gx1/gy1/gx2/gy2 is579* inside outerBounds.580*/581#define INSIDE(gx1, gy1, gx2, gy2, outerBounds) \582(((gx1) >= outerBounds.x1) && ((gy1) >= outerBounds.y1) && \583((gx2) <= outerBounds.x2) && ((gy2) <= outerBounds.y2))584585/**586* Evaluates to true if the rectangle defined by gx1/gy1/gx2/gy2 intersects587* the rectangle defined by bounds.588*/589#define INTERSECTS(gx1, gy1, gx2, gy2, bounds) \590((bounds.x2 > (gx1)) && (bounds.y2 > (gy1)) && \591(bounds.x1 < (gx2)) && (bounds.y1 < (gy2)))592593/**594* This method checks to see if the given LCD glyph bounds fall within the595* cached destination texture bounds. If so, this method can return596* immediately. If not, this method will copy a chunk of framebuffer data597* into the cached destination texture and then update the current cached598* destination bounds before returning.599*/600static void601OGLTR_UpdateCachedDestination(OGLSDOps *dstOps, GlyphInfo *ginfo,602jint gx1, jint gy1, jint gx2, jint gy2,603jint glyphIndex, jint totalGlyphs)604{605jint dx1, dy1, dx2, dy2;606jint dx1adj, dy1adj;607608if (isCachedDestValid && INSIDE(gx1, gy1, gx2, gy2, cachedDestBounds)) {609// glyph is already within the cached destination bounds; no need610// to read back the entire destination region again, but we do611// need to see if the current glyph overlaps the previous glyph...612613if (INTERSECTS(gx1, gy1, gx2, gy2, previousGlyphBounds)) {614// the current glyph overlaps the destination region touched615// by the previous glyph, so now we need to read back the part616// of the destination corresponding to the previous glyph617dx1 = previousGlyphBounds.x1;618dy1 = previousGlyphBounds.y1;619dx2 = previousGlyphBounds.x2;620dy2 = previousGlyphBounds.y2;621622// this accounts for lower-left origin of the destination region623dx1adj = dstOps->xOffset + dx1;624dy1adj = dstOps->yOffset + dstOps->height - dy2;625626// copy destination into subregion of cached texture tile:627// dx1-cachedDestBounds.x1 == +xoffset from left side of texture628// cachedDestBounds.y2-dy2 == +yoffset from bottom of texture629j2d_glActiveTextureARB(GL_TEXTURE1_ARB);630j2d_glCopyTexSubImage2D(GL_TEXTURE_2D, 0,631dx1 - cachedDestBounds.x1,632cachedDestBounds.y2 - dy2,633dx1adj, dy1adj,634dx2-dx1, dy2-dy1);635}636} else {637jint remainingWidth;638639// destination region is not valid, so we need to read back a640// chunk of the destination into our cached texture641642// position the upper-left corner of the destination region on the643// "top" line of glyph list644// REMIND: this isn't ideal; it would be better if we had some idea645// of the bounding box of the whole glyph list (this is646// do-able, but would require iterating through the whole647// list up front, which may present its own problems)648dx1 = gx1;649dy1 = gy1;650651if (ginfo->advanceX > 0) {652// estimate the width based on our current position in the glyph653// list and using the x advance of the current glyph (this is just654// a quick and dirty heuristic; if this is a "thin" glyph image,655// then we're likely to underestimate, and if it's "thick" then we656// may end up reading back more than we need to)657remainingWidth =658(jint)(ginfo->advanceX * (totalGlyphs - glyphIndex));659if (remainingWidth > OGLTR_CACHED_DEST_WIDTH) {660remainingWidth = OGLTR_CACHED_DEST_WIDTH;661} else if (remainingWidth < ginfo->width) {662// in some cases, the x-advance may be slightly smaller663// than the actual width of the glyph; if so, adjust our664// estimate so that we can accommodate the entire glyph665remainingWidth = ginfo->width;666}667} else {668// a negative advance is possible when rendering rotated text,669// in which case it is difficult to estimate an appropriate670// region for readback, so we will pick a region that671// encompasses just the current glyph672remainingWidth = ginfo->width;673}674dx2 = dx1 + remainingWidth;675676// estimate the height (this is another sloppy heuristic; we'll677// make the cached destination region tall enough to encompass most678// glyphs that are small enough to fit in the glyph cache, and then679// we add a little something extra to account for descenders680dy2 = dy1 + OGLTR_CACHE_CELL_HEIGHT + 2;681682// this accounts for lower-left origin of the destination region683dx1adj = dstOps->xOffset + dx1;684dy1adj = dstOps->yOffset + dstOps->height - dy2;685686// copy destination into cached texture tile (the lower-left corner687// of the destination region will be positioned at the lower-left688// corner (0,0) of the texture)689j2d_glActiveTextureARB(GL_TEXTURE1_ARB);690j2d_glCopyTexSubImage2D(GL_TEXTURE_2D, 0,6910, 0, dx1adj, dy1adj,692dx2-dx1, dy2-dy1);693694// update the cached bounds and mark it valid695cachedDestBounds.x1 = dx1;696cachedDestBounds.y1 = dy1;697cachedDestBounds.x2 = dx2;698cachedDestBounds.y2 = dy2;699isCachedDestValid = JNI_TRUE;700}701702// always update the previous glyph bounds703previousGlyphBounds.x1 = gx1;704previousGlyphBounds.y1 = gy1;705previousGlyphBounds.x2 = gx2;706previousGlyphBounds.y2 = gy2;707}708709static jboolean710OGLTR_DrawLCDGlyphViaCache(OGLContext *oglc, OGLSDOps *dstOps,711GlyphInfo *ginfo, jint x, jint y,712jint glyphIndex, jint totalGlyphs,713jboolean rgbOrder, jint contrast,714GLuint dstTextureID)715{716CacheCellInfo *cell;717jint dx1, dy1, dx2, dy2;718jfloat dtx1, dty1, dtx2, dty2;719720if (glyphMode != MODE_USE_CACHE_LCD) {721OGLTR_DisableGlyphModeState();722CHECK_PREVIOUS_OP(GL_TEXTURE_2D);723j2d_glPixelStorei(GL_UNPACK_ALIGNMENT, 1);724725if (glyphCacheLCD == NULL) {726if (!OGLTR_InitGlyphCache(JNI_TRUE)) {727return JNI_FALSE;728}729}730731if (rgbOrder != lastRGBOrder) {732// need to invalidate the cache in this case; see comments733// for lastRGBOrder above734AccelGlyphCache_Invalidate(glyphCacheLCD);735lastRGBOrder = rgbOrder;736}737738if (!OGLTR_EnableLCDGlyphModeState(glyphCacheLCD->cacheID,739dstTextureID, contrast))740{741return JNI_FALSE;742}743744// when a fragment shader is enabled, the texture function state is745// ignored, so the following line is not needed...746// OGLC_UPDATE_TEXTURE_FUNCTION(oglc, GL_MODULATE);747748glyphMode = MODE_USE_CACHE_LCD;749}750751if (ginfo->cellInfo == NULL) {752// rowBytes will always be a multiple of 3, so the following is safe753j2d_glPixelStorei(GL_UNPACK_ROW_LENGTH, ginfo->rowBytes / 3);754755// make sure the glyph cache texture is bound to texture unit 0756j2d_glActiveTextureARB(GL_TEXTURE0_ARB);757758// attempt to add glyph to accelerated glyph cache759OGLTR_AddToGlyphCache(ginfo, rgbOrder ? GL_RGB : GL_BGR);760761if (ginfo->cellInfo == NULL) {762// we'll just no-op in the rare case that the cell is NULL763return JNI_TRUE;764}765}766767cell = (CacheCellInfo *) (ginfo->cellInfo);768cell->timesRendered++;769770// location of the glyph in the destination's coordinate space771dx1 = x;772dy1 = y;773dx2 = dx1 + ginfo->width;774dy2 = dy1 + ginfo->height;775776if (dstTextureID == 0) {777// copy destination into second cached texture, if necessary778OGLTR_UpdateCachedDestination(dstOps, ginfo,779dx1, dy1, dx2, dy2,780glyphIndex, totalGlyphs);781782// texture coordinates of the destination tile783dtx1 = ((jfloat)(dx1 - cachedDestBounds.x1)) / OGLTR_CACHED_DEST_WIDTH;784dty1 = ((jfloat)(cachedDestBounds.y2 - dy1)) / OGLTR_CACHED_DEST_HEIGHT;785dtx2 = ((jfloat)(dx2 - cachedDestBounds.x1)) / OGLTR_CACHED_DEST_WIDTH;786dty2 = ((jfloat)(cachedDestBounds.y2 - dy2)) / OGLTR_CACHED_DEST_HEIGHT;787} else {788jint gw = ginfo->width;789jint gh = ginfo->height;790791// this accounts for lower-left origin of the destination region792jint dxadj = dstOps->xOffset + x;793jint dyadj = dstOps->yOffset + dstOps->height - (y + gh);794795// update the remaining destination texture coordinates796dtx1 =((GLfloat)dxadj) / dstOps->textureWidth;797dtx2 = ((GLfloat)dxadj + gw) / dstOps->textureWidth;798799dty1 = ((GLfloat)dyadj + gh) / dstOps->textureHeight;800dty2 = ((GLfloat)dyadj) / dstOps->textureHeight;801802j2d_glTextureBarrierNV();803}804805// render composed texture to the destination surface806j2d_glBegin(GL_QUADS);807j2d_glMultiTexCoord2fARB(GL_TEXTURE0_ARB, cell->tx1, cell->ty1);808j2d_glMultiTexCoord2fARB(GL_TEXTURE1_ARB, dtx1, dty1);809j2d_glVertex2i(dx1, dy1);810j2d_glMultiTexCoord2fARB(GL_TEXTURE0_ARB, cell->tx2, cell->ty1);811j2d_glMultiTexCoord2fARB(GL_TEXTURE1_ARB, dtx2, dty1);812j2d_glVertex2i(dx2, dy1);813j2d_glMultiTexCoord2fARB(GL_TEXTURE0_ARB, cell->tx2, cell->ty2);814j2d_glMultiTexCoord2fARB(GL_TEXTURE1_ARB, dtx2, dty2);815j2d_glVertex2i(dx2, dy2);816j2d_glMultiTexCoord2fARB(GL_TEXTURE0_ARB, cell->tx1, cell->ty2);817j2d_glMultiTexCoord2fARB(GL_TEXTURE1_ARB, dtx1, dty2);818j2d_glVertex2i(dx1, dy2);819j2d_glEnd();820821return JNI_TRUE;822}823824static jboolean825OGLTR_DrawGrayscaleGlyphNoCache(OGLContext *oglc,826GlyphInfo *ginfo, jint x, jint y)827{828jint tw, th;829jint sx, sy, sw, sh;830jint x0;831jint w = ginfo->width;832jint h = ginfo->height;833834if (glyphMode != MODE_NO_CACHE_GRAY) {835OGLTR_DisableGlyphModeState();836CHECK_PREVIOUS_OP(OGL_STATE_MASK_OP);837glyphMode = MODE_NO_CACHE_GRAY;838}839840x0 = x;841tw = OGLVC_MASK_CACHE_TILE_WIDTH;842th = OGLVC_MASK_CACHE_TILE_HEIGHT;843844for (sy = 0; sy < h; sy += th, y += th) {845x = x0;846sh = ((sy + th) > h) ? (h - sy) : th;847848for (sx = 0; sx < w; sx += tw, x += tw) {849sw = ((sx + tw) > w) ? (w - sx) : tw;850851OGLVertexCache_AddMaskQuad(oglc,852sx, sy, x, y, sw, sh,853w, ginfo->image);854}855}856857return JNI_TRUE;858}859860static jboolean861OGLTR_DrawLCDGlyphNoCache(OGLContext *oglc, OGLSDOps *dstOps,862GlyphInfo *ginfo, jint x, jint y,863jint rowBytesOffset,864jboolean rgbOrder, jint contrast,865GLuint dstTextureID)866{867GLfloat tx1, ty1, tx2, ty2;868GLfloat dtx1, dty1, dtx2, dty2;869jint tw, th;870jint sx, sy, sw, sh, dxadj, dyadj;871jint x0;872jint w = ginfo->width;873jint h = ginfo->height;874GLenum pixelFormat = rgbOrder ? GL_RGB : GL_BGR;875876if (glyphMode != MODE_NO_CACHE_LCD) {877OGLTR_DisableGlyphModeState();878CHECK_PREVIOUS_OP(GL_TEXTURE_2D);879j2d_glPixelStorei(GL_UNPACK_ALIGNMENT, 1);880881if (oglc->blitTextureID == 0) {882if (!OGLContext_InitBlitTileTexture(oglc)) {883return JNI_FALSE;884}885}886887if (!OGLTR_EnableLCDGlyphModeState(oglc->blitTextureID,888dstTextureID, contrast))889{890return JNI_FALSE;891}892893// when a fragment shader is enabled, the texture function state is894// ignored, so the following line is not needed...895// OGLC_UPDATE_TEXTURE_FUNCTION(oglc, GL_MODULATE);896897glyphMode = MODE_NO_CACHE_LCD;898}899900// rowBytes will always be a multiple of 3, so the following is safe901j2d_glPixelStorei(GL_UNPACK_ROW_LENGTH, ginfo->rowBytes / 3);902903x0 = x;904tx1 = 0.0f;905ty1 = 0.0f;906dtx1 = 0.0f;907dty2 = 0.0f;908tw = OGLTR_NOCACHE_TILE_SIZE;909th = OGLTR_NOCACHE_TILE_SIZE;910911for (sy = 0; sy < h; sy += th, y += th) {912x = x0;913sh = ((sy + th) > h) ? (h - sy) : th;914915for (sx = 0; sx < w; sx += tw, x += tw) {916sw = ((sx + tw) > w) ? (w - sx) : tw;917918// update the source pointer offsets919j2d_glPixelStorei(GL_UNPACK_SKIP_PIXELS, sx);920j2d_glPixelStorei(GL_UNPACK_SKIP_ROWS, sy);921922// copy LCD mask into glyph texture tile923j2d_glActiveTextureARB(GL_TEXTURE0_ARB);924j2d_glTexSubImage2D(GL_TEXTURE_2D, 0,9250, 0, sw, sh,926pixelFormat, GL_UNSIGNED_BYTE,927ginfo->image + rowBytesOffset);928929// update the lower-right glyph texture coordinates930tx2 = ((GLfloat)sw) / OGLC_BLIT_TILE_SIZE;931ty2 = ((GLfloat)sh) / OGLC_BLIT_TILE_SIZE;932933// this accounts for lower-left origin of the destination region934dxadj = dstOps->xOffset + x;935dyadj = dstOps->yOffset + dstOps->height - (y + sh);936937if (dstTextureID == 0) {938// copy destination into cached texture tile (the lower-left939// corner of the destination region will be positioned at the940// lower-left corner (0,0) of the texture)941j2d_glActiveTextureARB(GL_TEXTURE1_ARB);942j2d_glCopyTexSubImage2D(GL_TEXTURE_2D, 0,9430, 0,944dxadj, dyadj,945sw, sh);946// update the remaining destination texture coordinates947dtx2 = ((GLfloat)sw) / OGLTR_CACHED_DEST_WIDTH;948dty1 = ((GLfloat)sh) / OGLTR_CACHED_DEST_HEIGHT;949} else {950// use the destination texture directly951// update the remaining destination texture coordinates952dtx1 =((GLfloat)dxadj) / dstOps->textureWidth;953dtx2 = ((GLfloat)dxadj + sw) / dstOps->textureWidth;954955dty1 = ((GLfloat)dyadj + sh) / dstOps->textureHeight;956dty2 = ((GLfloat)dyadj) / dstOps->textureHeight;957958j2d_glTextureBarrierNV();959}960961// render composed texture to the destination surface962j2d_glBegin(GL_QUADS);963j2d_glMultiTexCoord2fARB(GL_TEXTURE0_ARB, tx1, ty1);964j2d_glMultiTexCoord2fARB(GL_TEXTURE1_ARB, dtx1, dty1);965j2d_glVertex2i(x, y);966j2d_glMultiTexCoord2fARB(GL_TEXTURE0_ARB, tx2, ty1);967j2d_glMultiTexCoord2fARB(GL_TEXTURE1_ARB, dtx2, dty1);968j2d_glVertex2i(x + sw, y);969j2d_glMultiTexCoord2fARB(GL_TEXTURE0_ARB, tx2, ty2);970j2d_glMultiTexCoord2fARB(GL_TEXTURE1_ARB, dtx2, dty2);971j2d_glVertex2i(x + sw, y + sh);972j2d_glMultiTexCoord2fARB(GL_TEXTURE0_ARB, tx1, ty2);973j2d_glMultiTexCoord2fARB(GL_TEXTURE1_ARB, dtx1, dty2);974j2d_glVertex2i(x, y + sh);975j2d_glEnd();976}977}978979return JNI_TRUE;980}981982static jboolean983OGLTR_DrawColorGlyphNoCache(OGLContext *oglc, GlyphInfo *ginfo, jint x, jint y)984{985if (glyphMode != MODE_NO_CACHE_COLOR) {986OGLTR_DisableGlyphModeState();987RESET_PREVIOUS_OP();988glyphMode = MODE_NO_CACHE_COLOR;989}990991// see OGLBlitSwToSurface() in OGLBlitLoops.c992// for more info on the following two lines993j2d_glRasterPos2i(0, 0);994j2d_glBitmap(0, 0, 0, 0, (GLfloat) x, (GLfloat) (-y), NULL);995996// in OpenGL image data is assumed to contain lines from bottom to top997j2d_glPixelZoom(1, -1);998999j2d_glDrawPixels(ginfo->width, ginfo->height, GL_BGRA, GL_UNSIGNED_BYTE,1000ginfo->image);10011002// restoring state1003j2d_glPixelZoom(1, 1);10041005return JNI_TRUE;1006}100710081009// see DrawGlyphList.c for more on this macro...1010#define FLOOR_ASSIGN(l, r) \1011if ((r)<0) (l) = ((int)floor(r)); else (l) = ((int)(r))10121013void1014OGLTR_DrawGlyphList(JNIEnv *env, OGLContext *oglc, OGLSDOps *dstOps,1015jint totalGlyphs, jboolean usePositions,1016jboolean subPixPos, jboolean rgbOrder, jint lcdContrast,1017jfloat glyphListOrigX, jfloat glyphListOrigY,1018unsigned char *images, unsigned char *positions)1019{1020int glyphCounter;1021GLuint dstTextureID = 0;10221023J2dTraceLn(J2D_TRACE_INFO, "OGLTR_DrawGlyphList");10241025RETURN_IF_NULL(oglc);1026RETURN_IF_NULL(dstOps);1027RETURN_IF_NULL(images);1028if (usePositions) {1029RETURN_IF_NULL(positions);1030}10311032glyphMode = MODE_NOT_INITED;1033isCachedDestValid = JNI_FALSE;10341035// We have to obtain an information about destination content1036// in order to render lcd glyphs. It could be done by copying1037// a part of desitination buffer into an intermediate texture1038// using glCopyTexSubImage2D(). However, on macosx this path is1039// slow, and it dramatically reduces the overall speed of lcd1040// text rendering.1041//1042// In some cases, we can use a texture from the destination1043// surface data in oredr to avoid this slow reading routine.1044// It requires:1045// * An appropriate textureTarget for the destination SD.1046// In particular, we need GL_TEXTURE_2D1047// * Means to prevent read-after-write problem.1048// At the moment, a GL_NV_texture_barrier extension is used1049// to achieve this.1050if (OGLC_IS_CAP_PRESENT(oglc, CAPS_EXT_TEXBARRIER) &&1051dstOps->textureTarget == GL_TEXTURE_2D)1052{1053dstTextureID = dstOps->textureID;1054}10551056for (glyphCounter = 0; glyphCounter < totalGlyphs; glyphCounter++) {1057jint x, y;1058jfloat glyphx, glyphy;1059jboolean ok;1060GlyphInfo *ginfo = (GlyphInfo *)jlong_to_ptr(NEXT_LONG(images));10611062if (ginfo == NULL) {1063// this shouldn't happen, but if it does we'll just break out...1064J2dRlsTraceLn(J2D_TRACE_ERROR,1065"OGLTR_DrawGlyphList: glyph info is null");1066break;1067}10681069if (usePositions) {1070jfloat posx = NEXT_FLOAT(positions);1071jfloat posy = NEXT_FLOAT(positions);1072glyphx = glyphListOrigX + posx + ginfo->topLeftX;1073glyphy = glyphListOrigY + posy + ginfo->topLeftY;1074FLOOR_ASSIGN(x, glyphx);1075FLOOR_ASSIGN(y, glyphy);1076} else {1077glyphx = glyphListOrigX + ginfo->topLeftX;1078glyphy = glyphListOrigY + ginfo->topLeftY;1079FLOOR_ASSIGN(x, glyphx);1080FLOOR_ASSIGN(y, glyphy);1081glyphListOrigX += ginfo->advanceX;1082glyphListOrigY += ginfo->advanceY;1083}10841085if (ginfo->image == NULL) {1086continue;1087}10881089if (ginfo->rowBytes == ginfo->width) {1090// grayscale or monochrome glyph data1091if (ginfo->width <= OGLTR_CACHE_CELL_WIDTH &&1092ginfo->height <= OGLTR_CACHE_CELL_HEIGHT)1093{1094ok = OGLTR_DrawGrayscaleGlyphViaCache(oglc, ginfo, x, y);1095} else {1096ok = OGLTR_DrawGrayscaleGlyphNoCache(oglc, ginfo, x, y);1097}1098} else if (ginfo->rowBytes == ginfo->width * 4) {1099// color glyph data1100ok = OGLTR_DrawColorGlyphNoCache(oglc, ginfo, x, y);1101} else {1102// LCD-optimized glyph data1103jint rowBytesOffset = 0;11041105if (subPixPos) {1106jint frac = (jint)((glyphx - x) * 3);1107if (frac != 0) {1108rowBytesOffset = 3 - frac;1109x += 1;1110}1111}11121113if (rowBytesOffset == 0 &&1114ginfo->width <= OGLTR_CACHE_CELL_WIDTH &&1115ginfo->height <= OGLTR_CACHE_CELL_HEIGHT)1116{1117ok = OGLTR_DrawLCDGlyphViaCache(oglc, dstOps,1118ginfo, x, y,1119glyphCounter, totalGlyphs,1120rgbOrder, lcdContrast,1121dstTextureID);1122} else {1123ok = OGLTR_DrawLCDGlyphNoCache(oglc, dstOps,1124ginfo, x, y,1125rowBytesOffset,1126rgbOrder, lcdContrast,1127dstTextureID);1128}1129}11301131if (!ok) {1132break;1133}1134}11351136OGLTR_DisableGlyphModeState();1137}11381139JNIEXPORT void JNICALL1140Java_sun_java2d_opengl_OGLTextRenderer_drawGlyphList1141(JNIEnv *env, jobject self,1142jint numGlyphs, jboolean usePositions,1143jboolean subPixPos, jboolean rgbOrder, jint lcdContrast,1144jfloat glyphListOrigX, jfloat glyphListOrigY,1145jlongArray imgArray, jfloatArray posArray)1146{1147unsigned char *images;11481149J2dTraceLn(J2D_TRACE_INFO, "OGLTextRenderer_drawGlyphList");11501151images = (unsigned char *)1152(*env)->GetPrimitiveArrayCritical(env, imgArray, NULL);1153if (images != NULL) {1154OGLContext *oglc = OGLRenderQueue_GetCurrentContext();1155OGLSDOps *dstOps = OGLRenderQueue_GetCurrentDestination();11561157if (usePositions) {1158unsigned char *positions = (unsigned char *)1159(*env)->GetPrimitiveArrayCritical(env, posArray, NULL);1160if (positions != NULL) {1161OGLTR_DrawGlyphList(env, oglc, dstOps,1162numGlyphs, usePositions,1163subPixPos, rgbOrder, lcdContrast,1164glyphListOrigX, glyphListOrigY,1165images, positions);1166(*env)->ReleasePrimitiveArrayCritical(env, posArray,1167positions, JNI_ABORT);1168}1169} else {1170OGLTR_DrawGlyphList(env, oglc, dstOps,1171numGlyphs, usePositions,1172subPixPos, rgbOrder, lcdContrast,1173glyphListOrigX, glyphListOrigY,1174images, NULL);1175}11761177// 6358147: reset current state, and ensure rendering is1178// flushed to dest1179if (oglc != NULL) {1180RESET_PREVIOUS_OP();1181j2d_glFlush();1182}11831184(*env)->ReleasePrimitiveArrayCritical(env, imgArray,1185images, JNI_ABORT);1186}1187}11881189#endif /* !HEADLESS */119011911192