/*
* Copyright (c) 2007, 2008, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#include "D3DGlyphCache.h"
#include "D3DTextRenderer.h"
#include "D3DRenderQueue.h"
void D3DGlyphCache_FlushGlyphVertexCache();
// static
HRESULT
D3DGlyphCache::CreateInstance(D3DContext *pCtx, GlyphCacheType gcType,
D3DGlyphCache **ppGlyphCache)
{
HRESULT res;
J2dTraceLn(J2D_TRACE_INFO, "D3DGlyphCache::CreateInstance");
*ppGlyphCache = new D3DGlyphCache(gcType);
if (FAILED(res = (*ppGlyphCache)->Init(pCtx))) {
delete *ppGlyphCache;
*ppGlyphCache = NULL;
}
return res;
}
D3DGlyphCache::D3DGlyphCache(GlyphCacheType type)
{
J2dTraceLn1(J2D_TRACE_INFO, "D3DGlyphCache::D3DGlyphCache gcType=%d", type);
pCtx = NULL;
gcType = type;
pGlyphCacheRes = NULL;
pGlyphCache = NULL;
tileFormat = (gcType == CACHE_GRAY) ? TILEFMT_1BYTE_ALPHA : TILEFMT_UNKNOWN;
lastRGBOrder = JNI_FALSE;
}
D3DGlyphCache::~D3DGlyphCache()
{
J2dTraceLn(J2D_TRACE_INFO, "D3DGlyphCache::~D3DGlyphCache");
ReleaseDefPoolResources();
pCtx = NULL;
if (pGlyphCache != NULL) {
AccelGlyphCache_Free(pGlyphCache);
pGlyphCache = NULL;
}
}
void
D3DGlyphCache::ReleaseDefPoolResources()
{
J2dTraceLn(J2D_TRACE_INFO, "D3DGlyphCache::ReleaseDefPoolResources");
AccelGlyphCache_Invalidate(pGlyphCache);
// REMIND: the glyph cache texture is not in the default pool, so
// this can be optimized not to release the texture
pCtx->GetResourceManager()->ReleaseResource(pGlyphCacheRes);
pGlyphCacheRes = NULL;
}
HRESULT
D3DGlyphCache::Init(D3DContext *pCtx)
{
D3DFORMAT format;
RETURN_STATUS_IF_NULL(pCtx, E_FAIL);
J2dTraceLn1(J2D_TRACE_INFO, "D3DGlyphCache::Init pCtx=%x", pCtx);
this->pCtx = pCtx;
if (pGlyphCache == NULL) {
// init glyph cache data structure
pGlyphCache = AccelGlyphCache_Init(D3DTR_CACHE_WIDTH,
D3DTR_CACHE_HEIGHT,
D3DTR_CACHE_CELL_WIDTH,
D3DTR_CACHE_CELL_HEIGHT,
D3DGlyphCache_FlushGlyphVertexCache);
if (pGlyphCache == NULL) {
J2dRlsTraceLn(J2D_TRACE_ERROR,
"D3DGlyphCache::Init: "\
"could not init D3D glyph cache");
return E_FAIL;
}
}
if (gcType == CACHE_GRAY) {
format = pCtx->IsTextureFormatSupported(D3DFMT_A8) ?
D3DFMT_A8 : D3DFMT_A8R8G8B8;
} else { // gcType == CACHE_LCD
format = pCtx->IsTextureFormatSupported(D3DFMT_R8G8B8) ?
D3DFMT_R8G8B8 : D3DFMT_A8R8G8B8;
}
HRESULT res = pCtx->GetResourceManager()->
CreateTexture(D3DTR_CACHE_WIDTH, D3DTR_CACHE_HEIGHT,
FALSE/*isRTT*/, FALSE/*isOpaque*/, &format, 0/*usage*/,
&pGlyphCacheRes);
if (FAILED(res)) {
J2dRlsTraceLn(J2D_TRACE_ERROR,
"D3DGlyphCache::Init: "\
"could not create glyph cache texture");
}
return res;
}
HRESULT
D3DGlyphCache::AddGlyph(GlyphInfo *glyph)
{
HRESULT res = S_OK;
RETURN_STATUS_IF_NULL(pGlyphCacheRes, E_FAIL);
CacheCellInfo *cellInfo = AccelGlyphCache_AddGlyph(pGlyphCache, glyph);
if (cellInfo != NULL) {
jint pixelsTouchedL = 0, pixelsTouchedR = 0;
// store glyph image in texture cell
res = pCtx->UploadTileToTexture(pGlyphCacheRes,
glyph->image,
cellInfo->x, cellInfo->y,
0, 0,
glyph->width, glyph->height,
glyph->rowBytes, tileFormat,
&pixelsTouchedL,
&pixelsTouchedR);
// LCD text rendering optimization: if the number of pixels touched on
// the first or last column of the glyph image is less than 1/3 of the
// height of the glyph we do not consider them touched.
// See D3DTextRenderer.cpp:UpdateCachedDestination for more information.
// The leftOff/rightOff are only used in LCD cache case.
if (gcType == CACHE_LCD) {
jint threshold = glyph->height/3;
cellInfo->leftOff = pixelsTouchedL < threshold ? 1 : 0;
cellInfo->rightOff = pixelsTouchedR < threshold ? -1 : 0;
} else {
cellInfo->leftOff = 0;
cellInfo->rightOff = 0;
}
}
return res;
}
HRESULT
D3DGlyphCache::CheckGlyphCacheByteOrder(jboolean rgbOrder)
{
J2dTraceLn(J2D_TRACE_INFO, "D3DGlyphCache::CheckGlyphCacheByteOrder");
if (gcType != CACHE_LCD) {
J2dTraceLn(J2D_TRACE_ERROR, "D3DGlyphCache::CheckGlyphCacheByteOrder"\
" invoked on CACHE_GRAY cache type instance!");
return E_FAIL;
}
if (rgbOrder != lastRGBOrder) {
// need to invalidate the cache in this case; see comments
// for lastRGBOrder
AccelGlyphCache_Invalidate(pGlyphCache);
lastRGBOrder = rgbOrder;
}
tileFormat = rgbOrder ? TILEFMT_3BYTE_RGB : TILEFMT_3BYTE_BGR;
return S_OK;
}
/**
* This method is invoked in the (relatively rare) case where one or
* more glyphs is about to be kicked out of the glyph cache texture.
* Here we simply flush the vertex queue of the current context in case
* any pending vertices are dependent upon the current glyph cache layout.
*/
static void
D3DGlyphCache_FlushGlyphVertexCache()
{
D3DContext *d3dc = D3DRQ_GetCurrentContext();
if (d3dc != NULL) {
J2dTraceLn(J2D_TRACE_INFO, "D3DGlyphCache_FlushGlyphVertexCache");
d3dc->FlushVertexQueue();
}
}