alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Java example source code file (D3DTextRenderer.cpp)

This example Java source code file (D3DTextRenderer.cpp) is included in the alvinalexander.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Learn more about this Java project at its project page.

Java - Java tags/keywords

d3dsdops, d3dtexf_none, d3dtr_cached_dest_width, d3dtr_disableglyphmodestate, d3dtr_drawglyphlist, e_fail, floor_assign, glyphinfo, hresult, idirect3ddevice9, j2dtraceln, return_status_if_failed, return_status_if_null, s_ok

The D3DTextRenderer.cpp Java example source code

/*
 * Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * 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.
 */

#include <malloc.h>
#include <math.h>
#include <jlong.h>

#include "sun_java2d_d3d_D3DTextRenderer.h"
#include "sun_java2d_pipe_BufferedTextPipe.h"

#include "SurfaceData.h"
#include "D3DContext.h"
#include "D3DSurfaceData.h"
#include "D3DRenderQueue.h"
#include "D3DTextRenderer.h"
#include "D3DGlyphCache.h"
#include "AccelGlyphCache.h"
#include "fontscalerdefs.h"

/**
 * 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 D3DTR_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 {
    MODE_NOT_INITED,
    MODE_USE_CACHE_GRAY,
    MODE_USE_CACHE_LCD,
    MODE_NO_CACHE_GRAY,
    MODE_NO_CACHE_LCD
} GlyphMode;
static GlyphMode glyphMode = MODE_NOT_INITED;

/**
 * The current bounds of the "cached destination" texture, in destination
 * coordinate space.  The width/height of these bounds will not exceed the
 * D3DTR_CACHED_DEST_WIDTH/HEIGHT values defined above.  These bounds are
 * only considered valid when the isCachedDestValid flag is JNI_TRUE.
 */
static SurfaceDataBounds cachedDestBounds;

/**
 * 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 D3DTR_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.
 */
static jboolean isCachedDestValid = JNI_FALSE;

/**
 * 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.
 */
static SurfaceDataBounds previousGlyphBounds;

/**
 * Updates the gamma and inverse gamma values for the LCD text shader.
 */
static HRESULT
D3DTR_UpdateLCDTextContrast(D3DContext *d3dc, jint contrast)
{
    HRESULT res;
    IDirect3DDevice9 *pd3dDevice = d3dc->Get3DDevice();

    jfloat fcon = ((jfloat)contrast) / 100.0f;
    jfloat invgamma = fcon;
    jfloat gamma = 1.0f / invgamma;
    jfloat vals[4];

    // update the "invgamma" parameter of the shader program
    vals[0] = invgamma;
    vals[1] = invgamma;
    vals[2] = invgamma;
    vals[3] = 0.0f; // unused
    pd3dDevice->SetPixelShaderConstantF(1, vals, 1);

    // update the "gamma" parameter of the shader program
    vals[0] = gamma;
    vals[1] = gamma;
    vals[2] = gamma;
    vals[3] = 0.0f; // unused
    res = pd3dDevice->SetPixelShaderConstantF(2, vals, 1);

    return res;
}

/**
 * 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 HRESULT
D3DTR_UpdateLCDTextColor(D3DContext *d3dc, jint contrast)
{
    IDirect3DDevice9 *pd3dDevice = d3dc->Get3DDevice();
    jfloat gamma = ((jfloat)contrast) / 100.0f;
    jfloat clr[4];

    J2dTraceLn1(J2D_TRACE_INFO,
                "D3DTR_UpdateLCDTextColor: contrast=%d", contrast);

    /*
     * Note: Ideally we would update the "srcAdj" uniform parameter only
     * when there is a change in the source color.  Fortunately, the cost
     * of querying the current D3D 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 D3D primary color state
    jint color = d3dc->pVCacher->GetColor();
    clr[0] = (jfloat)((color >> 16) & 0xff) / 255.0f;
    clr[1] = (jfloat)((color >>  8) & 0xff) / 255.0f;
    clr[2] = (jfloat)((color >>  0) & 0xff) / 255.0f;
    clr[3] = 0.0f; // unused

    // gamma adjust the primary color
    clr[0] = (jfloat)pow(clr[0], gamma);
    clr[1] = (jfloat)pow(clr[1], gamma);
    clr[2] = (jfloat)pow(clr[2], gamma);

    // update the "srcAdj" parameter of the shader program with this value
    return pd3dDevice->SetPixelShaderConstantF(0, clr, 1);
}

/**
 * Enables the LCD text shader and updates any related state, such as the
 * gamma values.
 */
