blob: abd2d881a35fa23271518ad209e8b716a1472290 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2003-2006 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26#include <malloc.h>
27#include "jni.h"
28#include "AccelGlyphCache.h"
29#include "Trace.h"
30
31/**
32 * When the cache is full, we will try to reuse the cache cells that have
33 * been used relatively less than the others (and we will save the cells that
34 * have been rendered more than the threshold defined here).
35 */
36#define TIMES_RENDERED_THRESHOLD 5
37
38/**
39 * Creates a new GlyphCacheInfo structure, fills in the initial values, and
40 * then returns a pointer to the GlyphCacheInfo record.
41 *
42 * Note that this method only sets up a data structure describing a
43 * rectangular region of accelerated memory, containing "virtual" cells of
44 * the requested size. The cell information is added lazily to the linked
45 * list describing the cache as new glyphs are added. Platform specific
46 * glyph caching code is responsible for actually creating the accelerated
47 * memory surface that will contain the individual glyph images.
48 */
49GlyphCacheInfo *
50AccelGlyphCache_Init(jint width, jint height,
51 jint cellWidth, jint cellHeight,
52 FlushFunc *func)
53{
54 GlyphCacheInfo *gcinfo;
55
56 J2dTraceLn(J2D_TRACE_INFO, "AccelGlyphCache_Init");
57
58 gcinfo = (GlyphCacheInfo *)malloc(sizeof(GlyphCacheInfo));
59 if (gcinfo == NULL) {
60 J2dRlsTraceLn(J2D_TRACE_ERROR,
61 "AccelGlyphCache_Init: could not allocate GlyphCacheInfo");
62 return NULL;
63 }
64
65 gcinfo->head = NULL;
66 gcinfo->tail = NULL;
67 gcinfo->width = width;
68 gcinfo->height = height;
69 gcinfo->cellWidth = cellWidth;
70 gcinfo->cellHeight = cellHeight;
71 gcinfo->isFull = JNI_FALSE;
72 gcinfo->Flush = func;
73
74 return gcinfo;
75}
76
77/**
78 * Attempts to add the provided glyph to the specified cache. If the
79 * operation is successful, a pointer to the newly occupied cache cell is
80 * stored in the glyph's cellInfo field; otherwise, its cellInfo field is
81 * set to NULL, indicating that the glyph's original bits should be rendered
82 * instead. If the cache is full, the least-recently-used glyph is
83 * invalidated and its cache cell is reassigned to the new glyph being added.
84 *
85 * Note that this method only ensures that a rectangular region in the
86 * "virtual" glyph cache is available for the glyph image. Platform specific
87 * glyph caching code is responsible for actually caching the glyph image
88 * in the associated accelerated memory surface.
89 */
90void
91AccelGlyphCache_AddGlyph(GlyphCacheInfo *cache, GlyphInfo *glyph)
92{
93 CacheCellInfo *cellinfo = NULL;
94 jint w = glyph->width;
95 jint h = glyph->height;
96
97 J2dTraceLn(J2D_TRACE_INFO, "AccelGlyphCache_AddGlyph");
98
99 if ((glyph->width > cache->cellWidth) ||
100 (glyph->height > cache->cellHeight))
101 {
102 return;
103 }
104
105 if (!cache->isFull) {
106 jint x, y;
107
108 if (cache->head == NULL) {
109 x = 0;
110 y = 0;
111 } else {
112 x = cache->tail->x + cache->cellWidth;
113 y = cache->tail->y;
114 if ((x + cache->cellWidth) > cache->width) {
115 x = 0;
116 y += cache->cellHeight;
117 if ((y + cache->cellHeight) > cache->height) {
118 // no room left for a new cell; we'll go through the
119 // isFull path below
120 cache->isFull = JNI_TRUE;
121 }
122 }
123 }
124
125 if (!cache->isFull) {
126 // create new CacheCellInfo
127 cellinfo = (CacheCellInfo *)malloc(sizeof(CacheCellInfo));
128 if (cellinfo == NULL) {
129 glyph->cellInfo = NULL;
130 J2dTraceLn(J2D_TRACE_ERROR, "could not allocate CellInfo");
131 return;
132 }
133
134 cellinfo->cacheInfo = cache;
135 cellinfo->glyphInfo = glyph;
136 cellinfo->timesRendered = 0;
137 cellinfo->x = x;
138 cellinfo->y = y;
139 cellinfo->tx1 = (jfloat)cellinfo->x / cache->width;
140 cellinfo->ty1 = (jfloat)cellinfo->y / cache->height;
141 cellinfo->tx2 = cellinfo->tx1 + ((jfloat)w / cache->width);
142 cellinfo->ty2 = cellinfo->ty1 + ((jfloat)h / cache->height);
143
144 if (cache->head == NULL) {
145 // initialize the head cell
146 cache->head = cellinfo;
147 } else {
148 // update existing tail cell
149 cache->tail->next = cellinfo;
150 }
151
152 // add the new cell to the end of the list
153 cache->tail = cellinfo;
154 cellinfo->next = NULL;
155 }
156 }
157
158 if (cache->isFull) {
159 /**
160 * Search through the cells, and for each cell:
161 * - reset its timesRendered counter to zero
162 * - toss it to the end of the list
163 * Eventually we will find a cell that either:
164 * - is empty, or
165 * - has been used less than the threshold
166 * When we find such a cell, we will:
167 * - break out of the loop
168 * - invalidate any glyph that may be residing in that cell
169 * - update the cell with the new resident glyph's information
170 *
171 * The goal here is to keep the glyphs rendered most often in the
172 * cache, while younger glyphs hang out near the end of the list.
173 * Those young glyphs that have only been used a few times will move
174 * towards the head of the list and will eventually be kicked to
175 * the curb.
176 *
177 * In the worst-case scenario, all cells will be occupied and they
178 * will all have timesRendered counts above the threshold, so we will
179 * end up iterating through all the cells exactly once. Since we are
180 * resetting their counters along the way, we are guaranteed to
181 * eventually hit the original "head" cell, whose counter is now zero.
182 * This avoids the possibility of an infinite loop.
183 */
184
185 do {
186 // the head cell will be updated on each iteration
187 CacheCellInfo *current = cache->head;
188
189 if ((current->glyphInfo == NULL) ||
190 (current->timesRendered < TIMES_RENDERED_THRESHOLD))
191 {
192 // all bow before the chosen one (we will break out of the
193 // loop now that we've found an appropriate cell)
194 cellinfo = current;
195 }
196
197 // move cell to the end of the list; update existing head and
198 // tail pointers
199 cache->head = current->next;
200 cache->tail->next = current;
201 cache->tail = current;
202 current->next = NULL;
203 current->timesRendered = 0;
204 } while (cellinfo == NULL);
205
206 if (cellinfo->glyphInfo != NULL) {
207 // flush in case any pending vertices are depending on the
208 // glyph that is about to be kicked out
209 if (cache->Flush != NULL) {
210 cache->Flush();
211 }
212
213 // if the cell is occupied, notify the base glyph that its
214 // cached version is about to be kicked out
215 cellinfo->glyphInfo->cellInfo = NULL;
216 }
217
218 // update cellinfo with glyph's occupied region information
219 cellinfo->glyphInfo = glyph;
220 cellinfo->tx2 = cellinfo->tx1 + ((jfloat)w / cache->width);
221 cellinfo->ty2 = cellinfo->ty1 + ((jfloat)h / cache->height);
222 }
223
224 // update the glyph's reference to its cache cell
225 glyph->cellInfo = cellinfo;
226}
227
228/**
229 * Invalidates all cells in the cache. Note that this method does not
230 * attempt to compact the cache in any way; it just invalidates any cells
231 * that already exist.
232 */
233void
234AccelGlyphCache_Invalidate(GlyphCacheInfo *cache)
235{
236 CacheCellInfo *cellinfo;
237
238 J2dTraceLn(J2D_TRACE_INFO, "AccelGlyphCache_Invalidate");
239
240 if (cache == NULL) {
241 return;
242 }
243
244 // flush any pending vertices that may be depending on the current
245 // glyph cache layout
246 if (cache->Flush != NULL) {
247 cache->Flush();
248 }
249
250 cellinfo = cache->head;
251 while (cellinfo != NULL) {
252 if (cellinfo->glyphInfo != NULL) {
253 // if the cell is occupied, notify the base glyph that its
254 // cached version is about to be invalidated
255 cellinfo->glyphInfo->cellInfo = NULL;
256 cellinfo->glyphInfo = NULL;
257 }
258 cellinfo = cellinfo->next;
259 }
260}