Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/src/java.desktop/share/native/common/font/AccelGlyphCache.c
41153 views
1
/*
2
* Copyright (c) 2003, 2012, Oracle and/or its affiliates. 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. Oracle designates this
8
* particular file as subject to the "Classpath" exception as provided
9
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22
* or visit www.oracle.com if you need additional information or have any
23
* questions.
24
*/
25
26
#include <stdlib.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
*
49
* Each glyph contains a reference to a list of cell infos - one per glyph
50
* cache. There may be multiple glyph caches (for example, one per graphics
51
* adapter), so if the glyph is cached on two devices its cell list will
52
* consists of two elements corresponding to different glyph caches.
53
*
54
* The platform-specific glyph caching code is supposed to use
55
* GetCellInfoForCache method for retrieving cache infos from the glyph's list.
56
*
57
* Note that if it is guaranteed that there will be only one global glyph
58
* cache then it one does not have to use AccelGlyphCache_GetCellInfoForCache
59
* for retrieving cell info for the glyph, but instead just use the struct's
60
* field directly.
61
*/
62
GlyphCacheInfo *
63
AccelGlyphCache_Init(jint width, jint height,
64
jint cellWidth, jint cellHeight,
65
FlushFunc *func)
66
{
67
GlyphCacheInfo *gcinfo;
68
69
J2dTraceLn(J2D_TRACE_INFO, "AccelGlyphCache_Init");
70
71
gcinfo = (GlyphCacheInfo *)malloc(sizeof(GlyphCacheInfo));
72
if (gcinfo == NULL) {
73
J2dRlsTraceLn(J2D_TRACE_ERROR,
74
"AccelGlyphCache_Init: could not allocate GlyphCacheInfo");
75
return NULL;
76
}
77
78
gcinfo->head = NULL;
79
gcinfo->tail = NULL;
80
gcinfo->width = width;
81
gcinfo->height = height;
82
gcinfo->cellWidth = cellWidth;
83
gcinfo->cellHeight = cellHeight;
84
gcinfo->isFull = JNI_FALSE;
85
gcinfo->Flush = func;
86
87
return gcinfo;
88
}
89
90
/**
91
* Attempts to add the provided glyph to the specified cache. If the
92
* operation is successful, a pointer to the newly occupied cache cell is
93
* stored in the glyph's cellInfo field; otherwise, its cellInfo field is
94
* set to NULL, indicating that the glyph's original bits should be rendered
95
* instead. If the cache is full, the least-recently-used glyph is
96
* invalidated and its cache cell is reassigned to the new glyph being added.
97
*
98
* Note that this method only ensures that a rectangular region in the
99
* "virtual" glyph cache is available for the glyph image. Platform specific
100
* glyph caching code is responsible for actually caching the glyph image
101
* in the associated accelerated memory surface.
102
*
103
* Returns created cell info if it was successfully created and added to the
104
* cache and glyph's cell lists, NULL otherwise.
105
*/
106
CacheCellInfo *
107
AccelGlyphCache_AddGlyph(GlyphCacheInfo *cache, GlyphInfo *glyph)
108
{
109
CacheCellInfo *cellinfo = NULL;
110
jint w = glyph->width;
111
jint h = glyph->height;
112
113
J2dTraceLn(J2D_TRACE_INFO, "AccelGlyphCache_AddGlyph");
114
115
if ((glyph->width > cache->cellWidth) ||
116
(glyph->height > cache->cellHeight))
117
{
118
return NULL;
119
}
120
121
if (!cache->isFull) {
122
jint x, y;
123
124
if (cache->head == NULL) {
125
x = 0;
126
y = 0;
127
} else {
128
x = cache->tail->x + cache->cellWidth;
129
y = cache->tail->y;
130
if ((x + cache->cellWidth) > cache->width) {
131
x = 0;
132
y += cache->cellHeight;
133
if ((y + cache->cellHeight) > cache->height) {
134
// no room left for a new cell; we'll go through the
135
// isFull path below
136
cache->isFull = JNI_TRUE;
137
}
138
}
139
}
140
141
if (!cache->isFull) {
142
// create new CacheCellInfo
143
cellinfo = (CacheCellInfo *)malloc(sizeof(CacheCellInfo));
144
if (cellinfo == NULL) {
145
J2dTraceLn(J2D_TRACE_ERROR, "could not allocate CellInfo");
146
return NULL;
147
}
148
149
cellinfo->cacheInfo = cache;
150
cellinfo->glyphInfo = glyph;
151
cellinfo->timesRendered = 0;
152
cellinfo->x = x;
153
cellinfo->y = y;
154
cellinfo->leftOff = 0;
155
cellinfo->rightOff = 0;
156
cellinfo->tx1 = (jfloat)cellinfo->x / cache->width;
157
cellinfo->ty1 = (jfloat)cellinfo->y / cache->height;
158
cellinfo->tx2 = cellinfo->tx1 + ((jfloat)w / cache->width);
159
cellinfo->ty2 = cellinfo->ty1 + ((jfloat)h / cache->height);
160
161
if (cache->head == NULL) {
162
// initialize the head cell
163
cache->head = cellinfo;
164
} else {
165
// update existing tail cell
166
cache->tail->next = cellinfo;
167
}
168
169
// add the new cell to the end of the list
170
cache->tail = cellinfo;
171
cellinfo->next = NULL;
172
cellinfo->nextGCI = NULL;
173
}
174
}
175
176
if (cache->isFull) {
177
/**
178
* Search through the cells, and for each cell:
179
* - reset its timesRendered counter to zero
180
* - toss it to the end of the list
181
* Eventually we will find a cell that either:
182
* - is empty, or
183
* - has been used less than the threshold
184
* When we find such a cell, we will:
185
* - break out of the loop
186
* - invalidate any glyph that may be residing in that cell
187
* - update the cell with the new resident glyph's information
188
*
189
* The goal here is to keep the glyphs rendered most often in the
190
* cache, while younger glyphs hang out near the end of the list.
191
* Those young glyphs that have only been used a few times will move
192
* towards the head of the list and will eventually be kicked to
193
* the curb.
194
*
195
* In the worst-case scenario, all cells will be occupied and they
196
* will all have timesRendered counts above the threshold, so we will
197
* end up iterating through all the cells exactly once. Since we are
198
* resetting their counters along the way, we are guaranteed to
199
* eventually hit the original "head" cell, whose counter is now zero.
200
* This avoids the possibility of an infinite loop.
201
*/
202
203
do {
204
// the head cell will be updated on each iteration
205
CacheCellInfo *current = cache->head;
206
207
if ((current->glyphInfo == NULL) ||
208
(current->timesRendered < TIMES_RENDERED_THRESHOLD))
209
{
210
// all bow before the chosen one (we will break out of the
211
// loop now that we've found an appropriate cell)
212
cellinfo = current;
213
}
214
215
// move cell to the end of the list; update existing head and
216
// tail pointers
217
cache->head = current->next;
218
cache->tail->next = current;
219
cache->tail = current;
220
current->next = NULL;
221
current->timesRendered = 0;
222
} while (cellinfo == NULL);
223
224
if (cellinfo->glyphInfo != NULL) {
225
// flush in case any pending vertices are depending on the
226
// glyph that is about to be kicked out
227
if (cache->Flush != NULL) {
228
cache->Flush();
229
}
230
231
// if the cell is occupied, notify the base glyph that the
232
// cached version for this cache is about to be kicked out
233
AccelGlyphCache_RemoveCellInfo(cellinfo->glyphInfo, cellinfo);
234
}
235
236
// update cellinfo with glyph's occupied region information
237
cellinfo->glyphInfo = glyph;
238
cellinfo->tx2 = cellinfo->tx1 + ((jfloat)w / cache->width);
239
cellinfo->ty2 = cellinfo->ty1 + ((jfloat)h / cache->height);
240
}
241
242
// add cache cell to the glyph's cells list
243
AccelGlyphCache_AddCellInfo(glyph, cellinfo);
244
return cellinfo;
245
}
246
247
/**
248
* Invalidates all cells in the cache. Note that this method does not
249
* attempt to compact the cache in any way; it just invalidates any cells
250
* that already exist.
251
*/
252
void
253
AccelGlyphCache_Invalidate(GlyphCacheInfo *cache)
254
{
255
CacheCellInfo *cellinfo;
256
257
J2dTraceLn(J2D_TRACE_INFO, "AccelGlyphCache_Invalidate");
258
259
if (cache == NULL) {
260
return;
261
}
262
263
// flush any pending vertices that may be depending on the current
264
// glyph cache layout
265
if (cache->Flush != NULL) {
266
cache->Flush();
267
}
268
269
cellinfo = cache->head;
270
while (cellinfo != NULL) {
271
if (cellinfo->glyphInfo != NULL) {
272
// if the cell is occupied, notify the base glyph that its
273
// cached version for this cache is about to be invalidated
274
AccelGlyphCache_RemoveCellInfo(cellinfo->glyphInfo, cellinfo);
275
}
276
cellinfo = cellinfo->next;
277
}
278
}
279
280
/**
281
* Invalidates and frees all cells and the cache itself. The "cache" pointer
282
* becomes invalid after this function returns.
283
*/
284
void
285
AccelGlyphCache_Free(GlyphCacheInfo *cache)
286
{
287
CacheCellInfo *cellinfo;
288
289
J2dTraceLn(J2D_TRACE_INFO, "AccelGlyphCache_Free");
290
291
if (cache == NULL) {
292
return;
293
}
294
295
// flush any pending vertices that may be depending on the current
296
// glyph cache
297
if (cache->Flush != NULL) {
298
cache->Flush();
299
}
300
301
while (cache->head != NULL) {
302
cellinfo = cache->head;
303
if (cellinfo->glyphInfo != NULL) {
304
// if the cell is occupied, notify the base glyph that its
305
// cached version for this cache is about to be invalidated
306
AccelGlyphCache_RemoveCellInfo(cellinfo->glyphInfo, cellinfo);
307
}
308
cache->head = cellinfo->next;
309
free(cellinfo);
310
}
311
free(cache);
312
}
313
314
/**
315
* Add cell info to the head of the glyph's list of cached cells.
316
*/
317
void
318
AccelGlyphCache_AddCellInfo(GlyphInfo *glyph, CacheCellInfo *cellInfo)
319
{
320
// assert (glyph != NULL && cellInfo != NULL)
321
J2dTraceLn(J2D_TRACE_INFO, "AccelGlyphCache_AddCellInfo");
322
J2dTraceLn2(J2D_TRACE_VERBOSE, " glyph 0x%x: adding cell 0x%x to the list",
323
glyph, cellInfo);
324
325
cellInfo->glyphInfo = glyph;
326
cellInfo->nextGCI = glyph->cellInfo;
327
glyph->cellInfo = cellInfo;
328
glyph->managed = MANAGED_GLYPH;
329
}
330
331
/**
332
* Removes cell info from the glyph's list of cached cells.
333
*/
334
void
335
AccelGlyphCache_RemoveCellInfo(GlyphInfo *glyph, CacheCellInfo *cellInfo)
336
{
337
CacheCellInfo *currCellInfo = glyph->cellInfo;
338
CacheCellInfo *prevInfo = NULL;
339
// assert (glyph!= NULL && glyph->cellInfo != NULL && cellInfo != NULL)
340
J2dTraceLn(J2D_TRACE_INFO, "AccelGlyphCache_RemoveCellInfo");
341
do {
342
if (currCellInfo == cellInfo) {
343
J2dTraceLn2(J2D_TRACE_VERBOSE,
344
" glyph 0x%x: removing cell 0x%x from glyph's list",
345
glyph, currCellInfo);
346
if (prevInfo == NULL) { // it's the head, chop-chop
347
glyph->cellInfo = currCellInfo->nextGCI;
348
} else {
349
prevInfo->nextGCI = currCellInfo->nextGCI;
350
}
351
currCellInfo->glyphInfo = NULL;
352
currCellInfo->nextGCI = NULL;
353
return;
354
}
355
prevInfo = currCellInfo;
356
currCellInfo = currCellInfo->nextGCI;
357
} while (currCellInfo != NULL);
358
J2dTraceLn2(J2D_TRACE_WARNING, "AccelGlyphCache_RemoveCellInfo: "\
359
"no cell 0x%x in glyph 0x%x's cell list",
360
cellInfo, glyph);
361
}
362
363
/**
364
* Removes cell info from the glyph's list of cached cells.
365
*/
366
JNIEXPORT void
367
AccelGlyphCache_RemoveAllCellInfos(GlyphInfo *glyph)
368
{
369
CacheCellInfo *currCell, *prevCell;
370
371
J2dTraceLn(J2D_TRACE_INFO, "AccelGlyphCache_RemoveAllCellInfos");
372
373
if (glyph == NULL || glyph->cellInfo == NULL) {
374
return;
375
}
376
377
// invalidate all of this glyph's accelerated cache cells
378
currCell = glyph->cellInfo;
379
do {
380
currCell->glyphInfo = NULL;
381
prevCell = currCell;
382
currCell = currCell->nextGCI;
383
prevCell->nextGCI = NULL;
384
} while (currCell != NULL);
385
386
glyph->cellInfo = NULL;
387
}
388
389
/**
390
* Returns cell info associated with particular cache from the glyph's list of
391
* cached cells.
392
*/
393
CacheCellInfo *
394
AccelGlyphCache_GetCellInfoForCache(GlyphInfo *glyph, GlyphCacheInfo *cache)
395
{
396
// assert (glyph != NULL && cache != NULL)
397
J2dTraceLn(J2D_TRACE_VERBOSE2, "AccelGlyphCache_GetCellInfoForCache");
398
399
if (glyph->cellInfo != NULL) {
400
CacheCellInfo *cellInfo = glyph->cellInfo;
401
do {
402
if (cellInfo->cacheInfo == cache) {
403
J2dTraceLn3(J2D_TRACE_VERBOSE2,
404
" glyph 0x%x: found cell 0x%x for cache 0x%x",
405
glyph, cellInfo, cache);
406
return cellInfo;
407
}
408
cellInfo = cellInfo->nextGCI;
409
} while (cellInfo != NULL);
410
}
411
J2dTraceLn2(J2D_TRACE_VERBOSE2, " glyph 0x%x: no cell for cache 0x%x",
412
glyph, cache);
413
return NULL;
414
}
415
416
417