static HRESULT
D3DTR_EnableLCDGlyphModeState(D3DContext *d3dc, D3DSDOps *dstOps,
                              jboolean useCache, jint contrast)
{
    D3DResource *pGlyphTexRes, *pCachedDestTexRes;
    IDirect3DTexture9 *pGlyphTex, *pCachedDestTex;

    RETURN_STATUS_IF_NULL(dstOps->pResource, E_FAIL);

    HRESULT res = S_OK;
    if (useCache) {
        // glyph cache had been already initialized
        pGlyphTexRes = d3dc->GetLCDGlyphCache()->GetGlyphCacheTexture();
    } else {
        res = d3dc->GetResourceManager()->GetBlitTexture(&pGlyphTexRes);
    }
    RETURN_STATUS_IF_FAILED(res);

    pGlyphTex = pGlyphTexRes->GetTexture();

    res = d3dc->GetResourceManager()->
        GetCachedDestTexture(dstOps->pResource->GetDesc()->Format,
                             &pCachedDestTexRes);
    RETURN_STATUS_IF_FAILED(res);
    pCachedDestTex = pCachedDestTexRes->GetTexture();

    IDirect3DDevice9 *pd3dDevice = d3dc->Get3DDevice();
    D3DTEXTUREFILTERTYPE fhint =
        d3dc->IsTextureFilteringSupported(D3DTEXF_NONE) ?
        D3DTEXF_NONE : D3DTEXF_POINT;
    pd3dDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, fhint);
    pd3dDevice->SetSamplerState(0, D3DSAMP_MINFILTER, fhint);
    pd3dDevice->SetSamplerState(1, D3DSAMP_MAGFILTER, fhint);
    pd3dDevice->SetSamplerState(1, D3DSAMP_MINFILTER, fhint);
    d3dc->UpdateTextureColorState(D3DTA_TEXTURE, 1);

    // bind the texture containing glyph data to texture unit 0
    d3dc->SetTexture(pGlyphTex, 0);

    // bind the texture tile containing destination data to texture unit 1
    d3dc->SetTexture(pCachedDestTex, 1);

    // create/enable the LCD text shader
    res = d3dc->EnableLCDTextProgram();
    RETURN_STATUS_IF_FAILED(res);

    // update the current contrast settings (note: these change very rarely,
    // but it seems that D3D pixel shader registers aren't maintained as
    // part of the pixel shader instance, so we need to update these
    // everytime around in case another shader blew away the contents
    // of those registers)
    D3DTR_UpdateLCDTextContrast(d3dc, contrast);

    // update the current color settings
    return D3DTR_UpdateLCDTextColor(d3dc, contrast);
}

HRESULT
D3DTR_EnableGlyphVertexCache(D3DContext *d3dc)
{
    J2dTraceLn(J2D_TRACE_INFO, "D3DTR_EnableGlyphVertexCache");

    IDirect3DDevice9 *pd3dDevice = d3dc->Get3DDevice();
    D3DTEXTUREFILTERTYPE fhint =
        d3dc->IsTextureFilteringSupported(D3DTEXF_NONE) ?
        D3DTEXF_NONE : D3DTEXF_POINT;
    pd3dDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, fhint);
    pd3dDevice->SetSamplerState(0, D3DSAMP_MINFILTER, fhint);

    // glyph cache had been successfully initialized if we got here
    D3DResource *pGlyphCacheTexRes =
        d3dc->GetGrayscaleGlyphCache()->GetGlyphCacheTexture();
    return d3dc->SetTexture(pGlyphCacheTexRes->GetTexture(), 0);
}

HRESULT
D3DTR_DisableGlyphVertexCache(D3DContext *d3dc)
{
    J2dTraceLn(J2D_TRACE_INFO, "D3DTR_DisableGlyphVertexCache");

    return d3dc->SetTexture(NULL, 0);
}

/**
 * Disables any pending state associated with the current "glyph mode".
 */
