xref: /plan9/sys/src/cmd/gs/src/gxccman.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
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
RELOC_PTRS_BEGIN(cc_ptr_reloc_ptrs)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
gx_char_cache_alloc(gs_memory_t * struct_mem,gs_memory_t * bits_mem,gs_font_dir * pdir,uint bmax,uint mmax,uint cmax,uint upper)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
gx_char_cache_init(register gs_font_dir * dir)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
gx_purge_selected_cached_chars(gs_font_dir * dir,bool (* proc)(const gs_memory_t * mem,cached_char *,void *),void * proc_data)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
gx_add_fm_pair(register gs_font_dir * dir,gs_font * font,const gs_uid * puid,const gs_matrix * char_tm,const gs_log2_scale_point * log2_scale,bool design_grid,cached_fm_pair ** ppair)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
gx_lookup_xfont(const gs_state * pgs,cached_fm_pair * pair,int encoding_index)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
purge_fm_pair_char(const gs_memory_t * mem,cached_char * cc,void * vpair)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
purge_fm_pair_char_xfont(const gs_memory_t * mem,cached_char * cc,void * vpair)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
gs_purge_fm_pair(gs_font_dir * dir,cached_fm_pair * pair,int xfont_only)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 *
lookup_xfont_by_name(gx_device * fdev,const gx_xfont_procs * procs,gs_font_name * pfstr,int encoding_index,const cached_fm_pair * pair,const gs_matrix * pmat)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 *
gx_alloc_char_bits(gs_font_dir * dir,gx_device_memory * dev,gx_device_memory * dev2,ushort iwidth,ushort iheight,const gs_log2_scale_point * pscale,int depth)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
gx_open_cache_device(gx_device_memory * dev,cached_char * cc)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
gx_free_cached_char(gs_font_dir * dir,cached_char * cc)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
gx_add_cached_char(gs_font_dir * dir,gx_device_memory * dev,cached_char * cc,cached_fm_pair * pair,const gs_log2_scale_point * pscale)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
gx_add_char_bits(gs_font_dir * dir,cached_char * cc,const gs_log2_scale_point * plog2_scale)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
gs_purge_font_from_char_caches(gs_font_dir * dir,const gs_font * font)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 *
alloc_char(gs_font_dir * dir,ulong icdsize)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 *
alloc_char_in_chunk(gs_font_dir * dir,ulong icdsize)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
hash_remove_cached_char(gs_font_dir * dir,uint chi)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
shorten_cached_char(gs_font_dir * dir,cached_char * cc,uint diff)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 }
854