2 |
- |
1 |
/* Copyright (C) 1989, 1995, 1996, 1997, 1999 Aladdin Enterprises. All rights reserved.
|
|
|
2 |
|
|
|
3 |
This software is provided AS-IS with no warranty, either express or
|
|
|
4 |
implied.
|
|
|
5 |
|
|
|
6 |
This software is distributed under license and may not be copied,
|
|
|
7 |
modified or distributed except as expressly authorized under the terms
|
|
|
8 |
of the license contained in the file LICENSE in this distribution.
|
|
|
9 |
|
|
|
10 |
For more information about licensing, please refer to
|
|
|
11 |
http://www.ghostscript.com/licensing/. For information on
|
|
|
12 |
commercial licensing, go to http://www.artifex.com/licensing/ or
|
|
|
13 |
contact Artifex Software, Inc., 101 Lucas Valley Road #110,
|
|
|
14 |
San Rafael, CA 94903, U.S.A., +1(415)492-9861.
|
|
|
15 |
*/
|
|
|
16 |
|
|
|
17 |
/* $Id: gxccman.c,v 1.30 2004/12/08 21:35:13 stefan Exp $ */
|
|
|
18 |
/* Character cache management routines for Ghostscript library */
|
|
|
19 |
#include "gx.h"
|
|
|
20 |
#include "memory_.h"
|
|
|
21 |
#include "gpcheck.h"
|
|
|
22 |
#include "gserrors.h"
|
|
|
23 |
#include "gsstruct.h"
|
|
|
24 |
#include "gsbitops.h"
|
|
|
25 |
#include "gsutil.h" /* for gs_next_ids */
|
|
|
26 |
#include "gxfixed.h"
|
|
|
27 |
#include "gxmatrix.h"
|
|
|
28 |
#include "gzstate.h"
|
|
|
29 |
#include "gxpath.h"
|
|
|
30 |
#include "gxdevice.h"
|
|
|
31 |
#include "gxdevmem.h"
|
|
|
32 |
#include "gxchar.h"
|
|
|
33 |
#include "gxfont.h"
|
|
|
34 |
#include "gxfcache.h"
|
|
|
35 |
#include "gxxfont.h"
|
|
|
36 |
#include "gxttfb.h"
|
|
|
37 |
#include "gxfont42.h"
|
|
|
38 |
#include <assert.h>
|
|
|
39 |
|
|
|
40 |
/* Define the descriptors for the cache structures. */
|
|
|
41 |
private_st_cached_fm_pair();
|
|
|
42 |
private_st_cached_fm_pair_elt();
|
|
|
43 |
/*private_st_cached_char(); *//* unused */
|
|
|
44 |
private_st_cached_char_ptr(); /* unused */
|
|
|
45 |
private_st_cached_char_ptr_elt();
|
|
|
46 |
/*
|
|
|
47 |
* Define a descriptor for the cache data. This is equivalent to st_bytes,
|
|
|
48 |
* but it identifies the cache data as such in a memory dump.
|
|
|
49 |
*/
|
|
|
50 |
gs_private_st_simple(st_font_cache_bytes, byte, "font cache bytes");
|
|
|
51 |
/* GC procedures */
|
|
|
52 |
/* We do all the work in font_dir_enum/reloc_ptrs in gsfont.c. */
|
|
|
53 |
/* See gxfcache.h for details. */
|
|
|
54 |
private
|
|
|
55 |
ENUM_PTRS_BEGIN(cc_ptr_enum_ptrs) return 0;
|
|
|
56 |
|
|
|
57 |
ENUM_PTRS_END
|
|
|
58 |
private RELOC_PTRS_BEGIN(cc_ptr_reloc_ptrs)
|
|
|
59 |
{
|
|
|
60 |
}
|
|
|
61 |
RELOC_PTRS_END
|
|
|
62 |
|
|
|
63 |
/* Forward references */
|
|
|
64 |
private gx_xfont * lookup_xfont_by_name(gx_device *, const gx_xfont_procs *, gs_font_name *, int, const cached_fm_pair *, const gs_matrix *);
|
|
|
65 |
private cached_char *alloc_char(gs_font_dir *, ulong);
|
|
|
66 |
private cached_char *alloc_char_in_chunk(gs_font_dir *, ulong);
|
|
|
67 |
private void hash_remove_cached_char(gs_font_dir *, uint);
|
|
|
68 |
private void shorten_cached_char(gs_font_dir *, cached_char *, uint);
|
|
|
69 |
|
|
|
70 |
/* ====== Initialization ====== */
|
|
|
71 |
|
|
|
72 |
/* Allocate and initialize the character cache elements of a font directory. */
|
|
|
73 |
int
|
|
|
74 |
gx_char_cache_alloc(gs_memory_t * struct_mem, gs_memory_t * bits_mem,
|
|
|
75 |
gs_font_dir * pdir, uint bmax, uint mmax, uint cmax, uint upper)
|
|
|
76 |
{ /* Since we use open hashing, we must increase cmax somewhat. */
|
|
|
77 |
uint chsize = (cmax + (cmax >> 1)) | 31;
|
|
|
78 |
cached_fm_pair *mdata;
|
|
|
79 |
cached_char **chars;
|
|
|
80 |
|
|
|
81 |
/* Round up chsize to a power of 2. */
|
|
|
82 |
while (chsize & (chsize + 1))
|
|
|
83 |
chsize |= chsize >> 1;
|
|
|
84 |
chsize++;
|
|
|
85 |
mdata = gs_alloc_struct_array(struct_mem, mmax, cached_fm_pair,
|
|
|
86 |
&st_cached_fm_pair_element,
|
|
|
87 |
"font_dir_alloc(mdata)");
|
|
|
88 |
chars = gs_alloc_struct_array(struct_mem, chsize, cached_char *,
|
|
|
89 |
&st_cached_char_ptr_element,
|
|
|
90 |
"font_dir_alloc(chars)");
|
|
|
91 |
if (mdata == 0 || chars == 0) {
|
|
|
92 |
gs_free_object(struct_mem, chars, "font_dir_alloc(chars)");
|
|
|
93 |
gs_free_object(struct_mem, mdata, "font_dir_alloc(mdata)");
|
|
|
94 |
return_error(gs_error_VMerror);
|
|
|
95 |
}
|
|
|
96 |
pdir->fmcache.mmax = mmax;
|
|
|
97 |
pdir->fmcache.mdata = mdata;
|
|
|
98 |
pdir->ccache.struct_memory = struct_mem;
|
|
|
99 |
pdir->ccache.bits_memory = bits_mem;
|
|
|
100 |
pdir->ccache.bmax = bmax;
|
|
|
101 |
pdir->ccache.cmax = cmax;
|
|
|
102 |
pdir->ccache.lower = upper / 10;
|
|
|
103 |
pdir->ccache.upper = upper;
|
|
|
104 |
pdir->ccache.table = chars;
|
|
|
105 |
pdir->ccache.table_mask = chsize - 1;
|
|
|
106 |
gx_char_cache_init(pdir);
|
|
|
107 |
return 0;
|
|
|
108 |
}
|
|
|
109 |
|
|
|
110 |
/* Initialize the character cache. */
|
|
|
111 |
void
|
|
|
112 |
gx_char_cache_init(register gs_font_dir * dir)
|
|
|
113 |
{
|
|
|
114 |
int i;
|
|
|
115 |
cached_fm_pair *pair;
|
|
|
116 |
char_cache_chunk *cck = (char_cache_chunk *)
|
|
|
117 |
gs_alloc_bytes_immovable(dir->ccache.bits_memory,
|
|
|
118 |
sizeof(char_cache_chunk),
|
|
|
119 |
"initial_chunk");
|
|
|
120 |
|
|
|
121 |
dir->fmcache.msize = 0;
|
|
|
122 |
dir->fmcache.mnext = 0;
|
|
|
123 |
gx_bits_cache_chunk_init(cck, NULL, 0);
|
|
|
124 |
gx_bits_cache_init((gx_bits_cache *) & dir->ccache, cck);
|
|
|
125 |
dir->ccache.bspace = 0;
|
|
|
126 |
memset((char *)dir->ccache.table, 0,
|
|
|
127 |
(dir->ccache.table_mask + 1) * sizeof(cached_char *));
|
|
|
128 |
for (i = 0, pair = dir->fmcache.mdata;
|
|
|
129 |
i < dir->fmcache.mmax; i++, pair++) {
|
|
|
130 |
pair->index = i;
|
|
|
131 |
fm_pair_init(pair);
|
|
|
132 |
pair->ttf = 0;
|
|
|
133 |
pair->ttr = 0;
|
|
|
134 |
}
|
|
|
135 |
}
|
|
|
136 |
|
|
|
137 |
/* ====== Purging ====== */
|
|
|
138 |
|
|
|
139 |
/* Purge from the character cache all entries selected by */
|
|
|
140 |
/* a client-supplied procedure. */
|
|
|
141 |
void
|
|
|
142 |
gx_purge_selected_cached_chars(gs_font_dir * dir,
|
|
|
143 |
bool(*proc) (const gs_memory_t *mem,
|
|
|
144 |
cached_char *, void *),
|
|
|
145 |
void *proc_data)
|
|
|
146 |
{
|
|
|
147 |
int chi;
|
|
|
148 |
int cmax = dir->ccache.table_mask;
|
|
|
149 |
|
|
|
150 |
for (chi = 0; chi <= cmax;) {
|
|
|
151 |
cached_char *cc = dir->ccache.table[chi];
|
|
|
152 |
|
|
|
153 |
if (cc != 0 && (*proc) (dir->memory, cc, proc_data)) {
|
|
|
154 |
hash_remove_cached_char(dir, chi);
|
|
|
155 |
gx_free_cached_char(dir, cc);
|
|
|
156 |
} else
|
|
|
157 |
chi++;
|
|
|
158 |
}
|
|
|
159 |
}
|
|
|
160 |
|
|
|
161 |
/* ====== Font-level routines ====== */
|
|
|
162 |
|
|
|
163 |
/* Add a font/matrix pair to the cache. */
|
|
|
164 |
/* (This is only exported for gxccache.c.) */
|
|
|
165 |
int
|
|
|
166 |
gx_add_fm_pair(register gs_font_dir * dir, gs_font * font, const gs_uid * puid,
|
|
|
167 |
const gs_matrix * char_tm, const gs_log2_scale_point *log2_scale,
|
|
|
168 |
bool design_grid, cached_fm_pair **ppair)
|
|
|
169 |
{
|
|
|
170 |
float mxx, mxy, myx, myy;
|
|
|
171 |
register cached_fm_pair *pair = dir->fmcache.mdata + dir->fmcache.mnext;
|
|
|
172 |
cached_fm_pair *mend = dir->fmcache.mdata + dir->fmcache.mmax;
|
|
|
173 |
|
|
|
174 |
gx_compute_ccache_key(font, char_tm, log2_scale, design_grid,
|
|
|
175 |
&mxx, &mxy, &myx, &myy);
|
|
|
176 |
if (dir->fmcache.msize == dir->fmcache.mmax) { /* cache is full *//* Prefer an entry with num_chars == 0, if any. */
|
|
|
177 |
int count;
|
|
|
178 |
|
|
|
179 |
for (count = dir->fmcache.mmax;
|
|
|
180 |
--count >= 0 && pair->num_chars != 0;
|
|
|
181 |
)
|
|
|
182 |
if (++pair == mend)
|
|
|
183 |
pair = dir->fmcache.mdata;
|
|
|
184 |
gs_purge_fm_pair(dir, pair, 0);
|
|
|
185 |
} else { /* Look for an empty entry. (We know there is one.) */
|
|
|
186 |
while (!fm_pair_is_free(pair))
|
|
|
187 |
if (++pair == mend)
|
|
|
188 |
pair = dir->fmcache.mdata;
|
|
|
189 |
}
|
|
|
190 |
dir->fmcache.msize++;
|
|
|
191 |
dir->fmcache.mnext = pair + 1 - dir->fmcache.mdata;
|
|
|
192 |
if (dir->fmcache.mnext == dir->fmcache.mmax)
|
|
|
193 |
dir->fmcache.mnext = 0;
|
|
|
194 |
pair->font = font;
|
|
|
195 |
pair->UID = *puid;
|
|
|
196 |
pair->FontType = font->FontType;
|
|
|
197 |
/* The OSF/1 compiler doesn't like casting a pointer to */
|
|
|
198 |
/* a shorter int.... */
|
|
|
199 |
pair->hash = (uint) (ulong) pair % 549; /* scramble bits */
|
|
|
200 |
pair->mxx = mxx, pair->mxy = mxy;
|
|
|
201 |
pair->myx = myx, pair->myy = myy;
|
|
|
202 |
pair->num_chars = 0;
|
|
|
203 |
pair->xfont_tried = false;
|
|
|
204 |
pair->xfont = 0;
|
|
|
205 |
pair->ttf = 0;
|
|
|
206 |
pair->ttr = 0;
|
|
|
207 |
pair->design_grid = false;
|
|
|
208 |
if (font->FontType == ft_TrueType || font->FontType == ft_CID_TrueType)
|
|
|
209 |
if (((gs_font_type42 *)font)->FAPI==NULL) {
|
|
|
210 |
int code;
|
|
|
211 |
float cxx, cxy, cyx, cyy;
|
|
|
212 |
gs_matrix m;
|
|
|
213 |
gx_compute_char_matrix(char_tm, log2_scale, &cxx, &cxy, &cyx, &cyy);
|
|
|
214 |
|
|
|
215 |
pair->design_grid = design_grid;
|
|
|
216 |
m.xx = cxx;
|
|
|
217 |
m.xy = cxy;
|
|
|
218 |
m.yx = cyx;
|
|
|
219 |
m.yy = cyy;
|
|
|
220 |
m.tx = m.ty = 0;
|
|
|
221 |
pair->ttr = gx_ttfReader__create(dir->memory);
|
|
|
222 |
if (!pair->ttr)
|
|
|
223 |
return_error(gs_error_VMerror);
|
|
|
224 |
/* We could use a single the reader instance for all fonts ... */
|
|
|
225 |
pair->ttf = ttfFont__create(dir);
|
|
|
226 |
if (!pair->ttf)
|
|
|
227 |
return_error(gs_error_VMerror);
|
|
|
228 |
gx_ttfReader__set_font(pair->ttr, (gs_font_type42 *)font);
|
|
|
229 |
code = ttfFont__Open_aux(pair->ttf, dir->tti, pair->ttr,
|
|
|
230 |
(gs_font_type42 *)font, &m, log2_scale, design_grid);
|
|
|
231 |
gx_ttfReader__set_font(pair->ttr, NULL);
|
|
|
232 |
if (code < 0)
|
|
|
233 |
return code;
|
|
|
234 |
}
|
|
|
235 |
pair->memory = 0;
|
|
|
236 |
if_debug8('k', "[k]adding pair 0x%lx: font=0x%lx [%g %g %g %g] UID %ld, 0x%lx\n",
|
|
|
237 |
(ulong) pair, (ulong) font,
|
|
|
238 |
pair->mxx, pair->mxy, pair->myx, pair->myy,
|
|
|
239 |
(long)pair->UID.id, (ulong) pair->UID.xvalues);
|
|
|
240 |
*ppair = pair;
|
|
|
241 |
return 0;
|
|
|
242 |
}
|
|
|
243 |
|
|
|
244 |
/* Look up the xfont for a font/matrix pair. */
|
|
|
245 |
/* (This is only exported for gxccache.c.) */
|
|
|
246 |
void
|
|
|
247 |
gx_lookup_xfont(const gs_state * pgs, cached_fm_pair * pair, int encoding_index)
|
|
|
248 |
{
|
|
|
249 |
gx_device *dev = gs_currentdevice(pgs);
|
|
|
250 |
gx_device *fdev = (*dev_proc(dev, get_xfont_device)) (dev);
|
|
|
251 |
gs_font *font = pair->font;
|
|
|
252 |
const gx_xfont_procs *procs = (*dev_proc(fdev, get_xfont_procs)) (fdev);
|
|
|
253 |
gx_xfont *xf = 0;
|
|
|
254 |
|
|
|
255 |
/* We mustn't attempt to use xfonts for stroked characters, */
|
|
|
256 |
/* because such characters go outside their bounding box. */
|
|
|
257 |
if (procs != 0 && font->PaintType == 0) {
|
|
|
258 |
gs_matrix mat;
|
|
|
259 |
|
|
|
260 |
mat.xx = pair->mxx, mat.xy = pair->mxy;
|
|
|
261 |
mat.yx = pair->myx, mat.yy = pair->myy;
|
|
|
262 |
mat.tx = 0, mat.ty = 0;
|
|
|
263 |
/* xfonts can outlive their invocations, */
|
|
|
264 |
/* but restore purges them properly. */
|
|
|
265 |
pair->memory = pgs->memory;
|
|
|
266 |
if (font->key_name.size != 0)
|
|
|
267 |
xf = lookup_xfont_by_name(fdev, procs,
|
|
|
268 |
&font->key_name, encoding_index,
|
|
|
269 |
pair, &mat);
|
|
|
270 |
#define font_name_eq(pfn1,pfn2)\
|
|
|
271 |
((pfn1)->size == (pfn2)->size && (pfn1)->size != 0 &&\
|
|
|
272 |
!memcmp((char *)(pfn1)->chars, (char *)(pfn2)->chars, (pfn1)->size))
|
|
|
273 |
if (xf == 0 && font->font_name.size != 0 &&
|
|
|
274 |
/* Avoid redundant lookup */
|
|
|
275 |
!font_name_eq(&font->font_name, &font->key_name)
|
|
|
276 |
)
|
|
|
277 |
xf = lookup_xfont_by_name(fdev, procs,
|
|
|
278 |
&font->font_name, encoding_index,
|
|
|
279 |
pair, &mat);
|
|
|
280 |
if (xf == 0 && font->FontType != ft_composite &&
|
|
|
281 |
uid_is_valid(&((gs_font_base *) font)->UID)
|
|
|
282 |
) { /* Look for an original font with the same UID. */
|
|
|
283 |
gs_font_dir *pdir = font->dir;
|
|
|
284 |
gs_font *pfont;
|
|
|
285 |
|
|
|
286 |
for (pfont = pdir->orig_fonts; pfont != 0;
|
|
|
287 |
pfont = pfont->next
|
|
|
288 |
) {
|
|
|
289 |
if (pfont->FontType != ft_composite &&
|
|
|
290 |
uid_equal(&((gs_font_base *) pfont)->UID,
|
|
|
291 |
&((gs_font_base *) font)->UID) &&
|
|
|
292 |
pfont->key_name.size != 0 &&
|
|
|
293 |
!font_name_eq(&font->key_name,
|
|
|
294 |
&pfont->key_name)
|
|
|
295 |
) {
|
|
|
296 |
xf = lookup_xfont_by_name(fdev, procs,
|
|
|
297 |
&pfont->key_name,
|
|
|
298 |
encoding_index, pair, &mat);
|
|
|
299 |
if (xf != 0)
|
|
|
300 |
break;
|
|
|
301 |
}
|
|
|
302 |
}
|
|
|
303 |
}
|
|
|
304 |
}
|
|
|
305 |
pair->xfont = xf;
|
|
|
306 |
}
|
|
|
307 |
|
|
|
308 |
/* ------ Internal routines ------ */
|
|
|
309 |
|
|
|
310 |
/* Purge from the caches all references to a given font/matrix pair, */
|
|
|
311 |
/* or just characters that depend on its xfont. */
|
|
|
312 |
#define cpair ((cached_fm_pair *)vpair)
|
|
|
313 |
private bool
|
|
|
314 |
purge_fm_pair_char(const gs_memory_t *mem, cached_char * cc, void *vpair)
|
|
|
315 |
{
|
|
|
316 |
return cc_pair(cc) == cpair;
|
|
|
317 |
}
|
|
|
318 |
private bool
|
|
|
319 |
purge_fm_pair_char_xfont(const gs_memory_t *mem, cached_char * cc, void *vpair)
|
|
|
320 |
{
|
|
|
321 |
return cc_pair(cc) == cpair && cpair->xfont == 0 && !cc_has_bits(cc);
|
|
|
322 |
}
|
|
|
323 |
#undef cpair
|
|
|
324 |
void
|
|
|
325 |
gs_purge_fm_pair(gs_font_dir * dir, cached_fm_pair * pair, int xfont_only)
|
|
|
326 |
{
|
|
|
327 |
if_debug2('k', "[k]purging pair 0x%lx%s\n",
|
|
|
328 |
(ulong) pair, (xfont_only ? " (xfont only)" : ""));
|
|
|
329 |
if (pair->xfont != 0) {
|
|
|
330 |
(*pair->xfont->common.procs->release) (pair->xfont,
|
|
|
331 |
pair->memory);
|
|
|
332 |
pair->xfont_tried = false;
|
|
|
333 |
pair->xfont = 0;
|
|
|
334 |
}
|
|
|
335 |
gx_purge_selected_cached_chars(dir,
|
|
|
336 |
(xfont_only ? purge_fm_pair_char_xfont :
|
|
|
337 |
purge_fm_pair_char),
|
|
|
338 |
pair);
|
|
|
339 |
if (pair->ttr)
|
|
|
340 |
gx_ttfReader__destroy(pair->ttr);
|
|
|
341 |
pair->ttr = 0;
|
|
|
342 |
if (pair->ttf)
|
|
|
343 |
ttfFont__destroy(pair->ttf, dir);
|
|
|
344 |
pair->ttf = 0;
|
|
|
345 |
if (!xfont_only) {
|
|
|
346 |
#ifdef DEBUG
|
|
|
347 |
if (pair->num_chars != 0) {
|
|
|
348 |
lprintf1("Error in gs_purge_fm_pair: num_chars =%d\n",
|
|
|
349 |
pair->num_chars);
|
|
|
350 |
}
|
|
|
351 |
#endif
|
|
|
352 |
fm_pair_set_free(pair);
|
|
|
353 |
dir->fmcache.msize--;
|
|
|
354 |
}
|
|
|
355 |
}
|
|
|
356 |
|
|
|
357 |
/* Look up an xfont by name. */
|
|
|
358 |
/* The caller must already have done get_xfont_device to get the proper */
|
|
|
359 |
/* device to pass as the first argument to lookup_font. */
|
|
|
360 |
private gx_xfont *
|
|
|
361 |
lookup_xfont_by_name(gx_device * fdev, const gx_xfont_procs * procs,
|
|
|
362 |
gs_font_name * pfstr, int encoding_index, const cached_fm_pair * pair,
|
|
|
363 |
const gs_matrix * pmat)
|
|
|
364 |
{
|
|
|
365 |
gx_xfont *xf;
|
|
|
366 |
|
|
|
367 |
if_debug5('k', "[k]lookup xfont %s [%g %g %g %g]\n",
|
|
|
368 |
pfstr->chars, pmat->xx, pmat->xy, pmat->yx, pmat->yy);
|
|
|
369 |
xf = (*procs->lookup_font) (fdev,
|
|
|
370 |
&pfstr->chars[0], pfstr->size,
|
|
|
371 |
encoding_index, &pair->UID,
|
|
|
372 |
pmat, pair->memory);
|
|
|
373 |
if_debug1('k', "[k]... xfont=0x%lx\n", (ulong) xf);
|
|
|
374 |
return xf;
|
|
|
375 |
}
|
|
|
376 |
|
|
|
377 |
/* ====== Character-level routines ====== */
|
|
|
378 |
|
|
|
379 |
/*
|
|
|
380 |
* Allocate storage for caching a rendered character with possible
|
|
|
381 |
* oversampling and/or alpha. Return the cached_char if OK, 0 if too big.
|
|
|
382 |
* If the character is being oversampled, make the size decision
|
|
|
383 |
* on the basis of the final (scaled-down) size.
|
|
|
384 |
*
|
|
|
385 |
* The iwidth and iheight parameters include scaling up for oversampling
|
|
|
386 |
* (multiplication by 1 << pscale->{x,y}.)
|
|
|
387 |
* The depth parameter is the final number of alpha bits;
|
|
|
388 |
* depth <= x scale * y scale.
|
|
|
389 |
* If dev == NULL, this is an xfont-only entry.
|
|
|
390 |
* If dev != NULL, set up the memory device(s); in this case, if dev2 is
|
|
|
391 |
* not NULL, dev should be an alpha-buffer device with dev2 (an alpha
|
|
|
392 |
* device) as target.
|
|
|
393 |
*/
|
|
|
394 |
cached_char *
|
|
|
395 |
gx_alloc_char_bits(gs_font_dir * dir, gx_device_memory * dev,
|
|
|
396 |
gx_device_memory * dev2, ushort iwidth, ushort iheight,
|
|
|
397 |
const gs_log2_scale_point * pscale, int depth)
|
|
|
398 |
{
|
|
|
399 |
int log2_xscale = pscale->x;
|
|
|
400 |
int log2_yscale = pscale->y;
|
|
|
401 |
int log2_depth = ilog2(depth);
|
|
|
402 |
uint nwidth_bits = (iwidth >> log2_xscale) << log2_depth;
|
|
|
403 |
ulong isize, icdsize;
|
|
|
404 |
uint iraster;
|
|
|
405 |
cached_char *cc;
|
|
|
406 |
gx_device_memory mdev;
|
|
|
407 |
gx_device_memory *pdev = dev;
|
|
|
408 |
gx_device_memory *pdev2;
|
|
|
409 |
|
|
|
410 |
if (dev == NULL) {
|
|
|
411 |
mdev.memory = 0;
|
|
|
412 |
mdev.target = 0;
|
|
|
413 |
pdev = &mdev;
|
|
|
414 |
}
|
|
|
415 |
pdev2 = (dev2 == 0 ? pdev : dev2);
|
|
|
416 |
|
|
|
417 |
/* Compute the scaled-down bitmap size, and test against */
|
|
|
418 |
/* the maximum cachable character size. */
|
|
|
419 |
|
|
|
420 |
iraster = bitmap_raster(nwidth_bits);
|
|
|
421 |
if (iraster != 0 && iheight >> log2_yscale > dir->ccache.upper / iraster) {
|
|
|
422 |
if_debug5('k', "[k]no cache bits: scale=%dx%d, raster/scale=%u, height/scale=%u, upper=%u\n",
|
|
|
423 |
1 << log2_xscale, 1 << log2_yscale,
|
|
|
424 |
iraster, iheight, dir->ccache.upper);
|
|
|
425 |
return 0; /* too big */
|
|
|
426 |
}
|
|
|
427 |
/* Compute the actual bitmap size(s) and allocate the bits. */
|
|
|
428 |
if (dev2 == 0) {
|
|
|
429 |
/*
|
|
|
430 |
* Render to a full (possibly oversampled) bitmap; compress
|
|
|
431 |
* (if needed) when done.
|
|
|
432 |
*
|
|
|
433 |
* HACK: Preserve the reference count and retained flag.
|
|
|
434 |
*/
|
|
|
435 |
rc_header rc;
|
|
|
436 |
bool retained = pdev->retained;
|
|
|
437 |
gx_device *target = pdev->target;
|
|
|
438 |
|
|
|
439 |
rc = pdev->rc;
|
|
|
440 |
/* Pass the correct target, but decrement its refct afterwards. */
|
|
|
441 |
gs_make_mem_mono_device(pdev, pdev->memory, target);
|
|
|
442 |
rc_decrement_only(target, "gx_alloc_char_bits"); /* can't go to 0 */
|
|
|
443 |
pdev->rc = rc;
|
|
|
444 |
pdev->retained = retained;
|
|
|
445 |
pdev->width = iwidth;
|
|
|
446 |
pdev->height = iheight;
|
|
|
447 |
isize = gdev_mem_bitmap_size(pdev);
|
|
|
448 |
} else {
|
|
|
449 |
/* Use an alpha-buffer device to compress as we go. */
|
|
|
450 |
/* Preserve the reference counts, if any. */
|
|
|
451 |
rc_header rc;
|
|
|
452 |
|
|
|
453 |
rc = dev2->rc;
|
|
|
454 |
gs_make_mem_alpha_device(dev2, dev2->memory, NULL, depth);
|
|
|
455 |
dev2->rc = rc;
|
|
|
456 |
dev2->width = iwidth >> log2_xscale;
|
|
|
457 |
dev2->height = iheight >> log2_yscale;
|
|
|
458 |
rc = dev->rc;
|
|
|
459 |
gs_make_mem_abuf_device(dev, dev->memory, (gx_device *) dev2,
|
|
|
460 |
pscale, depth, 0);
|
|
|
461 |
dev->rc = rc;
|
|
|
462 |
dev->width = iwidth;
|
|
|
463 |
dev->height = 2 << log2_yscale;
|
|
|
464 |
isize = gdev_mem_bitmap_size(dev) +
|
|
|
465 |
gdev_mem_bitmap_size(dev2);
|
|
|
466 |
}
|
|
|
467 |
icdsize = isize + sizeof_cached_char;
|
|
|
468 |
cc = alloc_char(dir, icdsize);
|
|
|
469 |
if (cc == 0)
|
|
|
470 |
return 0;
|
|
|
471 |
if_debug4('k', "[k]adding char 0x%lx:%u(%u,%u)\n",
|
|
|
472 |
(ulong) cc, (uint) icdsize, iwidth, iheight);
|
|
|
473 |
|
|
|
474 |
/* Fill in the entry. */
|
|
|
475 |
|
|
|
476 |
cc_set_depth(cc, depth);
|
|
|
477 |
cc->xglyph = gx_no_xglyph;
|
|
|
478 |
/* Set the width and height to those of the device. */
|
|
|
479 |
/* Note that if we are oversampling without an alpha buffer. */
|
|
|
480 |
/* these are not the final unscaled dimensions. */
|
|
|
481 |
cc->width = pdev2->width;
|
|
|
482 |
cc->height = pdev2->height;
|
|
|
483 |
cc->shift = 0;
|
|
|
484 |
cc_set_raster(cc, gdev_mem_raster(pdev2));
|
|
|
485 |
cc_set_pair_only(cc, 0); /* not linked in yet */
|
|
|
486 |
cc->id = gx_no_bitmap_id;
|
|
|
487 |
cc->subpix_origin.x = cc->subpix_origin.y = 0;
|
|
|
488 |
cc->linked = false;
|
|
|
489 |
|
|
|
490 |
/* Open the cache device(s). */
|
|
|
491 |
|
|
|
492 |
if (dev2) { /* The second device is an alpha device that targets */
|
|
|
493 |
/* the real storage for the character. */
|
|
|
494 |
byte *bits = cc_bits(cc);
|
|
|
495 |
uint bsize = (uint) gdev_mem_bitmap_size(dev2);
|
|
|
496 |
|
|
|
497 |
memset(bits, 0, bsize);
|
|
|
498 |
dev2->base = bits;
|
|
|
499 |
(*dev_proc(dev2, open_device)) ((gx_device *) dev2);
|
|
|
500 |
dev->base = bits + bsize;
|
|
|
501 |
(*dev_proc(dev, open_device)) ((gx_device *) dev);
|
|
|
502 |
} else if (dev)
|
|
|
503 |
gx_open_cache_device(dev, cc);
|
|
|
504 |
|
|
|
505 |
return cc;
|
|
|
506 |
}
|
|
|
507 |
|
|
|
508 |
/* Open the cache device. */
|
|
|
509 |
void
|
|
|
510 |
gx_open_cache_device(gx_device_memory * dev, cached_char * cc)
|
|
|
511 |
{
|
|
|
512 |
byte *bits = cc_bits(cc);
|
|
|
513 |
|
|
|
514 |
dev->width = cc->width;
|
|
|
515 |
dev->height = cc->height;
|
|
|
516 |
memset((char *)bits, 0, (uint) gdev_mem_bitmap_size(dev));
|
|
|
517 |
dev->base = bits;
|
|
|
518 |
(*dev_proc(dev, open_device)) ((gx_device *) dev); /* initialize */
|
|
|
519 |
}
|
|
|
520 |
|
|
|
521 |
/* Remove a character from the cache. */
|
|
|
522 |
void
|
|
|
523 |
gx_free_cached_char(gs_font_dir * dir, cached_char * cc)
|
|
|
524 |
{
|
|
|
525 |
char_cache_chunk *cck = cc->chunk;
|
|
|
526 |
|
|
|
527 |
dir->ccache.chunks = cck;
|
|
|
528 |
dir->ccache.cnext = (byte *) cc - cck->data;
|
|
|
529 |
if (cc->linked)
|
|
|
530 |
cc_pair(cc)->num_chars--;
|
|
|
531 |
if_debug2('k', "[k]freeing char 0x%lx, pair=0x%lx\n",
|
|
|
532 |
(ulong) cc, (ulong) cc_pair(cc));
|
|
|
533 |
gx_bits_cache_free((gx_bits_cache *) & dir->ccache, &cc->head, cck);
|
|
|
534 |
}
|
|
|
535 |
|
|
|
536 |
/* Add a character to the cache */
|
|
|
537 |
void
|
|
|
538 |
gx_add_cached_char(gs_font_dir * dir, gx_device_memory * dev,
|
|
|
539 |
cached_char * cc, cached_fm_pair * pair, const gs_log2_scale_point * pscale)
|
|
|
540 |
{
|
|
|
541 |
if_debug5('k', "[k]chaining char 0x%lx: pair=0x%lx, glyph=0x%lx, wmode=%d, depth=%d\n",
|
|
|
542 |
(ulong) cc, (ulong) pair, (ulong) cc->code,
|
|
|
543 |
cc->wmode, cc_depth(cc));
|
|
|
544 |
if (dev != NULL) {
|
|
|
545 |
static const gs_log2_scale_point no_scale =
|
|
|
546 |
{0, 0};
|
|
|
547 |
|
|
|
548 |
/* Close the device, to flush the alpha buffer if any. */
|
|
|
549 |
(*dev_proc(dev, close_device)) ((gx_device *) dev);
|
|
|
550 |
gx_add_char_bits(dir, cc,
|
|
|
551 |
(gs_device_is_abuf((gx_device *) dev) ?
|
|
|
552 |
&no_scale : pscale));
|
|
|
553 |
}
|
|
|
554 |
/* Add the new character to the hash table. */
|
|
|
555 |
{
|
|
|
556 |
uint chi = chars_head_index(cc->code, pair);
|
|
|
557 |
|
|
|
558 |
while (dir->ccache.table[chi &= dir->ccache.table_mask] != 0)
|
|
|
559 |
chi++;
|
|
|
560 |
dir->ccache.table[chi] = cc;
|
|
|
561 |
if (cc->pair == NULL) {
|
|
|
562 |
/* gx_show_text_retry could reset it when bbox_draw
|
|
|
563 |
discovered an insufficient FontBBox and enlarged it.
|
|
|
564 |
Glyph raster params could change then. */
|
|
|
565 |
cc->pair = pair;
|
|
|
566 |
} else
|
|
|
567 |
assert(cc->pair == pair);
|
|
|
568 |
cc->linked = true;
|
|
|
569 |
cc_set_pair(cc, pair);
|
|
|
570 |
pair->num_chars++;
|
|
|
571 |
}
|
|
|
572 |
}
|
|
|
573 |
|
|
|
574 |
/* Adjust the bits of a newly-rendered character, by unscaling */
|
|
|
575 |
/* and compressing or converting to alpha values if necessary. */
|
|
|
576 |
void
|
|
|
577 |
gx_add_char_bits(gs_font_dir * dir, cached_char * cc,
|
|
|
578 |
const gs_log2_scale_point * plog2_scale)
|
|
|
579 |
{
|
|
|
580 |
int log2_x = plog2_scale->x, log2_y = plog2_scale->y;
|
|
|
581 |
uint raster = cc_raster(cc);
|
|
|
582 |
byte *bits = cc_bits(cc);
|
|
|
583 |
int depth = cc_depth(cc);
|
|
|
584 |
int log2_depth = ilog2(depth);
|
|
|
585 |
uint nwidth_bits, nraster;
|
|
|
586 |
gs_int_rect bbox;
|
|
|
587 |
|
|
|
588 |
#ifdef DEBUG
|
|
|
589 |
if (cc->width % (1 << log2_x) != 0 ||
|
|
|
590 |
cc->height % (1 << log2_y) != 0
|
|
|
591 |
) {
|
|
|
592 |
lprintf4("size %d,%d not multiple of scale %d,%d!\n",
|
|
|
593 |
cc->width, cc->height,
|
|
|
594 |
1 << log2_x, 1 << log2_y);
|
|
|
595 |
cc->width &= -1 << log2_x;
|
|
|
596 |
cc->height &= -1 << log2_y;
|
|
|
597 |
}
|
|
|
598 |
#endif
|
|
|
599 |
|
|
|
600 |
/*
|
|
|
601 |
* Compute the bounding box before compressing.
|
|
|
602 |
* We may have to scan more bits, but this is a lot faster than
|
|
|
603 |
* compressing the white space. Note that all bbox values are
|
|
|
604 |
* in bits, not pixels.
|
|
|
605 |
*/
|
|
|
606 |
|
|
|
607 |
bits_bounding_box(bits, cc->height, raster, &bbox);
|
|
|
608 |
|
|
|
609 |
/*
|
|
|
610 |
* If the character was oversampled, compress it now.
|
|
|
611 |
* In this case we know that log2_depth <= log2_x.
|
|
|
612 |
* If the character was not oversampled, or if we converted
|
|
|
613 |
* oversampling to alpha dynamically (using an alpha buffer
|
|
|
614 |
* intermediate device), log2_x and log2_y are both zero,
|
|
|
615 |
* but in the latter case we may still have depth > 1.
|
|
|
616 |
*/
|
|
|
617 |
|
|
|
618 |
if ((log2_x | log2_y) != 0) {
|
|
|
619 |
if_debug5('k', "[k]compressing %dx%d by %dx%d to depth=%d\n",
|
|
|
620 |
cc->width, cc->height, 1 << log2_x, 1 << log2_y,
|
|
|
621 |
depth);
|
|
|
622 |
if (gs_debug_c('K'))
|
|
|
623 |
debug_dump_bitmap(bits, raster, cc->height,
|
|
|
624 |
"[K]uncompressed bits");
|
|
|
625 |
/* Truncate/round the bbox to a multiple of the scale. */
|
|
|
626 |
{
|
|
|
627 |
int scale_x = 1 << log2_x;
|
|
|
628 |
|
|
|
629 |
bbox.p.x &= -scale_x;
|
|
|
630 |
bbox.q.x = (bbox.q.x + scale_x - 1) & -scale_x;
|
|
|
631 |
}
|
|
|
632 |
{
|
|
|
633 |
int scale_y = 1 << log2_y;
|
|
|
634 |
|
|
|
635 |
bbox.p.y &= -scale_y;
|
|
|
636 |
bbox.q.y = (bbox.q.y + scale_y - 1) & -scale_y;
|
|
|
637 |
}
|
|
|
638 |
cc->width = (bbox.q.x - bbox.p.x) >> log2_x;
|
|
|
639 |
cc->height = (bbox.q.y - bbox.p.y) >> log2_y;
|
|
|
640 |
nwidth_bits = cc->width << log2_depth;
|
|
|
641 |
nraster = bitmap_raster(nwidth_bits);
|
|
|
642 |
bits_compress_scaled(bits + raster * bbox.p.y, bbox.p.x,
|
|
|
643 |
cc->width << log2_x,
|
|
|
644 |
cc->height << log2_y,
|
|
|
645 |
raster,
|
|
|
646 |
bits, nraster, plog2_scale, log2_depth);
|
|
|
647 |
bbox.p.x >>= log2_x;
|
|
|
648 |
bbox.p.y >>= log2_y;
|
|
|
649 |
} else {
|
|
|
650 |
/* No oversampling, just remove white space on all 4 sides. */
|
|
|
651 |
const byte *from = bits + raster * bbox.p.y + (bbox.p.x >> 3);
|
|
|
652 |
|
|
|
653 |
cc->height = bbox.q.y - bbox.p.y;
|
|
|
654 |
bbox.p.x &= ~7; /* adjust to byte boundary */
|
|
|
655 |
bbox.p.x >>= log2_depth; /* bits => pixels */
|
|
|
656 |
bbox.q.x = (bbox.q.x + depth - 1) >> log2_depth; /* ditto */
|
|
|
657 |
cc->width = bbox.q.x - bbox.p.x;
|
|
|
658 |
nwidth_bits = cc->width << log2_depth;
|
|
|
659 |
nraster = bitmap_raster(nwidth_bits);
|
|
|
660 |
if (bbox.p.x != 0 || nraster != raster) {
|
|
|
661 |
/* Move the bits down and over. */
|
|
|
662 |
byte *to = bits;
|
|
|
663 |
uint n = cc->height;
|
|
|
664 |
|
|
|
665 |
/* We'd like to move only
|
|
|
666 |
uint nbytes = (nwidth_bits + 7) >> 3;
|
|
|
667 |
* bytes per scan line, but unfortunately this drops
|
|
|
668 |
* the guaranteed zero padding at the end.
|
|
|
669 |
*/
|
|
|
670 |
|
|
|
671 |
for (; n--; from += raster, to += nraster)
|
|
|
672 |
memmove(to, from, /*nbytes */ nraster);
|
|
|
673 |
} else if (bbox.p.y != 0) { /* Just move the bits down. */
|
|
|
674 |
memmove(bits, from, raster * cc->height);
|
|
|
675 |
}
|
|
|
676 |
}
|
|
|
677 |
|
|
|
678 |
/* Adjust the offsets to account for removed white space. */
|
|
|
679 |
|
|
|
680 |
cc->offset.x -= int2fixed(bbox.p.x);
|
|
|
681 |
cc->offset.y -= int2fixed(bbox.p.y);
|
|
|
682 |
|
|
|
683 |
/* Discard the memory device overhead that follows the bits, */
|
|
|
684 |
/* and any space reclaimed from unscaling or compression. */
|
|
|
685 |
|
|
|
686 |
cc_set_raster(cc, nraster);
|
|
|
687 |
{
|
|
|
688 |
uint diff = ROUND_DOWN(cc->head.size - sizeof_cached_char -
|
|
|
689 |
nraster * cc->height,
|
|
|
690 |
align_cached_char_mod);
|
|
|
691 |
|
|
|
692 |
if (diff >= sizeof(cached_char_head)) {
|
|
|
693 |
shorten_cached_char(dir, cc, diff);
|
|
|
694 |
if_debug2('K', "[K]shortening char 0x%lx by %u (adding)\n",
|
|
|
695 |
(ulong) cc, diff);
|
|
|
696 |
}
|
|
|
697 |
}
|
|
|
698 |
|
|
|
699 |
/* Assign a bitmap id. */
|
|
|
700 |
|
|
|
701 |
cc->id = gs_next_ids(dir->orig_fonts->memory, 1);
|
|
|
702 |
}
|
|
|
703 |
|
|
|
704 |
/* Purge from the caches all references to a given font. */
|
|
|
705 |
void
|
|
|
706 |
gs_purge_font_from_char_caches(gs_font_dir * dir, const gs_font * font)
|
|
|
707 |
{
|
|
|
708 |
cached_fm_pair *pair = dir->fmcache.mdata;
|
|
|
709 |
int count = dir->fmcache.mmax;
|
|
|
710 |
|
|
|
711 |
if_debug1('k', "[k]purging font 0x%lx\n",
|
|
|
712 |
(ulong) font);
|
|
|
713 |
while (count--) {
|
|
|
714 |
if (pair->font == font) {
|
|
|
715 |
if (uid_is_valid(&pair->UID)) { /* Keep the entry. */
|
|
|
716 |
pair->font = 0;
|
|
|
717 |
} else
|
|
|
718 |
gs_purge_fm_pair(dir, pair, 0);
|
|
|
719 |
}
|
|
|
720 |
pair++;
|
|
|
721 |
}
|
|
|
722 |
}
|
|
|
723 |
|
|
|
724 |
/* ------ Internal routines ------ */
|
|
|
725 |
|
|
|
726 |
/* Allocate data space for a cached character, adding a new chunk if needed. */
|
|
|
727 |
private cached_char *
|
|
|
728 |
alloc_char(gs_font_dir * dir, ulong icdsize)
|
|
|
729 |
{ /* Try allocating at the current position first. */
|
|
|
730 |
cached_char *cc = alloc_char_in_chunk(dir, icdsize);
|
|
|
731 |
|
|
|
732 |
if (cc == 0) {
|
|
|
733 |
if (dir->ccache.bspace < dir->ccache.bmax) { /* Allocate another chunk. */
|
|
|
734 |
gs_memory_t *mem = dir->ccache.bits_memory;
|
|
|
735 |
char_cache_chunk *cck_prev = dir->ccache.chunks;
|
|
|
736 |
char_cache_chunk *cck;
|
|
|
737 |
uint cksize = dir->ccache.bmax / 5 + 1;
|
|
|
738 |
uint tsize = dir->ccache.bmax - dir->ccache.bspace;
|
|
|
739 |
byte *cdata;
|
|
|
740 |
|
|
|
741 |
if (cksize > tsize)
|
|
|
742 |
cksize = tsize;
|
|
|
743 |
if (icdsize + sizeof(cached_char_head) > cksize) {
|
|
|
744 |
if_debug2('k', "[k]no cache bits: cdsize+head=%lu, cksize=%u\n",
|
|
|
745 |
icdsize + sizeof(cached_char_head),
|
|
|
746 |
cksize);
|
|
|
747 |
return 0; /* wouldn't fit */
|
|
|
748 |
}
|
|
|
749 |
cck = (char_cache_chunk *)
|
|
|
750 |
gs_alloc_bytes_immovable(mem, sizeof(*cck),
|
|
|
751 |
"char cache chunk");
|
|
|
752 |
if (cck == 0)
|
|
|
753 |
return 0;
|
|
|
754 |
cdata =
|
|
|
755 |
gs_alloc_struct_array_immovable(mem, cksize, byte,
|
|
|
756 |
&st_font_cache_bytes,
|
|
|
757 |
"char cache chunk(data)");
|
|
|
758 |
if (cdata == 0) {
|
|
|
759 |
gs_free_object(mem, cck, "char cache chunk");
|
|
|
760 |
return 0;
|
|
|
761 |
}
|
|
|
762 |
gx_bits_cache_chunk_init(cck, cdata, cksize);
|
|
|
763 |
cck->next = cck_prev->next;
|
|
|
764 |
cck_prev->next = cck;
|
|
|
765 |
dir->ccache.bspace += cksize;
|
|
|
766 |
dir->ccache.chunks = cck;
|
|
|
767 |
} else { /* Cycle through existing chunks. */
|
|
|
768 |
char_cache_chunk *cck_init = dir->ccache.chunks;
|
|
|
769 |
char_cache_chunk *cck = cck_init;
|
|
|
770 |
|
|
|
771 |
while ((dir->ccache.chunks = cck = cck->next) != cck_init) {
|
|
|
772 |
dir->ccache.cnext = 0;
|
|
|
773 |
cc = alloc_char_in_chunk(dir, icdsize);
|
|
|
774 |
if (cc != 0)
|
|
|
775 |
return cc;
|
|
|
776 |
}
|
|
|
777 |
}
|
|
|
778 |
dir->ccache.cnext = 0;
|
|
|
779 |
cc = alloc_char_in_chunk(dir, icdsize);
|
|
|
780 |
}
|
|
|
781 |
return cc;
|
|
|
782 |
}
|
|
|
783 |
|
|
|
784 |
/* Allocate a character in the current chunk. */
|
|
|
785 |
private cached_char *
|
|
|
786 |
alloc_char_in_chunk(gs_font_dir * dir, ulong icdsize)
|
|
|
787 |
{
|
|
|
788 |
char_cache_chunk *cck = dir->ccache.chunks;
|
|
|
789 |
cached_char_head *cch;
|
|
|
790 |
|
|
|
791 |
#define cc ((cached_char *)cch)
|
|
|
792 |
|
|
|
793 |
while (gx_bits_cache_alloc((gx_bits_cache *) & dir->ccache,
|
|
|
794 |
icdsize, &cch) < 0
|
|
|
795 |
) {
|
|
|
796 |
if (cch == 0) { /* Not enough room to allocate in this chunk. */
|
|
|
797 |
return 0;
|
|
|
798 |
} { /* Free the character */
|
|
|
799 |
cached_fm_pair *pair = cc_pair(cc);
|
|
|
800 |
|
|
|
801 |
if (pair != 0) {
|
|
|
802 |
uint chi = chars_head_index(cc->code, pair);
|
|
|
803 |
|
|
|
804 |
while (dir->ccache.table[chi & dir->ccache.table_mask] != cc)
|
|
|
805 |
chi++;
|
|
|
806 |
hash_remove_cached_char(dir, chi);
|
|
|
807 |
}
|
|
|
808 |
gx_free_cached_char(dir, cc);
|
|
|
809 |
}
|
|
|
810 |
}
|
|
|
811 |
cc->chunk = cck;
|
|
|
812 |
cc->loc = (byte *) cc - cck->data;
|
|
|
813 |
return cc;
|
|
|
814 |
#undef cc
|
|
|
815 |
}
|
|
|
816 |
|
|
|
817 |
/* Remove the cached_char at a given index in the hash table. */
|
|
|
818 |
/* In order not to slow down lookup, we relocate following entries. */
|
|
|
819 |
private void
|
|
|
820 |
hash_remove_cached_char(gs_font_dir * dir, uint chi)
|
|
|
821 |
{
|
|
|
822 |
uint mask = dir->ccache.table_mask;
|
|
|
823 |
uint from = ((chi &= mask) + 1) & mask;
|
|
|
824 |
cached_char *cc;
|
|
|
825 |
|
|
|
826 |
dir->ccache.table[chi] = 0;
|
|
|
827 |
while ((cc = dir->ccache.table[from]) != 0) { /* Loop invariants: chars[chi] == 0; */
|
|
|
828 |
/* chars[chi+1..from] != 0. */
|
|
|
829 |
uint fchi = chars_head_index(cc->code, cc_pair(cc));
|
|
|
830 |
|
|
|
831 |
/* If chi <= fchi < from, we relocate the character. */
|
|
|
832 |
/* Note that '<=' must take wraparound into account. */
|
|
|
833 |
if ((chi < from ? chi <= fchi && fchi < from :
|
|
|
834 |
chi <= fchi || fchi < from)
|
|
|
835 |
) {
|
|
|
836 |
dir->ccache.table[chi] = cc;
|
|
|
837 |
dir->ccache.table[from] = 0;
|
|
|
838 |
chi = from;
|
|
|
839 |
}
|
|
|
840 |
from = (from + 1) & mask;
|
|
|
841 |
}
|
|
|
842 |
}
|
|
|
843 |
|
|
|
844 |
/* Shorten a cached character. */
|
|
|
845 |
/* diff >= sizeof(cached_char_head). */
|
|
|
846 |
private void
|
|
|
847 |
shorten_cached_char(gs_font_dir * dir, cached_char * cc, uint diff)
|
|
|
848 |
{
|
|
|
849 |
gx_bits_cache_shorten((gx_bits_cache *) & dir->ccache, &cc->head,
|
|
|
850 |
diff, cc->chunk);
|
|
|
851 |
if_debug2('K', "[K]shortening creates free block 0x%lx(%u)\n",
|
|
|
852 |
(ulong) ((byte *) cc + cc->head.size), diff);
|
|
|
853 |
}
|