static HRESULT
D3DTR_DisableGlyphModeState(D3DContext *d3dc)
{
    HRESULT res = S_OK;
    IDirect3DDevice9 *pd3dDevice = d3dc->Get3DDevice();

    switch (glyphMode) {
    case MODE_NO_CACHE_LCD:
    case MODE_USE_CACHE_LCD:
        d3dc->FlushVertexQueue();
        pd3dDevice->SetPixelShader(NULL);
        res = d3dc->SetTexture(NULL, 1);
        break;

    case MODE_NO_CACHE_GRAY:
    case MODE_USE_CACHE_GRAY:
    case MODE_NOT_INITED:
    default:
        break;
    }
    return res;
}

static HRESULT
D3DTR_DrawGrayscaleGlyphViaCache(D3DContext *d3dc,
                                 GlyphInfo *ginfo, jint x, jint y)
{
    HRESULT res = S_OK;
    D3DGlyphCache *pGrayscaleGCache;
    CacheCellInfo *cell;
    GlyphCacheInfo *gcache;
    jfloat x1, y1, x2, y2;

    J2dTraceLn(J2D_TRACE_VERBOSE, "D3DTR_DrawGrayscaleGlyphViaCache");

    if (glyphMode != MODE_USE_CACHE_GRAY) {
        D3DTR_DisableGlyphModeState(d3dc);

        res = d3dc->BeginScene(STATE_GLYPHOP);
        RETURN_STATUS_IF_FAILED(res);

        glyphMode = MODE_USE_CACHE_GRAY;
    }

    pGrayscaleGCache = d3dc->GetGrayscaleGlyphCache();
    gcache = pGrayscaleGCache->GetGlyphCache();
    cell = AccelGlyphCache_GetCellInfoForCache(ginfo, gcache);
    if (cell == NULL) {
        // attempt to add glyph to accelerated glyph cache
        res = pGrayscaleGCache->AddGlyph(ginfo);
        RETURN_STATUS_IF_FAILED(res);

        cell = AccelGlyphCache_GetCellInfoForCache(ginfo, gcache);
        RETURN_STATUS_IF_NULL(cell, E_FAIL);
    }

    cell->timesRendered++;

    x1 = (jfloat)x;
    y1 = (jfloat)y;
    x2 = x1 + ginfo->width;
    y2 = y1 + ginfo->height;

    return d3dc->pVCacher->DrawTexture(x1, y1, x2, y2,
                                       cell->tx1, cell->ty1,
                                       cell->tx2, cell->ty2);
}

/**
 * Evaluates to true if the rectangle defined by gx1/gy1/gx2/gy2 is
 * inside outerBounds.
 */
#define INSIDE(gx1, gy1, gx2, gy2, outerBounds) \
    (((gx1) >= outerBounds.x1) && ((gy1) >= outerBounds.y1) && \
     ((gx2) <= outerBounds.x2) && ((gy2) <= outerBounds.y2))

/**
 * Evaluates to true if the rectangle defined by gx1/gy1/gx2/gy2 intersects
 * the rectangle defined by bounds.
 */
#define INTERSECTS(gx1, gy1, gx2, gy2, bounds) \
    ((bounds.x2   > (gx1)) && (bounds.y2 > (gy1)) && \
     (bounds.x1   < (gx2)) && (bounds.y1 < (gy2)))

/**
 * 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.
 *
 * The agx1, agx2 are "adjusted" glyph bounds, which are only used when checking
 * against the previous glyph bounds.
 */
