Exemplo n.º 1
0
/**
 * 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);
}
Exemplo n.º 2
0
HRESULT D3DPIPELINE_API
D3DRenderer_FillAAParallelogram(D3DContext *d3dc,
                                jfloat fx11, jfloat fy11,
                                jfloat dx21, jfloat dy21,
                                jfloat dx12, jfloat dy12)
{
    IDirect3DDevice9 *pd3dDevice;
    HRESULT res;

    J2dTraceLn6(J2D_TRACE_INFO,
                "D3DRenderer_FillAAParallelogram "
                "x=%6.2f y=%6.2f "
                "dx1=%6.2f dy1=%6.2f "
                "dx2=%6.2f dy2=%6.2f ",
                fx11, fy11,
                dx21, dy21,
                dx12, dy12);

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

    pd3dDevice = d3dc->Get3DDevice();
    if (pd3dDevice == NULL) {
        return E_FAIL;
    }

    res = d3dc->pVCacher->FillParallelogramAA(fx11, fy11,
                                              dx21, dy21,
                                              dx12, dy12);
    return res;
}
Exemplo n.º 3
0
HRESULT D3DPipelineManager::InitAdapters()
{
    HRESULT res = E_FAIL;

    J2dTraceLn(J2D_TRACE_INFO, "D3DPPLM::InitAdapters()");
    if (pAdapters != NULL) {
        ReleaseAdapters();
    }

    adapterCount = pd3d9->GetAdapterCount();
    pAdapters = new D3DAdapter[adapterCount];
    if (pAdapters == NULL) {
        J2dRlsTraceLn(J2D_TRACE_ERROR, "InitAdapters: out of memory");
        adapterCount = 0;
        return E_FAIL;
    }
    ZeroMemory(pAdapters, adapterCount * sizeof(D3DAdapter));

    res = CheckAdaptersInfo();
    RETURN_STATUS_IF_FAILED(res);

    currentFSFocusAdapter = -1;
    if (CreateDefaultFocusWindow() == 0) {
        return E_FAIL;
    }

    return S_OK;
}
Exemplo n.º 4
0
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);
}
Exemplo n.º 5
0
HRESULT
D3DVertexCacher::Init(D3DContext *pCtx)
{
    D3DCAPS9 caps;

    RETURN_STATUS_IF_NULL(pCtx, E_FAIL);

    ReleaseDefPoolResources();

    this->pCtx = pCtx;

    firstPendingBatch = 0;
    firstPendingVertex = 0;
    firstUnusedVertex = 0;
    currentBatch = 0;
    ZeroMemory(vertices, sizeof(vertices));
    ZeroMemory(batches, sizeof(batches));

    lpD3DDevice = pCtx->Get3DDevice();
    RETURN_STATUS_IF_NULL(lpD3DDevice, E_FAIL);

    ZeroMemory(&caps, sizeof(caps));
    lpD3DDevice->GetDeviceCaps(&caps);

    D3DPOOL pool = (caps.DeviceType == D3DDEVTYPE_HAL) ?
            D3DPOOL_DEFAULT : D3DPOOL_SYSTEMMEM;
    // usage depends on whether we use hw or sw vertex processing
    HRESULT res =
        lpD3DDevice->CreateVertexBuffer(MAX_BATCH_SIZE*sizeof(J2DLVERTEX),
            D3DUSAGE_DYNAMIC|D3DUSAGE_WRITEONLY, D3DFVF_J2DLVERTEX,
            pool, &lpD3DVertexBuffer, NULL);
    RETURN_STATUS_IF_FAILED(res);

    res = lpD3DDevice->SetStreamSource(0, lpD3DVertexBuffer, 0,
                                       sizeof(J2DLVERTEX));
    RETURN_STATUS_IF_FAILED(res);

    lpD3DDevice->SetFVF(D3DFVF_J2DLVERTEX);
    return res;
}
Exemplo n.º 6
0
HRESULT D3DPipelineManager::D3DEnabledOnAdapter(UINT adapter)
{
    HRESULT res;
    D3DDISPLAYMODE dm;

    res = pd3d9->GetAdapterDisplayMode(adapter, &dm);
    RETURN_STATUS_IF_FAILED(res);

    res = pd3d9->CheckDeviceType(adapter, devType, dm.Format, dm.Format, TRUE);
    if (FAILED(res)) {
        J2dRlsTraceLn1(J2D_TRACE_ERROR,
                "D3DPPLM::D3DEnabledOnAdapter: no " \
                "suitable d3d device on adapter %d", adapter);
    }

    return res;
}
Exemplo n.º 7
0
HRESULT D3DMaskCache::Enable()
{
    HRESULT res;

    J2dTraceLn(J2D_TRACE_INFO, "D3DMaskCache::Enable");

    D3DResource *pMaskTexRes;
    res = pCtx->GetResourceManager()->GetMaskTexture(&pMaskTexRes);
    RETURN_STATUS_IF_FAILED(res);

    res = pCtx->SetTexture(pMaskTexRes->GetTexture(), 0);

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

    return res;
}
Exemplo n.º 8
0
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;
}
Exemplo n.º 9
0
HRESULT
D3DBufImgOps_EnableRescaleOp(D3DContext *d3dc,
                             jboolean nonPremult,
                             unsigned char *scaleFactors,
                             unsigned char *offsets)
{
    HRESULT res;
    IDirect3DDevice9 *pd3dDevice;
    jint flags = 0;

    J2dTraceLn(J2D_TRACE_INFO, "D3DBufImgOps_EnableRescaleOp");

    RETURN_STATUS_IF_NULL(d3dc, E_FAIL);

    d3dc->UpdateState(STATE_CHANGE);

    // choose the appropriate shader, depending on the source image
    if (nonPremult) {
        flags |= RESCALE_NON_PREMULT;
    }

    // locate/enable the shader program for the given flags
    res = d3dc->EnableRescaleProgram(flags);
    RETURN_STATUS_IF_FAILED(res);

    // update the "uniform" scale factor values (note that the Java-level
    // dispatching code always passes down 4 values here, regardless of
    // the original source image type)
    pd3dDevice = d3dc->Get3DDevice();
    pd3dDevice->SetPixelShaderConstantF(0, (float *)scaleFactors, 1);

    // update the "uniform" offset values (note that the Java-level
    // dispatching code always passes down 4 values here, and that the
    // offsets will have already been normalized to the range [0,1])
    return pd3dDevice->SetPixelShaderConstantF(1, (float *)offsets, 1);
}
Exemplo n.º 10
0
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);
}
Exemplo n.º 11
0
/**
 * 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 accomodate 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;
}
Exemplo n.º 12
0
HRESULT D3DPIPELINE_API
D3DRenderer_DrawAAParallelogram(D3DContext *d3dc,
                                jfloat fx11, jfloat fy11,
                                jfloat dx21, jfloat dy21,
                                jfloat dx12, jfloat dy12,
                                jfloat lwr21, jfloat lwr12)
{
    IDirect3DDevice9 *pd3dDevice;
    // dx,dy for line width in the "21" and "12" directions.
    jfloat ldx21, ldy21, ldx12, ldy12;
    // parameters for "outer" parallelogram
    jfloat ofx11, ofy11, odx21, ody21, odx12, ody12;
    // parameters for "inner" parallelogram
    jfloat ifx11, ify11, idx21, idy21, idx12, idy12;
    HRESULT res;

    J2dTraceLn8(J2D_TRACE_INFO,
                "D3DRenderer_DrawAAParallelogram "
                "x=%6.2f y=%6.2f "
                "dx1=%6.2f dy1=%6.2f lwr1=%6.2f "
                "dx2=%6.2f dy2=%6.2f lwr2=%6.2f ",
                fx11, fy11,
                dx21, dy21, lwr21,
                dx12, dy12, lwr12);

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

    pd3dDevice = d3dc->Get3DDevice();
    if (pd3dDevice == NULL) {
        return E_FAIL;
    }

    // calculate true dx,dy for line widths from the "line width ratios"
    ldx21 = dx21 * lwr21;
    ldy21 = dy21 * lwr21;
    ldx12 = dx12 * lwr12;
    ldy12 = dy12 * lwr12;

    // calculate coordinates of the outer parallelogram
    ofx11 = fx11 - (ldx21 + ldx12) / 2.0f;
    ofy11 = fy11 - (ldy21 + ldy12) / 2.0f;
    odx21 = dx21 + ldx21;
    ody21 = dy21 + ldy21;
    odx12 = dx12 + ldx12;
    ody12 = dy12 + ldy12;

    // Only process the inner parallelogram if the line width ratio
    // did not consume the entire interior of the parallelogram
    // (i.e. if the width ratio was less than 1.0)
    if (lwr21 < 1.0f && lwr12 < 1.0f) {
        // calculate coordinates of the inner parallelogram
        ifx11 = fx11 + (ldx21 + ldx12) / 2.0f;
        ify11 = fy11 + (ldy21 + ldy12) / 2.0f;
        idx21 = dx21 - ldx21;
        idy21 = dy21 - ldy21;
        idx12 = dx12 - ldx12;
        idy12 = dy12 - ldy12;

        res = d3dc->pVCacher->DrawParallelogramAA(ofx11, ofy11,
                                                  odx21, ody21,
                                                  odx12, ody12,
                                                  ifx11, ify11,
                                                  idx21, idy21,
                                                  idx12, idy12);
    } else {
        // Just invoke a regular fill on the outer parallelogram
        res = d3dc->pVCacher->FillParallelogramAA(ofx11, ofy11,
                                                  odx21, ody21,
                                                  odx12, ody12);
    }

    return res;
}
Exemplo n.º 13
0
HRESULT
D3DBufImgOps_EnableLookupOp(D3DContext *d3dc,
                            jboolean nonPremult, jboolean shortData,
                            jint numBands, jint bandLength, jint offset,
                            void *tableValues)
{
    HRESULT res;
    IDirect3DDevice9 *pd3dDevice;
    D3DResource *pLutTexRes;
    IDirect3DTexture9 *pLutTex;
    int bytesPerElem = (shortData ? 2 : 1);
    jfloat foffsets[4];
    void *bands[4];
    int i;
    jint flags = 0;

    J2dTraceLn4(J2D_TRACE_INFO,
                "D3DBufImgOps_EnableLookupOp: short=%d num=%d len=%d off=%d",
                shortData, numBands, bandLength, offset);

    RETURN_STATUS_IF_NULL(d3dc, E_FAIL);

    d3dc->UpdateState(STATE_CHANGE);

    // choose the appropriate shader, depending on the source image
    // and the number of bands involved
    if (numBands != 4) {
        flags |= LOOKUP_USE_SRC_ALPHA;
    }
    if (nonPremult) {
        flags |= LOOKUP_NON_PREMULT;
    }

    // locate/enable the shader program for the given flags
    res = d3dc->EnableLookupProgram(flags);
    RETURN_STATUS_IF_FAILED(res);

    // update the "uniform" offset value
    for (i = 0; i < 4; i++) {
        foffsets[i] = offset / 255.0f;
    }
    pd3dDevice = d3dc->Get3DDevice();
    pd3dDevice->SetPixelShaderConstantF(0, foffsets, 1);

    res = d3dc->GetResourceManager()->GetLookupOpLutTexture(&pLutTexRes);
    RETURN_STATUS_IF_FAILED(res);
    pLutTex = pLutTexRes->GetTexture();

    // update the lookup table with the user-provided values
    if (numBands == 1) {
        // replicate the single band for R/G/B; alpha band is unused
        for (i = 0; i < 3; i++) {
            bands[i] = tableValues;
        }
        bands[3] = NULL;
    } else if (numBands == 3) {
        // user supplied band for each of R/G/B; alpha band is unused
        for (i = 0; i < 3; i++) {
            bands[i] = PtrAddBytes(tableValues, i*bandLength*bytesPerElem);
        }
        bands[3] = NULL;
    } else if (numBands == 4) {
        // user supplied band for each of R/G/B/A
        for (i = 0; i < 4; i++) {
            bands[i] = PtrAddBytes(tableValues, i*bandLength*bytesPerElem);
        }
    }

    // upload the bands one row at a time into our lookup table texture
    D3DLOCKED_RECT lockedRect;
    res = pLutTex->LockRect(0, &lockedRect, NULL, D3DLOCK_NOSYSLOCK);
    RETURN_STATUS_IF_FAILED(res);

    jushort *pBase = (jushort*)lockedRect.pBits;
    for (i = 0; i < 4; i++) {
        jushort *pDst;
        if (bands[i] == NULL) {
            continue;
        }
        pDst = pBase + (i * 256);
        if (shortData) {
            memcpy(pDst, bands[i], bandLength*sizeof(jushort));
        } else {
            int j;
            jubyte *pSrc = (jubyte *)bands[i];
            for (j = 0; j < bandLength; j++) {
                pDst[j] = (jushort)(pSrc[j] << 8);
            }
        }
    }
    pLutTex->UnlockRect(0);

    // bind the lookup table to texture unit 1 and enable texturing
    res = d3dc->SetTexture(pLutTex, 1);
    pd3dDevice->SetSamplerState(1, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP);
    pd3dDevice->SetSamplerState(1, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP);
    pd3dDevice->SetSamplerState(1, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
    pd3dDevice->SetSamplerState(1, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
    return res;
}
Exemplo n.º 14
0
HRESULT
D3DResourceManager::CreateSwapChain(HWND hWnd, UINT numBuffers,
                                    UINT width, UINT height,
                                    D3DSWAPEFFECT swapEffect,
                                    UINT presentationInterval,
                                    D3DResource ** ppSwapChainResource)
{
    HRESULT res;
    IDirect3DDevice9 *pd3dDevice;
    IDirect3DSwapChain9 *pSwapChain = NULL;
    D3DPRESENT_PARAMETERS newParams, *curParams;

    J2dTraceLn(J2D_TRACE_INFO, "D3DRM::CreateSwapChain");
    J2dTraceLn4(J2D_TRACE_VERBOSE, "  w=%d h=%d hwnd=%x numBuffers=%d",
                width, height, hWnd, numBuffers);

    if (pCtx == NULL || ppSwapChainResource == NULL ||
        (pd3dDevice = pCtx->Get3DDevice()) == NULL)
    {
        return E_FAIL;
    }
    RETURN_STATUS_IF_FAILED(res = pd3dDevice->TestCooperativeLevel());

    curParams = pCtx->GetPresentationParams();

    if (curParams->Windowed == FALSE) {
        // there's a single swap chain in full-screen mode, use it if
        // it fits our parameters, reset the device otherwise
        if (curParams->BackBufferCount != numBuffers ||
            curParams->SwapEffect != swapEffect ||
            curParams->PresentationInterval != presentationInterval)
        {
            newParams = *curParams;
            newParams.BackBufferCount = numBuffers;
            newParams.SwapEffect = swapEffect;
            newParams.PresentationInterval = presentationInterval;

            res = pCtx->ConfigureContext(&newParams);
            RETURN_STATUS_IF_FAILED(res);
            // this reset will not have released the device, so our pd3dDevice
            // is still valid, but to be on a safe side, reset it
            pd3dDevice = pCtx->Get3DDevice();
        }
        res = pd3dDevice->GetSwapChain(0, &pSwapChain);
    } else {
        ZeroMemory(&newParams, sizeof(D3DPRESENT_PARAMETERS));
        newParams.BackBufferWidth = width;
        newParams.BackBufferHeight = height;
        newParams.hDeviceWindow = hWnd;
        newParams.Windowed = TRUE;
        newParams.BackBufferCount = numBuffers;
        newParams.SwapEffect = swapEffect;
        newParams.PresentationInterval = presentationInterval;

        res = pd3dDevice->CreateAdditionalSwapChain(&newParams, &pSwapChain);
    }

    if (SUCCEEDED(res)) {
        J2dTraceLn1(J2D_TRACE_VERBOSE,"  created swap chain: 0x%x ",pSwapChain);
        *ppSwapChainResource = new D3DResource(pSwapChain);
        res = AddResource(*ppSwapChainResource);
    } else {
        DebugPrintD3DError(res, "D3DRM::CreateSwapChain failed");
        *ppSwapChainResource = NULL;
    }
    return res;
}
Exemplo n.º 15
0
HRESULT D3DMaskCache::AddMaskQuad(int srcx, int srcy,
                                  int dstx, int dsty,
                                  int width, int height,
                                  int maskscan, void *mask)
{
    HRESULT res;
    float tx1, ty1, tx2, ty2;
    float dx1, dy1, dx2, dy2;

    J2dTraceLn1(J2D_TRACE_INFO, "D3DVertexCacher::AddMaskQuad: %d",
                maskCacheIndex);

    if (maskCacheIndex >= D3D_MASK_CACHE_MAX_INDEX ||
        pCtx->pVCacher->GetFreeVertices() < 6)
    {
        res = pCtx->pVCacher->Render();
        RETURN_STATUS_IF_FAILED(res);
        maskCacheIndex = 0;
    }

    if (mask != NULL) {
        int texx = D3D_MASK_CACHE_TILE_WIDTH *
            (maskCacheIndex % D3D_MASK_CACHE_WIDTH_IN_TILES);
        int texy = D3D_MASK_CACHE_TILE_HEIGHT *
            (maskCacheIndex / D3D_MASK_CACHE_WIDTH_IN_TILES);
        D3DResource *pMaskTexRes;

        res = pCtx->GetResourceManager()->GetMaskTexture(&pMaskTexRes);
        RETURN_STATUS_IF_FAILED(res);

        // copy alpha mask into texture tile
        pCtx->UploadTileToTexture(pMaskTexRes, mask,
                                  texx, texy,
                                  srcx, srcy,
                                  width, height,
                                  maskscan,
                                  TILEFMT_1BYTE_ALPHA);

        tx1 = ((float)texx) / D3D_MASK_CACHE_WIDTH_IN_TEXELS;
        ty1 = ((float)texy) / D3D_MASK_CACHE_HEIGHT_IN_TEXELS;

        maskCacheIndex++;
    } else {
        // use special fully opaque tile
        tx1 = ((float)D3D_MASK_CACHE_SPECIAL_TILE_X) /
            D3D_MASK_CACHE_WIDTH_IN_TEXELS;
        ty1 = ((float)D3D_MASK_CACHE_SPECIAL_TILE_Y) /
            D3D_MASK_CACHE_HEIGHT_IN_TEXELS;
    }

    tx2 = tx1 + (((float)width) / D3D_MASK_CACHE_WIDTH_IN_TEXELS);
    ty2 = ty1 + (((float)height) / D3D_MASK_CACHE_HEIGHT_IN_TEXELS);

    dx1 = (float)dstx;
    dy1 = (float)dsty;
    dx2 = dx1 + width;
    dy2 = dy1 + height;

    return pCtx->pVCacher->DrawTexture(dx1, dy1, dx2, dy2,
                                       tx1, ty1, tx2, ty2);
}
Exemplo n.º 16
0
HRESULT
D3DBufImgOps_EnableConvolveOp(D3DContext *d3dc, jlong pSrcOps,
                              jboolean edgeZeroFill,
                              jint kernelWidth, jint kernelHeight,
                              unsigned char *kernel)
{
    HRESULT res;
    IDirect3DDevice9 *pd3dDevice;
    D3DSDOps *srcOps = (D3DSDOps *)jlong_to_ptr(pSrcOps);
    jint kernelSize = kernelWidth * kernelHeight;
    jint texW, texH;
    jfloat xoff, yoff;
    jfloat edgeX, edgeY;
    jfloat imgEdge[4];
    jfloat kernelVals[MAX_KERNEL_SIZE*4];
    jint i, j, kIndex;
    jint flags = 0;

    J2dTraceLn2(J2D_TRACE_INFO,
                "D3DBufImgOps_EnableConvolveOp: kernelW=%d kernelH=%d",
                kernelWidth, kernelHeight);

    RETURN_STATUS_IF_NULL(d3dc, E_FAIL);
    RETURN_STATUS_IF_NULL(srcOps, E_FAIL);

    d3dc->UpdateState(STATE_CHANGE);

    // texcoords are specified in the range [0,1], so to achieve an
    // x/y offset of approximately one pixel we have to normalize
    // to that range here
    texW = srcOps->pResource->GetDesc()->Width;
    texH = srcOps->pResource->GetDesc()->Height;
    xoff = 1.0f / texW;
    yoff = 1.0f / texH;

    if (edgeZeroFill) {
        flags |= CONVOLVE_EDGE_ZERO_FILL;
    }
    if (kernelWidth == 5 && kernelHeight == 5) {
        flags |= CONVOLVE_5X5;
    }

    // locate/enable the shader program for the given flags
    res = d3dc->EnableConvolveProgram(flags);
    RETURN_STATUS_IF_FAILED(res);

    // update the "uniform" image min/max values
    // (texcoords are in the range [0,1])
    //   imgEdge[0] = imgMin.x
    //   imgEdge[1] = imgMin.y
    //   imgEdge[2] = imgMax.x
    //   imgEdge[3] = imgMax.y
    edgeX = (kernelWidth/2) * xoff;
    edgeY = (kernelHeight/2) * yoff;
    imgEdge[0] = edgeX;
    imgEdge[1] = edgeY;
    imgEdge[2] = (((jfloat)srcOps->width)  / texW) - edgeX;
    imgEdge[3] = (((jfloat)srcOps->height) / texH) - edgeY;
    pd3dDevice = d3dc->Get3DDevice();
    pd3dDevice->SetPixelShaderConstantF(0, imgEdge, 1);

    // update the "uniform" kernel offsets and values
    kIndex = 0;
    for (i = -kernelHeight/2; i < kernelHeight/2+1; i++) {
        for (j = -kernelWidth/2; j < kernelWidth/2+1; j++) {
            kernelVals[kIndex+0] = j*xoff;
            kernelVals[kIndex+1] = i*yoff;
            kernelVals[kIndex+2] = NEXT_FLOAT(kernel);
            kernelVals[kIndex+3] = 0.0f; // unused
            kIndex += 4;
        }
    }
    return pd3dDevice->SetPixelShaderConstantF(1, kernelVals, kernelSize);
}
Exemplo n.º 17
0
HRESULT D3DPipelineManager::CheckDeviceCaps(UINT adapter)
{
    HRESULT res;
    D3DCAPS9 d3dCaps;

    J2dTraceLn(J2D_TRACE_INFO, "D3DPPLM::CheckDeviceCaps");

    res = pd3d9->GetDeviceCaps(adapter, devType, &d3dCaps);
    RETURN_STATUS_IF_FAILED(res);

    CHECK_CAP(d3dCaps.DevCaps, D3DDEVCAPS_DRAWPRIMTLVERTEX);

    // by requiring hardware tnl we are hoping for better drivers quality
    if (!IsD3DForced()) {
        // fail if not hw tnl unless d3d was forced
        CHECK_CAP(d3dCaps.DevCaps, D3DDEVCAPS_HWTRANSFORMANDLIGHT);
    }
    if (d3dCaps.DeviceType == D3DDEVTYPE_HAL) {
        CHECK_CAP(d3dCaps.DevCaps, D3DDEVCAPS_HWRASTERIZATION);
    }

    CHECK_CAP(d3dCaps.RasterCaps, D3DPRASTERCAPS_SCISSORTEST);

    CHECK_CAP(d3dCaps.Caps3, D3DCAPS3_ALPHA_FULLSCREEN_FLIP_OR_DISCARD);

    CHECK_CAP(d3dCaps.PrimitiveMiscCaps, D3DPMISCCAPS_CULLNONE);
    CHECK_CAP(d3dCaps.PrimitiveMiscCaps, D3DPMISCCAPS_BLENDOP);
    CHECK_CAP(d3dCaps.PrimitiveMiscCaps, D3DPMISCCAPS_MASKZ);

    CHECK_CAP(d3dCaps.ZCmpCaps, D3DPCMPCAPS_ALWAYS);
    CHECK_CAP(d3dCaps.ZCmpCaps, D3DPCMPCAPS_LESS);

    CHECK_CAP(d3dCaps.SrcBlendCaps, D3DPBLENDCAPS_ZERO);
    CHECK_CAP(d3dCaps.SrcBlendCaps, D3DPBLENDCAPS_ONE);
    CHECK_CAP(d3dCaps.SrcBlendCaps, D3DPBLENDCAPS_SRCALPHA);
    CHECK_CAP(d3dCaps.SrcBlendCaps, D3DPBLENDCAPS_DESTALPHA);
    CHECK_CAP(d3dCaps.SrcBlendCaps, D3DPBLENDCAPS_INVSRCALPHA);
    CHECK_CAP(d3dCaps.SrcBlendCaps, D3DPBLENDCAPS_INVDESTALPHA);

    CHECK_CAP(d3dCaps.DestBlendCaps, D3DPBLENDCAPS_ZERO);
    CHECK_CAP(d3dCaps.DestBlendCaps, D3DPBLENDCAPS_ONE);
    CHECK_CAP(d3dCaps.DestBlendCaps, D3DPBLENDCAPS_SRCALPHA);
    CHECK_CAP(d3dCaps.DestBlendCaps, D3DPBLENDCAPS_DESTALPHA);
    CHECK_CAP(d3dCaps.DestBlendCaps, D3DPBLENDCAPS_INVSRCALPHA);
    CHECK_CAP(d3dCaps.DestBlendCaps, D3DPBLENDCAPS_INVDESTALPHA);

    CHECK_CAP(d3dCaps.TextureAddressCaps, D3DPTADDRESSCAPS_CLAMP);
    CHECK_CAP(d3dCaps.TextureAddressCaps, D3DPTADDRESSCAPS_WRAP);

    CHECK_CAP(d3dCaps.TextureOpCaps, D3DTEXOPCAPS_MODULATE);

    if (d3dCaps.PixelShaderVersion < D3DPS_VERSION(2,0) && !IsD3DForced()) {
        J2dRlsTraceLn1(J2D_TRACE_ERROR,
                       "D3DPPLM::CheckDeviceCaps: adapter %d: Failed "\
                       "(pixel shaders 2.0 required)", adapter);
        return E_FAIL;
    }

    J2dRlsTraceLn1(J2D_TRACE_INFO,
                   "D3DPPLM::CheckDeviceCaps: adapter %d: Passed", adapter);
    return S_OK;
}
Exemplo n.º 18
0
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;
}
Exemplo n.º 19
0
HRESULT D3DPIPELINE_API
D3DRenderer_DrawParallelogram(D3DContext *d3dc,
                              jfloat fx11, jfloat fy11,
                              jfloat dx21, jfloat dy21,
                              jfloat dx12, jfloat dy12,
                              jfloat lwr21, jfloat lwr12)
{
    HRESULT res;

    J2dTraceLn8(J2D_TRACE_INFO,
                "D3DRenderer_DrawParallelogram "
                "x=%6.2f y=%6.2f "
                "dx1=%6.2f dy1=%6.2f lwr1=%6.2f "
                "dx2=%6.2f dy2=%6.2f lwr2=%6.2f ",
                fx11, fy11,
                dx21, dy21, lwr21,
                dx12, dy12, lwr12);

    // dx,dy for line width in the "21" and "12" directions.
    jfloat ldx21 = dx21 * lwr21;
    jfloat ldy21 = dy21 * lwr21;
    jfloat ldx12 = dx12 * lwr12;
    jfloat ldy12 = dy12 * lwr12;

    // calculate origin of the outer parallelogram
    jfloat ox11 = fx11 - (ldx21 + ldx12) / 2.0f;
    jfloat oy11 = fy11 - (ldy21 + ldy12) / 2.0f;

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

    // Only need to generate 4 quads if the interior still
    // has a hole in it (i.e. if the line width ratio was
    // less than 1.0)
    if (lwr21 < 1.0f && lwr12 < 1.0f) {
        // Note: "TOP", "BOTTOM", "LEFT" and "RIGHT" here are
        // relative to whether the dxNN variables are positive
        // and negative.  The math works fine regardless of
        // their signs, but for conceptual simplicity the
        // comments will refer to the sides as if the dxNN
        // were all positive.  "TOP" and "BOTTOM" segments
        // are defined by the dxy21 deltas.  "LEFT" and "RIGHT"
        // segments are defined by the dxy12 deltas.

        // Each segment includes its starting corner and comes
        // to just short of the following corner.  Thus, each
        // corner is included just once and the only lengths
        // needed are the original parallelogram delta lengths
        // and the "line width deltas".  The sides will cover
        // the following relative territories:
        //
        //     T T T T T R
        //      L         R
        //       L         R
        //        L         R
        //         L         R
        //          L B B B B B

        // TOP segment, to left side of RIGHT edge
        // "width" of original pgram, "height" of hor. line size
        fx11 = ox11;
        fy11 = oy11;
        res = d3dc->pVCacher->FillParallelogram(fx11, fy11,
                                                dx21, dy21,
                                                ldx12, ldy12);

        // RIGHT segment, to top of BOTTOM edge
        // "width" of vert. line size , "height" of original pgram
        fx11 = ox11 + dx21;
        fy11 = oy11 + dy21;
        res = d3dc->pVCacher->FillParallelogram(fx11, fy11,
                                                ldx21, ldy21,
                                                dx12, dy12);

        // BOTTOM segment, from right side of LEFT edge
        // "width" of original pgram, "height" of hor. line size
        fx11 = ox11 + dx12 + ldx21;
        fy11 = oy11 + dy12 + ldy21;
        res = d3dc->pVCacher->FillParallelogram(fx11, fy11,
                                                dx21, dy21,
                                                ldx12, ldy12);

        // LEFT segment, from bottom of TOP edge
        // "width" of vert. line size , "height" of inner pgram
        fx11 = ox11 + ldx12;
        fy11 = oy11 + ldy12;
        res = d3dc->pVCacher->FillParallelogram(fx11, fy11,
                                                ldx21, ldy21,
                                                dx12, dy12);
    } else {
        // The line width ratios were large enough to consume
        // the entire hole in the middle of the parallelogram
        // so we can just issue one large quad for the outer
        // parallelogram.
        dx21 += ldx21;
        dy21 += ldy21;
        dx12 += ldx12;
        dy12 += ldy12;

        res = d3dc->pVCacher->FillParallelogram(ox11, oy11,
                                                dx21, dy21,
                                                dx12, dy12);
    }

    return res;
}