/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#ifndef HEADLESS
#include <stdlib.h>
#include <math.h>
#include <jlong.h>
#include "sun_java2d_opengl_OGLTextRenderer.h"
#include "SurfaceData.h"
#include "OGLContext.h"
#include "OGLSurfaceData.h"
#include "OGLRenderQueue.h"
#include "OGLTextRenderer.h"
#include "OGLVertexCache.h"
#include "AccelGlyphCache.h"
#include "fontscalerdefs.h"
/**
* The following constants define the inner and outer bounds of the
* accelerated glyph cache.
*/
/**
* The current "glyph mode" state. This variable is used to track the
* codepath used to render a particular glyph. This variable is reset to
* MODE_NOT_INITED at the beginning of every call to OGLTR_DrawGlyphList().
* As each glyph is rendered, the glyphMode variable is updated to reflect
* the current mode, so if the current mode is the same as the mode used
* to render the previous glyph, we can avoid doing costly setup operations
* each time.
*/
typedef enum {
} GlyphMode;
/**
* This enum indicates the current state of the hardware glyph cache.
* Initially the CacheStatus is set to CACHE_NOT_INITED, and then it is
* set to either GRAY or LCD when the glyph cache is initialized.
*/
typedef enum {
} CacheStatus;
/**
* This is the one glyph cache. Once it is initialized as either GRAY or
* LCD, it stays in that mode for the duration of the application. It should
* be safe to use this one glyph cache for all screens in a multimon
* environment, since the glyph cache texture is shared between all contexts,
* and (in theory) OpenGL drivers should be smart enough to manage that
* texture across all screens.
*/
/**
* The handle to the LCD text fragment program object.
*/
/**
* The size of one of the gamma LUT textures in any one dimension along
* the edge, in texels.
*/
/**
* These are the texture object handles for the gamma and inverse gamma
* lookup tables.
*/
/**
* This value tracks the previous LCD contrast setting, so if the contrast
* value hasn't changed since the last time the lookup tables were
* generated (not very common), then we can skip updating the tables.
*/
/**
* This value tracks the previous LCD rgbOrder setting, so if the rgbOrder
* value has changed since the last time, it indicates that we need to
* invalidate the cache, which may already store glyph images in the reverse
* order. Note that in most real world applications this value will not
* change over the course of the application, but tests like Font2DTest
* allow for changing the ordering at runtime, so we need to handle that case.
*/
/**
* This constant defines the size of the tile to use in the
* OGLTR_DrawLCDGlyphNoCache() method. See below for more on why we
* restrict this value to a particular size.
*/
/**
* These constants define the size of the "cached destination" texture.
* This texture is only used when rendering LCD-optimized text, as that
* codepath needs direct access to the destination. There is no way to
* access the framebuffer directly from an OpenGL shader, so we need to first
* copy the destination region corresponding to a particular glyph into
* this cached texture, and then that texture will be accessed inside the
* shader. Copying the destination into this cached texture can be a very
* expensive operation (accounting for about half the rendering time for
* LCD text), so to mitigate this cost we try to bulk read a horizontal
* region of the destination at a time. (These values are empirically
* derived for the common case where text runs horizontally.)
*
* Note: It is assumed in various calculations below that:
* (OGLTR_CACHED_DEST_WIDTH >= OGLTR_CACHE_CELL_WIDTH) &&
* (OGLTR_CACHED_DEST_WIDTH >= OGLTR_NOCACHE_TILE_SIZE) &&
* (OGLTR_CACHED_DEST_HEIGHT >= OGLTR_CACHE_CELL_HEIGHT) &&
* (OGLTR_CACHED_DEST_HEIGHT >= OGLTR_NOCACHE_TILE_SIZE)
*/
/**
* The handle to the "cached destination" texture object.
*/
/**
* The current bounds of the "cached destination" texture, in destination
* OGLTR_CACHED_DEST_WIDTH/HEIGHT values defined above. These bounds are
* only considered valid when the isCachedDestValid flag is JNI_TRUE.
*/
/**
* This flag indicates whether the "cached destination" texture contains
* valid data. This flag is reset to JNI_FALSE at the beginning of every
* call to OGLTR_DrawGlyphList(). Once we copy valid destination data
* into the cached texture, this flag is set to JNI_TRUE. This way, we can
* limit the number of times we need to copy destination data, which is a
* very costly operation.
*/
/**
* The bounds of the previously rendered LCD glyph, in destination
* coordinate space. We use these bounds to determine whether the glyph
* currently being rendered overlaps the previously rendered glyph (i.e.
* its bounding box intersects that of the previously rendered glyph). If
* so, we need to re-read the destination area associated with that previous
* glyph so that we can correctly blend with the actual destination data.
*/
/**
* Initializes the one glyph cache (texture and data structure).
* If lcdCache is JNI_TRUE, the texture will contain RGB data,
* otherwise we will simply store the grayscale/monochrome glyph images
* as intensity values (which work well with the GL_MODULATE function).
*/
static jboolean
{
// init glyph cache data structure
"OGLTR_InitGlyphCache: could not init OGL glyph cache");
return JNI_FALSE;
}
// init cache texture object
glyphCache = gcinfo;
return JNI_TRUE;
}
/**
* Adds the given glyph to the glyph cache (texture and data structure)
* associated with the given OGLContext.
*/
static void
{
return;
}
if (cacheStatus == CACHE_LCD) {
} else {
}
// store glyph image in texture cell
}
}
/**
* This is the GLSL fragment shader source code for rendering LCD-optimized
* text. Do not be frightened; it is much easier to understand than the
* equivalent ASM-like fragment program!
*
* The "uniform" variables at the top are initialized once the program is
* linked, and are updated at runtime as needed (e.g. when the source color
* changes, we will modify the "src_adj" value in OGLTR_UpdateLCDTextColor()).
*
* The "main" function is executed for each "fragment" (or pixel) in the
* glyph image. We have determined that the pow() function can be quite
* slow and it only operates on scalar values, not vectors as we require.
* So instead we build two 3D textures containing gamma (and inverse gamma)
* lookup tables that allow us to approximate a component-wise pow() function
* with a single 3D texture lookup. This approach is at least 2x faster
* than the equivalent pow() calls.
*
* The variables involved in the equation can be expressed as follows:
*
* Cs = Color component of the source (foreground color) [0.0, 1.0]
* Cd = Color component of the destination (background color) [0.0, 1.0]
* Cr = Color component to be written to the destination [0.0, 1.0]
* Ag = Glyph alpha (aka intensity or coverage) [0.0, 1.0]
* Ga = Gamma adjustment in the range [1.0, 2.5]
* (^ means raised to the power)
*
* And here is the theoretical equation approximated by this shader:
*
* Cr = (Ag*(Cs^Ga) + (1-Ag)*(Cd^Ga)) ^ (1/Ga)
*/
static const char *lcdTextShaderSource =
"uniform vec3 src_adj;"
"uniform sampler2D glyph_tex;"
"uniform sampler2D dst_tex;"
"uniform sampler3D invgamma_tex;"
"uniform sampler3D gamma_tex;"
""
"void main(void)"
"{"
// load the RGB value from the glyph image at the current texcoord
" vec3 glyph_clr = vec3(texture2D(glyph_tex, gl_TexCoord[0].st));"
" if (glyph_clr == vec3(0.0)) {"
// zero coverage, so skip this fragment
" discard;"
" }"
// load the RGB value from the corresponding destination pixel
" vec3 dst_clr = vec3(texture2D(dst_tex, gl_TexCoord[1].st));"
// gamma adjust the dest color using the invgamma LUT
" vec3 dst_adj = vec3(texture3D(invgamma_tex, dst_clr.stp));"
// linearly interpolate the three color values
" vec3 result = mix(dst_adj, src_adj, glyph_clr);"
// gamma re-adjust the resulting color (alpha is always set to 1.0)
" gl_FragColor = vec4(vec3(texture3D(gamma_tex, result.stp)), 1.0);"
"}";
/**
* Compiles and links the LCD text shader program. If successful, this
* function returns a handle to the newly created shader program; otherwise
* returns 0.
*/
static GLhandleARB
{
if (lcdTextProgram == 0) {
"OGLTR_CreateLCDTextProgram: error creating program");
return 0;
}
// "use" the program object temporarily so that we can set the uniforms
// set the "uniform" values
// "unuse" the program object; it will be re-bound later as needed
return lcdTextProgram;
}
/**
* Initializes a 3D texture object for use as a three-dimensional gamma
* lookup table. Note that the wrap mode is initialized to GL_LINEAR so
* that the table will interpolate adjacent values when the index falls
* somewhere in between.
*/
static GLuint
{
return lutTextureID;
}
/**
* Updates the lookup table in the given texture object with the float
* values in the given system memory buffer. Note that we could use
* glTexSubImage3D() when updating the texture after its first
* initialization, but since we're updating the entire table (with
* power-of-two dimensions) and this is a relatively rare event, we'll
* just stick with glTexImage3D().
*/
static void
{
}
/**
* (Re)Initializes the gamma lookup table textures.
*
* The given contrast value is an int in the range [100, 250] which we will
* then scale to fit in the range [1.0, 2.5]. We create two LUTs, one
* that essentially calculates pow(x, gamma) and the other calculates
* pow(x, 1/gamma). These values are replicated in all three dimensions, so
* given a single 3D texture coordinate (typically this will be a triplet
* in the form (r,g,b)), the 3D texture lookup will return an RGB triplet:
*
* (pow(r,g), pow(y,g), pow(z,g)
*
* where g is either gamma or 1/gamma, depending on the table.
*/
static jboolean
{
double g = 1.0 / ig;
int min = 0;
int x, y, z;
"OGLTR_UpdateLCDTextContrast: contrast=%d", contrast);
}
}
}
if (gammaLutTextureID == 0) {
}
if (invGammaLutTextureID == 0) {
}
return JNI_TRUE;
}
/**
* Updates the current gamma-adjusted source color ("src_adj") of the LCD
* text shader program. Note that we could calculate this value in the
* shader (e.g. just as we do for "dst_adj"), but would be unnecessary work
* (and a measurable performance hit, maybe around 5%) since this value is
* constant over the entire glyph list. So instead we just calculate the
* gamma-adjusted value once and update the uniform parameter of the LCD
* shader as needed.
*/
static jboolean
{
"OGLTR_UpdateLCDTextColor: contrast=%d", contrast);
/*
* Note: Ideally we would update the "src_adj" uniform parameter only
* when there is a change in the source color. Fortunately, the cost
* of querying the current OpenGL color state and updating the uniform
* value is quite small, and in the common case we only need to do this
* once per GlyphList, so we gain little from trying to optimize too
* eagerly here.
*/
// get the current OpenGL primary color state
// gamma adjust the primary color
// update the "src_adj" parameter of the shader program with this value
return JNI_TRUE;
}
/**
* Enables the LCD text shader and updates any related state, such as the
* gamma lookup table textures.
*/
static jboolean
{
// bind the texture containing glyph data to texture unit 0
// bind the texture tile containing destination data to texture unit 1
if (cachedDestTextureID == 0) {
if (cachedDestTextureID == 0) {
return JNI_FALSE;
}
}
// note that GL_TEXTURE_2D was already enabled for texture unit 0,
// but we need to explicitly enable it for texture unit 1
// create the LCD text shader, if necessary
if (lcdTextProgram == 0) {
if (lcdTextProgram == 0) {
return JNI_FALSE;
}
}
// enable the LCD text shader
// update the current contrast settings, if necessary
if (lastLCDContrast != contrast) {
if (!OGLTR_UpdateLCDTextContrast(contrast)) {
return JNI_FALSE;
}
}
// update the current color settings
if (!OGLTR_UpdateLCDTextColor(contrast)) {
return JNI_FALSE;
}
// bind the gamma LUT textures
return JNI_TRUE;
}
void
{
if (!OGLVertexCache_InitVertexCache(oglc)) {
return;
}
if (glyphCache == NULL) {
if (!OGLTR_InitGlyphCache(JNI_FALSE)) {
return;
}
}
// for grayscale/monochrome text, the current OpenGL source color
// is modulated with the glyph image as part of the texture
// application stage, so we use GL_MODULATE here
}
void
{
}
/**
* Disables any pending state associated with the current "glyph mode".
*/
static void
{
switch (glyphMode) {
case MODE_NO_CACHE_LCD:
/* FALLTHROUGH */
case MODE_USE_CACHE_LCD:
break;
case MODE_NO_CACHE_GRAY:
case MODE_USE_CACHE_GRAY:
case MODE_NOT_INITED:
default:
break;
}
}
static jboolean
{
if (glyphMode != MODE_USE_CACHE_GRAY) {
}
// attempt to add glyph to accelerated glyph cache
// we'll just no-op in the rare case that the cell is NULL
return JNI_TRUE;
}
}
cell->timesRendered++;
return JNI_TRUE;
}
/**
* inside outerBounds.
*/
/**
* the rectangle defined by bounds.
*/
/**
* This method checks to see if the given LCD glyph bounds fall within the
* cached destination texture bounds. If so, this method can return
* immediately. If not, this method will copy a chunk of framebuffer data
* into the cached destination texture and then update the current cached
* destination bounds before returning.
*/
static void
{
// glyph is already within the cached destination bounds; no need
// to read back the entire destination region again, but we do
// need to see if the current glyph overlaps the previous glyph...
// the current glyph overlaps the destination region touched
// by the previous glyph, so now we need to read back the part
// of the destination corresponding to the previous glyph
// this accounts for lower-left origin of the destination region
// copy destination into subregion of cached texture tile:
// dx1-cachedDestBounds.x1 == +xoffset from left side of texture
// cachedDestBounds.y2-dy2 == +yoffset from bottom of texture
}
} else {
// destination region is not valid, so we need to read back a
// chunk of the destination into our cached texture
// position the upper-left corner of the destination region on the
// "top" line of glyph list
// REMIND: this isn't ideal; it would be better if we had some idea
// of the bounding box of the whole glyph list (this is
// do-able, but would require iterating through the whole
// list up front, which may present its own problems)
// estimate the width based on our current position in the glyph
// list and using the x advance of the current glyph (this is just
// a quick and dirty heuristic; if this is a "thin" glyph image,
// then we're likely to underestimate, and if it's "thick" then we
// may end up reading back more than we need to)
if (remainingWidth > OGLTR_CACHED_DEST_WIDTH) {
// in some cases, the x-advance may be slightly smaller
// than the actual width of the glyph; if so, adjust our
// estimate so that we can accomodate the entire glyph
}
} else {
// a negative advance is possible when rendering rotated text,
// in which case it is difficult to estimate an appropriate
// region for readback, so we will pick a region that
// encompasses just the current glyph
}
// estimate the height (this is another sloppy heuristic; we'll
// make the cached destination region tall enough to encompass most
// glyphs that are small enough to fit in the glyph cache, and then
// we add a little something extra to account for descenders
// this accounts for lower-left origin of the destination region
// copy destination into cached texture tile (the lower-left corner
// of the destination region will be positioned at the lower-left
// corner (0,0) of the texture)
// update the cached bounds and mark it valid
}
// always update the previous glyph bounds
}
static jboolean
{
if (glyphMode != MODE_USE_CACHE_LCD) {
if (glyphCache == NULL) {
if (!OGLTR_InitGlyphCache(JNI_TRUE)) {
return JNI_FALSE;
}
}
if (rgbOrder != lastRGBOrder) {
// need to invalidate the cache in this case; see comments
// for lastRGBOrder above
}
return JNI_FALSE;
}
// when a fragment shader is enabled, the texture function state is
// ignored, so the following line is not needed...
// OGLC_UPDATE_TEXTURE_FUNCTION(oglc, GL_MODULATE);
}
// rowBytes will always be a multiple of 3, so the following is safe
// make sure the glyph cache texture is bound to texture unit 0
// attempt to add glyph to accelerated glyph cache
// we'll just no-op in the rare case that the cell is NULL
return JNI_TRUE;
}
}
cell->timesRendered++;
// location of the glyph in the destination's coordinate space
dx1 = x;
dy1 = y;
// copy destination into second cached texture, if necessary
// texture coordinates of the destination tile
// render composed texture to the destination surface
j2d_glEnd();
return JNI_TRUE;
}
static jboolean
{
if (glyphMode != MODE_NO_CACHE_GRAY) {
}
x0 = x;
x = x0;
}
}
return JNI_TRUE;
}
static jboolean
{
if (glyphMode != MODE_NO_CACHE_LCD) {
if (oglc->blitTextureID == 0) {
if (!OGLContext_InitBlitTileTexture(oglc)) {
return JNI_FALSE;
}
}
return JNI_FALSE;
}
// when a fragment shader is enabled, the texture function state is
// ignored, so the following line is not needed...
// OGLC_UPDATE_TEXTURE_FUNCTION(oglc, GL_MODULATE);
}
// rowBytes will always be a multiple of 3, so the following is safe
x0 = x;
tx1 = 0.0f;
ty1 = 0.0f;
dtx1 = 0.0f;
dty2 = 0.0f;
x = x0;
// update the source pointer offsets
// copy LCD mask into glyph texture tile
// update the lower-right glyph texture coordinates
// this accounts for lower-left origin of the destination region
// copy destination into cached texture tile (the lower-left
// corner of the destination region will be positioned at the
// lower-left corner (0,0) of the texture)
0, 0,
// update the remaining destination texture coordinates
// render composed texture to the destination surface
j2d_glVertex2i(x, y);
j2d_glVertex2i(x + sw, y);
j2d_glVertex2i(x, y + sh);
j2d_glEnd();
}
}
return JNI_TRUE;
}
// see DrawGlyphList.c for more on this macro...
#define FLOOR_ASSIGN(l, r) \
if ((r)<0) (l) = ((int)floor(r)); else (l) = ((int)(r))
void
{
int glyphCounter;
if (usePositions) {
}
jint x, y;
// this shouldn't happen, but if it does we'll just break out...
"OGLTR_DrawGlyphList: glyph info is null");
break;
}
if (usePositions) {
FLOOR_ASSIGN(x, glyphx);
FLOOR_ASSIGN(y, glyphy);
} else {
FLOOR_ASSIGN(x, glyphx);
FLOOR_ASSIGN(y, glyphy);
}
continue;
}
if (grayscale) {
// grayscale or monochrome glyph data
if (cacheStatus != CACHE_LCD &&
{
} else {
}
} else {
// LCD-optimized glyph data
if (subPixPos) {
if (frac != 0) {
x += 1;
}
}
if (rowBytesOffset == 0 &&
cacheStatus != CACHE_GRAY &&
{
ginfo, x, y,
} else {
ginfo, x, y,
}
}
if (!ok) {
break;
}
}
}
{
unsigned char *images;
images = (unsigned char *)
if (usePositions) {
unsigned char *positions = (unsigned char *)
}
} else {
}
// 6358147: reset current state, and ensure rendering is
// flushed to dest
j2d_glFlush();
}
}
}
#endif /* !HEADLESS */