static HRESULT
D3DTR_UpdateCachedDestination(D3DContext *d3dc, D3DSDOps *dstOps,
                              GlyphInfo *ginfo,
                              jint gx1, jint gy1, jint gx2, jint gy2,
                              jint agx1, jint agx2,
                              jint glyphIndex, jint totalGlyphs)
{
    jint dx1, dy1, dx2, dy2;
    D3DResource *pCachedDestTexRes;
    IDirect3DSurface9 *pCachedDestSurface, *pDst;
    HRESULT res;

    if (isCachedDestValid && INSIDE(gx1, gy1, gx2, gy2, cachedDestBounds)) {
        // 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...

        // only use the "adjusted" glyph bounds when checking against
        // previous glyph's bounds
        gx1 = agx1;
        gx2 = agx2;

        if (INTERSECTS(gx1, gy1, gx2, gy2, previousGlyphBounds)) {
            // 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
            dx1 = previousGlyphBounds.x1;
            dy1 = previousGlyphBounds.y1;
            dx2 = previousGlyphBounds.x2;
            dy2 = previousGlyphBounds.y2;

            // REMIND: make sure we flush any pending primitives that are
            // dependent on the current contents of the cached dest
            d3dc->FlushVertexQueue();

            RETURN_STATUS_IF_NULL(dstOps->pResource, E_FAIL);
            RETURN_STATUS_IF_NULL(pDst = dstOps->pResource->GetSurface(),
                                  E_FAIL);
            res = d3dc->GetResourceManager()->
                GetCachedDestTexture(dstOps->pResource->GetDesc()->Format,
                                     &pCachedDestTexRes);
            RETURN_STATUS_IF_FAILED(res);
            pCachedDestSurface = pCachedDestTexRes->GetSurface();

            // now dxy12 represent the "desired" destination bounds, but the
            // StretchRect() call may fail if these fall outside the actual
            // surface bounds; therefore, we use cxy12 to represent the
            // clamped bounds, and dxy12 are saved for later
            jint cx1 = (dx1 < 0) ? 0 : dx1;
            jint cy1 = (dy1 < 0) ? 0 : dy1;
            jint cx2 = (dx2 > dstOps->width)  ? dstOps->width  : dx2;
            jint cy2 = (dy2 > dstOps->height) ? dstOps->height : dy2;

            if (cx2 > cx1 && cy2 > cy1) {
                // copy destination into subregion of cached texture tile
                //   cx1-cachedDestBounds.x1 == +xoffset from left of texture
                //   cy1-cachedDestBounds.y1 == +yoffset from top of texture
                //   cx2-cachedDestBounds.x1 == +xoffset from left of texture
                //   cy2-cachedDestBounds.y1 == +yoffset from top of texture
                jint cdx1 = cx1-cachedDestBounds.x1;
                jint cdy1 = cy1-cachedDestBounds.y1;
                jint cdx2 = cx2-cachedDestBounds.x1;
                jint cdy2 = cy2-cachedDestBounds.y1;
                RECT srcRect = {  cx1,  cy1,  cx2,  cy2 };
                RECT dstRect = { cdx1, cdy1, cdx2, cdy2 };

                IDirect3DDevice9 *pd3dDevice = d3dc->Get3DDevice();
                res = pd3dDevice->StretchRect(pDst, &srcRect,
                                              pCachedDestSurface, &dstRect,
                                              D3DTEXF_NONE);
            }
        }
    } 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)
        dx1 = gx1;
        dy1 = gy1;

        jint remainingWidth;
        if (ginfo->advanceX > 0) {
            // 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)
            remainingWidth =
                (jint)(ginfo->advanceX * (totalGlyphs - glyphIndex));
            if (remainingWidth > D3DTR_CACHED_DEST_WIDTH) {
                remainingWidth = D3DTR_CACHED_DEST_WIDTH;
            } else if (remainingWidth < ginfo->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 accommodate the entire glyph
                remainingWidth = ginfo->width;
            }
        } 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
            remainingWidth = ginfo->width;
        }
        dx2 = dx1 + remainingWidth;

        // 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
        dy2 = dy1 + D3DTR_CACHE_CELL_HEIGHT + 2;

        // REMIND: make sure we flush any pending primitives that are
        // dependent on the current contents of the cached dest
        d3dc->FlushVertexQueue();

        RETURN_STATUS_IF_NULL(dstOps->pResource, E_FAIL);
        RETURN_STATUS_IF_NULL(pDst = dstOps->pResource->GetSurface(), E_FAIL);
        res = d3dc->GetResourceManager()->
            GetCachedDestTexture(dstOps->pResource->GetDesc()->Format,
                                 &pCachedDestTexRes);
        RETURN_STATUS_IF_FAILED(res);
        pCachedDestSurface = pCachedDestTexRes->GetSurface();

        // now dxy12 represent the "desired" destination bounds, but the
        // StretchRect() call may fail if these fall outside the actual
        // surface bounds; therefore, we use cxy12 to represent the
        // clamped bounds, and dxy12 are saved for later
        jint cx1 = (dx1 < 0) ? 0 : dx1;
        jint cy1 = (dy1 < 0) ? 0 : dy1;
        jint cx2 = (dx2 > dstOps->width)  ? dstOps->width  : dx2;
        jint cy2 = (dy2 > dstOps->height) ? dstOps->height : dy2;

        if (cx2 > cx1 && cy2 > cy1) {
            // copy destination into cached texture tile (the upper-left
            // corner of the destination region will be positioned at the
            // upper-left corner (0,0) of the texture)
            RECT srcRect = { cx1, cy1, cx2, cy2 };
            RECT dstRect = { cx1-dx1, cy1-dy1, cx2-dx1, cy2-dy1 };

            IDirect3DDevice9 *pd3dDevice = d3dc->Get3DDevice();
            res = pd3dDevice->StretchRect(pDst, &srcRect,
                                          pCachedDestSurface, &dstRect,
                                          D3DTEXF_NONE);
        }

        // update the cached bounds and mark it valid
        cachedDestBounds.x1 = dx1;
        cachedDestBounds.y1 = dy1;
        cachedDestBounds.x2 = dx2;
        cachedDestBounds.y2 = dy2;
        isCachedDestValid = JNI_TRUE;
    }

    // always update the previous glyph bounds
    previousGlyphBounds.x1 = gx1;
    previousGlyphBounds.y1 = gy1;
    previousGlyphBounds.x2 = gx2;
    previousGlyphBounds.y2 = gy2;

    return res;
}

