Path: blob/master/src/java.desktop/share/native/common/font/AccelGlyphCache.c
41153 views
/*1* Copyright (c) 2003, 2012, 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 <stdlib.h>26#include "jni.h"27#include "AccelGlyphCache.h"28#include "Trace.h"2930/**31* When the cache is full, we will try to reuse the cache cells that have32* been used relatively less than the others (and we will save the cells that33* have been rendered more than the threshold defined here).34*/35#define TIMES_RENDERED_THRESHOLD 53637/**38* Creates a new GlyphCacheInfo structure, fills in the initial values, and39* then returns a pointer to the GlyphCacheInfo record.40*41* Note that this method only sets up a data structure describing a42* rectangular region of accelerated memory, containing "virtual" cells of43* the requested size. The cell information is added lazily to the linked44* list describing the cache as new glyphs are added. Platform specific45* glyph caching code is responsible for actually creating the accelerated46* memory surface that will contain the individual glyph images.47*48* Each glyph contains a reference to a list of cell infos - one per glyph49* cache. There may be multiple glyph caches (for example, one per graphics50* adapter), so if the glyph is cached on two devices its cell list will51* consists of two elements corresponding to different glyph caches.52*53* The platform-specific glyph caching code is supposed to use54* GetCellInfoForCache method for retrieving cache infos from the glyph's list.55*56* Note that if it is guaranteed that there will be only one global glyph57* cache then it one does not have to use AccelGlyphCache_GetCellInfoForCache58* for retrieving cell info for the glyph, but instead just use the struct's59* field directly.60*/61GlyphCacheInfo *62AccelGlyphCache_Init(jint width, jint height,63jint cellWidth, jint cellHeight,64FlushFunc *func)65{66GlyphCacheInfo *gcinfo;6768J2dTraceLn(J2D_TRACE_INFO, "AccelGlyphCache_Init");6970gcinfo = (GlyphCacheInfo *)malloc(sizeof(GlyphCacheInfo));71if (gcinfo == NULL) {72J2dRlsTraceLn(J2D_TRACE_ERROR,73"AccelGlyphCache_Init: could not allocate GlyphCacheInfo");74return NULL;75}7677gcinfo->head = NULL;78gcinfo->tail = NULL;79gcinfo->width = width;80gcinfo->height = height;81gcinfo->cellWidth = cellWidth;82gcinfo->cellHeight = cellHeight;83gcinfo->isFull = JNI_FALSE;84gcinfo->Flush = func;8586return gcinfo;87}8889/**90* Attempts to add the provided glyph to the specified cache. If the91* operation is successful, a pointer to the newly occupied cache cell is92* stored in the glyph's cellInfo field; otherwise, its cellInfo field is93* set to NULL, indicating that the glyph's original bits should be rendered94* instead. If the cache is full, the least-recently-used glyph is95* invalidated and its cache cell is reassigned to the new glyph being added.96*97* Note that this method only ensures that a rectangular region in the98* "virtual" glyph cache is available for the glyph image. Platform specific99* glyph caching code is responsible for actually caching the glyph image100* in the associated accelerated memory surface.101*102* Returns created cell info if it was successfully created and added to the103* cache and glyph's cell lists, NULL otherwise.104*/105CacheCellInfo *106AccelGlyphCache_AddGlyph(GlyphCacheInfo *cache, GlyphInfo *glyph)107{108CacheCellInfo *cellinfo = NULL;109jint w = glyph->width;110jint h = glyph->height;111112J2dTraceLn(J2D_TRACE_INFO, "AccelGlyphCache_AddGlyph");113114if ((glyph->width > cache->cellWidth) ||115(glyph->height > cache->cellHeight))116{117return NULL;118}119120if (!cache->isFull) {121jint x, y;122123if (cache->head == NULL) {124x = 0;125y = 0;126} else {127x = cache->tail->x + cache->cellWidth;128y = cache->tail->y;129if ((x + cache->cellWidth) > cache->width) {130x = 0;131y += cache->cellHeight;132if ((y + cache->cellHeight) > cache->height) {133// no room left for a new cell; we'll go through the134// isFull path below135cache->isFull = JNI_TRUE;136}137}138}139140if (!cache->isFull) {141// create new CacheCellInfo142cellinfo = (CacheCellInfo *)malloc(sizeof(CacheCellInfo));143if (cellinfo == NULL) {144J2dTraceLn(J2D_TRACE_ERROR, "could not allocate CellInfo");145return NULL;146}147148cellinfo->cacheInfo = cache;149cellinfo->glyphInfo = glyph;150cellinfo->timesRendered = 0;151cellinfo->x = x;152cellinfo->y = y;153cellinfo->leftOff = 0;154cellinfo->rightOff = 0;155cellinfo->tx1 = (jfloat)cellinfo->x / cache->width;156cellinfo->ty1 = (jfloat)cellinfo->y / cache->height;157cellinfo->tx2 = cellinfo->tx1 + ((jfloat)w / cache->width);158cellinfo->ty2 = cellinfo->ty1 + ((jfloat)h / cache->height);159160if (cache->head == NULL) {161// initialize the head cell162cache->head = cellinfo;163} else {164// update existing tail cell165cache->tail->next = cellinfo;166}167168// add the new cell to the end of the list169cache->tail = cellinfo;170cellinfo->next = NULL;171cellinfo->nextGCI = NULL;172}173}174175if (cache->isFull) {176/**177* Search through the cells, and for each cell:178* - reset its timesRendered counter to zero179* - toss it to the end of the list180* Eventually we will find a cell that either:181* - is empty, or182* - has been used less than the threshold183* When we find such a cell, we will:184* - break out of the loop185* - invalidate any glyph that may be residing in that cell186* - update the cell with the new resident glyph's information187*188* The goal here is to keep the glyphs rendered most often in the189* cache, while younger glyphs hang out near the end of the list.190* Those young glyphs that have only been used a few times will move191* towards the head of the list and will eventually be kicked to192* the curb.193*194* In the worst-case scenario, all cells will be occupied and they195* will all have timesRendered counts above the threshold, so we will196* end up iterating through all the cells exactly once. Since we are197* resetting their counters along the way, we are guaranteed to198* eventually hit the original "head" cell, whose counter is now zero.199* This avoids the possibility of an infinite loop.200*/201202do {203// the head cell will be updated on each iteration204CacheCellInfo *current = cache->head;205206if ((current->glyphInfo == NULL) ||207(current->timesRendered < TIMES_RENDERED_THRESHOLD))208{209// all bow before the chosen one (we will break out of the210// loop now that we've found an appropriate cell)211cellinfo = current;212}213214// move cell to the end of the list; update existing head and215// tail pointers216cache->head = current->next;217cache->tail->next = current;218cache->tail = current;219current->next = NULL;220current->timesRendered = 0;221} while (cellinfo == NULL);222223if (cellinfo->glyphInfo != NULL) {224// flush in case any pending vertices are depending on the225// glyph that is about to be kicked out226if (cache->Flush != NULL) {227cache->Flush();228}229230// if the cell is occupied, notify the base glyph that the231// cached version for this cache is about to be kicked out232AccelGlyphCache_RemoveCellInfo(cellinfo->glyphInfo, cellinfo);233}234235// update cellinfo with glyph's occupied region information236cellinfo->glyphInfo = glyph;237cellinfo->tx2 = cellinfo->tx1 + ((jfloat)w / cache->width);238cellinfo->ty2 = cellinfo->ty1 + ((jfloat)h / cache->height);239}240241// add cache cell to the glyph's cells list242AccelGlyphCache_AddCellInfo(glyph, cellinfo);243return cellinfo;244}245246/**247* Invalidates all cells in the cache. Note that this method does not248* attempt to compact the cache in any way; it just invalidates any cells249* that already exist.250*/251void252AccelGlyphCache_Invalidate(GlyphCacheInfo *cache)253{254CacheCellInfo *cellinfo;255256J2dTraceLn(J2D_TRACE_INFO, "AccelGlyphCache_Invalidate");257258if (cache == NULL) {259return;260}261262// flush any pending vertices that may be depending on the current263// glyph cache layout264if (cache->Flush != NULL) {265cache->Flush();266}267268cellinfo = cache->head;269while (cellinfo != NULL) {270if (cellinfo->glyphInfo != NULL) {271// if the cell is occupied, notify the base glyph that its272// cached version for this cache is about to be invalidated273AccelGlyphCache_RemoveCellInfo(cellinfo->glyphInfo, cellinfo);274}275cellinfo = cellinfo->next;276}277}278279/**280* Invalidates and frees all cells and the cache itself. The "cache" pointer281* becomes invalid after this function returns.282*/283void284AccelGlyphCache_Free(GlyphCacheInfo *cache)285{286CacheCellInfo *cellinfo;287288J2dTraceLn(J2D_TRACE_INFO, "AccelGlyphCache_Free");289290if (cache == NULL) {291return;292}293294// flush any pending vertices that may be depending on the current295// glyph cache296if (cache->Flush != NULL) {297cache->Flush();298}299300while (cache->head != NULL) {301cellinfo = cache->head;302if (cellinfo->glyphInfo != NULL) {303// if the cell is occupied, notify the base glyph that its304// cached version for this cache is about to be invalidated305AccelGlyphCache_RemoveCellInfo(cellinfo->glyphInfo, cellinfo);306}307cache->head = cellinfo->next;308free(cellinfo);309}310free(cache);311}312313/**314* Add cell info to the head of the glyph's list of cached cells.315*/316void317AccelGlyphCache_AddCellInfo(GlyphInfo *glyph, CacheCellInfo *cellInfo)318{319// assert (glyph != NULL && cellInfo != NULL)320J2dTraceLn(J2D_TRACE_INFO, "AccelGlyphCache_AddCellInfo");321J2dTraceLn2(J2D_TRACE_VERBOSE, " glyph 0x%x: adding cell 0x%x to the list",322glyph, cellInfo);323324cellInfo->glyphInfo = glyph;325cellInfo->nextGCI = glyph->cellInfo;326glyph->cellInfo = cellInfo;327glyph->managed = MANAGED_GLYPH;328}329330/**331* Removes cell info from the glyph's list of cached cells.332*/333void334AccelGlyphCache_RemoveCellInfo(GlyphInfo *glyph, CacheCellInfo *cellInfo)335{336CacheCellInfo *currCellInfo = glyph->cellInfo;337CacheCellInfo *prevInfo = NULL;338// assert (glyph!= NULL && glyph->cellInfo != NULL && cellInfo != NULL)339J2dTraceLn(J2D_TRACE_INFO, "AccelGlyphCache_RemoveCellInfo");340do {341if (currCellInfo == cellInfo) {342J2dTraceLn2(J2D_TRACE_VERBOSE,343" glyph 0x%x: removing cell 0x%x from glyph's list",344glyph, currCellInfo);345if (prevInfo == NULL) { // it's the head, chop-chop346glyph->cellInfo = currCellInfo->nextGCI;347} else {348prevInfo->nextGCI = currCellInfo->nextGCI;349}350currCellInfo->glyphInfo = NULL;351currCellInfo->nextGCI = NULL;352return;353}354prevInfo = currCellInfo;355currCellInfo = currCellInfo->nextGCI;356} while (currCellInfo != NULL);357J2dTraceLn2(J2D_TRACE_WARNING, "AccelGlyphCache_RemoveCellInfo: "\358"no cell 0x%x in glyph 0x%x's cell list",359cellInfo, glyph);360}361362/**363* Removes cell info from the glyph's list of cached cells.364*/365JNIEXPORT void366AccelGlyphCache_RemoveAllCellInfos(GlyphInfo *glyph)367{368CacheCellInfo *currCell, *prevCell;369370J2dTraceLn(J2D_TRACE_INFO, "AccelGlyphCache_RemoveAllCellInfos");371372if (glyph == NULL || glyph->cellInfo == NULL) {373return;374}375376// invalidate all of this glyph's accelerated cache cells377currCell = glyph->cellInfo;378do {379currCell->glyphInfo = NULL;380prevCell = currCell;381currCell = currCell->nextGCI;382prevCell->nextGCI = NULL;383} while (currCell != NULL);384385glyph->cellInfo = NULL;386}387388/**389* Returns cell info associated with particular cache from the glyph's list of390* cached cells.391*/392CacheCellInfo *393AccelGlyphCache_GetCellInfoForCache(GlyphInfo *glyph, GlyphCacheInfo *cache)394{395// assert (glyph != NULL && cache != NULL)396J2dTraceLn(J2D_TRACE_VERBOSE2, "AccelGlyphCache_GetCellInfoForCache");397398if (glyph->cellInfo != NULL) {399CacheCellInfo *cellInfo = glyph->cellInfo;400do {401if (cellInfo->cacheInfo == cache) {402J2dTraceLn3(J2D_TRACE_VERBOSE2,403" glyph 0x%x: found cell 0x%x for cache 0x%x",404glyph, cellInfo, cache);405return cellInfo;406}407cellInfo = cellInfo->nextGCI;408} while (cellInfo != NULL);409}410J2dTraceLn2(J2D_TRACE_VERBOSE2, " glyph 0x%x: no cell for cache 0x%x",411glyph, cache);412return NULL;413}414415416417