0N/A/*
2362N/A * Copyright (c) 2007, 2008, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A
0N/A#include <malloc.h>
430N/A#include <math.h>
430N/A#include <jlong.h>
430N/A
0N/A#include "sun_java2d_d3d_D3DTextRenderer.h"
430N/A#include "sun_java2d_pipe_BufferedTextPipe.h"
430N/A
0N/A#include "SurfaceData.h"
430N/A#include "D3DContext.h"
430N/A#include "D3DSurfaceData.h"
430N/A#include "D3DRenderQueue.h"
430N/A#include "D3DTextRenderer.h"
430N/A#include "D3DGlyphCache.h"
430N/A#include "AccelGlyphCache.h"
430N/A#include "fontscalerdefs.h"
430N/A
430N/A/**
430N/A * The current "glyph mode" state. This variable is used to track the
430N/A * codepath used to render a particular glyph. This variable is reset to
430N/A * MODE_NOT_INITED at the beginning of every call to D3DTR_DrawGlyphList().
430N/A * As each glyph is rendered, the glyphMode variable is updated to reflect
430N/A * the current mode, so if the current mode is the same as the mode used
430N/A * to render the previous glyph, we can avoid doing costly setup operations
430N/A * each time.
430N/A */
430N/Atypedef enum {
430N/A MODE_NOT_INITED,
430N/A MODE_USE_CACHE_GRAY,
430N/A MODE_USE_CACHE_LCD,
430N/A MODE_NO_CACHE_GRAY,
430N/A MODE_NO_CACHE_LCD
430N/A} GlyphMode;
430N/Astatic GlyphMode glyphMode = MODE_NOT_INITED;
430N/A
430N/A/**
430N/A * The current bounds of the "cached destination" texture, in destination
430N/A * coordinate space. The width/height of these bounds will not exceed the
430N/A * D3DTR_CACHED_DEST_WIDTH/HEIGHT values defined above. These bounds are
430N/A * only considered valid when the isCachedDestValid flag is JNI_TRUE.
430N/A */
430N/Astatic SurfaceDataBounds cachedDestBounds;
430N/A
430N/A/**
430N/A * This flag indicates whether the "cached destination" texture contains
430N/A * valid data. This flag is reset to JNI_FALSE at the beginning of every
430N/A * call to D3DTR_DrawGlyphList(). Once we copy valid destination data
430N/A * into the cached texture, this flag is set to JNI_TRUE. This way, we
430N/A * can limit the number of times we need to copy destination data, which
430N/A * is a very costly operation.
430N/A */
430N/Astatic jboolean isCachedDestValid = JNI_FALSE;
430N/A
430N/A/**
430N/A * The bounds of the previously rendered LCD glyph, in destination
430N/A * coordinate space. We use these bounds to determine whether the glyph
430N/A * currently being rendered overlaps the previously rendered glyph (i.e.
430N/A * its bounding box intersects that of the previously rendered glyph).
430N/A * If so, we need to re-read the destination area associated with that
430N/A * previous glyph so that we can correctly blend with the actual
430N/A * destination data.
430N/A */
430N/Astatic SurfaceDataBounds previousGlyphBounds;
430N/A
430N/A/**
430N/A * Updates the gamma and inverse gamma values for the LCD text shader.
430N/A */
430N/Astatic HRESULT
430N/AD3DTR_UpdateLCDTextContrast(D3DContext *d3dc, jint contrast)
430N/A{
430N/A HRESULT res;
430N/A IDirect3DDevice9 *pd3dDevice = d3dc->Get3DDevice();
0N/A
430N/A jfloat fcon = ((jfloat)contrast) / 100.0f;
430N/A jfloat invgamma = fcon;
430N/A jfloat gamma = 1.0f / invgamma;
430N/A jfloat vals[4];
430N/A
430N/A // update the "invgamma" parameter of the shader program
430N/A vals[0] = invgamma;
430N/A vals[1] = invgamma;
430N/A vals[2] = invgamma;
430N/A vals[3] = 0.0f; // unused
430N/A pd3dDevice->SetPixelShaderConstantF(1, vals, 1);
430N/A
430N/A // update the "gamma" parameter of the shader program
430N/A vals[0] = gamma;
430N/A vals[1] = gamma;
430N/A vals[2] = gamma;
430N/A vals[3] = 0.0f; // unused
430N/A res = pd3dDevice->SetPixelShaderConstantF(2, vals, 1);
430N/A
430N/A return res;
430N/A}
430N/A
430N/A/**
430N/A * Updates the current gamma-adjusted source color ("src_adj") of the LCD
430N/A * text shader program. Note that we could calculate this value in the
430N/A * shader (e.g. just as we do for "dst_adj"), but would be unnecessary work
430N/A * (and a measurable performance hit, maybe around 5%) since this value is
430N/A * constant over the entire glyph list. So instead we just calculate the
430N/A * gamma-adjusted value once and update the uniform parameter of the LCD
430N/A * shader as needed.
430N/A */
430N/Astatic HRESULT
430N/AD3DTR_UpdateLCDTextColor(D3DContext *d3dc, jint contrast)
430N/A{
430N/A IDirect3DDevice9 *pd3dDevice = d3dc->Get3DDevice();
430N/A jfloat gamma = ((jfloat)contrast) / 100.0f;
430N/A jfloat clr[4];
430N/A
430N/A J2dTraceLn1(J2D_TRACE_INFO,
430N/A "D3DTR_UpdateLCDTextColor: contrast=%d", contrast);
430N/A
430N/A /*
430N/A * Note: Ideally we would update the "srcAdj" uniform parameter only
430N/A * when there is a change in the source color. Fortunately, the cost
430N/A * of querying the current D3D color state and updating the uniform
430N/A * value is quite small, and in the common case we only need to do this
430N/A * once per GlyphList, so we gain little from trying to optimize too
430N/A * eagerly here.
430N/A */
430N/A
430N/A // get the current D3D primary color state
430N/A jint color = d3dc->pVCacher->GetColor();
430N/A clr[0] = (jfloat)((color >> 16) & 0xff) / 255.0f;
430N/A clr[1] = (jfloat)((color >> 8) & 0xff) / 255.0f;
430N/A clr[2] = (jfloat)((color >> 0) & 0xff) / 255.0f;
430N/A clr[3] = 0.0f; // unused
430N/A
430N/A // gamma adjust the primary color
430N/A clr[0] = (jfloat)pow(clr[0], gamma);
430N/A clr[1] = (jfloat)pow(clr[1], gamma);
430N/A clr[2] = (jfloat)pow(clr[2], gamma);
430N/A
430N/A // update the "srcAdj" parameter of the shader program with this value
430N/A return pd3dDevice->SetPixelShaderConstantF(0, clr, 1);
430N/A}
0N/A
430N/A/**
430N/A * Enables the LCD text shader and updates any related state, such as the
430N/A * gamma values.
430N/A */
430N/Astatic HRESULT
430N/AD3DTR_EnableLCDGlyphModeState(D3DContext *d3dc, D3DSDOps *dstOps,
430N/A jboolean useCache, jint contrast)
430N/A{
430N/A D3DResource *pGlyphTexRes, *pCachedDestTexRes;
430N/A IDirect3DTexture9 *pGlyphTex, *pCachedDestTex;
430N/A
430N/A RETURN_STATUS_IF_NULL(dstOps->pResource, E_FAIL);
430N/A
430N/A HRESULT res = S_OK;
430N/A if (useCache) {
430N/A // glyph cache had been already initialized
430N/A pGlyphTexRes = d3dc->GetLCDGlyphCache()->GetGlyphCacheTexture();
430N/A } else {
430N/A res = d3dc->GetResourceManager()->GetBlitTexture(&pGlyphTexRes);
430N/A }
430N/A RETURN_STATUS_IF_FAILED(res);
430N/A
430N/A pGlyphTex = pGlyphTexRes->GetTexture();
430N/A
430N/A res = d3dc->GetResourceManager()->
430N/A GetCachedDestTexture(dstOps->pResource->GetDesc()->Format,
430N/A &pCachedDestTexRes);
430N/A RETURN_STATUS_IF_FAILED(res);
430N/A pCachedDestTex = pCachedDestTexRes->GetTexture();
430N/A
430N/A IDirect3DDevice9 *pd3dDevice = d3dc->Get3DDevice();
430N/A D3DTEXTUREFILTERTYPE fhint =
430N/A d3dc->IsTextureFilteringSupported(D3DTEXF_NONE) ?
430N/A D3DTEXF_NONE : D3DTEXF_POINT;
430N/A pd3dDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, fhint);
430N/A pd3dDevice->SetSamplerState(0, D3DSAMP_MINFILTER, fhint);
430N/A pd3dDevice->SetSamplerState(1, D3DSAMP_MAGFILTER, fhint);
430N/A pd3dDevice->SetSamplerState(1, D3DSAMP_MINFILTER, fhint);
430N/A d3dc->UpdateTextureColorState(D3DTA_TEXTURE, 1);
430N/A
430N/A // bind the texture containing glyph data to texture unit 0
430N/A d3dc->SetTexture(pGlyphTex, 0);
430N/A
430N/A // bind the texture tile containing destination data to texture unit 1
430N/A d3dc->SetTexture(pCachedDestTex, 1);
430N/A
430N/A // create/enable the LCD text shader
430N/A res = d3dc->EnableLCDTextProgram();
430N/A RETURN_STATUS_IF_FAILED(res);
430N/A
430N/A // update the current contrast settings (note: these change very rarely,
430N/A // but it seems that D3D pixel shader registers aren't maintained as
430N/A // part of the pixel shader instance, so we need to update these
430N/A // everytime around in case another shader blew away the contents
430N/A // of those registers)
430N/A D3DTR_UpdateLCDTextContrast(d3dc, contrast);
430N/A
430N/A // update the current color settings
430N/A return D3DTR_UpdateLCDTextColor(d3dc, contrast);
430N/A}
430N/A
430N/AHRESULT
430N/AD3DTR_EnableGlyphVertexCache(D3DContext *d3dc)
430N/A{
430N/A J2dTraceLn(J2D_TRACE_INFO, "D3DTR_EnableGlyphVertexCache");
430N/A
430N/A IDirect3DDevice9 *pd3dDevice = d3dc->Get3DDevice();
430N/A D3DTEXTUREFILTERTYPE fhint =
430N/A d3dc->IsTextureFilteringSupported(D3DTEXF_NONE) ?
430N/A D3DTEXF_NONE : D3DTEXF_POINT;
430N/A pd3dDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, fhint);
430N/A pd3dDevice->SetSamplerState(0, D3DSAMP_MINFILTER, fhint);
430N/A
430N/A // glyph cache had been successfully initialized if we got here
430N/A D3DResource *pGlyphCacheTexRes =
430N/A d3dc->GetGrayscaleGlyphCache()->GetGlyphCacheTexture();
430N/A return d3dc->SetTexture(pGlyphCacheTexRes->GetTexture(), 0);
430N/A}
430N/A
430N/AHRESULT
430N/AD3DTR_DisableGlyphVertexCache(D3DContext *d3dc)
430N/A{
430N/A J2dTraceLn(J2D_TRACE_INFO, "D3DTR_DisableGlyphVertexCache");
430N/A
430N/A return d3dc->SetTexture(NULL, 0);
430N/A}
430N/A
430N/A/**
430N/A * Disables any pending state associated with the current "glyph mode".
430N/A */
430N/Astatic HRESULT
430N/AD3DTR_DisableGlyphModeState(D3DContext *d3dc)
430N/A{
430N/A HRESULT res = S_OK;
430N/A IDirect3DDevice9 *pd3dDevice = d3dc->Get3DDevice();
430N/A
430N/A switch (glyphMode) {
430N/A case MODE_NO_CACHE_LCD:
430N/A case MODE_USE_CACHE_LCD:
430N/A d3dc->FlushVertexQueue();
430N/A pd3dDevice->SetPixelShader(NULL);
430N/A res = d3dc->SetTexture(NULL, 1);
430N/A break;
430N/A
430N/A case MODE_NO_CACHE_GRAY:
430N/A case MODE_USE_CACHE_GRAY:
430N/A case MODE_NOT_INITED:
430N/A default:
430N/A break;
430N/A }
430N/A return res;
430N/A}
430N/A
430N/Astatic HRESULT
430N/AD3DTR_DrawGrayscaleGlyphViaCache(D3DContext *d3dc,
430N/A GlyphInfo *ginfo, jint x, jint y)
430N/A{
430N/A HRESULT res = S_OK;
430N/A D3DGlyphCache *pGrayscaleGCache;
430N/A CacheCellInfo *cell;
430N/A GlyphCacheInfo *gcache;
430N/A jfloat x1, y1, x2, y2;
430N/A
430N/A J2dTraceLn(J2D_TRACE_VERBOSE, "D3DTR_DrawGrayscaleGlyphViaCache");
430N/A
430N/A if (glyphMode != MODE_USE_CACHE_GRAY) {
430N/A D3DTR_DisableGlyphModeState(d3dc);
430N/A
430N/A res = d3dc->BeginScene(STATE_GLYPHOP);
430N/A RETURN_STATUS_IF_FAILED(res);
430N/A
430N/A glyphMode = MODE_USE_CACHE_GRAY;
430N/A }
430N/A
430N/A pGrayscaleGCache = d3dc->GetGrayscaleGlyphCache();
430N/A gcache = pGrayscaleGCache->GetGlyphCache();
430N/A cell = AccelGlyphCache_GetCellInfoForCache(ginfo, gcache);
430N/A if (cell == NULL) {
430N/A // attempt to add glyph to accelerated glyph cache
430N/A res = pGrayscaleGCache->AddGlyph(ginfo);
430N/A RETURN_STATUS_IF_FAILED(res);
430N/A
430N/A cell = AccelGlyphCache_GetCellInfoForCache(ginfo, gcache);
430N/A RETURN_STATUS_IF_NULL(cell, E_FAIL);
430N/A }
430N/A
430N/A cell->timesRendered++;
430N/A
430N/A x1 = (jfloat)x;
430N/A y1 = (jfloat)y;
430N/A x2 = x1 + ginfo->width;
430N/A y2 = y1 + ginfo->height;
430N/A
430N/A return d3dc->pVCacher->DrawTexture(x1, y1, x2, y2,
430N/A cell->tx1, cell->ty1,
430N/A cell->tx2, cell->ty2);
430N/A}
0N/A
0N/A/**
430N/A * Evaluates to true if the rectangle defined by gx1/gy1/gx2/gy2 is
430N/A * inside outerBounds.
430N/A */
430N/A#define INSIDE(gx1, gy1, gx2, gy2, outerBounds) \
430N/A (((gx1) >= outerBounds.x1) && ((gy1) >= outerBounds.y1) && \
430N/A ((gx2) <= outerBounds.x2) && ((gy2) <= outerBounds.y2))
430N/A
430N/A/**
430N/A * Evaluates to true if the rectangle defined by gx1/gy1/gx2/gy2 intersects
430N/A * the rectangle defined by bounds.
0N/A */
430N/A#define INTERSECTS(gx1, gy1, gx2, gy2, bounds) \
430N/A ((bounds.x2 > (gx1)) && (bounds.y2 > (gy1)) && \
430N/A (bounds.x1 < (gx2)) && (bounds.y1 < (gy2)))
430N/A
430N/A/**
430N/A * This method checks to see if the given LCD glyph bounds fall within the
430N/A * cached destination texture bounds. If so, this method can return
430N/A * immediately. If not, this method will copy a chunk of framebuffer data
430N/A * into the cached destination texture and then update the current cached
430N/A * destination bounds before returning.
430N/A *
430N/A * The agx1, agx2 are "adjusted" glyph bounds, which are only used when checking
430N/A * against the previous glyph bounds.
430N/A */
430N/Astatic HRESULT
430N/AD3DTR_UpdateCachedDestination(D3DContext *d3dc, D3DSDOps *dstOps,
430N/A GlyphInfo *ginfo,
430N/A jint gx1, jint gy1, jint gx2, jint gy2,
430N/A jint agx1, jint agx2,
430N/A jint glyphIndex, jint totalGlyphs)
0N/A{
0N/A jint dx1, dy1, dx2, dy2;
430N/A D3DResource *pCachedDestTexRes;
430N/A IDirect3DSurface9 *pCachedDestSurface, *pDst;
430N/A HRESULT res;
430N/A
430N/A if (isCachedDestValid && INSIDE(gx1, gy1, gx2, gy2, cachedDestBounds)) {
430N/A // glyph is already within the cached destination bounds; no need
430N/A // to read back the entire destination region again, but we do
430N/A // need to see if the current glyph overlaps the previous glyph...
430N/A
430N/A // only use the "adjusted" glyph bounds when checking against
430N/A // previous glyph's bounds
430N/A gx1 = agx1;
430N/A gx2 = agx2;
430N/A
430N/A if (INTERSECTS(gx1, gy1, gx2, gy2, previousGlyphBounds)) {
430N/A // the current glyph overlaps the destination region touched
430N/A // by the previous glyph, so now we need to read back the part
430N/A // of the destination corresponding to the previous glyph
430N/A dx1 = previousGlyphBounds.x1;
430N/A dy1 = previousGlyphBounds.y1;
430N/A dx2 = previousGlyphBounds.x2;
430N/A dy2 = previousGlyphBounds.y2;
430N/A
430N/A // REMIND: make sure we flush any pending primitives that are
430N/A // dependent on the current contents of the cached dest
430N/A d3dc->FlushVertexQueue();
430N/A
430N/A RETURN_STATUS_IF_NULL(dstOps->pResource, E_FAIL);
430N/A RETURN_STATUS_IF_NULL(pDst = dstOps->pResource->GetSurface(),
430N/A E_FAIL);
430N/A res = d3dc->GetResourceManager()->
430N/A GetCachedDestTexture(dstOps->pResource->GetDesc()->Format,
430N/A &pCachedDestTexRes);
430N/A RETURN_STATUS_IF_FAILED(res);
430N/A pCachedDestSurface = pCachedDestTexRes->GetSurface();
430N/A
430N/A // now dxy12 represent the "desired" destination bounds, but the
430N/A // StretchRect() call may fail if these fall outside the actual
430N/A // surface bounds; therefore, we use cxy12 to represent the
430N/A // clamped bounds, and dxy12 are saved for later
430N/A jint cx1 = (dx1 < 0) ? 0 : dx1;
430N/A jint cy1 = (dy1 < 0) ? 0 : dy1;
430N/A jint cx2 = (dx2 > dstOps->width) ? dstOps->width : dx2;
430N/A jint cy2 = (dy2 > dstOps->height) ? dstOps->height : dy2;
430N/A
430N/A if (cx2 > cx1 && cy2 > cy1) {
430N/A // copy destination into subregion of cached texture tile
430N/A // cx1-cachedDestBounds.x1 == +xoffset from left of texture
430N/A // cy1-cachedDestBounds.y1 == +yoffset from top of texture
430N/A // cx2-cachedDestBounds.x1 == +xoffset from left of texture
430N/A // cy2-cachedDestBounds.y1 == +yoffset from top of texture
430N/A jint cdx1 = cx1-cachedDestBounds.x1;
430N/A jint cdy1 = cy1-cachedDestBounds.y1;
430N/A jint cdx2 = cx2-cachedDestBounds.x1;
430N/A jint cdy2 = cy2-cachedDestBounds.y1;
430N/A RECT srcRect = { cx1, cy1, cx2, cy2 };
430N/A RECT dstRect = { cdx1, cdy1, cdx2, cdy2 };
430N/A
430N/A IDirect3DDevice9 *pd3dDevice = d3dc->Get3DDevice();
430N/A res = pd3dDevice->StretchRect(pDst, &srcRect,
430N/A pCachedDestSurface, &dstRect,
430N/A D3DTEXF_NONE);
430N/A }
430N/A }
430N/A } else {
430N/A // destination region is not valid, so we need to read back a
430N/A // chunk of the destination into our cached texture
430N/A
430N/A // position the upper-left corner of the destination region on the
430N/A // "top" line of glyph list
430N/A // REMIND: this isn't ideal; it would be better if we had some idea
430N/A // of the bounding box of the whole glyph list (this is
430N/A // do-able, but would require iterating through the whole
430N/A // list up front, which may present its own problems)
430N/A dx1 = gx1;
430N/A dy1 = gy1;
430N/A
430N/A jint remainingWidth;
430N/A if (ginfo->advanceX > 0) {
430N/A // estimate the width based on our current position in the glyph
430N/A // list and using the x advance of the current glyph (this is just
430N/A // a quick and dirty heuristic; if this is a "thin" glyph image,
430N/A // then we're likely to underestimate, and if it's "thick" then we
430N/A // may end up reading back more than we need to)
430N/A remainingWidth =
430N/A (jint)(ginfo->advanceX * (totalGlyphs - glyphIndex));
430N/A if (remainingWidth > D3DTR_CACHED_DEST_WIDTH) {
430N/A remainingWidth = D3DTR_CACHED_DEST_WIDTH;
430N/A } else if (remainingWidth < ginfo->width) {
430N/A // in some cases, the x-advance may be slightly smaller
430N/A // than the actual width of the glyph; if so, adjust our
430N/A // estimate so that we can accomodate the entire glyph
430N/A remainingWidth = ginfo->width;
430N/A }
430N/A } else {
430N/A // a negative advance is possible when rendering rotated text,
430N/A // in which case it is difficult to estimate an appropriate
430N/A // region for readback, so we will pick a region that
430N/A // encompasses just the current glyph
430N/A remainingWidth = ginfo->width;
430N/A }
430N/A dx2 = dx1 + remainingWidth;
430N/A
430N/A // estimate the height (this is another sloppy heuristic; we'll
430N/A // make the cached destination region tall enough to encompass most
430N/A // glyphs that are small enough to fit in the glyph cache, and then
430N/A // we add a little something extra to account for descenders
430N/A dy2 = dy1 + D3DTR_CACHE_CELL_HEIGHT + 2;
430N/A
430N/A // REMIND: make sure we flush any pending primitives that are
430N/A // dependent on the current contents of the cached dest
430N/A d3dc->FlushVertexQueue();
430N/A
430N/A RETURN_STATUS_IF_NULL(dstOps->pResource, E_FAIL);
430N/A RETURN_STATUS_IF_NULL(pDst = dstOps->pResource->GetSurface(), E_FAIL);
430N/A res = d3dc->GetResourceManager()->
430N/A GetCachedDestTexture(dstOps->pResource->GetDesc()->Format,
430N/A &pCachedDestTexRes);
430N/A RETURN_STATUS_IF_FAILED(res);
430N/A pCachedDestSurface = pCachedDestTexRes->GetSurface();
430N/A
430N/A // now dxy12 represent the "desired" destination bounds, but the
430N/A // StretchRect() call may fail if these fall outside the actual
430N/A // surface bounds; therefore, we use cxy12 to represent the
430N/A // clamped bounds, and dxy12 are saved for later
430N/A jint cx1 = (dx1 < 0) ? 0 : dx1;
430N/A jint cy1 = (dy1 < 0) ? 0 : dy1;
430N/A jint cx2 = (dx2 > dstOps->width) ? dstOps->width : dx2;
430N/A jint cy2 = (dy2 > dstOps->height) ? dstOps->height : dy2;
0N/A
430N/A if (cx2 > cx1 && cy2 > cy1) {
430N/A // copy destination into cached texture tile (the upper-left
430N/A // corner of the destination region will be positioned at the
430N/A // upper-left corner (0,0) of the texture)
430N/A RECT srcRect = { cx1, cy1, cx2, cy2 };
430N/A RECT dstRect = { cx1-dx1, cy1-dy1, cx2-dx1, cy2-dy1 };
430N/A
430N/A IDirect3DDevice9 *pd3dDevice = d3dc->Get3DDevice();
430N/A res = pd3dDevice->StretchRect(pDst, &srcRect,
430N/A pCachedDestSurface, &dstRect,
430N/A D3DTEXF_NONE);
430N/A }
430N/A
430N/A // update the cached bounds and mark it valid
430N/A cachedDestBounds.x1 = dx1;
430N/A cachedDestBounds.y1 = dy1;
430N/A cachedDestBounds.x2 = dx2;
430N/A cachedDestBounds.y2 = dy2;
430N/A isCachedDestValid = JNI_TRUE;
430N/A }
430N/A
430N/A // always update the previous glyph bounds
430N/A previousGlyphBounds.x1 = gx1;
430N/A previousGlyphBounds.y1 = gy1;
430N/A previousGlyphBounds.x2 = gx2;
430N/A previousGlyphBounds.y2 = gy2;
430N/A
430N/A return res;
430N/A}
430N/A
430N/Astatic HRESULT
430N/AD3DTR_DrawLCDGlyphViaCache(D3DContext *d3dc, D3DSDOps *dstOps,
430N/A GlyphInfo *ginfo, jint x, jint y,
430N/A jint glyphIndex, jint totalGlyphs,
430N/A jboolean rgbOrder, jint contrast)
430N/A{
430N/A HRESULT res;
430N/A D3DGlyphCache *pLCDGCache;
430N/A CacheCellInfo *cell;
430N/A GlyphCacheInfo *gcache;
430N/A jint dx1, dy1, dx2, dy2;
430N/A jfloat dtx1, dty1, dtx2, dty2;
430N/A
430N/A J2dTraceLn(J2D_TRACE_VERBOSE, "D3DTR_DrawLCDGlyphViaCache");
430N/A
430N/A // the glyph cache is initialized before this method is called
430N/A pLCDGCache = d3dc->GetLCDGlyphCache();
430N/A
430N/A if (glyphMode != MODE_USE_CACHE_LCD) {
430N/A D3DTR_DisableGlyphModeState(d3dc);
430N/A
430N/A res = d3dc->BeginScene(STATE_TEXTUREOP);
430N/A RETURN_STATUS_IF_FAILED(res);
430N/A
430N/A pLCDGCache->CheckGlyphCacheByteOrder(rgbOrder);
430N/A
430N/A res = D3DTR_EnableLCDGlyphModeState(d3dc, dstOps, JNI_TRUE, contrast);
430N/A RETURN_STATUS_IF_FAILED(res);
430N/A
430N/A glyphMode = MODE_USE_CACHE_LCD;
430N/A }
430N/A
430N/A gcache = pLCDGCache->GetGlyphCache();
430N/A cell = AccelGlyphCache_GetCellInfoForCache(ginfo, gcache);
430N/A if (cell == NULL) {
430N/A // attempt to add glyph to accelerated glyph cache
430N/A res = pLCDGCache->AddGlyph(ginfo);
430N/A RETURN_STATUS_IF_FAILED(res);
0N/A
430N/A // we'll just no-op in the rare case that the cell is NULL
430N/A cell = AccelGlyphCache_GetCellInfoForCache(ginfo, gcache);
430N/A RETURN_STATUS_IF_NULL(cell, E_FAIL);
430N/A }
430N/A
430N/A cell->timesRendered++;
430N/A
430N/A // location of the glyph in the destination's coordinate space
430N/A dx1 = x;
430N/A dy1 = y;
430N/A dx2 = dx1 + ginfo->width;
430N/A dy2 = dy1 + ginfo->height;
430N/A
430N/A // copy destination into second cached texture, if necessary
430N/A D3DTR_UpdateCachedDestination(d3dc,
430N/A dstOps, ginfo,
430N/A dx1, dy1,
430N/A dx2, dy2,
430N/A dx1 + cell->leftOff, // adjusted dx1
430N/A dx2 + cell->rightOff, // adjusted dx2
430N/A glyphIndex, totalGlyphs);
430N/A
430N/A // texture coordinates of the destination tile
430N/A dtx1 = ((jfloat)(dx1 - cachedDestBounds.x1)) / D3DTR_CACHED_DEST_WIDTH;
430N/A dty1 = ((jfloat)(dy1 - cachedDestBounds.y1)) / D3DTR_CACHED_DEST_HEIGHT;
430N/A dtx2 = ((jfloat)(dx2 - cachedDestBounds.x1)) / D3DTR_CACHED_DEST_WIDTH;
430N/A dty2 = ((jfloat)(dy2 - cachedDestBounds.y1)) / D3DTR_CACHED_DEST_HEIGHT;
430N/A
430N/A // render composed texture to the destination surface
430N/A return d3dc->pVCacher->DrawTexture((jfloat)dx1, (jfloat)dy1,
430N/A (jfloat)dx2, (jfloat)dy2,
430N/A cell->tx1, cell->ty1,
430N/A cell->tx2, cell->ty2,
430N/A dtx1, dty1, dtx2, dty2);
430N/A}
430N/A
430N/Astatic HRESULT
430N/AD3DTR_DrawGrayscaleGlyphNoCache(D3DContext *d3dc,
430N/A GlyphInfo *ginfo, jint x, jint y)
430N/A{
430N/A jint tw, th;
430N/A jint sx, sy, sw, sh;
430N/A jint x0;
430N/A jint w = ginfo->width;
430N/A jint h = ginfo->height;
430N/A HRESULT res = S_OK;
430N/A
430N/A J2dTraceLn(J2D_TRACE_VERBOSE, "D3DTR_DrawGrayscaleGlyphNoCache");
430N/A
430N/A if (glyphMode != MODE_NO_CACHE_GRAY) {
430N/A D3DTR_DisableGlyphModeState(d3dc);
430N/A
430N/A res = d3dc->BeginScene(STATE_MASKOP);
430N/A RETURN_STATUS_IF_FAILED(res);
430N/A
430N/A glyphMode = MODE_NO_CACHE_GRAY;
430N/A }
430N/A
430N/A x0 = x;
430N/A tw = D3D_MASK_CACHE_TILE_WIDTH;
430N/A th = D3D_MASK_CACHE_TILE_HEIGHT;
430N/A
430N/A for (sy = 0; sy < h; sy += th, y += th) {
430N/A x = x0;
430N/A sh = ((sy + th) > h) ? (h - sy) : th;
430N/A
430N/A for (sx = 0; sx < w; sx += tw, x += tw) {
430N/A sw = ((sx + tw) > w) ? (w - sx) : tw;
430N/A
430N/A res = d3dc->GetMaskCache()->AddMaskQuad(sx, sy, x, y, sw, sh,
430N/A w, ginfo->image);
0N/A }
0N/A }
0N/A
430N/A return res;
430N/A}
430N/A
430N/Astatic HRESULT
430N/AD3DTR_DrawLCDGlyphNoCache(D3DContext *d3dc, D3DSDOps *dstOps,
430N/A GlyphInfo *ginfo, jint x, jint y,
430N/A jint rowBytesOffset,
430N/A jboolean rgbOrder, jint contrast)
430N/A{
430N/A jfloat tx1, ty1, tx2, ty2;
430N/A jfloat dx1, dy1, dx2, dy2;
430N/A jfloat dtx1, dty1, dtx2, dty2;
430N/A jint tw, th;
430N/A jint sx, sy, sw, sh;
430N/A jint cx1, cy1, cx2, cy2;
430N/A jint x0;
430N/A jint w = ginfo->width;
430N/A jint h = ginfo->height;
430N/A TileFormat tileFormat = rgbOrder ? TILEFMT_3BYTE_RGB : TILEFMT_3BYTE_BGR;
430N/A
430N/A IDirect3DDevice9 *pd3dDevice = d3dc->Get3DDevice();
430N/A D3DResource *pBlitTextureRes, *pCachedDestTextureRes;
430N/A IDirect3DTexture9 *pBlitTexture;
430N/A IDirect3DSurface9 *pCachedDestSurface, *pDst;
430N/A HRESULT res;
430N/A
430N/A J2dTraceLn(J2D_TRACE_VERBOSE, "D3DTR_DrawLCDGlyphNoCache");
430N/A
430N/A RETURN_STATUS_IF_NULL(dstOps->pResource, E_FAIL);
430N/A RETURN_STATUS_IF_NULL(pDst = dstOps->pResource->GetSurface(), E_FAIL);
430N/A
430N/A res = d3dc->GetResourceManager()->GetBlitTexture(&pBlitTextureRes);
430N/A RETURN_STATUS_IF_FAILED(res);
430N/A
430N/A res = d3dc->GetResourceManager()->
430N/A GetCachedDestTexture(dstOps->pResource->GetDesc()->Format,
430N/A &pCachedDestTextureRes);
430N/A RETURN_STATUS_IF_FAILED(res);
430N/A
430N/A pBlitTexture = pBlitTextureRes->GetTexture();
430N/A pCachedDestSurface = pCachedDestTextureRes->GetSurface();
430N/A
430N/A if (glyphMode != MODE_NO_CACHE_LCD) {
430N/A D3DTR_DisableGlyphModeState(d3dc);
430N/A
430N/A res = d3dc->BeginScene(STATE_TEXTUREOP);
430N/A RETURN_STATUS_IF_FAILED(res);
430N/A res = D3DTR_EnableLCDGlyphModeState(d3dc,dstOps, JNI_FALSE, contrast);
430N/A RETURN_STATUS_IF_FAILED(res);
430N/A
430N/A glyphMode = MODE_NO_CACHE_LCD;
430N/A }
430N/A
430N/A x0 = x;
430N/A tx1 = 0.0f;
430N/A ty1 = 0.0f;
430N/A dtx1 = 0.0f;
430N/A dty1 = 0.0f;
430N/A tw = D3DTR_NOCACHE_TILE_SIZE;
430N/A th = D3DTR_NOCACHE_TILE_SIZE;
0N/A
430N/A for (sy = 0; sy < h; sy += th, y += th) {
430N/A x = x0;
430N/A sh = ((sy + th) > h) ? (h - sy) : th;
430N/A
430N/A for (sx = 0; sx < w; sx += tw, x += tw) {
430N/A sw = ((sx + tw) > w) ? (w - sx) : tw;
430N/A
430N/A // calculate the bounds of the tile to be copied from the
430N/A // destination into the cached tile
430N/A cx1 = x;
430N/A cy1 = y;
430N/A cx2 = cx1 + sw;
430N/A cy2 = cy1 + sh;
430N/A
430N/A // need to clamp to the destination bounds, otherwise the
430N/A // StretchRect() call may fail
430N/A if (cx1 < 0) cx1 = 0;
430N/A if (cy1 < 0) cy1 = 0;
430N/A if (cx2 > dstOps->width) cx2 = dstOps->width;
430N/A if (cy2 > dstOps->height) cy2 = dstOps->height;
430N/A
430N/A if (cx2 > cx1 && cy2 > cy1) {
430N/A // copy LCD mask into glyph texture tile
430N/A d3dc->UploadTileToTexture(pBlitTextureRes,
430N/A ginfo->image+rowBytesOffset,
430N/A 0, 0, sx, sy, sw, sh,
430N/A ginfo->rowBytes, tileFormat);
430N/A
430N/A // update the lower-right glyph texture coordinates
430N/A tx2 = ((jfloat)sw) / D3DC_BLIT_TILE_SIZE;
430N/A ty2 = ((jfloat)sh) / D3DC_BLIT_TILE_SIZE;
430N/A
430N/A // calculate the actual destination vertices
430N/A dx1 = (jfloat)x;
430N/A dy1 = (jfloat)y;
430N/A dx2 = dx1 + sw;
430N/A dy2 = dy1 + sh;
430N/A
430N/A // copy destination into cached texture tile (the upper-left
430N/A // corner of the destination region will be positioned at the
430N/A // upper-left corner (0,0) of the texture)
430N/A RECT srcRect = { cx1, cy1, cx2, cy2 };
430N/A RECT dstRect = { cx1-x, cy1-y, cx2-x, cy2-y };
430N/A pd3dDevice->StretchRect(pDst, &srcRect,
430N/A pCachedDestSurface,
430N/A &dstRect,
430N/A D3DTEXF_NONE);
430N/A
430N/A // update the remaining destination texture coordinates
430N/A dtx2 = ((jfloat)sw) / D3DTR_CACHED_DEST_WIDTH;
430N/A dty2 = ((jfloat)sh) / D3DTR_CACHED_DEST_HEIGHT;
430N/A
430N/A // render composed texture to the destination surface
430N/A res = d3dc->pVCacher->DrawTexture( dx1, dy1, dx2, dy2,
430N/A tx1, ty1, tx2, ty2,
430N/A dtx1, dty1, dtx2, dty2);
430N/A
430N/A // unfortunately we need to flush after each tile
430N/A d3dc->FlushVertexQueue();
430N/A }
430N/A }
430N/A }
430N/A
430N/A return res;
0N/A}
0N/A
430N/A// see DrawGlyphList.c for more on this macro...
430N/A#define FLOOR_ASSIGN(l, r) \
430N/A if ((r)<0) (l) = ((int)floor(r)); else (l) = ((int)(r))
0N/A
430N/AHRESULT
430N/AD3DTR_DrawGlyphList(D3DContext *d3dc, D3DSDOps *dstOps,
430N/A jint totalGlyphs, jboolean usePositions,
430N/A jboolean subPixPos, jboolean rgbOrder, jint lcdContrast,
430N/A jfloat glyphListOrigX, jfloat glyphListOrigY,
430N/A unsigned char *images, unsigned char *positions)
0N/A{
430N/A int glyphCounter;
430N/A HRESULT res = S_OK;
430N/A J2dTraceLn(J2D_TRACE_INFO, "D3DTR_DrawGlyphList");
0N/A
430N/A RETURN_STATUS_IF_NULL(d3dc, E_FAIL);
430N/A RETURN_STATUS_IF_NULL(d3dc->Get3DDevice(), E_FAIL);
430N/A RETURN_STATUS_IF_NULL(dstOps, E_FAIL);
430N/A RETURN_STATUS_IF_NULL(images, E_FAIL);
430N/A if (usePositions) {
430N/A RETURN_STATUS_IF_NULL(positions, E_FAIL);
0N/A }
0N/A
430N/A glyphMode = MODE_NOT_INITED;
430N/A isCachedDestValid = JNI_FALSE;
430N/A
430N/A for (glyphCounter = 0; glyphCounter < totalGlyphs; glyphCounter++) {
430N/A jint x, y;
430N/A jfloat glyphx, glyphy;
430N/A jboolean grayscale;
430N/A GlyphInfo *ginfo = (GlyphInfo *)jlong_to_ptr(NEXT_LONG(images));
430N/A
430N/A if (ginfo == NULL) {
430N/A // this shouldn't happen, but if it does we'll just break out...
430N/A J2dRlsTraceLn(J2D_TRACE_ERROR,
430N/A "D3DTR_DrawGlyphList: glyph info is null");
430N/A break;
430N/A }
430N/A
430N/A grayscale = (ginfo->rowBytes == ginfo->width);
430N/A
430N/A if (usePositions) {
430N/A jfloat posx = NEXT_FLOAT(positions);
430N/A jfloat posy = NEXT_FLOAT(positions);
430N/A glyphx = glyphListOrigX + posx + ginfo->topLeftX;
430N/A glyphy = glyphListOrigY + posy + ginfo->topLeftY;
430N/A FLOOR_ASSIGN(x, glyphx);
430N/A FLOOR_ASSIGN(y, glyphy);
430N/A } else {
430N/A glyphx = glyphListOrigX + ginfo->topLeftX;
430N/A glyphy = glyphListOrigY + ginfo->topLeftY;
430N/A FLOOR_ASSIGN(x, glyphx);
430N/A FLOOR_ASSIGN(y, glyphy);
430N/A glyphListOrigX += ginfo->advanceX;
430N/A glyphListOrigY += ginfo->advanceY;
430N/A }
430N/A
430N/A if (ginfo->image == NULL) {
430N/A continue;
430N/A }
0N/A
430N/A if (grayscale) {
430N/A // grayscale or monochrome glyph data
430N/A if (ginfo->width <= D3DTR_CACHE_CELL_WIDTH &&
430N/A ginfo->height <= D3DTR_CACHE_CELL_HEIGHT &&
430N/A SUCCEEDED(d3dc->InitGrayscaleGlyphCache()))
430N/A {
430N/A res = D3DTR_DrawGrayscaleGlyphViaCache(d3dc, ginfo, x, y);
430N/A } else {
430N/A res = D3DTR_DrawGrayscaleGlyphNoCache(d3dc, ginfo, x, y);
430N/A }
430N/A } else {
430N/A // LCD-optimized glyph data
430N/A jint rowBytesOffset = 0;
430N/A
430N/A if (subPixPos) {
430N/A jint frac = (jint)((glyphx - x) * 3);
430N/A if (frac != 0) {
430N/A rowBytesOffset = 3 - frac;
430N/A x += 1;
430N/A }
430N/A }
430N/A
430N/A if (rowBytesOffset == 0 &&
430N/A ginfo->width <= D3DTR_CACHE_CELL_WIDTH &&
430N/A ginfo->height <= D3DTR_CACHE_CELL_HEIGHT &&
430N/A SUCCEEDED(d3dc->InitLCDGlyphCache()))
430N/A {
430N/A res = D3DTR_DrawLCDGlyphViaCache(d3dc, dstOps,
430N/A ginfo, x, y,
430N/A glyphCounter, totalGlyphs,
430N/A rgbOrder, lcdContrast);
430N/A } else {
430N/A res = D3DTR_DrawLCDGlyphNoCache(d3dc, dstOps,
430N/A ginfo, x, y,
430N/A rowBytesOffset,
430N/A rgbOrder, lcdContrast);
430N/A }
430N/A }
430N/A
430N/A if (FAILED(res)) {
430N/A break;
430N/A }
0N/A }
0N/A
430N/A D3DTR_DisableGlyphModeState(d3dc);
430N/A return res;
0N/A}
0N/A
430N/AJNIEXPORT void JNICALL
430N/AJava_sun_java2d_d3d_D3DTextRenderer_drawGlyphList
430N/A (JNIEnv *env, jobject self,
430N/A jint numGlyphs, jboolean usePositions,
430N/A jboolean subPixPos, jboolean rgbOrder, jint lcdContrast,
430N/A jfloat glyphListOrigX, jfloat glyphListOrigY,
430N/A jlongArray imgArray, jfloatArray posArray)
430N/A{
430N/A unsigned char *images;
430N/A
430N/A J2dTraceLn(J2D_TRACE_INFO, "D3DTextRenderer_drawGlyphList");
430N/A
430N/A images = (unsigned char *)
430N/A env->GetPrimitiveArrayCritical(imgArray, NULL);
430N/A if (images != NULL) {
430N/A D3DContext *d3dc = D3DRQ_GetCurrentContext();
430N/A D3DSDOps *dstOps = D3DRQ_GetCurrentDestination();
430N/A
430N/A if (usePositions) {
430N/A unsigned char *positions = (unsigned char *)
430N/A env->GetPrimitiveArrayCritical(posArray, NULL);
430N/A if (positions != NULL) {
430N/A D3DTR_DrawGlyphList(d3dc, dstOps,
430N/A numGlyphs, usePositions,
430N/A subPixPos, rgbOrder, lcdContrast,
430N/A glyphListOrigX, glyphListOrigY,
430N/A images, positions);
430N/A env->ReleasePrimitiveArrayCritical(posArray,
430N/A positions, JNI_ABORT);
430N/A }
430N/A } else {
430N/A D3DTR_DrawGlyphList(d3dc, dstOps,
430N/A numGlyphs, usePositions,
430N/A subPixPos, rgbOrder, lcdContrast,
430N/A glyphListOrigX, glyphListOrigY,
430N/A images, NULL);
430N/A }
430N/A
430N/A // reset current state, and ensure rendering is flushed to dest
430N/A if (d3dc != NULL) {
430N/A d3dc->FlushVertexQueue();
430N/A }
430N/A
430N/A env->ReleasePrimitiveArrayCritical(imgArray,
430N/A images, JNI_ABORT);
430N/A }
0N/A}