static HRESULT
D3DTR_DrawLCDGlyphViaCache(D3DContext *d3dc, D3DSDOps *dstOps,
                           GlyphInfo *ginfo, jint x, jint y,
                           jint glyphIndex, jint totalGlyphs,
                           jboolean rgbOrder, jint contrast)
{
    HRESULT res;
    D3DGlyphCache *pLCDGCache;
    CacheCellInfo *cell;
    GlyphCacheInfo *gcache;
    jint dx1, dy1, dx2, dy2;
    jfloat dtx1, dty1, dtx2, dty2;

    J2dTraceLn(J2D_TRACE_VERBOSE, "D3DTR_DrawLCDGlyphViaCache");

    // the glyph cache is initialized before this method is called
    pLCDGCache = d3dc->GetLCDGlyphCache();

    if (glyphMode != MODE_USE_CACHE_LCD) {
        D3DTR_DisableGlyphModeState(d3dc);

        res = d3dc->BeginScene(STATE_TEXTUREOP);
        RETURN_STATUS_IF_FAILED(res);

        pLCDGCache->CheckGlyphCacheByteOrder(rgbOrder);

        res = D3DTR_EnableLCDGlyphModeState(d3dc, dstOps, JNI_TRUE, contrast);
        RETURN_STATUS_IF_FAILED(res);

        glyphMode = MODE_USE_CACHE_LCD;
    }

    gcache = pLCDGCache->GetGlyphCache();
    cell = AccelGlyphCache_GetCellInfoForCache(ginfo, gcache);
    if (cell == NULL) {
        // attempt to add glyph to accelerated glyph cache
        res = pLCDGCache->AddGlyph(ginfo);
        RETURN_STATUS_IF_FAILED(res);

        // we'll just no-op in the rare case that the cell is NULL
        cell = AccelGlyphCache_GetCellInfoForCache(ginfo, gcache);
        RETURN_STATUS_IF_NULL(cell, E_FAIL);
    }

    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
    D3DTR_UpdateCachedDestination(d3dc,
                                  dstOps, ginfo,
                                  dx1, dy1,
                                  dx2, dy2,
                                  dx1 + cell->leftOff,  // adjusted dx1
                                  dx2 + cell->rightOff, // adjusted dx2
                                  glyphIndex, totalGlyphs);

    // texture coordinates of the destination tile
    dtx1 = ((jfloat)(dx1 - cachedDestBounds.x1)) / D3DTR_CACHED_DEST_WIDTH;
    dty1 = ((jfloat)(dy1 - cachedDestBounds.y1)) / D3DTR_CACHED_DEST_HEIGHT;
    dtx2 = ((jfloat)(dx2 - cachedDestBounds.x1)) / D3DTR_CACHED_DEST_WIDTH;
    dty2 = ((jfloat)(dy2 - cachedDestBounds.y1)) / D3DTR_CACHED_DEST_HEIGHT;

    // render composed texture to the destination surface
    return d3dc->pVCacher->DrawTexture((jfloat)dx1, (jfloat)dy1,
                                       (jfloat)dx2, (jfloat)dy2,
                                        cell->tx1, cell->ty1,
                                        cell->tx2, cell->ty2,
                                        dtx1, dty1, dtx2, dty2);
}

