Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/native/sun/java2d/opengl/OGLTextRenderer.c
38918 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_LCD66} GlyphMode;67static GlyphMode glyphMode = MODE_NOT_INITED;6869/**70* There are two separate glyph caches: for AA and for LCD.71* Once one of them is initialized as either GRAY or LCD, it72* stays in that mode for the duration of the application. It should73* be safe to use this one glyph cache for all screens in a multimon74* environment, since the glyph cache texture is shared between all contexts,75* and (in theory) OpenGL drivers should be smart enough to manage that76* texture across all screens.77*/7879static GlyphCacheInfo *glyphCacheLCD = NULL;80static GlyphCacheInfo *glyphCacheAA = NULL;8182/**83* The handle to the LCD text fragment program object.84*/85static GLhandleARB lcdTextProgram = 0;8687/**88* This value tracks the previous LCD contrast setting, so if the contrast89* value hasn't changed since the last time the gamma uniforms were90* updated (not very common), then we can skip updating the unforms.91*/92static jint lastLCDContrast = -1;9394/**95* This value tracks the previous LCD rgbOrder setting, so if the rgbOrder96* value has changed since the last time, it indicates that we need to97* invalidate the cache, which may already store glyph images in the reverse98* order. Note that in most real world applications this value will not99* change over the course of the application, but tests like Font2DTest100* allow for changing the ordering at runtime, so we need to handle that case.101*/102static jboolean lastRGBOrder = JNI_TRUE;103104/**105* This constant defines the size of the tile to use in the106* OGLTR_DrawLCDGlyphNoCache() method. See below for more on why we107* restrict this value to a particular size.108*/109#define OGLTR_NOCACHE_TILE_SIZE 32110111/**112* These constants define the size of the "cached destination" texture.113* This texture is only used when rendering LCD-optimized text, as that114* codepath needs direct access to the destination. There is no way to115* access the framebuffer directly from an OpenGL shader, so we need to first116* copy the destination region corresponding to a particular glyph into117* this cached texture, and then that texture will be accessed inside the118* shader. Copying the destination into this cached texture can be a very119* expensive operation (accounting for about half the rendering time for120* LCD text), so to mitigate this cost we try to bulk read a horizontal121* region of the destination at a time. (These values are empirically122* derived for the common case where text runs horizontally.)123*124* Note: It is assumed in various calculations below that:125* (OGLTR_CACHED_DEST_WIDTH >= OGLTR_CACHE_CELL_WIDTH) &&126* (OGLTR_CACHED_DEST_WIDTH >= OGLTR_NOCACHE_TILE_SIZE) &&127* (OGLTR_CACHED_DEST_HEIGHT >= OGLTR_CACHE_CELL_HEIGHT) &&128* (OGLTR_CACHED_DEST_HEIGHT >= OGLTR_NOCACHE_TILE_SIZE)129*/130#define OGLTR_CACHED_DEST_WIDTH 512131#define OGLTR_CACHED_DEST_HEIGHT (OGLTR_CACHE_CELL_HEIGHT * 2)132133/**134* The handle to the "cached destination" texture object.135*/136static GLuint cachedDestTextureID = 0;137138/**139* The current bounds of the "cached destination" texture, in destination140* coordinate space. The width/height of these bounds will not exceed the141* OGLTR_CACHED_DEST_WIDTH/HEIGHT values defined above. These bounds are142* only considered valid when the isCachedDestValid flag is JNI_TRUE.143*/144static SurfaceDataBounds cachedDestBounds;145146/**147* This flag indicates whether the "cached destination" texture contains148* valid data. This flag is reset to JNI_FALSE at the beginning of every149* call to OGLTR_DrawGlyphList(). Once we copy valid destination data150* into the cached texture, this flag is set to JNI_TRUE. This way, we can151* limit the number of times we need to copy destination data, which is a152* very costly operation.153*/154static jboolean isCachedDestValid = JNI_FALSE;155156/**157* The bounds of the previously rendered LCD glyph, in destination158* coordinate space. We use these bounds to determine whether the glyph159* currently being rendered overlaps the previously rendered glyph (i.e.160* its bounding box intersects that of the previously rendered glyph). If161* so, we need to re-read the destination area associated with that previous162* glyph so that we can correctly blend with the actual destination data.163*/164static SurfaceDataBounds previousGlyphBounds;165166/**167* Initializes the one glyph cache (texture and data structure).168* If lcdCache is JNI_TRUE, the texture will contain RGB data,169* otherwise we will simply store the grayscale/monochrome glyph images170* as intensity values (which work well with the GL_MODULATE function).171*/172static jboolean173OGLTR_InitGlyphCache(jboolean lcdCache)174{175GlyphCacheInfo *gcinfo;176GLclampf priority = 1.0f;177GLenum internalFormat = lcdCache ? GL_RGB8 : GL_INTENSITY8;178GLenum pixelFormat = lcdCache ? GL_RGB : GL_LUMINANCE;179180J2dTraceLn(J2D_TRACE_INFO, "OGLTR_InitGlyphCache");181182// init glyph cache data structure183gcinfo = AccelGlyphCache_Init(OGLTR_CACHE_WIDTH,184OGLTR_CACHE_HEIGHT,185OGLTR_CACHE_CELL_WIDTH,186OGLTR_CACHE_CELL_HEIGHT,187OGLVertexCache_FlushVertexCache);188if (gcinfo == NULL) {189J2dRlsTraceLn(J2D_TRACE_ERROR,190"OGLTR_InitGlyphCache: could not init OGL glyph cache");191return JNI_FALSE;192}193194// init cache texture object195j2d_glGenTextures(1, &gcinfo->cacheID);196j2d_glBindTexture(GL_TEXTURE_2D, gcinfo->cacheID);197j2d_glPrioritizeTextures(1, &gcinfo->cacheID, &priority);198j2d_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);199j2d_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);200201j2d_glTexImage2D(GL_TEXTURE_2D, 0, internalFormat,202OGLTR_CACHE_WIDTH, OGLTR_CACHE_HEIGHT, 0,203pixelFormat, GL_UNSIGNED_BYTE, NULL);204205if (lcdCache) {206glyphCacheLCD = gcinfo;207} else {208glyphCacheAA = gcinfo;209}210211return JNI_TRUE;212}213214/**215* Adds the given glyph to the glyph cache (texture and data structure)216* associated with the given OGLContext.217*/218static void219OGLTR_AddToGlyphCache(GlyphInfo *glyph, GLenum pixelFormat)220{221CacheCellInfo *ccinfo;222GlyphCacheInfo *gcinfo;223224J2dTraceLn(J2D_TRACE_INFO, "OGLTR_AddToGlyphCache");225226if (pixelFormat == GL_LUMINANCE) {227gcinfo = glyphCacheAA;228} else {229gcinfo = glyphCacheLCD;230}231232if ((gcinfo == NULL) || (glyph->image == NULL)) {233return;234}235236AccelGlyphCache_AddGlyph(gcinfo, glyph);237ccinfo = (CacheCellInfo *) glyph->cellInfo;238239if (ccinfo != NULL) {240// store glyph image in texture cell241j2d_glTexSubImage2D(GL_TEXTURE_2D, 0,242ccinfo->x, ccinfo->y,243glyph->width, glyph->height,244pixelFormat, GL_UNSIGNED_BYTE, glyph->image);245}246}247248/**249* This is the GLSL fragment shader source code for rendering LCD-optimized250* text. Do not be frightened; it is much easier to understand than the251* equivalent ASM-like fragment program!252*253* The "uniform" variables at the top are initialized once the program is254* linked, and are updated at runtime as needed (e.g. when the source color255* changes, we will modify the "src_adj" value in OGLTR_UpdateLCDTextColor()).256*257* The "main" function is executed for each "fragment" (or pixel) in the258* glyph image. The pow() routine operates on vectors, gives precise results,259* and provides acceptable level of performance, so we use it to perform260* the gamma adjustment.261*262* The variables involved in the equation can be expressed as follows:263*264* Cs = Color component of the source (foreground color) [0.0, 1.0]265* Cd = Color component of the destination (background color) [0.0, 1.0]266* Cr = Color component to be written to the destination [0.0, 1.0]267* Ag = Glyph alpha (aka intensity or coverage) [0.0, 1.0]268* Ga = Gamma adjustment in the range [1.0, 2.5]269* (^ means raised to the power)270*271* And here is the theoretical equation approximated by this shader:272*273* Cr = (Ag*(Cs^Ga) + (1-Ag)*(Cd^Ga)) ^ (1/Ga)274*/275static const char *lcdTextShaderSource =276"uniform vec3 src_adj;"277"uniform sampler2D glyph_tex;"278"uniform sampler2D dst_tex;"279"uniform vec3 gamma;"280"uniform vec3 invgamma;"281""282"void main(void)"283"{"284// load the RGB value from the glyph image at the current texcoord285" vec3 glyph_clr = vec3(texture2D(glyph_tex, gl_TexCoord[0].st));"286" if (glyph_clr == vec3(0.0)) {"287// zero coverage, so skip this fragment288" discard;"289" }"290// load the RGB value from the corresponding destination pixel291" vec3 dst_clr = vec3(texture2D(dst_tex, gl_TexCoord[1].st));"292// gamma adjust the dest color293" vec3 dst_adj = pow(dst_clr.rgb, gamma);"294// linearly interpolate the three color values295" vec3 result = mix(dst_adj, src_adj, glyph_clr);"296// gamma re-adjust the resulting color (alpha is always set to 1.0)297" gl_FragColor = vec4(pow(result.rgb, invgamma), 1.0);"298"}";299300/**301* Compiles and links the LCD text shader program. If successful, this302* function returns a handle to the newly created shader program; otherwise303* returns 0.304*/305static GLhandleARB306OGLTR_CreateLCDTextProgram()307{308GLhandleARB lcdTextProgram;309GLint loc;310311J2dTraceLn(J2D_TRACE_INFO, "OGLTR_CreateLCDTextProgram");312313lcdTextProgram = OGLContext_CreateFragmentProgram(lcdTextShaderSource);314if (lcdTextProgram == 0) {315J2dRlsTraceLn(J2D_TRACE_ERROR,316"OGLTR_CreateLCDTextProgram: error creating program");317return 0;318}319320// "use" the program object temporarily so that we can set the uniforms321j2d_glUseProgramObjectARB(lcdTextProgram);322323// set the "uniform" values324loc = j2d_glGetUniformLocationARB(lcdTextProgram, "glyph_tex");325j2d_glUniform1iARB(loc, 0); // texture unit 0326loc = j2d_glGetUniformLocationARB(lcdTextProgram, "dst_tex");327j2d_glUniform1iARB(loc, 1); // texture unit 1328329// "unuse" the program object; it will be re-bound later as needed330j2d_glUseProgramObjectARB(0);331332return lcdTextProgram;333}334335/**336* (Re)Initializes the gamma related uniforms.337*338* The given contrast value is an int in the range [100, 250] which we will339* then scale to fit in the range [1.0, 2.5].340*/341static jboolean342OGLTR_UpdateLCDTextContrast(jint contrast)343{344double g = ((double)contrast) / 100.0;345double ig = 1.0 / g;346GLint loc;347348J2dTraceLn1(J2D_TRACE_INFO,349"OGLTR_UpdateLCDTextContrast: contrast=%d", contrast);350351loc = j2d_glGetUniformLocationARB(lcdTextProgram, "gamma");352j2d_glUniform3fARB(loc, g, g, g);353354loc = j2d_glGetUniformLocationARB(lcdTextProgram, "invgamma");355j2d_glUniform3fARB(loc, ig, ig, ig);356357return JNI_TRUE;358}359360/**361* Updates the current gamma-adjusted source color ("src_adj") of the LCD362* text shader program. Note that we could calculate this value in the363* shader (e.g. just as we do for "dst_adj"), but would be unnecessary work364* (and a measurable performance hit, maybe around 5%) since this value is365* constant over the entire glyph list. So instead we just calculate the366* gamma-adjusted value once and update the uniform parameter of the LCD367* shader as needed.368*/369static jboolean370OGLTR_UpdateLCDTextColor(jint contrast)371{372double gamma = ((double)contrast) / 100.0;373GLfloat radj, gadj, badj;374GLfloat clr[4];375GLint loc;376377J2dTraceLn1(J2D_TRACE_INFO,378"OGLTR_UpdateLCDTextColor: contrast=%d", contrast);379380/*381* Note: Ideally we would update the "src_adj" uniform parameter only382* when there is a change in the source color. Fortunately, the cost383* of querying the current OpenGL color state and updating the uniform384* value is quite small, and in the common case we only need to do this385* once per GlyphList, so we gain little from trying to optimize too386* eagerly here.387*/388389// get the current OpenGL primary color state390j2d_glGetFloatv(GL_CURRENT_COLOR, clr);391392// gamma adjust the primary color393radj = (GLfloat)pow(clr[0], gamma);394gadj = (GLfloat)pow(clr[1], gamma);395badj = (GLfloat)pow(clr[2], gamma);396397// update the "src_adj" parameter of the shader program with this value398loc = j2d_glGetUniformLocationARB(lcdTextProgram, "src_adj");399j2d_glUniform3fARB(loc, radj, gadj, badj);400401return JNI_TRUE;402}403404/**405* Enables the LCD text shader and updates any related state, such as the406* gamma lookup table textures.407*/408static jboolean409OGLTR_EnableLCDGlyphModeState(GLuint glyphTextureID,410GLuint dstTextureID,411jint contrast)412{413// bind the texture containing glyph data to texture unit 0414j2d_glActiveTextureARB(GL_TEXTURE0_ARB);415j2d_glBindTexture(GL_TEXTURE_2D, glyphTextureID);416j2d_glEnable(GL_TEXTURE_2D);417418// bind the texture tile containing destination data to texture unit 1419j2d_glActiveTextureARB(GL_TEXTURE1_ARB);420if (dstTextureID != 0) {421j2d_glBindTexture(GL_TEXTURE_2D, dstTextureID);422} else {423if (cachedDestTextureID == 0) {424cachedDestTextureID =425OGLContext_CreateBlitTexture(GL_RGB8, GL_RGB,426OGLTR_CACHED_DEST_WIDTH,427OGLTR_CACHED_DEST_HEIGHT);428if (cachedDestTextureID == 0) {429return JNI_FALSE;430}431}432j2d_glBindTexture(GL_TEXTURE_2D, cachedDestTextureID);433}434435// note that GL_TEXTURE_2D was already enabled for texture unit 0,436// but we need to explicitly enable it for texture unit 1437j2d_glEnable(GL_TEXTURE_2D);438439// create the LCD text shader, if necessary440if (lcdTextProgram == 0) {441lcdTextProgram = OGLTR_CreateLCDTextProgram();442if (lcdTextProgram == 0) {443return JNI_FALSE;444}445}446447// enable the LCD text shader448j2d_glUseProgramObjectARB(lcdTextProgram);449450// update the current contrast settings, if necessary451if (lastLCDContrast != contrast) {452if (!OGLTR_UpdateLCDTextContrast(contrast)) {453return JNI_FALSE;454}455lastLCDContrast = contrast;456}457458// update the current color settings459if (!OGLTR_UpdateLCDTextColor(contrast)) {460return JNI_FALSE;461}462463return JNI_TRUE;464}465466void467OGLTR_EnableGlyphVertexCache(OGLContext *oglc)468{469J2dTraceLn(J2D_TRACE_INFO, "OGLTR_EnableGlyphVertexCache");470471if (!OGLVertexCache_InitVertexCache(oglc)) {472return;473}474475if (glyphCacheAA == NULL) {476if (!OGLTR_InitGlyphCache(JNI_FALSE)) {477return;478}479}480481j2d_glEnable(GL_TEXTURE_2D);482j2d_glBindTexture(GL_TEXTURE_2D, glyphCacheAA->cacheID);483j2d_glPixelStorei(GL_UNPACK_ALIGNMENT, 1);484485// for grayscale/monochrome text, the current OpenGL source color486// is modulated with the glyph image as part of the texture487// application stage, so we use GL_MODULATE here488OGLC_UPDATE_TEXTURE_FUNCTION(oglc, GL_MODULATE);489}490491void492OGLTR_DisableGlyphVertexCache(OGLContext *oglc)493{494J2dTraceLn(J2D_TRACE_INFO, "OGLTR_DisableGlyphVertexCache");495496OGLVertexCache_FlushVertexCache();497OGLVertexCache_RestoreColorState(oglc);498499j2d_glDisable(GL_TEXTURE_2D);500j2d_glPixelStorei(GL_UNPACK_ALIGNMENT, 4);501j2d_glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);502j2d_glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);503j2d_glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);504}505506/**507* Disables any pending state associated with the current "glyph mode".508*/509static void510OGLTR_DisableGlyphModeState()511{512switch (glyphMode) {513case MODE_NO_CACHE_LCD:514j2d_glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);515j2d_glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);516/* FALLTHROUGH */517518case MODE_USE_CACHE_LCD:519j2d_glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);520j2d_glPixelStorei(GL_UNPACK_ALIGNMENT, 4);521j2d_glUseProgramObjectARB(0);522j2d_glActiveTextureARB(GL_TEXTURE1_ARB);523j2d_glDisable(GL_TEXTURE_2D);524j2d_glActiveTextureARB(GL_TEXTURE0_ARB);525j2d_glDisable(GL_TEXTURE_2D);526break;527528case MODE_NO_CACHE_GRAY:529case MODE_USE_CACHE_GRAY:530case MODE_NOT_INITED:531default:532break;533}534}535536static jboolean537OGLTR_DrawGrayscaleGlyphViaCache(OGLContext *oglc,538GlyphInfo *ginfo, jint x, jint y)539{540CacheCellInfo *cell;541jfloat x1, y1, x2, y2;542543if (glyphMode != MODE_USE_CACHE_GRAY) {544OGLTR_DisableGlyphModeState();545CHECK_PREVIOUS_OP(OGL_STATE_GLYPH_OP);546glyphMode = MODE_USE_CACHE_GRAY;547}548549if (ginfo->cellInfo == NULL) {550// attempt to add glyph to accelerated glyph cache551OGLTR_AddToGlyphCache(ginfo, GL_LUMINANCE);552553if (ginfo->cellInfo == NULL) {554// we'll just no-op in the rare case that the cell is NULL555return JNI_TRUE;556}557}558559cell = (CacheCellInfo *) (ginfo->cellInfo);560cell->timesRendered++;561562x1 = (jfloat)x;563y1 = (jfloat)y;564x2 = x1 + ginfo->width;565y2 = y1 + ginfo->height;566567OGLVertexCache_AddGlyphQuad(oglc,568cell->tx1, cell->ty1,569cell->tx2, cell->ty2,570x1, y1, x2, y2);571572return JNI_TRUE;573}574575/**576* Evaluates to true if the rectangle defined by gx1/gy1/gx2/gy2 is577* inside outerBounds.578*/579#define INSIDE(gx1, gy1, gx2, gy2, outerBounds) \580(((gx1) >= outerBounds.x1) && ((gy1) >= outerBounds.y1) && \581((gx2) <= outerBounds.x2) && ((gy2) <= outerBounds.y2))582583/**584* Evaluates to true if the rectangle defined by gx1/gy1/gx2/gy2 intersects585* the rectangle defined by bounds.586*/587#define INTERSECTS(gx1, gy1, gx2, gy2, bounds) \588((bounds.x2 > (gx1)) && (bounds.y2 > (gy1)) && \589(bounds.x1 < (gx2)) && (bounds.y1 < (gy2)))590591/**592* This method checks to see if the given LCD glyph bounds fall within the593* cached destination texture bounds. If so, this method can return594* immediately. If not, this method will copy a chunk of framebuffer data595* into the cached destination texture and then update the current cached596* destination bounds before returning.597*/598static void599OGLTR_UpdateCachedDestination(OGLSDOps *dstOps, GlyphInfo *ginfo,600jint gx1, jint gy1, jint gx2, jint gy2,601jint glyphIndex, jint totalGlyphs)602{603jint dx1, dy1, dx2, dy2;604jint dx1adj, dy1adj;605606if (isCachedDestValid && INSIDE(gx1, gy1, gx2, gy2, cachedDestBounds)) {607// glyph is already within the cached destination bounds; no need608// to read back the entire destination region again, but we do609// need to see if the current glyph overlaps the previous glyph...610611if (INTERSECTS(gx1, gy1, gx2, gy2, previousGlyphBounds)) {612// the current glyph overlaps the destination region touched613// by the previous glyph, so now we need to read back the part614// of the destination corresponding to the previous glyph615dx1 = previousGlyphBounds.x1;616dy1 = previousGlyphBounds.y1;617dx2 = previousGlyphBounds.x2;618dy2 = previousGlyphBounds.y2;619620// this accounts for lower-left origin of the destination region621dx1adj = dstOps->xOffset + dx1;622dy1adj = dstOps->yOffset + dstOps->height - dy2;623624// copy destination into subregion of cached texture tile:625// dx1-cachedDestBounds.x1 == +xoffset from left side of texture626// cachedDestBounds.y2-dy2 == +yoffset from bottom of texture627j2d_glActiveTextureARB(GL_TEXTURE1_ARB);628j2d_glCopyTexSubImage2D(GL_TEXTURE_2D, 0,629dx1 - cachedDestBounds.x1,630cachedDestBounds.y2 - dy2,631dx1adj, dy1adj,632dx2-dx1, dy2-dy1);633}634} else {635jint remainingWidth;636637// destination region is not valid, so we need to read back a638// chunk of the destination into our cached texture639640// position the upper-left corner of the destination region on the641// "top" line of glyph list642// REMIND: this isn't ideal; it would be better if we had some idea643// of the bounding box of the whole glyph list (this is644// do-able, but would require iterating through the whole645// list up front, which may present its own problems)646dx1 = gx1;647dy1 = gy1;648649if (ginfo->advanceX > 0) {650// estimate the width based on our current position in the glyph651// list and using the x advance of the current glyph (this is just652// a quick and dirty heuristic; if this is a "thin" glyph image,653// then we're likely to underestimate, and if it's "thick" then we654// may end up reading back more than we need to)655remainingWidth =656(jint)(ginfo->advanceX * (totalGlyphs - glyphIndex));657if (remainingWidth > OGLTR_CACHED_DEST_WIDTH) {658remainingWidth = OGLTR_CACHED_DEST_WIDTH;659} else if (remainingWidth < ginfo->width) {660// in some cases, the x-advance may be slightly smaller661// than the actual width of the glyph; if so, adjust our662// estimate so that we can accommodate the entire glyph663remainingWidth = ginfo->width;664}665} else {666// a negative advance is possible when rendering rotated text,667// in which case it is difficult to estimate an appropriate668// region for readback, so we will pick a region that669// encompasses just the current glyph670remainingWidth = ginfo->width;671}672dx2 = dx1 + remainingWidth;673674// estimate the height (this is another sloppy heuristic; we'll675// make the cached destination region tall enough to encompass most676// glyphs that are small enough to fit in the glyph cache, and then677// we add a little something extra to account for descenders678dy2 = dy1 + OGLTR_CACHE_CELL_HEIGHT + 2;679680// this accounts for lower-left origin of the destination region681dx1adj = dstOps->xOffset + dx1;682dy1adj = dstOps->yOffset + dstOps->height - dy2;683684// copy destination into cached texture tile (the lower-left corner685// of the destination region will be positioned at the lower-left686// corner (0,0) of the texture)687j2d_glActiveTextureARB(GL_TEXTURE1_ARB);688j2d_glCopyTexSubImage2D(GL_TEXTURE_2D, 0,6890, 0, dx1adj, dy1adj,690dx2-dx1, dy2-dy1);691692// update the cached bounds and mark it valid693cachedDestBounds.x1 = dx1;694cachedDestBounds.y1 = dy1;695cachedDestBounds.x2 = dx2;696cachedDestBounds.y2 = dy2;697isCachedDestValid = JNI_TRUE;698}699700// always update the previous glyph bounds701previousGlyphBounds.x1 = gx1;702previousGlyphBounds.y1 = gy1;703previousGlyphBounds.x2 = gx2;704previousGlyphBounds.y2 = gy2;705}706707static jboolean708OGLTR_DrawLCDGlyphViaCache(OGLContext *oglc, OGLSDOps *dstOps,709GlyphInfo *ginfo, jint x, jint y,710jint glyphIndex, jint totalGlyphs,711jboolean rgbOrder, jint contrast,712GLuint dstTextureID)713{714CacheCellInfo *cell;715jint dx1, dy1, dx2, dy2;716jfloat dtx1, dty1, dtx2, dty2;717718if (glyphMode != MODE_USE_CACHE_LCD) {719OGLTR_DisableGlyphModeState();720CHECK_PREVIOUS_OP(GL_TEXTURE_2D);721j2d_glPixelStorei(GL_UNPACK_ALIGNMENT, 1);722723if (glyphCacheLCD == NULL) {724if (!OGLTR_InitGlyphCache(JNI_TRUE)) {725return JNI_FALSE;726}727}728729if (rgbOrder != lastRGBOrder) {730// need to invalidate the cache in this case; see comments731// for lastRGBOrder above732AccelGlyphCache_Invalidate(glyphCacheLCD);733lastRGBOrder = rgbOrder;734}735736if (!OGLTR_EnableLCDGlyphModeState(glyphCacheLCD->cacheID,737dstTextureID, contrast))738{739return JNI_FALSE;740}741742// when a fragment shader is enabled, the texture function state is743// ignored, so the following line is not needed...744// OGLC_UPDATE_TEXTURE_FUNCTION(oglc, GL_MODULATE);745746glyphMode = MODE_USE_CACHE_LCD;747}748749if (ginfo->cellInfo == NULL) {750// rowBytes will always be a multiple of 3, so the following is safe751j2d_glPixelStorei(GL_UNPACK_ROW_LENGTH, ginfo->rowBytes / 3);752753// make sure the glyph cache texture is bound to texture unit 0754j2d_glActiveTextureARB(GL_TEXTURE0_ARB);755756// attempt to add glyph to accelerated glyph cache757OGLTR_AddToGlyphCache(ginfo, rgbOrder ? GL_RGB : GL_BGR);758759if (ginfo->cellInfo == NULL) {760// we'll just no-op in the rare case that the cell is NULL761return JNI_TRUE;762}763}764765cell = (CacheCellInfo *) (ginfo->cellInfo);766cell->timesRendered++;767768// location of the glyph in the destination's coordinate space769dx1 = x;770dy1 = y;771dx2 = dx1 + ginfo->width;772dy2 = dy1 + ginfo->height;773774if (dstTextureID == 0) {775// copy destination into second cached texture, if necessary776OGLTR_UpdateCachedDestination(dstOps, ginfo,777dx1, dy1, dx2, dy2,778glyphIndex, totalGlyphs);779780// texture coordinates of the destination tile781dtx1 = ((jfloat)(dx1 - cachedDestBounds.x1)) / OGLTR_CACHED_DEST_WIDTH;782dty1 = ((jfloat)(cachedDestBounds.y2 - dy1)) / OGLTR_CACHED_DEST_HEIGHT;783dtx2 = ((jfloat)(dx2 - cachedDestBounds.x1)) / OGLTR_CACHED_DEST_WIDTH;784dty2 = ((jfloat)(cachedDestBounds.y2 - dy2)) / OGLTR_CACHED_DEST_HEIGHT;785} else {786jint gw = ginfo->width;787jint gh = ginfo->height;788789// this accounts for lower-left origin of the destination region790jint dxadj = dstOps->xOffset + x;791jint dyadj = dstOps->yOffset + dstOps->height - (y + gh);792793// update the remaining destination texture coordinates794dtx1 =((GLfloat)dxadj) / dstOps->textureWidth;795dtx2 = ((GLfloat)dxadj + gw) / dstOps->textureWidth;796797dty1 = ((GLfloat)dyadj + gh) / dstOps->textureHeight;798dty2 = ((GLfloat)dyadj) / dstOps->textureHeight;799800j2d_glTextureBarrierNV();801}802803// render composed texture to the destination surface804j2d_glBegin(GL_QUADS);805j2d_glMultiTexCoord2fARB(GL_TEXTURE0_ARB, cell->tx1, cell->ty1);806j2d_glMultiTexCoord2fARB(GL_TEXTURE1_ARB, dtx1, dty1);807j2d_glVertex2i(dx1, dy1);808j2d_glMultiTexCoord2fARB(GL_TEXTURE0_ARB, cell->tx2, cell->ty1);809j2d_glMultiTexCoord2fARB(GL_TEXTURE1_ARB, dtx2, dty1);810j2d_glVertex2i(dx2, dy1);811j2d_glMultiTexCoord2fARB(GL_TEXTURE0_ARB, cell->tx2, cell->ty2);812j2d_glMultiTexCoord2fARB(GL_TEXTURE1_ARB, dtx2, dty2);813j2d_glVertex2i(dx2, dy2);814j2d_glMultiTexCoord2fARB(GL_TEXTURE0_ARB, cell->tx1, cell->ty2);815j2d_glMultiTexCoord2fARB(GL_TEXTURE1_ARB, dtx1, dty2);816j2d_glVertex2i(dx1, dy2);817j2d_glEnd();818819return JNI_TRUE;820}821822static jboolean823OGLTR_DrawGrayscaleGlyphNoCache(OGLContext *oglc,824GlyphInfo *ginfo, jint x, jint y)825{826jint tw, th;827jint sx, sy, sw, sh;828jint x0;829jint w = ginfo->width;830jint h = ginfo->height;831832if (glyphMode != MODE_NO_CACHE_GRAY) {833OGLTR_DisableGlyphModeState();834CHECK_PREVIOUS_OP(OGL_STATE_MASK_OP);835glyphMode = MODE_NO_CACHE_GRAY;836}837838x0 = x;839tw = OGLVC_MASK_CACHE_TILE_WIDTH;840th = OGLVC_MASK_CACHE_TILE_HEIGHT;841842for (sy = 0; sy < h; sy += th, y += th) {843x = x0;844sh = ((sy + th) > h) ? (h - sy) : th;845846for (sx = 0; sx < w; sx += tw, x += tw) {847sw = ((sx + tw) > w) ? (w - sx) : tw;848849OGLVertexCache_AddMaskQuad(oglc,850sx, sy, x, y, sw, sh,851w, ginfo->image);852}853}854855return JNI_TRUE;856}857858static jboolean859OGLTR_DrawLCDGlyphNoCache(OGLContext *oglc, OGLSDOps *dstOps,860GlyphInfo *ginfo, jint x, jint y,861jint rowBytesOffset,862jboolean rgbOrder, jint contrast,863GLuint dstTextureID)864{865GLfloat tx1, ty1, tx2, ty2;866GLfloat dtx1, dty1, dtx2, dty2;867jint tw, th;868jint sx, sy, sw, sh, dxadj, dyadj;869jint x0;870jint w = ginfo->width;871jint h = ginfo->height;872GLenum pixelFormat = rgbOrder ? GL_RGB : GL_BGR;873874if (glyphMode != MODE_NO_CACHE_LCD) {875OGLTR_DisableGlyphModeState();876CHECK_PREVIOUS_OP(GL_TEXTURE_2D);877j2d_glPixelStorei(GL_UNPACK_ALIGNMENT, 1);878879if (oglc->blitTextureID == 0) {880if (!OGLContext_InitBlitTileTexture(oglc)) {881return JNI_FALSE;882}883}884885if (!OGLTR_EnableLCDGlyphModeState(oglc->blitTextureID,886dstTextureID, contrast))887{888return JNI_FALSE;889}890891// when a fragment shader is enabled, the texture function state is892// ignored, so the following line is not needed...893// OGLC_UPDATE_TEXTURE_FUNCTION(oglc, GL_MODULATE);894895glyphMode = MODE_NO_CACHE_LCD;896}897898// rowBytes will always be a multiple of 3, so the following is safe899j2d_glPixelStorei(GL_UNPACK_ROW_LENGTH, ginfo->rowBytes / 3);900901x0 = x;902tx1 = 0.0f;903ty1 = 0.0f;904dtx1 = 0.0f;905dty2 = 0.0f;906tw = OGLTR_NOCACHE_TILE_SIZE;907th = OGLTR_NOCACHE_TILE_SIZE;908909for (sy = 0; sy < h; sy += th, y += th) {910x = x0;911sh = ((sy + th) > h) ? (h - sy) : th;912913for (sx = 0; sx < w; sx += tw, x += tw) {914sw = ((sx + tw) > w) ? (w - sx) : tw;915916// update the source pointer offsets917j2d_glPixelStorei(GL_UNPACK_SKIP_PIXELS, sx);918j2d_glPixelStorei(GL_UNPACK_SKIP_ROWS, sy);919920// copy LCD mask into glyph texture tile921j2d_glActiveTextureARB(GL_TEXTURE0_ARB);922j2d_glTexSubImage2D(GL_TEXTURE_2D, 0,9230, 0, sw, sh,924pixelFormat, GL_UNSIGNED_BYTE,925ginfo->image + rowBytesOffset);926927// update the lower-right glyph texture coordinates928tx2 = ((GLfloat)sw) / OGLC_BLIT_TILE_SIZE;929ty2 = ((GLfloat)sh) / OGLC_BLIT_TILE_SIZE;930931// this accounts for lower-left origin of the destination region932dxadj = dstOps->xOffset + x;933dyadj = dstOps->yOffset + dstOps->height - (y + sh);934935if (dstTextureID == 0) {936// copy destination into cached texture tile (the lower-left937// corner of the destination region will be positioned at the938// lower-left corner (0,0) of the texture)939j2d_glActiveTextureARB(GL_TEXTURE1_ARB);940j2d_glCopyTexSubImage2D(GL_TEXTURE_2D, 0,9410, 0,942dxadj, dyadj,943sw, sh);944// update the remaining destination texture coordinates945dtx2 = ((GLfloat)sw) / OGLTR_CACHED_DEST_WIDTH;946dty1 = ((GLfloat)sh) / OGLTR_CACHED_DEST_HEIGHT;947} else {948// use the destination texture directly949// update the remaining destination texture coordinates950dtx1 =((GLfloat)dxadj) / dstOps->textureWidth;951dtx2 = ((GLfloat)dxadj + sw) / dstOps->textureWidth;952953dty1 = ((GLfloat)dyadj + sh) / dstOps->textureHeight;954dty2 = ((GLfloat)dyadj) / dstOps->textureHeight;955956j2d_glTextureBarrierNV();957}958959// render composed texture to the destination surface960j2d_glBegin(GL_QUADS);961j2d_glMultiTexCoord2fARB(GL_TEXTURE0_ARB, tx1, ty1);962j2d_glMultiTexCoord2fARB(GL_TEXTURE1_ARB, dtx1, dty1);963j2d_glVertex2i(x, y);964j2d_glMultiTexCoord2fARB(GL_TEXTURE0_ARB, tx2, ty1);965j2d_glMultiTexCoord2fARB(GL_TEXTURE1_ARB, dtx2, dty1);966j2d_glVertex2i(x + sw, y);967j2d_glMultiTexCoord2fARB(GL_TEXTURE0_ARB, tx2, ty2);968j2d_glMultiTexCoord2fARB(GL_TEXTURE1_ARB, dtx2, dty2);969j2d_glVertex2i(x + sw, y + sh);970j2d_glMultiTexCoord2fARB(GL_TEXTURE0_ARB, tx1, ty2);971j2d_glMultiTexCoord2fARB(GL_TEXTURE1_ARB, dtx1, dty2);972j2d_glVertex2i(x, y + sh);973j2d_glEnd();974}975}976977return JNI_TRUE;978}979980// see DrawGlyphList.c for more on this macro...981#define FLOOR_ASSIGN(l, r) \982if ((r)<0) (l) = ((int)floor(r)); else (l) = ((int)(r))983984void985OGLTR_DrawGlyphList(JNIEnv *env, OGLContext *oglc, OGLSDOps *dstOps,986jint totalGlyphs, jboolean usePositions,987jboolean subPixPos, jboolean rgbOrder, jint lcdContrast,988jfloat glyphListOrigX, jfloat glyphListOrigY,989unsigned char *images, unsigned char *positions)990{991int glyphCounter;992GLuint dstTextureID = 0;993994J2dTraceLn(J2D_TRACE_INFO, "OGLTR_DrawGlyphList");995996RETURN_IF_NULL(oglc);997RETURN_IF_NULL(dstOps);998RETURN_IF_NULL(images);999if (usePositions) {1000RETURN_IF_NULL(positions);1001}10021003glyphMode = MODE_NOT_INITED;1004isCachedDestValid = JNI_FALSE;10051006// We have to obtain an information about destination content1007// in order to render lcd glyphs. It could be done by copying1008// a part of desitination buffer into an intermediate texture1009// using glCopyTexSubImage2D(). However, on macosx this path is1010// slow, and it dramatically reduces the overall speed of lcd1011// text rendering.1012//1013// In some cases, we can use a texture from the destination1014// surface data in oredr to avoid this slow reading routine.1015// It requires:1016// * An appropriate textureTarget for the destination SD.1017// In particular, we need GL_TEXTURE_2D1018// * Means to prevent read-after-write problem.1019// At the moment, a GL_NV_texture_barrier extension is used1020// to achieve this.1021#ifdef MACOSX1022if (OGLC_IS_CAP_PRESENT(oglc, CAPS_EXT_TEXBARRIER) &&1023dstOps->textureTarget == GL_TEXTURE_2D)1024{1025dstTextureID = dstOps->textureID;1026}1027#endif10281029for (glyphCounter = 0; glyphCounter < totalGlyphs; glyphCounter++) {1030jint x, y;1031jfloat glyphx, glyphy;1032jboolean grayscale, ok;1033GlyphInfo *ginfo = (GlyphInfo *)jlong_to_ptr(NEXT_LONG(images));10341035if (ginfo == NULL) {1036// this shouldn't happen, but if it does we'll just break out...1037J2dRlsTraceLn(J2D_TRACE_ERROR,1038"OGLTR_DrawGlyphList: glyph info is null");1039break;1040}10411042grayscale = (ginfo->rowBytes == ginfo->width);10431044if (usePositions) {1045jfloat posx = NEXT_FLOAT(positions);1046jfloat posy = NEXT_FLOAT(positions);1047glyphx = glyphListOrigX + posx + ginfo->topLeftX;1048glyphy = glyphListOrigY + posy + ginfo->topLeftY;1049FLOOR_ASSIGN(x, glyphx);1050FLOOR_ASSIGN(y, glyphy);1051} else {1052glyphx = glyphListOrigX + ginfo->topLeftX;1053glyphy = glyphListOrigY + ginfo->topLeftY;1054FLOOR_ASSIGN(x, glyphx);1055FLOOR_ASSIGN(y, glyphy);1056glyphListOrigX += ginfo->advanceX;1057glyphListOrigY += ginfo->advanceY;1058}10591060if (ginfo->image == NULL) {1061continue;1062}10631064if (grayscale) {1065// grayscale or monochrome glyph data1066if (ginfo->width <= OGLTR_CACHE_CELL_WIDTH &&1067ginfo->height <= OGLTR_CACHE_CELL_HEIGHT)1068{1069ok = OGLTR_DrawGrayscaleGlyphViaCache(oglc, ginfo, x, y);1070} else {1071ok = OGLTR_DrawGrayscaleGlyphNoCache(oglc, ginfo, x, y);1072}1073} else {1074// LCD-optimized glyph data1075jint rowBytesOffset = 0;10761077if (subPixPos) {1078jint frac = (jint)((glyphx - x) * 3);1079if (frac != 0) {1080rowBytesOffset = 3 - frac;1081x += 1;1082}1083}10841085if (rowBytesOffset == 0 &&1086ginfo->width <= OGLTR_CACHE_CELL_WIDTH &&1087ginfo->height <= OGLTR_CACHE_CELL_HEIGHT)1088{1089ok = OGLTR_DrawLCDGlyphViaCache(oglc, dstOps,1090ginfo, x, y,1091glyphCounter, totalGlyphs,1092rgbOrder, lcdContrast,1093dstTextureID);1094} else {1095ok = OGLTR_DrawLCDGlyphNoCache(oglc, dstOps,1096ginfo, x, y,1097rowBytesOffset,1098rgbOrder, lcdContrast,1099dstTextureID);1100}1101}11021103if (!ok) {1104break;1105}1106}11071108OGLTR_DisableGlyphModeState();1109}11101111JNIEXPORT void JNICALL1112Java_sun_java2d_opengl_OGLTextRenderer_drawGlyphList1113(JNIEnv *env, jobject self,1114jint numGlyphs, jboolean usePositions,1115jboolean subPixPos, jboolean rgbOrder, jint lcdContrast,1116jfloat glyphListOrigX, jfloat glyphListOrigY,1117jlongArray imgArray, jfloatArray posArray)1118{1119unsigned char *images;11201121J2dTraceLn(J2D_TRACE_INFO, "OGLTextRenderer_drawGlyphList");11221123images = (unsigned char *)1124(*env)->GetPrimitiveArrayCritical(env, imgArray, NULL);1125if (images != NULL) {1126OGLContext *oglc = OGLRenderQueue_GetCurrentContext();1127OGLSDOps *dstOps = OGLRenderQueue_GetCurrentDestination();11281129if (usePositions) {1130unsigned char *positions = (unsigned char *)1131(*env)->GetPrimitiveArrayCritical(env, posArray, NULL);1132if (positions != NULL) {1133OGLTR_DrawGlyphList(env, oglc, dstOps,1134numGlyphs, usePositions,1135subPixPos, rgbOrder, lcdContrast,1136glyphListOrigX, glyphListOrigY,1137images, positions);1138(*env)->ReleasePrimitiveArrayCritical(env, posArray,1139positions, JNI_ABORT);1140}1141} else {1142OGLTR_DrawGlyphList(env, oglc, dstOps,1143numGlyphs, usePositions,1144subPixPos, rgbOrder, lcdContrast,1145glyphListOrigX, glyphListOrigY,1146images, NULL);1147}11481149// 6358147: reset current state, and ensure rendering is1150// flushed to dest1151if (oglc != NULL) {1152RESET_PREVIOUS_OP();1153j2d_glFlush();1154}11551156(*env)->ReleasePrimitiveArrayCritical(env, imgArray,1157images, JNI_ABORT);1158}1159}11601161#endif /* !HEADLESS */116211631164