Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/windows/native/sun/java2d/d3d/D3DTextRenderer.cpp
32288 views
/*1* Copyright (c) 2007, 2016, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425#include <malloc.h>26#include <math.h>27#include <jlong.h>2829#include "sun_java2d_d3d_D3DTextRenderer.h"30#include "sun_java2d_pipe_BufferedTextPipe.h"3132#include "SurfaceData.h"33#include "D3DContext.h"34#include "D3DSurfaceData.h"35#include "D3DRenderQueue.h"36#include "D3DTextRenderer.h"37#include "D3DGlyphCache.h"38#include "AccelGlyphCache.h"39#include "fontscalerdefs.h"4041/**42* The current "glyph mode" state. This variable is used to track the43* codepath used to render a particular glyph. This variable is reset to44* MODE_NOT_INITED at the beginning of every call to D3DTR_DrawGlyphList().45* As each glyph is rendered, the glyphMode variable is updated to reflect46* the current mode, so if the current mode is the same as the mode used47* to render the previous glyph, we can avoid doing costly setup operations48* each time.49*/50typedef enum {51MODE_NOT_INITED,52MODE_USE_CACHE_GRAY,53MODE_USE_CACHE_LCD,54MODE_NO_CACHE_GRAY,55MODE_NO_CACHE_LCD56} GlyphMode;57static GlyphMode glyphMode = MODE_NOT_INITED;5859/**60* The current bounds of the "cached destination" texture, in destination61* coordinate space. The width/height of these bounds will not exceed the62* D3DTR_CACHED_DEST_WIDTH/HEIGHT values defined above. These bounds are63* only considered valid when the isCachedDestValid flag is JNI_TRUE.64*/65static SurfaceDataBounds cachedDestBounds;6667/**68* This flag indicates whether the "cached destination" texture contains69* valid data. This flag is reset to JNI_FALSE at the beginning of every70* call to D3DTR_DrawGlyphList(). Once we copy valid destination data71* into the cached texture, this flag is set to JNI_TRUE. This way, we72* can limit the number of times we need to copy destination data, which73* is a very costly operation.74*/75static jboolean isCachedDestValid = JNI_FALSE;7677/**78* The bounds of the previously rendered LCD glyph, in destination79* coordinate space. We use these bounds to determine whether the glyph80* currently being rendered overlaps the previously rendered glyph (i.e.81* its bounding box intersects that of the previously rendered glyph).82* If so, we need to re-read the destination area associated with that83* previous glyph so that we can correctly blend with the actual84* destination data.85*/86static SurfaceDataBounds previousGlyphBounds;8788/**89* Updates the gamma and inverse gamma values for the LCD text shader.90*/91static HRESULT92D3DTR_UpdateLCDTextContrast(D3DContext *d3dc, jint contrast)93{94HRESULT res;95IDirect3DDevice9 *pd3dDevice = d3dc->Get3DDevice();9697jfloat fcon = ((jfloat)contrast) / 100.0f;98jfloat invgamma = fcon;99jfloat gamma = 1.0f / invgamma;100jfloat vals[4];101102// update the "invgamma" parameter of the shader program103vals[0] = invgamma;104vals[1] = invgamma;105vals[2] = invgamma;106vals[3] = 0.0f; // unused107pd3dDevice->SetPixelShaderConstantF(1, vals, 1);108109// update the "gamma" parameter of the shader program110vals[0] = gamma;111vals[1] = gamma;112vals[2] = gamma;113vals[3] = 0.0f; // unused114res = pd3dDevice->SetPixelShaderConstantF(2, vals, 1);115116return res;117}118119/**120* Updates the current gamma-adjusted source color ("src_adj") of the LCD121* text shader program. Note that we could calculate this value in the122* shader (e.g. just as we do for "dst_adj"), but would be unnecessary work123* (and a measurable performance hit, maybe around 5%) since this value is124* constant over the entire glyph list. So instead we just calculate the125* gamma-adjusted value once and update the uniform parameter of the LCD126* shader as needed.127*/128static HRESULT129D3DTR_UpdateLCDTextColor(D3DContext *d3dc, jint contrast)130{131IDirect3DDevice9 *pd3dDevice = d3dc->Get3DDevice();132jfloat gamma = ((jfloat)contrast) / 100.0f;133jfloat clr[4];134135J2dTraceLn1(J2D_TRACE_INFO,136"D3DTR_UpdateLCDTextColor: contrast=%d", contrast);137138/*139* Note: Ideally we would update the "srcAdj" uniform parameter only140* when there is a change in the source color. Fortunately, the cost141* of querying the current D3D color state and updating the uniform142* value is quite small, and in the common case we only need to do this143* once per GlyphList, so we gain little from trying to optimize too144* eagerly here.145*/146147// get the current D3D primary color state148jint color = d3dc->pVCacher->GetColor();149clr[0] = (jfloat)((color >> 16) & 0xff) / 255.0f;150clr[1] = (jfloat)((color >> 8) & 0xff) / 255.0f;151clr[2] = (jfloat)((color >> 0) & 0xff) / 255.0f;152clr[3] = 0.0f; // unused153154// gamma adjust the primary color155clr[0] = (jfloat)pow(clr[0], gamma);156clr[1] = (jfloat)pow(clr[1], gamma);157clr[2] = (jfloat)pow(clr[2], gamma);158159// update the "srcAdj" parameter of the shader program with this value160return pd3dDevice->SetPixelShaderConstantF(0, clr, 1);161}162163/**164* Enables the LCD text shader and updates any related state, such as the165* gamma values.166*/167static HRESULT168D3DTR_EnableLCDGlyphModeState(D3DContext *d3dc, D3DSDOps *dstOps,169jboolean useCache, jint contrast)170{171D3DResource *pGlyphTexRes, *pCachedDestTexRes;172IDirect3DTexture9 *pGlyphTex, *pCachedDestTex;173174RETURN_STATUS_IF_NULL(dstOps->pResource, E_FAIL);175176HRESULT res = S_OK;177if (useCache) {178// glyph cache had been already initialized179pGlyphTexRes = d3dc->GetLCDGlyphCache()->GetGlyphCacheTexture();180} else {181res = d3dc->GetResourceManager()->GetBlitTexture(&pGlyphTexRes);182}183RETURN_STATUS_IF_FAILED(res);184185pGlyphTex = pGlyphTexRes->GetTexture();186187res = d3dc->GetResourceManager()->188GetCachedDestTexture(dstOps->pResource->GetDesc()->Format,189&pCachedDestTexRes);190RETURN_STATUS_IF_FAILED(res);191pCachedDestTex = pCachedDestTexRes->GetTexture();192193IDirect3DDevice9 *pd3dDevice = d3dc->Get3DDevice();194D3DTEXTUREFILTERTYPE fhint =195d3dc->IsTextureFilteringSupported(D3DTEXF_NONE) ?196D3DTEXF_NONE : D3DTEXF_POINT;197pd3dDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, fhint);198pd3dDevice->SetSamplerState(0, D3DSAMP_MINFILTER, fhint);199pd3dDevice->SetSamplerState(1, D3DSAMP_MAGFILTER, fhint);200pd3dDevice->SetSamplerState(1, D3DSAMP_MINFILTER, fhint);201d3dc->UpdateTextureColorState(D3DTA_TEXTURE, 1);202203// bind the texture containing glyph data to texture unit 0204d3dc->SetTexture(pGlyphTex, 0);205206// bind the texture tile containing destination data to texture unit 1207d3dc->SetTexture(pCachedDestTex, 1);208209// create/enable the LCD text shader210res = d3dc->EnableLCDTextProgram();211RETURN_STATUS_IF_FAILED(res);212213// update the current contrast settings (note: these change very rarely,214// but it seems that D3D pixel shader registers aren't maintained as215// part of the pixel shader instance, so we need to update these216// everytime around in case another shader blew away the contents217// of those registers)218D3DTR_UpdateLCDTextContrast(d3dc, contrast);219220// update the current color settings221return D3DTR_UpdateLCDTextColor(d3dc, contrast);222}223224HRESULT225D3DTR_EnableGlyphVertexCache(D3DContext *d3dc)226{227J2dTraceLn(J2D_TRACE_INFO, "D3DTR_EnableGlyphVertexCache");228229IDirect3DDevice9 *pd3dDevice = d3dc->Get3DDevice();230D3DTEXTUREFILTERTYPE fhint =231d3dc->IsTextureFilteringSupported(D3DTEXF_NONE) ?232D3DTEXF_NONE : D3DTEXF_POINT;233pd3dDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, fhint);234pd3dDevice->SetSamplerState(0, D3DSAMP_MINFILTER, fhint);235236// glyph cache had been successfully initialized if we got here237D3DResource *pGlyphCacheTexRes =238d3dc->GetGrayscaleGlyphCache()->GetGlyphCacheTexture();239return d3dc->SetTexture(pGlyphCacheTexRes->GetTexture(), 0);240}241242HRESULT243D3DTR_DisableGlyphVertexCache(D3DContext *d3dc)244{245J2dTraceLn(J2D_TRACE_INFO, "D3DTR_DisableGlyphVertexCache");246247return d3dc->SetTexture(NULL, 0);248}249250/**251* Disables any pending state associated with the current "glyph mode".252*/253static HRESULT254D3DTR_DisableGlyphModeState(D3DContext *d3dc)255{256HRESULT res = S_OK;257IDirect3DDevice9 *pd3dDevice = d3dc->Get3DDevice();258259switch (glyphMode) {260case MODE_NO_CACHE_LCD:261case MODE_USE_CACHE_LCD:262d3dc->FlushVertexQueue();263pd3dDevice->SetPixelShader(NULL);264res = d3dc->SetTexture(NULL, 1);265break;266267case MODE_NO_CACHE_GRAY:268case MODE_USE_CACHE_GRAY:269case MODE_NOT_INITED:270default:271break;272}273return res;274}275276static HRESULT277D3DTR_DrawGrayscaleGlyphViaCache(D3DContext *d3dc,278GlyphInfo *ginfo, jint x, jint y)279{280HRESULT res = S_OK;281D3DGlyphCache *pGrayscaleGCache;282CacheCellInfo *cell;283GlyphCacheInfo *gcache;284jfloat x1, y1, x2, y2;285286J2dTraceLn(J2D_TRACE_VERBOSE, "D3DTR_DrawGrayscaleGlyphViaCache");287288if (glyphMode != MODE_USE_CACHE_GRAY) {289D3DTR_DisableGlyphModeState(d3dc);290291res = d3dc->BeginScene(STATE_GLYPHOP);292RETURN_STATUS_IF_FAILED(res);293294glyphMode = MODE_USE_CACHE_GRAY;295}296297pGrayscaleGCache = d3dc->GetGrayscaleGlyphCache();298gcache = pGrayscaleGCache->GetGlyphCache();299cell = AccelGlyphCache_GetCellInfoForCache(ginfo, gcache);300if (cell == NULL) {301// attempt to add glyph to accelerated glyph cache302res = pGrayscaleGCache->AddGlyph(ginfo);303RETURN_STATUS_IF_FAILED(res);304305cell = AccelGlyphCache_GetCellInfoForCache(ginfo, gcache);306RETURN_STATUS_IF_NULL(cell, E_FAIL);307}308309cell->timesRendered++;310311x1 = (jfloat)x;312y1 = (jfloat)y;313x2 = x1 + ginfo->width;314y2 = y1 + ginfo->height;315316return d3dc->pVCacher->DrawTexture(x1, y1, x2, y2,317cell->tx1, cell->ty1,318cell->tx2, cell->ty2);319}320321/**322* Evaluates to true if the rectangle defined by gx1/gy1/gx2/gy2 is323* inside outerBounds.324*/325#define INSIDE(gx1, gy1, gx2, gy2, outerBounds) \326(((gx1) >= outerBounds.x1) && ((gy1) >= outerBounds.y1) && \327((gx2) <= outerBounds.x2) && ((gy2) <= outerBounds.y2))328329/**330* Evaluates to true if the rectangle defined by gx1/gy1/gx2/gy2 intersects331* the rectangle defined by bounds.332*/333#define INTERSECTS(gx1, gy1, gx2, gy2, bounds) \334((bounds.x2 > (gx1)) && (bounds.y2 > (gy1)) && \335(bounds.x1 < (gx2)) && (bounds.y1 < (gy2)))336337/**338* This method checks to see if the given LCD glyph bounds fall within the339* cached destination texture bounds. If so, this method can return340* immediately. If not, this method will copy a chunk of framebuffer data341* into the cached destination texture and then update the current cached342* destination bounds before returning.343*344* The agx1, agx2 are "adjusted" glyph bounds, which are only used when checking345* against the previous glyph bounds.346*/347static HRESULT348D3DTR_UpdateCachedDestination(D3DContext *d3dc, D3DSDOps *dstOps,349GlyphInfo *ginfo,350jint gx1, jint gy1, jint gx2, jint gy2,351jint agx1, jint agx2,352jint glyphIndex, jint totalGlyphs)353{354jint dx1, dy1, dx2, dy2;355D3DResource *pCachedDestTexRes;356IDirect3DSurface9 *pCachedDestSurface, *pDst;357HRESULT res = S_OK;358359if (isCachedDestValid && INSIDE(gx1, gy1, gx2, gy2, cachedDestBounds)) {360// glyph is already within the cached destination bounds; no need361// to read back the entire destination region again, but we do362// need to see if the current glyph overlaps the previous glyph...363364// only use the "adjusted" glyph bounds when checking against365// previous glyph's bounds366gx1 = agx1;367gx2 = agx2;368369if (INTERSECTS(gx1, gy1, gx2, gy2, previousGlyphBounds)) {370// the current glyph overlaps the destination region touched371// by the previous glyph, so now we need to read back the part372// of the destination corresponding to the previous glyph373dx1 = previousGlyphBounds.x1;374dy1 = previousGlyphBounds.y1;375dx2 = previousGlyphBounds.x2;376dy2 = previousGlyphBounds.y2;377378// REMIND: make sure we flush any pending primitives that are379// dependent on the current contents of the cached dest380d3dc->FlushVertexQueue();381382RETURN_STATUS_IF_NULL(dstOps->pResource, E_FAIL);383RETURN_STATUS_IF_NULL(pDst = dstOps->pResource->GetSurface(),384E_FAIL);385res = d3dc->GetResourceManager()->386GetCachedDestTexture(dstOps->pResource->GetDesc()->Format,387&pCachedDestTexRes);388RETURN_STATUS_IF_FAILED(res);389pCachedDestSurface = pCachedDestTexRes->GetSurface();390391// now dxy12 represent the "desired" destination bounds, but the392// StretchRect() call may fail if these fall outside the actual393// surface bounds; therefore, we use cxy12 to represent the394// clamped bounds, and dxy12 are saved for later395jint cx1 = (dx1 < 0) ? 0 : dx1;396jint cy1 = (dy1 < 0) ? 0 : dy1;397jint cx2 = (dx2 > dstOps->width) ? dstOps->width : dx2;398jint cy2 = (dy2 > dstOps->height) ? dstOps->height : dy2;399400if (cx2 > cx1 && cy2 > cy1) {401// copy destination into subregion of cached texture tile402// cx1-cachedDestBounds.x1 == +xoffset from left of texture403// cy1-cachedDestBounds.y1 == +yoffset from top of texture404// cx2-cachedDestBounds.x1 == +xoffset from left of texture405// cy2-cachedDestBounds.y1 == +yoffset from top of texture406jint cdx1 = cx1-cachedDestBounds.x1;407jint cdy1 = cy1-cachedDestBounds.y1;408jint cdx2 = cx2-cachedDestBounds.x1;409jint cdy2 = cy2-cachedDestBounds.y1;410RECT srcRect = { cx1, cy1, cx2, cy2 };411RECT dstRect = { cdx1, cdy1, cdx2, cdy2 };412413IDirect3DDevice9 *pd3dDevice = d3dc->Get3DDevice();414res = pd3dDevice->StretchRect(pDst, &srcRect,415pCachedDestSurface, &dstRect,416D3DTEXF_NONE);417}418}419} else {420// destination region is not valid, so we need to read back a421// chunk of the destination into our cached texture422423// position the upper-left corner of the destination region on the424// "top" line of glyph list425// REMIND: this isn't ideal; it would be better if we had some idea426// of the bounding box of the whole glyph list (this is427// do-able, but would require iterating through the whole428// list up front, which may present its own problems)429dx1 = gx1;430dy1 = gy1;431432jint remainingWidth;433if (ginfo->advanceX > 0) {434// estimate the width based on our current position in the glyph435// list and using the x advance of the current glyph (this is just436// a quick and dirty heuristic; if this is a "thin" glyph image,437// then we're likely to underestimate, and if it's "thick" then we438// may end up reading back more than we need to)439remainingWidth =440(jint)(ginfo->advanceX * (totalGlyphs - glyphIndex));441if (remainingWidth > D3DTR_CACHED_DEST_WIDTH) {442remainingWidth = D3DTR_CACHED_DEST_WIDTH;443} else if (remainingWidth < ginfo->width) {444// in some cases, the x-advance may be slightly smaller445// than the actual width of the glyph; if so, adjust our446// estimate so that we can accommodate the entire glyph447remainingWidth = ginfo->width;448}449} else {450// a negative advance is possible when rendering rotated text,451// in which case it is difficult to estimate an appropriate452// region for readback, so we will pick a region that453// encompasses just the current glyph454remainingWidth = ginfo->width;455}456dx2 = dx1 + remainingWidth;457458// estimate the height (this is another sloppy heuristic; we'll459// make the cached destination region tall enough to encompass most460// glyphs that are small enough to fit in the glyph cache, and then461// we add a little something extra to account for descenders462dy2 = dy1 + D3DTR_CACHE_CELL_HEIGHT + 2;463464// REMIND: make sure we flush any pending primitives that are465// dependent on the current contents of the cached dest466d3dc->FlushVertexQueue();467468RETURN_STATUS_IF_NULL(dstOps->pResource, E_FAIL);469RETURN_STATUS_IF_NULL(pDst = dstOps->pResource->GetSurface(), E_FAIL);470res = d3dc->GetResourceManager()->471GetCachedDestTexture(dstOps->pResource->GetDesc()->Format,472&pCachedDestTexRes);473RETURN_STATUS_IF_FAILED(res);474pCachedDestSurface = pCachedDestTexRes->GetSurface();475476// now dxy12 represent the "desired" destination bounds, but the477// StretchRect() call may fail if these fall outside the actual478// surface bounds; therefore, we use cxy12 to represent the479// clamped bounds, and dxy12 are saved for later480jint cx1 = (dx1 < 0) ? 0 : dx1;481jint cy1 = (dy1 < 0) ? 0 : dy1;482jint cx2 = (dx2 > dstOps->width) ? dstOps->width : dx2;483jint cy2 = (dy2 > dstOps->height) ? dstOps->height : dy2;484485if (cx2 > cx1 && cy2 > cy1) {486// copy destination into cached texture tile (the upper-left487// corner of the destination region will be positioned at the488// upper-left corner (0,0) of the texture)489RECT srcRect = { cx1, cy1, cx2, cy2 };490RECT dstRect = { cx1-dx1, cy1-dy1, cx2-dx1, cy2-dy1 };491492IDirect3DDevice9 *pd3dDevice = d3dc->Get3DDevice();493res = pd3dDevice->StretchRect(pDst, &srcRect,494pCachedDestSurface, &dstRect,495D3DTEXF_NONE);496}497498// update the cached bounds and mark it valid499cachedDestBounds.x1 = dx1;500cachedDestBounds.y1 = dy1;501cachedDestBounds.x2 = dx2;502cachedDestBounds.y2 = dy2;503isCachedDestValid = JNI_TRUE;504}505506// always update the previous glyph bounds507previousGlyphBounds.x1 = gx1;508previousGlyphBounds.y1 = gy1;509previousGlyphBounds.x2 = gx2;510previousGlyphBounds.y2 = gy2;511512return res;513}514515static HRESULT516D3DTR_DrawLCDGlyphViaCache(D3DContext *d3dc, D3DSDOps *dstOps,517GlyphInfo *ginfo, jint x, jint y,518jint glyphIndex, jint totalGlyphs,519jboolean rgbOrder, jint contrast)520{521HRESULT res;522D3DGlyphCache *pLCDGCache;523CacheCellInfo *cell;524GlyphCacheInfo *gcache;525jint dx1, dy1, dx2, dy2;526jfloat dtx1, dty1, dtx2, dty2;527528J2dTraceLn(J2D_TRACE_VERBOSE, "D3DTR_DrawLCDGlyphViaCache");529530// the glyph cache is initialized before this method is called531pLCDGCache = d3dc->GetLCDGlyphCache();532533if (glyphMode != MODE_USE_CACHE_LCD) {534D3DTR_DisableGlyphModeState(d3dc);535536res = d3dc->BeginScene(STATE_TEXTUREOP);537RETURN_STATUS_IF_FAILED(res);538539pLCDGCache->CheckGlyphCacheByteOrder(rgbOrder);540541res = D3DTR_EnableLCDGlyphModeState(d3dc, dstOps, JNI_TRUE, contrast);542RETURN_STATUS_IF_FAILED(res);543544glyphMode = MODE_USE_CACHE_LCD;545}546547gcache = pLCDGCache->GetGlyphCache();548cell = AccelGlyphCache_GetCellInfoForCache(ginfo, gcache);549if (cell == NULL) {550// attempt to add glyph to accelerated glyph cache551res = pLCDGCache->AddGlyph(ginfo);552RETURN_STATUS_IF_FAILED(res);553554// we'll just no-op in the rare case that the cell is NULL555cell = AccelGlyphCache_GetCellInfoForCache(ginfo, gcache);556RETURN_STATUS_IF_NULL(cell, E_FAIL);557}558559cell->timesRendered++;560561// location of the glyph in the destination's coordinate space562dx1 = x;563dy1 = y;564dx2 = dx1 + ginfo->width;565dy2 = dy1 + ginfo->height;566567// copy destination into second cached texture, if necessary568D3DTR_UpdateCachedDestination(d3dc,569dstOps, ginfo,570dx1, dy1,571dx2, dy2,572dx1 + cell->leftOff, // adjusted dx1573dx2 + cell->rightOff, // adjusted dx2574glyphIndex, totalGlyphs);575576// texture coordinates of the destination tile577dtx1 = ((jfloat)(dx1 - cachedDestBounds.x1)) / D3DTR_CACHED_DEST_WIDTH;578dty1 = ((jfloat)(dy1 - cachedDestBounds.y1)) / D3DTR_CACHED_DEST_HEIGHT;579dtx2 = ((jfloat)(dx2 - cachedDestBounds.x1)) / D3DTR_CACHED_DEST_WIDTH;580dty2 = ((jfloat)(dy2 - cachedDestBounds.y1)) / D3DTR_CACHED_DEST_HEIGHT;581582// render composed texture to the destination surface583return d3dc->pVCacher->DrawTexture((jfloat)dx1, (jfloat)dy1,584(jfloat)dx2, (jfloat)dy2,585cell->tx1, cell->ty1,586cell->tx2, cell->ty2,587dtx1, dty1, dtx2, dty2);588}589590static HRESULT591D3DTR_DrawGrayscaleGlyphNoCache(D3DContext *d3dc,592GlyphInfo *ginfo, jint x, jint y)593{594jint tw, th;595jint sx, sy, sw, sh;596jint x0;597jint w = ginfo->width;598jint h = ginfo->height;599HRESULT res = S_OK;600601J2dTraceLn(J2D_TRACE_VERBOSE, "D3DTR_DrawGrayscaleGlyphNoCache");602603if (glyphMode != MODE_NO_CACHE_GRAY) {604D3DTR_DisableGlyphModeState(d3dc);605606res = d3dc->BeginScene(STATE_MASKOP);607RETURN_STATUS_IF_FAILED(res);608609glyphMode = MODE_NO_CACHE_GRAY;610}611612x0 = x;613tw = D3D_MASK_CACHE_TILE_WIDTH;614th = D3D_MASK_CACHE_TILE_HEIGHT;615616for (sy = 0; sy < h; sy += th, y += th) {617x = x0;618sh = ((sy + th) > h) ? (h - sy) : th;619620for (sx = 0; sx < w; sx += tw, x += tw) {621sw = ((sx + tw) > w) ? (w - sx) : tw;622623res = d3dc->GetMaskCache()->AddMaskQuad(sx, sy, x, y, sw, sh,624w, ginfo->image);625}626}627628return res;629}630631static HRESULT632D3DTR_DrawLCDGlyphNoCache(D3DContext *d3dc, D3DSDOps *dstOps,633GlyphInfo *ginfo, jint x, jint y,634jint rowBytesOffset,635jboolean rgbOrder, jint contrast)636{637jfloat tx1, ty1, tx2, ty2;638jfloat dx1, dy1, dx2, dy2;639jfloat dtx1, dty1, dtx2, dty2;640jint tw, th;641jint sx, sy, sw, sh;642jint cx1, cy1, cx2, cy2;643jint x0;644jint w = ginfo->width;645jint h = ginfo->height;646TileFormat tileFormat = rgbOrder ? TILEFMT_3BYTE_RGB : TILEFMT_3BYTE_BGR;647648IDirect3DDevice9 *pd3dDevice = d3dc->Get3DDevice();649D3DResource *pBlitTextureRes, *pCachedDestTextureRes;650IDirect3DTexture9 *pBlitTexture;651IDirect3DSurface9 *pCachedDestSurface, *pDst;652HRESULT res;653654J2dTraceLn(J2D_TRACE_VERBOSE, "D3DTR_DrawLCDGlyphNoCache");655656RETURN_STATUS_IF_NULL(dstOps->pResource, E_FAIL);657RETURN_STATUS_IF_NULL(pDst = dstOps->pResource->GetSurface(), E_FAIL);658659res = d3dc->GetResourceManager()->GetBlitTexture(&pBlitTextureRes);660RETURN_STATUS_IF_FAILED(res);661662res = d3dc->GetResourceManager()->663GetCachedDestTexture(dstOps->pResource->GetDesc()->Format,664&pCachedDestTextureRes);665RETURN_STATUS_IF_FAILED(res);666667pBlitTexture = pBlitTextureRes->GetTexture();668pCachedDestSurface = pCachedDestTextureRes->GetSurface();669670if (glyphMode != MODE_NO_CACHE_LCD) {671D3DTR_DisableGlyphModeState(d3dc);672673res = d3dc->BeginScene(STATE_TEXTUREOP);674RETURN_STATUS_IF_FAILED(res);675res = D3DTR_EnableLCDGlyphModeState(d3dc,dstOps, JNI_FALSE, contrast);676RETURN_STATUS_IF_FAILED(res);677678glyphMode = MODE_NO_CACHE_LCD;679}680681x0 = x;682tx1 = 0.0f;683ty1 = 0.0f;684dtx1 = 0.0f;685dty1 = 0.0f;686tw = D3DTR_NOCACHE_TILE_SIZE;687th = D3DTR_NOCACHE_TILE_SIZE;688689for (sy = 0; sy < h; sy += th, y += th) {690x = x0;691sh = ((sy + th) > h) ? (h - sy) : th;692693for (sx = 0; sx < w; sx += tw, x += tw) {694sw = ((sx + tw) > w) ? (w - sx) : tw;695696// calculate the bounds of the tile to be copied from the697// destination into the cached tile698cx1 = x;699cy1 = y;700cx2 = cx1 + sw;701cy2 = cy1 + sh;702703// need to clamp to the destination bounds, otherwise the704// StretchRect() call may fail705if (cx1 < 0) cx1 = 0;706if (cy1 < 0) cy1 = 0;707if (cx2 > dstOps->width) cx2 = dstOps->width;708if (cy2 > dstOps->height) cy2 = dstOps->height;709710if (cx2 > cx1 && cy2 > cy1) {711// copy LCD mask into glyph texture tile712d3dc->UploadTileToTexture(pBlitTextureRes,713ginfo->image+rowBytesOffset,7140, 0, sx, sy, sw, sh,715ginfo->rowBytes, tileFormat);716717// update the lower-right glyph texture coordinates718tx2 = ((jfloat)sw) / D3DC_BLIT_TILE_SIZE;719ty2 = ((jfloat)sh) / D3DC_BLIT_TILE_SIZE;720721// calculate the actual destination vertices722dx1 = (jfloat)x;723dy1 = (jfloat)y;724dx2 = dx1 + sw;725dy2 = dy1 + sh;726727// copy destination into cached texture tile (the upper-left728// corner of the destination region will be positioned at the729// upper-left corner (0,0) of the texture)730RECT srcRect = { cx1, cy1, cx2, cy2 };731RECT dstRect = { cx1-x, cy1-y, cx2-x, cy2-y };732pd3dDevice->StretchRect(pDst, &srcRect,733pCachedDestSurface,734&dstRect,735D3DTEXF_NONE);736737// update the remaining destination texture coordinates738dtx2 = ((jfloat)sw) / D3DTR_CACHED_DEST_WIDTH;739dty2 = ((jfloat)sh) / D3DTR_CACHED_DEST_HEIGHT;740741// render composed texture to the destination surface742res = d3dc->pVCacher->DrawTexture( dx1, dy1, dx2, dy2,743tx1, ty1, tx2, ty2,744dtx1, dty1, dtx2, dty2);745746// unfortunately we need to flush after each tile747d3dc->FlushVertexQueue();748}749}750}751752return res;753}754755// see DrawGlyphList.c for more on this macro...756#define FLOOR_ASSIGN(l, r) \757if ((r)<0) (l) = ((int)floor(r)); else (l) = ((int)(r))758759HRESULT760D3DTR_DrawGlyphList(D3DContext *d3dc, D3DSDOps *dstOps,761jint totalGlyphs, jboolean usePositions,762jboolean subPixPos, jboolean rgbOrder, jint lcdContrast,763jfloat glyphListOrigX, jfloat glyphListOrigY,764unsigned char *images, unsigned char *positions)765{766int glyphCounter;767HRESULT res = S_OK;768J2dTraceLn(J2D_TRACE_INFO, "D3DTR_DrawGlyphList");769770RETURN_STATUS_IF_NULL(d3dc, E_FAIL);771RETURN_STATUS_IF_NULL(d3dc->Get3DDevice(), E_FAIL);772RETURN_STATUS_IF_NULL(dstOps, E_FAIL);773RETURN_STATUS_IF_NULL(images, E_FAIL);774if (usePositions) {775RETURN_STATUS_IF_NULL(positions, E_FAIL);776}777778glyphMode = MODE_NOT_INITED;779isCachedDestValid = JNI_FALSE;780781for (glyphCounter = 0; glyphCounter < totalGlyphs; glyphCounter++) {782jint x, y;783jfloat glyphx, glyphy;784jboolean grayscale;785GlyphInfo *ginfo = (GlyphInfo *)jlong_to_ptr(NEXT_LONG(images));786787if (ginfo == NULL) {788// this shouldn't happen, but if it does we'll just break out...789J2dRlsTraceLn(J2D_TRACE_ERROR,790"D3DTR_DrawGlyphList: glyph info is null");791break;792}793794grayscale = (ginfo->rowBytes == ginfo->width);795796if (usePositions) {797jfloat posx = NEXT_FLOAT(positions);798jfloat posy = NEXT_FLOAT(positions);799glyphx = glyphListOrigX + posx + ginfo->topLeftX;800glyphy = glyphListOrigY + posy + ginfo->topLeftY;801FLOOR_ASSIGN(x, glyphx);802FLOOR_ASSIGN(y, glyphy);803} else {804glyphx = glyphListOrigX + ginfo->topLeftX;805glyphy = glyphListOrigY + ginfo->topLeftY;806FLOOR_ASSIGN(x, glyphx);807FLOOR_ASSIGN(y, glyphy);808glyphListOrigX += ginfo->advanceX;809glyphListOrigY += ginfo->advanceY;810}811812if (ginfo->image == NULL) {813continue;814}815816if (grayscale) {817// grayscale or monochrome glyph data818if (ginfo->width <= D3DTR_CACHE_CELL_WIDTH &&819ginfo->height <= D3DTR_CACHE_CELL_HEIGHT &&820SUCCEEDED(d3dc->InitGrayscaleGlyphCache()))821{822res = D3DTR_DrawGrayscaleGlyphViaCache(d3dc, ginfo, x, y);823} else {824res = D3DTR_DrawGrayscaleGlyphNoCache(d3dc, ginfo, x, y);825}826} else {827// LCD-optimized glyph data828jint rowBytesOffset = 0;829830if (subPixPos) {831jint frac = (jint)((glyphx - x) * 3);832if (frac != 0) {833rowBytesOffset = 3 - frac;834x += 1;835}836}837838if (rowBytesOffset == 0 &&839ginfo->width <= D3DTR_CACHE_CELL_WIDTH &&840ginfo->height <= D3DTR_CACHE_CELL_HEIGHT &&841SUCCEEDED(d3dc->InitLCDGlyphCache()))842{843res = D3DTR_DrawLCDGlyphViaCache(d3dc, dstOps,844ginfo, x, y,845glyphCounter, totalGlyphs,846rgbOrder, lcdContrast);847} else {848res = D3DTR_DrawLCDGlyphNoCache(d3dc, dstOps,849ginfo, x, y,850rowBytesOffset,851rgbOrder, lcdContrast);852}853}854855if (FAILED(res)) {856break;857}858}859860D3DTR_DisableGlyphModeState(d3dc);861return res;862}863864JNIEXPORT void JNICALL865Java_sun_java2d_d3d_D3DTextRenderer_drawGlyphList866(JNIEnv *env, jobject self,867jint numGlyphs, jboolean usePositions,868jboolean subPixPos, jboolean rgbOrder, jint lcdContrast,869jfloat glyphListOrigX, jfloat glyphListOrigY,870jlongArray imgArray, jfloatArray posArray)871{872unsigned char *images;873874J2dTraceLn(J2D_TRACE_INFO, "D3DTextRenderer_drawGlyphList");875876images = (unsigned char *)877env->GetPrimitiveArrayCritical(imgArray, NULL);878if (images != NULL) {879D3DContext *d3dc = D3DRQ_GetCurrentContext();880D3DSDOps *dstOps = D3DRQ_GetCurrentDestination();881882if (usePositions) {883unsigned char *positions = (unsigned char *)884env->GetPrimitiveArrayCritical(posArray, NULL);885if (positions != NULL) {886D3DTR_DrawGlyphList(d3dc, dstOps,887numGlyphs, usePositions,888subPixPos, rgbOrder, lcdContrast,889glyphListOrigX, glyphListOrigY,890images, positions);891env->ReleasePrimitiveArrayCritical(posArray,892positions, JNI_ABORT);893}894} else {895D3DTR_DrawGlyphList(d3dc, dstOps,896numGlyphs, usePositions,897subPixPos, rgbOrder, lcdContrast,898glyphListOrigX, glyphListOrigY,899images, NULL);900}901902// reset current state, and ensure rendering is flushed to dest903if (d3dc != NULL) {904d3dc->FlushVertexQueue();905}906907env->ReleasePrimitiveArrayCritical(imgArray,908images, JNI_ABORT);909}910}911912913