static HRESULT
D3DTR_DrawGrayscaleGlyphNoCache(D3DContext *d3dc,
                                GlyphInfo *ginfo, jint x, jint y)
{
    jint tw, th;
    jint sx, sy, sw, sh;
    jint x0;
    jint w = ginfo->width;
    jint h = ginfo->height;
    HRESULT res = S_OK;

    J2dTraceLn(J2D_TRACE_VERBOSE, "D3DTR_DrawGrayscaleGlyphNoCache");

    if (glyphMode != MODE_NO_CACHE_GRAY) {
        D3DTR_DisableGlyphModeState(d3dc);

        res = d3dc->BeginScene(STATE_MASKOP);
        RETURN_STATUS_IF_FAILED(res);

        glyphMode = MODE_NO_CACHE_GRAY;
    }

    x0 = x;
    tw = D3D_MASK_CACHE_TILE_WIDTH;
    th = D3D_MASK_CACHE_TILE_HEIGHT;

    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;

            res = d3dc->GetMaskCache()->AddMaskQuad(sx, sy, x, y, sw, sh,
                                                    w, ginfo->image);
        }
    }

    return res;
}

static HRESULT
D3DTR_DrawLCDGlyphNoCache(D3DContext *d3dc, D3DSDOps *dstOps,
                          GlyphInfo *ginfo, jint x, jint y,
                          jint rowBytesOffset,
                          jboolean rgbOrder, jint contrast)
{
    jfloat tx1, ty1, tx2, ty2;
    jfloat dx1, dy1, dx2, dy2;
    jfloat dtx1, dty1, dtx2, dty2;
    jint tw, th;
    jint sx, sy, sw, sh;
    jint cx1, cy1, cx2, cy2;
    jint x0;
    jint w = ginfo->width;
    jint h = ginfo->height;
    TileFormat tileFormat = rgbOrder ? TILEFMT_3BYTE_RGB : TILEFMT_3BYTE_BGR;

    IDirect3DDevice9 *pd3dDevice = d3dc->Get3DDevice();
    D3DResource *pBlitTextureRes, *pCachedDestTextureRes;
    IDirect3DTexture9 *pBlitTexture;
    IDirect3DSurface9 *pCachedDestSurface, *pDst;
    HRESULT res;

    J2dTraceLn(J2D_TRACE_VERBOSE, "D3DTR_DrawLCDGlyphNoCache");

    RETURN_STATUS_IF_NULL(dstOps->pResource, E_FAIL);
    RETURN_STATUS_IF_NULL(pDst = dstOps->pResource->GetSurface(), E_FAIL);

    res = d3dc->GetResourceManager()->GetBlitTexture(&pBlitTextureRes);
    RETURN_STATUS_IF_FAILED(res);

    res = d3dc->GetResourceManager()->
        GetCachedDestTexture(dstOps->pResource->GetDesc()->Format,
                             &pCachedDestTextureRes);
    RETURN_STATUS_IF_FAILED(res);

    pBlitTexture = pBlitTextureRes->GetTexture();
    pCachedDestSurface = pCachedDestTextureRes->GetSurface();

    if (glyphMode != MODE_NO_CACHE_LCD) {
        D3DTR_DisableGlyphModeState(d3dc);

        res = d3dc->BeginScene(STATE_TEXTUREOP);
        RETURN_STATUS_IF_FAILED(res);
        res = D3DTR_EnableLCDGlyphModeState(d3dc,dstOps, JNI_FALSE, contrast);
        RETURN_STATUS_IF_FAILED(res);

        glyphMode = MODE_NO_CACHE_LCD;
    }

    x0 = x;
    tx1 = 0.0f;
    ty1 = 0.0f;
    dtx1 = 0.0f;
    dty1 = 0.0f;
    tw = D3DTR_NOCACHE_TILE_SIZE;
    th = D3DTR_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;

            // calculate the bounds of the tile to be copied from the
            // destination into the cached tile
            cx1 = x;
            cy1 = y;
            cx2 = cx1 + sw;
            cy2 = cy1 + sh;

            // need to clamp to the destination bounds, otherwise the
            // StretchRect() call may fail
            if (cx1 < 0)              cx1 = 0;
            if (cy1 < 0)              cy1 = 0;
            if (cx2 > dstOps->width)  cx2 = dstOps->width;
            if (cy2 > dstOps->height) cy2 = dstOps->height;

            if (cx2 > cx1 && cy2 > cy1) {
                // copy LCD mask into glyph texture tile
                d3dc->UploadTileToTexture(pBlitTextureRes,
                                          ginfo->image+rowBytesOffset,
                                          0, 0, sx, sy, sw, sh,
                                          ginfo->rowBytes, tileFormat);

                // update the lower-right glyph texture coordinates
                tx2 = ((jfloat)sw) / D3DC_BLIT_TILE_SIZE;
                ty2 = ((jfloat)sh) / D3DC_BLIT_TILE_SIZE;

                // calculate the actual destination vertices
                dx1 = (jfloat)x;
                dy1 = (jfloat)y;
                dx2 = dx1 + sw;
                dy2 = dy1 + sh;

                // copy destination into cached texture tile (the upper-left
                // corner of the destination region will be positioned at the
                // upper-left corner (0,0) of the texture)
                RECT srcRect = { cx1, cy1, cx2, cy2 };
                RECT dstRect = { cx1-x, cy1-y, cx2-x, cy2-y };
                pd3dDevice->StretchRect(pDst, &srcRect,
                                        pCachedDestSurface,
                                        &dstRect,
                                        D3DTEXF_NONE);

                // update the remaining destination texture coordinates
                dtx2 = ((jfloat)sw) / D3DTR_CACHED_DEST_WIDTH;
                dty2 = ((jfloat)sh) / D3DTR_CACHED_DEST_HEIGHT;

                // render composed texture to the destination surface
                res = d3dc->pVCacher->DrawTexture( dx1,  dy1,  dx2,  dy2,
                                                   tx1,  ty1,  tx2,  ty2,
                                                   dtx1, dty1, dtx2, dty2);

                // unfortunately we need to flush after each tile
                d3dc->FlushVertexQueue();
            }
        }
    }

    return res;
}

