static jboolean OGLTR_DrawLCDGlyphViaCache(OGLContext *oglc, OGLSDOps *dstOps, GlyphInfo *ginfo, jint x, jint y, jint glyphIndex, jint totalGlyphs, jboolean rgbOrder, jint contrast) { CacheCellInfo *cell; jint dx1, dy1, dx2, dy2; jfloat dtx1, dty1, dtx2, dty2; if (glyphMode != MODE_USE_CACHE_LCD) { OGLTR_DisableGlyphModeState(); CHECK_PREVIOUS_OP(GL_TEXTURE_2D); j2d_glPixelStorei(GL_UNPACK_ALIGNMENT, 1); 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 AccelGlyphCache_Invalidate(glyphCache); lastRGBOrder = rgbOrder; } if (!OGLTR_EnableLCDGlyphModeState(glyphCache->cacheID, contrast)) { 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); glyphMode = MODE_USE_CACHE_LCD; } if (ginfo->cellInfo == NULL) { // rowBytes will always be a multiple of 3, so the following is safe j2d_glPixelStorei(GL_UNPACK_ROW_LENGTH, ginfo->rowBytes / 3); // make sure the glyph cache texture is bound to texture unit 0 j2d_glActiveTextureARB(GL_TEXTURE0_ARB); // attempt to add glyph to accelerated glyph cache OGLTR_AddToGlyphCache(ginfo, rgbOrder); if (ginfo->cellInfo == NULL) { // we'll just no-op in the rare case that the cell is NULL return JNI_TRUE; } } cell = (CacheCellInfo *) (ginfo->cellInfo); cell->timesRendered++; // location of the glyph in the destination's coordinate space dx1 = x; dy1 = y; dx2 = dx1 + ginfo->width; dy2 = dy1 + ginfo->height; // copy destination into second cached texture, if necessary OGLTR_UpdateCachedDestination(dstOps, ginfo, dx1, dy1, dx2, dy2, glyphIndex, totalGlyphs); // texture coordinates of the destination tile dtx1 = ((jfloat)(dx1 - cachedDestBounds.x1)) / OGLTR_CACHED_DEST_WIDTH; dty1 = ((jfloat)(cachedDestBounds.y2 - dy1)) / OGLTR_CACHED_DEST_HEIGHT; dtx2 = ((jfloat)(dx2 - cachedDestBounds.x1)) / OGLTR_CACHED_DEST_WIDTH; dty2 = ((jfloat)(cachedDestBounds.y2 - dy2)) / OGLTR_CACHED_DEST_HEIGHT; // render composed texture to the destination surface j2d_glBegin(GL_QUADS); j2d_glMultiTexCoord2fARB(GL_TEXTURE0_ARB, cell->tx1, cell->ty1); j2d_glMultiTexCoord2fARB(GL_TEXTURE1_ARB, dtx1, dty1); j2d_glVertex2i(dx1, dy1); j2d_glMultiTexCoord2fARB(GL_TEXTURE0_ARB, cell->tx2, cell->ty1); j2d_glMultiTexCoord2fARB(GL_TEXTURE1_ARB, dtx2, dty1); j2d_glVertex2i(dx2, dy1); j2d_glMultiTexCoord2fARB(GL_TEXTURE0_ARB, cell->tx2, cell->ty2); j2d_glMultiTexCoord2fARB(GL_TEXTURE1_ARB, dtx2, dty2); j2d_glVertex2i(dx2, dy2); j2d_glMultiTexCoord2fARB(GL_TEXTURE0_ARB, cell->tx1, cell->ty2); j2d_glMultiTexCoord2fARB(GL_TEXTURE1_ARB, dtx1, dty2); j2d_glVertex2i(dx1, dy2); j2d_glEnd(); return JNI_TRUE; }
static jboolean OGLTR_DrawLCDGlyphNoCache(OGLContext *oglc, OGLSDOps *dstOps, GlyphInfo *ginfo, jint x, jint y, jint rowBytesOffset, jboolean rgbOrder, jint contrast) { GLfloat tx1, ty1, tx2, ty2; GLfloat dtx1, dty1, dtx2, dty2; jint tw, th; jint sx, sy, sw, sh, dxadj, dyadj; jint x0; jint w = ginfo->width; jint h = ginfo->height; GLenum pixelFormat = rgbOrder ? GL_RGB : GL_BGR; if (glyphMode != MODE_NO_CACHE_LCD) { OGLTR_DisableGlyphModeState(); CHECK_PREVIOUS_OP(GL_TEXTURE_2D); j2d_glPixelStorei(GL_UNPACK_ALIGNMENT, 1); if (oglc->blitTextureID == 0) { if (!OGLContext_InitBlitTileTexture(oglc)) { return JNI_FALSE; } } if (!OGLTR_EnableLCDGlyphModeState(oglc->blitTextureID, contrast)) { 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); glyphMode = MODE_NO_CACHE_LCD; } // rowBytes will always be a multiple of 3, so the following is safe j2d_glPixelStorei(GL_UNPACK_ROW_LENGTH, ginfo->rowBytes / 3); x0 = x; tx1 = 0.0f; ty1 = 0.0f; dtx1 = 0.0f; dty2 = 0.0f; tw = OGLTR_NOCACHE_TILE_SIZE; th = OGLTR_NOCACHE_TILE_SIZE; for (sy = 0; sy < h; sy += th, y += th) { x = x0; sh = ((sy + th) > h) ? (h - sy) : th; for (sx = 0; sx < w; sx += tw, x += tw) { sw = ((sx + tw) > w) ? (w - sx) : tw; // update the source pointer offsets j2d_glPixelStorei(GL_UNPACK_SKIP_PIXELS, sx); j2d_glPixelStorei(GL_UNPACK_SKIP_ROWS, sy); // copy LCD mask into glyph texture tile j2d_glActiveTextureARB(GL_TEXTURE0_ARB); j2d_glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, sw, sh, pixelFormat, GL_UNSIGNED_BYTE, ginfo->image + rowBytesOffset); // update the lower-right glyph texture coordinates tx2 = ((GLfloat)sw) / OGLC_BLIT_TILE_SIZE; ty2 = ((GLfloat)sh) / OGLC_BLIT_TILE_SIZE; // this accounts for lower-left origin of the destination region dxadj = dstOps->xOffset + x; dyadj = dstOps->yOffset + dstOps->height - (y + sh); // 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) j2d_glActiveTextureARB(GL_TEXTURE1_ARB); j2d_glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, dxadj, dyadj, sw, sh); // update the remaining destination texture coordinates dtx2 = ((GLfloat)sw) / OGLTR_CACHED_DEST_WIDTH; dty1 = ((GLfloat)sh) / OGLTR_CACHED_DEST_HEIGHT; // render composed texture to the destination surface j2d_glBegin(GL_QUADS); j2d_glMultiTexCoord2fARB(GL_TEXTURE0_ARB, tx1, ty1); j2d_glMultiTexCoord2fARB(GL_TEXTURE1_ARB, dtx1, dty1); j2d_glVertex2i(x, y); j2d_glMultiTexCoord2fARB(GL_TEXTURE0_ARB, tx2, ty1); j2d_glMultiTexCoord2fARB(GL_TEXTURE1_ARB, dtx2, dty1); j2d_glVertex2i(x + sw, y); j2d_glMultiTexCoord2fARB(GL_TEXTURE0_ARB, tx2, ty2); j2d_glMultiTexCoord2fARB(GL_TEXTURE1_ARB, dtx2, dty2); j2d_glVertex2i(x + sw, y + sh); j2d_glMultiTexCoord2fARB(GL_TEXTURE0_ARB, tx1, ty2); j2d_glMultiTexCoord2fARB(GL_TEXTURE1_ARB, dtx1, dty2); j2d_glVertex2i(x, y + sh); j2d_glEnd(); } } return JNI_TRUE; }
/** * Used to track whether we are within a series of simple primitive operations * or texturing operations. The op parameter determines the nature of the * operation that is to follow. Valid values for this op parameter are: * * GL_QUADS * GL_LINES * GL_LINE_LOOP * GL_LINE_STRIP * (basically any of the valid parameters for glBegin()) * * GL_TEXTURE_2D * GL_TEXTURE_RECTANGLE_ARB * * OGL_STATE_RESET * OGL_STATE_CHANGE * OGL_STATE_MASK_OP * OGL_STATE_GLYPH_OP * * Note that the above constants are guaranteed to be unique values. The * last few are defined to be negative values to differentiate them from * the core GL* constants, which are defined to be non-negative. * * For simple primitives, this method allows us to batch similar primitives * within the same glBegin()/glEnd() pair. For example, if we have 100 * consecutive FILL_RECT operations, we only have to call glBegin(GL_QUADS) * for the first op, and then subsequent operations will consist only of * glVertex*() calls, which helps improve performance. The glEnd() call * only needs to be issued before an operation that cannot happen within a * glBegin()/glEnd() pair (e.g. updating the clip), or one that requires a * different primitive mode (e.g. GL_LINES). * * For operations that involve texturing, this method helps us to avoid * calling glEnable(GL_TEXTURE_2D) and glDisable(GL_TEXTURE_2D) around each * operation. For example, if we have an alternating series of ISO_BLIT * and MASK_BLIT operations (both of which involve texturing), we need * only to call glEnable(GL_TEXTURE_2D) before the first ISO_BLIT operation. * The glDisable(GL_TEXTURE_2D) call only needs to be issued before an * operation that cannot (or should not) happen while texturing is enabled * (e.g. a context change, or a simple primitive operation like GL_QUADS). */ void OGLRenderQueue_CheckPreviousOp(jint op) { if (previousOp == op) { // The op is the same as last time, so we can return immediately. return; } J2dTraceLn1(J2D_TRACE_VERBOSE, "OGLRenderQueue_CheckPreviousOp: new op=%d", op); switch (previousOp) { case GL_TEXTURE_2D: case GL_TEXTURE_RECTANGLE_ARB: if (op == OGL_STATE_CHANGE) { // Optimization: Certain state changes (those marked as // OGL_STATE_CHANGE) are allowed while texturing is enabled. // In this case, we can allow previousOp to remain as it is and // then return early. return; } else { // Otherwise, op must be a primitive operation, or a reset, so // we will disable texturing. j2d_glDisable(previousOp); // This next step of binding to zero should not be strictly // necessary, but on some older Nvidia boards (e.g. GeForce 2) // problems will arise if GL_TEXTURE_2D and // GL_TEXTURE_RECTANGLE_ARB are bound at the same time, so we // will do this just to be safe. j2d_glBindTexture(previousOp, 0); } break; case OGL_STATE_MASK_OP: OGLVertexCache_DisableMaskCache(oglc); break; case OGL_STATE_GLYPH_OP: OGLTR_DisableGlyphVertexCache(oglc); break; case OGL_STATE_PGRAM_OP: OGLRenderer_DisableAAParallelogramProgram(); break; case OGL_STATE_RESET: case OGL_STATE_CHANGE: // No-op break; default: // In this case, op must be one of: // - the start of a different primitive type (glBegin()) // - a texturing operation // - a state change (not allowed within glBegin()/glEnd() pairs) // - a reset // so we must first complete the previous primitive operation. j2d_glEnd(); break; } switch (op) { case GL_TEXTURE_2D: case GL_TEXTURE_RECTANGLE_ARB: // We are starting a texturing operation, so enable texturing. j2d_glEnable(op); break; case OGL_STATE_MASK_OP: OGLVertexCache_EnableMaskCache(oglc); break; case OGL_STATE_GLYPH_OP: OGLTR_EnableGlyphVertexCache(oglc); break; case OGL_STATE_PGRAM_OP: OGLRenderer_EnableAAParallelogramProgram(); break; case OGL_STATE_RESET: case OGL_STATE_CHANGE: // No-op break; default: // We are starting a primitive operation, so call glBegin() with // the given primitive type. j2d_glBegin(op); break; } previousOp = op; }