// see DrawGlyphList.c for more on this macro...
#define FLOOR_ASSIGN(l, r) \
    if ((r)<0) (l) = ((int)floor(r)); else (l) = ((int)(r))

HRESULT
D3DTR_DrawGlyphList(D3DContext *d3dc, D3DSDOps *dstOps,
                    jint totalGlyphs, jboolean usePositions,
                    jboolean subPixPos, jboolean rgbOrder, jint lcdContrast,
                    jfloat glyphListOrigX, jfloat glyphListOrigY,
                    unsigned char *images, unsigned char *positions)
{
    int glyphCounter;
    HRESULT res = S_OK;
    J2dTraceLn(J2D_TRACE_INFO, "D3DTR_DrawGlyphList");

    RETURN_STATUS_IF_NULL(d3dc, E_FAIL);
    RETURN_STATUS_IF_NULL(d3dc->Get3DDevice(), E_FAIL);
    RETURN_STATUS_IF_NULL(dstOps, E_FAIL);
    RETURN_STATUS_IF_NULL(images, E_FAIL);
    if (usePositions) {
        RETURN_STATUS_IF_NULL(positions, E_FAIL);
    }

    glyphMode = MODE_NOT_INITED;
    isCachedDestValid = JNI_FALSE;

    for (glyphCounter = 0; glyphCounter < totalGlyphs; glyphCounter++) {
        jint x, y;
        jfloat glyphx, glyphy;
        jboolean grayscale;
        GlyphInfo *ginfo = (GlyphInfo *)jlong_to_ptr(NEXT_LONG(images));

        if (ginfo == NULL) {
            // this shouldn't happen, but if it does we'll just break out...
            J2dRlsTraceLn(J2D_TRACE_ERROR,
                          "D3DTR_DrawGlyphList: glyph info is null");
            break;
        }

        grayscale = (ginfo->rowBytes == ginfo->width);

        if (usePositions) {
            jfloat posx = NEXT_FLOAT(positions);
            jfloat posy = NEXT_FLOAT(positions);
            glyphx = glyphListOrigX + posx + ginfo->topLeftX;
            glyphy = glyphListOrigY + posy + ginfo->topLeftY;
            FLOOR_ASSIGN(x, glyphx);
            FLOOR_ASSIGN(y, glyphy);
        } else {
            glyphx = glyphListOrigX + ginfo->topLeftX;
            glyphy = glyphListOrigY + ginfo->topLeftY;
            FLOOR_ASSIGN(x, glyphx);
            FLOOR_ASSIGN(y, glyphy);
            glyphListOrigX += ginfo->advanceX;
            glyphListOrigY += ginfo->advanceY;
        }

        if (ginfo->image == NULL) {
            continue;
        }

        if (grayscale) {
            // grayscale or monochrome glyph data
            if (ginfo->width <= D3DTR_CACHE_CELL_WIDTH &&
                ginfo->height <= D3DTR_CACHE_CELL_HEIGHT &&
                SUCCEEDED(d3dc->InitGrayscaleGlyphCache()))
            {
                res = D3DTR_DrawGrayscaleGlyphViaCache(d3dc, ginfo, x, y);
            } else {
                res = D3DTR_DrawGrayscaleGlyphNoCache(d3dc, ginfo, x, y);
            }
        } else {
            // LCD-optimized glyph data
            jint rowBytesOffset = 0;

            if (subPixPos) {
                jint frac = (jint)((glyphx - x) * 3);
                if (frac != 0) {
                    rowBytesOffset = 3 - frac;
                    x += 1;
                }
            }

            if (rowBytesOffset == 0 &&
                ginfo->width <= D3DTR_CACHE_CELL_WIDTH &&
                ginfo->height <= D3DTR_CACHE_CELL_HEIGHT &&
                SUCCEEDED(d3dc->InitLCDGlyphCache()))
            {
                res = D3DTR_DrawLCDGlyphViaCache(d3dc, dstOps,
                                                 ginfo, x, y,
                                                 glyphCounter, totalGlyphs,
                                                 rgbOrder, lcdContrast);
            } else {
                res = D3DTR_DrawLCDGlyphNoCache(d3dc, dstOps,
                                                ginfo, x, y,
                                                rowBytesOffset,
                                                rgbOrder, lcdContrast);
            }
        }

        if (FAILED(res)) {
            break;
        }
    }

    D3DTR_DisableGlyphModeState(d3dc);
    return res;
}

JNIEXPORT void JNICALL
Java_sun_java2d_d3d_D3DTextRenderer_drawGlyphList
    (JNIEnv *env, jobject self,
     jint numGlyphs, jboolean usePositions,
     jboolean subPixPos, jboolean rgbOrder, jint lcdContrast,
     jfloat glyphListOrigX, jfloat glyphListOrigY,
     jlongArray imgArray, jfloatArray posArray)
{
    unsigned char *images;

    J2dTraceLn(J2D_TRACE_INFO, "D3DTextRenderer_drawGlyphList");

    images = (unsigned char *)
        env->GetPrimitiveArrayCritical(imgArray, NULL);
    if (images != NULL) {
        D3DContext *d3dc = D3DRQ_GetCurrentContext();
        D3DSDOps *dstOps = D3DRQ_GetCurrentDestination();

        if (usePositions) {
            unsigned char *positions = (unsigned char *)
                env->GetPrimitiveArrayCritical(posArray, NULL);
            if (positions != NULL) {
                D3DTR_DrawGlyphList(d3dc, dstOps,
                                    numGlyphs, usePositions,
                                    subPixPos, rgbOrder, lcdContrast,
                                    glyphListOrigX, glyphListOrigY,
                                    images, positions);
                env->ReleasePrimitiveArrayCritical(posArray,
                                                   positions, JNI_ABORT);
            }
        } else {
            D3DTR_DrawGlyphList(d3dc, dstOps,
                                numGlyphs, usePositions,
                                subPixPos, rgbOrder, lcdContrast,
                                glyphListOrigX, glyphListOrigY,
                                images, NULL);
        }

        // reset current state, and ensure rendering is flushed to dest
        if (d3dc != NULL) {
            d3dc->FlushVertexQueue();
        }

        env->ReleasePrimitiveArrayCritical(imgArray,
                                           images, JNI_ABORT);
    }
}

Other Java examples (source code examples)

Here is a short list of links related to this Java D3DTextRenderer.cpp source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.