xref: /plan9-contrib/sys/src/cmd/gs/src/gxccache.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
17dd7cddfSDavid du Colombier /* Copyright (C) 1989, 1995, 1996, 1997, 1998 Aladdin Enterprises.  All rights reserved.
27dd7cddfSDavid du Colombier 
3*593dc095SDavid du Colombier   This software is provided AS-IS with no warranty, either express or
4*593dc095SDavid du Colombier   implied.
57dd7cddfSDavid du Colombier 
6*593dc095SDavid du Colombier   This software is distributed under license and may not be copied,
7*593dc095SDavid du Colombier   modified or distributed except as expressly authorized under the terms
8*593dc095SDavid du Colombier   of the license contained in the file LICENSE in this distribution.
97dd7cddfSDavid du Colombier 
10*593dc095SDavid du Colombier   For more information about licensing, please refer to
11*593dc095SDavid du Colombier   http://www.ghostscript.com/licensing/. For information on
12*593dc095SDavid du Colombier   commercial licensing, go to http://www.artifex.com/licensing/ or
13*593dc095SDavid du Colombier   contact Artifex Software, Inc., 101 Lucas Valley Road #110,
14*593dc095SDavid du Colombier   San Rafael, CA  94903, U.S.A., +1(415)492-9861.
157dd7cddfSDavid du Colombier */
167dd7cddfSDavid du Colombier 
17*593dc095SDavid du Colombier /* $Id: gxccache.c,v 1.34 2005/06/15 18:40:07 igor Exp $ */
187dd7cddfSDavid du Colombier /* Fast case character cache routines for Ghostscript library */
19*593dc095SDavid du Colombier #include "memory_.h"
207dd7cddfSDavid du Colombier #include "gx.h"
217dd7cddfSDavid du Colombier #include "gpcheck.h"
227dd7cddfSDavid du Colombier #include "gserrors.h"
237dd7cddfSDavid du Colombier #include "gsstruct.h"
24*593dc095SDavid du Colombier #include "gscencs.h"
257dd7cddfSDavid du Colombier #include "gxfixed.h"
267dd7cddfSDavid du Colombier #include "gxmatrix.h"
277dd7cddfSDavid du Colombier #include "gzstate.h"
287dd7cddfSDavid du Colombier #include "gzpath.h"
297dd7cddfSDavid du Colombier #include "gxdevice.h"
307dd7cddfSDavid du Colombier #include "gxdevmem.h"
317dd7cddfSDavid du Colombier #include "gzcpath.h"
327dd7cddfSDavid du Colombier #include "gxchar.h"
337dd7cddfSDavid du Colombier #include "gxfont.h"
347dd7cddfSDavid du Colombier #include "gxfcache.h"
357dd7cddfSDavid du Colombier #include "gxxfont.h"
367dd7cddfSDavid du Colombier #include "gscspace.h"		/* for gsimage.h */
377dd7cddfSDavid du Colombier #include "gsimage.h"
387dd7cddfSDavid du Colombier #include "gxhttile.h"
397dd7cddfSDavid du Colombier 
407dd7cddfSDavid du Colombier /* Forward references */
41*593dc095SDavid du Colombier private byte *compress_alpha_bits(const cached_char *, gs_memory_t *);
427dd7cddfSDavid du Colombier 
437dd7cddfSDavid du Colombier /* Define a scale factor of 1. */
44*593dc095SDavid du Colombier private const gs_log2_scale_point scale_log2_1 =
457dd7cddfSDavid du Colombier {0, 0};
467dd7cddfSDavid du Colombier 
47*593dc095SDavid du Colombier void
gx_compute_char_matrix(const gs_matrix * char_tm,const gs_log2_scale_point * log2_scale,float * mxx,float * mxy,float * myx,float * myy)48*593dc095SDavid du Colombier gx_compute_char_matrix(const gs_matrix *char_tm, const gs_log2_scale_point *log2_scale,
49*593dc095SDavid du Colombier     float *mxx, float *mxy, float *myx, float *myy)
507dd7cddfSDavid du Colombier {
51*593dc095SDavid du Colombier     int scale_x = 1 << log2_scale->x;
52*593dc095SDavid du Colombier     int scale_y = 1 << log2_scale->y;
53*593dc095SDavid du Colombier 
54*593dc095SDavid du Colombier     *mxx = char_tm->xx * scale_x;
55*593dc095SDavid du Colombier     *mxy = char_tm->xy * scale_x;
56*593dc095SDavid du Colombier     *myx = char_tm->yx * scale_y;
57*593dc095SDavid du Colombier     *myy = char_tm->yy * scale_y;
58*593dc095SDavid du Colombier }
59*593dc095SDavid du Colombier 
60*593dc095SDavid du Colombier void
gx_compute_ccache_key(gs_font * pfont,const gs_matrix * char_tm,const gs_log2_scale_point * log2_scale,bool design_grid,float * mxx,float * mxy,float * myx,float * myy)61*593dc095SDavid du Colombier gx_compute_ccache_key(gs_font * pfont, const gs_matrix *char_tm,
62*593dc095SDavid du Colombier     const gs_log2_scale_point *log2_scale, bool design_grid,
63*593dc095SDavid du Colombier     float *mxx, float *mxy, float *myx, float *myy)
64*593dc095SDavid du Colombier {
65*593dc095SDavid du Colombier     if (design_grid &&
66*593dc095SDavid du Colombier 	    (pfont->FontType == ft_TrueType || pfont->FontType == ft_CID_TrueType)) {
67*593dc095SDavid du Colombier 	/*
68*593dc095SDavid du Colombier 	 * We need a special face for this case, because the TT interpreter
69*593dc095SDavid du Colombier 	 * can't generate both grid_fitted and non-grid-fitted outlines
70*593dc095SDavid du Colombier 	 * with a same face instance. This happens due to control
71*593dc095SDavid du Colombier 	 * values in 'cvt' must be different.
72*593dc095SDavid du Colombier 	 * Since a single face satisfies all font sizes,
73*593dc095SDavid du Colombier 	 * we use a zero matrix as the cache entry key.
74*593dc095SDavid du Colombier 	 */
75*593dc095SDavid du Colombier 	*mxx = *mxy = *myx = *myy = 0;
76*593dc095SDavid du Colombier     } else
77*593dc095SDavid du Colombier 	gx_compute_char_matrix(char_tm, log2_scale, mxx, mxy, myx, myy);
78*593dc095SDavid du Colombier }
79*593dc095SDavid du Colombier 
80*593dc095SDavid du Colombier /* Look up, and if necessary add, a font/matrix pair in the cache */
81*593dc095SDavid du Colombier int
gx_lookup_fm_pair(gs_font * pfont,const gs_matrix * char_tm,const gs_log2_scale_point * log2_scale,bool design_grid,cached_fm_pair ** ppair)82*593dc095SDavid du Colombier gx_lookup_fm_pair(gs_font * pfont, const gs_matrix *char_tm,
83*593dc095SDavid du Colombier     const gs_log2_scale_point *log2_scale, bool design_grid, cached_fm_pair **ppair)
84*593dc095SDavid du Colombier {
85*593dc095SDavid du Colombier     float mxx, mxy, myx, myy;
867dd7cddfSDavid du Colombier     gs_font *font = pfont;
877dd7cddfSDavid du Colombier     register gs_font_dir *dir = font->dir;
887dd7cddfSDavid du Colombier     register cached_fm_pair *pair =
897dd7cddfSDavid du Colombier     dir->fmcache.mdata + dir->fmcache.mnext;
907dd7cddfSDavid du Colombier     int count = dir->fmcache.mmax;
917dd7cddfSDavid du Colombier     gs_uid uid;
927dd7cddfSDavid du Colombier 
93*593dc095SDavid du Colombier     gx_compute_ccache_key(pfont, char_tm, log2_scale, design_grid,
94*593dc095SDavid du Colombier 			    &mxx, &mxy, &myx, &myy);
957dd7cddfSDavid du Colombier     if (font->FontType == ft_composite || font->PaintType != 0) {	/* We can't cache by UID alone. */
967dd7cddfSDavid du Colombier 	uid_set_invalid(&uid);
977dd7cddfSDavid du Colombier     } else {
987dd7cddfSDavid du Colombier 	uid = ((gs_font_base *) font)->UID;
997dd7cddfSDavid du Colombier 	if (uid_is_valid(&uid))
1007dd7cddfSDavid du Colombier 	    font = 0;
1017dd7cddfSDavid du Colombier     }
1027dd7cddfSDavid du Colombier     while (count--) {
1037dd7cddfSDavid du Colombier 	if (pair == dir->fmcache.mdata)
1047dd7cddfSDavid du Colombier 	    pair += dir->fmcache.mmax;
1057dd7cddfSDavid du Colombier 	pair--;
1067dd7cddfSDavid du Colombier 	/* We have either a non-zero font and an invalid UID, */
1077dd7cddfSDavid du Colombier 	/* or a zero font and a valid UID. */
1087dd7cddfSDavid du Colombier 	/* We have to break up the test */
1097dd7cddfSDavid du Colombier 	/* because of a bug in the Zortech compiler. */
1107dd7cddfSDavid du Colombier 	if (font != 0) {
1117dd7cddfSDavid du Colombier 	    if (pair->font != font)
1127dd7cddfSDavid du Colombier 		continue;
1137dd7cddfSDavid du Colombier 	} else {
1147dd7cddfSDavid du Colombier 	    if (!uid_equal(&pair->UID, &uid) ||
1157dd7cddfSDavid du Colombier 		pair->FontType != pfont->FontType
1167dd7cddfSDavid du Colombier 		)
1177dd7cddfSDavid du Colombier 		continue;
1187dd7cddfSDavid du Colombier 	}
1197dd7cddfSDavid du Colombier 	if (pair->mxx == mxx && pair->mxy == mxy &&
1207dd7cddfSDavid du Colombier 	    pair->myx == myx && pair->myy == myy
121*593dc095SDavid du Colombier 	    && pair->design_grid == design_grid) {
1227dd7cddfSDavid du Colombier 	    if (pair->font == 0) {
1237dd7cddfSDavid du Colombier 		pair->font = pfont;
1247dd7cddfSDavid du Colombier 		if_debug2('k', "[k]updating pair 0x%lx with font 0x%lx\n",
1257dd7cddfSDavid du Colombier 			  (ulong) pair, (ulong) pfont);
1267dd7cddfSDavid du Colombier 	    } else {
1277dd7cddfSDavid du Colombier 		if_debug2('k', "[k]found pair 0x%lx: font=0x%lx\n",
1287dd7cddfSDavid du Colombier 			  (ulong) pair, (ulong) pair->font);
1297dd7cddfSDavid du Colombier 	    }
130*593dc095SDavid du Colombier 	    *ppair = pair;
131*593dc095SDavid du Colombier 	    return 0;
1327dd7cddfSDavid du Colombier 	}
1337dd7cddfSDavid du Colombier     }
134*593dc095SDavid du Colombier     return gx_add_fm_pair(dir, pfont, &uid, char_tm, log2_scale, design_grid, ppair);
1357dd7cddfSDavid du Colombier }
1367dd7cddfSDavid du Colombier 
137*593dc095SDavid du Colombier /* Look up a glyph with the right depth in the cache. */
1387dd7cddfSDavid du Colombier /* Return the cached_char or 0. */
1397dd7cddfSDavid du Colombier cached_char *
gx_lookup_cached_char(const gs_font * pfont,const cached_fm_pair * pair,gs_glyph glyph,int wmode,int depth,gs_fixed_point * subpix_origin)1407dd7cddfSDavid du Colombier gx_lookup_cached_char(const gs_font * pfont, const cached_fm_pair * pair,
141*593dc095SDavid du Colombier 		      gs_glyph glyph, int wmode, int depth,
142*593dc095SDavid du Colombier 		      gs_fixed_point *subpix_origin)
1437dd7cddfSDavid du Colombier {
1447dd7cddfSDavid du Colombier     gs_font_dir *dir = pfont->dir;
1457dd7cddfSDavid du Colombier     uint chi = chars_head_index(glyph, pair);
1467dd7cddfSDavid du Colombier     register cached_char *cc;
1477dd7cddfSDavid du Colombier 
1487dd7cddfSDavid du Colombier     while ((cc = dir->ccache.table[chi & dir->ccache.table_mask]) != 0) {
1497dd7cddfSDavid du Colombier 	if (cc->code == glyph && cc_pair(cc) == pair &&
150*593dc095SDavid du Colombier 	    cc->subpix_origin.x == subpix_origin->x &&
151*593dc095SDavid du Colombier 	    cc->subpix_origin.y == subpix_origin->y &&
152*593dc095SDavid du Colombier 	    cc->wmode == wmode && cc_depth(cc) == depth
1537dd7cddfSDavid du Colombier 	    ) {
1547dd7cddfSDavid du Colombier 	    if_debug4('K', "[K]found 0x%lx (depth=%d) for glyph=0x%lx, wmode=%d\n",
1557dd7cddfSDavid du Colombier 		      (ulong) cc, cc_depth(cc), (ulong) glyph, wmode);
1567dd7cddfSDavid du Colombier 	    return cc;
1577dd7cddfSDavid du Colombier 	}
1587dd7cddfSDavid du Colombier 	chi++;
1597dd7cddfSDavid du Colombier     }
160*593dc095SDavid du Colombier     if_debug3('K', "[K]not found: glyph=0x%lx, wmode=%d, depth=%d\n",
161*593dc095SDavid du Colombier 	      (ulong) glyph, wmode, depth);
1627dd7cddfSDavid du Colombier     return 0;
1637dd7cddfSDavid du Colombier }
1647dd7cddfSDavid du Colombier 
1657dd7cddfSDavid du Colombier /* Look up a character in an external font. */
1667dd7cddfSDavid du Colombier /* Return the cached_char or 0. */
1677dd7cddfSDavid du Colombier cached_char *
gx_lookup_xfont_char(const gs_state * pgs,cached_fm_pair * pair,gs_char chr,gs_glyph glyph,int wmode)1687dd7cddfSDavid du Colombier gx_lookup_xfont_char(const gs_state * pgs, cached_fm_pair * pair,
169*593dc095SDavid du Colombier 		     gs_char chr, gs_glyph glyph, int wmode)
1707dd7cddfSDavid du Colombier {
1717dd7cddfSDavid du Colombier     gs_font *font = pair->font;
1727dd7cddfSDavid du Colombier     int enc_index;
1737dd7cddfSDavid du Colombier     gx_xfont *xf;
1747dd7cddfSDavid du Colombier     gx_xglyph xg;
1757dd7cddfSDavid du Colombier     gs_log2_scale_point log2_scale;
1767dd7cddfSDavid du Colombier     gs_point wxy;
1777dd7cddfSDavid du Colombier     gs_int_rect bbox;
1787dd7cddfSDavid du Colombier     cached_char *cc;
1797dd7cddfSDavid du Colombier 
1807dd7cddfSDavid du Colombier     if (font == 0)
1817dd7cddfSDavid du Colombier 	return NULL;
1827dd7cddfSDavid du Colombier     enc_index =
1837dd7cddfSDavid du Colombier 	(font->FontType == ft_composite ? -1 :
1847dd7cddfSDavid du Colombier 	 ((gs_font_base *) font)->nearest_encoding_index);
1857dd7cddfSDavid du Colombier     if (!pair->xfont_tried) {	/* Look for an xfont now. */
1867dd7cddfSDavid du Colombier 	gx_lookup_xfont(pgs, pair, enc_index);
1877dd7cddfSDavid du Colombier 	pair->xfont_tried = true;
1887dd7cddfSDavid du Colombier     }
1897dd7cddfSDavid du Colombier     xf = pair->xfont;
1907dd7cddfSDavid du Colombier     if (xf == 0)
1917dd7cddfSDavid du Colombier 	return NULL;
1927dd7cddfSDavid du Colombier     {
1937dd7cddfSDavid du Colombier 	const gx_xfont_procs *procs = xf->common.procs;
194*593dc095SDavid du Colombier 	gs_const_string gstr;
195*593dc095SDavid du Colombier 	int code = font->procs.glyph_name(font, glyph, &gstr);
1967dd7cddfSDavid du Colombier 
197*593dc095SDavid du Colombier 	if (code < 0)
198*593dc095SDavid du Colombier 	    return NULL;
199*593dc095SDavid du Colombier 	if (enc_index >= 0 && ((gs_font_base *)font)->encoding_index < 0) {
200*593dc095SDavid du Colombier 	    /*
201*593dc095SDavid du Colombier 	     * Use the registered encoding only if this glyph
202*593dc095SDavid du Colombier 	     * is the same as the one in the registered encoding.
203*593dc095SDavid du Colombier 	     */
204*593dc095SDavid du Colombier 	    gs_const_string kstr;
205*593dc095SDavid du Colombier 
206*593dc095SDavid du Colombier 	    if (gs_c_glyph_name(gs_c_known_encode(chr, enc_index), &kstr) < 0 ||
207*593dc095SDavid du Colombier 		kstr.size != gstr.size ||
208*593dc095SDavid du Colombier 		memcmp(kstr.data, gstr.data, kstr.size)
2097dd7cddfSDavid du Colombier 		)
2107dd7cddfSDavid du Colombier 		enc_index = -1;
2117dd7cddfSDavid du Colombier 	}
212*593dc095SDavid du Colombier 	xg = procs->char_xglyph(xf, chr, enc_index, glyph, &gstr);
2137dd7cddfSDavid du Colombier 	if (xg == gx_no_xglyph)
2147dd7cddfSDavid du Colombier 	    return NULL;
2157dd7cddfSDavid du Colombier 	if ((*procs->char_metrics) (xf, xg, wmode, &wxy, &bbox) < 0)
2167dd7cddfSDavid du Colombier 	    return NULL;
2177dd7cddfSDavid du Colombier     }
2187dd7cddfSDavid du Colombier     log2_scale.x = log2_scale.y = 1;
219*593dc095SDavid du Colombier     cc = gx_alloc_char_bits(font->dir, NULL, NULL,
220*593dc095SDavid du Colombier 		(ushort)(bbox.q.x - bbox.p.x), (ushort)(bbox.q.y - bbox.p.y),
221*593dc095SDavid du Colombier 		&log2_scale, 1);
2227dd7cddfSDavid du Colombier     if (cc == 0)
2237dd7cddfSDavid du Colombier 	return NULL;
2247dd7cddfSDavid du Colombier     /* Success.  Make the cache entry. */
2257dd7cddfSDavid du Colombier     cc->code = glyph;
2267dd7cddfSDavid du Colombier     cc->wmode = wmode;
2277dd7cddfSDavid du Colombier     cc->xglyph = xg;
2287dd7cddfSDavid du Colombier     cc->wxy.x = float2fixed(wxy.x);
2297dd7cddfSDavid du Colombier     cc->wxy.y = float2fixed(wxy.y);
2307dd7cddfSDavid du Colombier     cc->offset.x = int2fixed(-bbox.p.x);
2317dd7cddfSDavid du Colombier     cc->offset.y = int2fixed(-bbox.p.y);
232*593dc095SDavid du Colombier     cc_set_pair(cc, pair);
2337dd7cddfSDavid du Colombier     if_debug5('k', "[k]xfont %s char %d/0x%x#0x%lx=>0x%lx\n",
2347dd7cddfSDavid du Colombier 	      font->font_name.chars, enc_index, (int)chr,
2357dd7cddfSDavid du Colombier 	      (ulong) glyph, (ulong) xg);
2367dd7cddfSDavid du Colombier     if_debug6('k', "     wxy=(%g,%g) bbox=(%d,%d),(%d,%d)\n",
2377dd7cddfSDavid du Colombier 	      wxy.x, wxy.y, bbox.p.x, bbox.p.y, bbox.q.x, bbox.q.y);
2387dd7cddfSDavid du Colombier     gx_add_cached_char(font->dir, NULL, cc, pair, &scale_log2_1);
2397dd7cddfSDavid du Colombier     return cc;
2407dd7cddfSDavid du Colombier }
2417dd7cddfSDavid du Colombier 
2427dd7cddfSDavid du Colombier /* Copy a cached character to the screen. */
2437dd7cddfSDavid du Colombier /* Assume the caller has already done gx_color_load. */
2447dd7cddfSDavid du Colombier /* Return 0 if OK, 1 if we couldn't do the operation but no error */
2457dd7cddfSDavid du Colombier /* should be signalled, or a negative error code. */
2467dd7cddfSDavid du Colombier int
gx_image_cached_char(register gs_show_enum * penum,register cached_char * cc)2477dd7cddfSDavid du Colombier gx_image_cached_char(register gs_show_enum * penum, register cached_char * cc)
2487dd7cddfSDavid du Colombier {
2497dd7cddfSDavid du Colombier     register gs_state *pgs = penum->pgs;
2507dd7cddfSDavid du Colombier     gx_device_color *pdevc = pgs->dev_color;
2517dd7cddfSDavid du Colombier     int x, y, w, h, depth;
2527dd7cddfSDavid du Colombier     int code;
2537dd7cddfSDavid du Colombier     gs_fixed_point pt;
2543ff48bf5SDavid du Colombier     gx_device *dev = penum->dev;
2553ff48bf5SDavid du Colombier     gx_device *imaging_dev = penum->imaging_dev ? penum->imaging_dev : dev;
2563ff48bf5SDavid du Colombier     gx_device *orig_dev = imaging_dev;
2577dd7cddfSDavid du Colombier     gx_device_clip cdev;
2587dd7cddfSDavid du Colombier     gx_xglyph xg = cc->xglyph;
2597dd7cddfSDavid du Colombier     gx_xfont *xf;
2607dd7cddfSDavid du Colombier     byte *bits;
2617dd7cddfSDavid du Colombier 
2627dd7cddfSDavid du Colombier   top:code = gx_path_current_point_inline(pgs->path, &pt);
2637dd7cddfSDavid du Colombier     if (code < 0)
2647dd7cddfSDavid du Colombier 	return code;
2657dd7cddfSDavid du Colombier     /*
2667dd7cddfSDavid du Colombier      * If the character doesn't lie entirely within the inner
2677dd7cddfSDavid du Colombier      * clipping rectangle, we set up an intermediate clipping device.
2687dd7cddfSDavid du Colombier      * Note that if the original device implements fill_mask, we may
2697dd7cddfSDavid du Colombier      * never actually use the clipping device.
2707dd7cddfSDavid du Colombier      */
271*593dc095SDavid du Colombier     pt.x -= cc->offset.x + cc->subpix_origin.x;
2727dd7cddfSDavid du Colombier     x = fixed2int_var_rounded(pt.x) + penum->ftx;
273*593dc095SDavid du Colombier     pt.y -= cc->offset.y + cc->subpix_origin.y;
2747dd7cddfSDavid du Colombier     y = fixed2int_var_rounded(pt.y) + penum->fty;
2757dd7cddfSDavid du Colombier     w = cc->width;
2767dd7cddfSDavid du Colombier     h = cc->height;
2777dd7cddfSDavid du Colombier #ifdef DEBUG
2787dd7cddfSDavid du Colombier     if (gs_debug_c('K')) {
2797dd7cddfSDavid du Colombier 	if (cc_has_bits(cc))
2807dd7cddfSDavid du Colombier 	    debug_dump_bitmap(cc_bits(cc), cc_raster(cc), h,
2817dd7cddfSDavid du Colombier 			      "[K]bits");
2827dd7cddfSDavid du Colombier 	else
2837dd7cddfSDavid du Colombier 	    dputs("[K]no bits\n");
2847dd7cddfSDavid du Colombier 	dlprintf3("[K]copying 0x%lx, offset=(%g,%g)\n", (ulong) cc,
2857dd7cddfSDavid du Colombier 		  fixed2float(-cc->offset.x),
2867dd7cddfSDavid du Colombier 		  fixed2float(-cc->offset.y));
2877dd7cddfSDavid du Colombier 	dlprintf6("   at (%g,%g)+(%d,%d)->(%d,%d)\n",
2887dd7cddfSDavid du Colombier 		  fixed2float(pt.x), fixed2float(pt.y),
2897dd7cddfSDavid du Colombier 		  penum->ftx, penum->fty, x, y);
2907dd7cddfSDavid du Colombier     }
2917dd7cddfSDavid du Colombier #endif
2927dd7cddfSDavid du Colombier     if ((x < penum->ibox.p.x || x + w > penum->ibox.q.x ||
2937dd7cddfSDavid du Colombier 	 y < penum->ibox.p.y || y + h > penum->ibox.q.y) &&
2943ff48bf5SDavid du Colombier 	imaging_dev != (gx_device *) & cdev	/* might be 2nd time around */
2957dd7cddfSDavid du Colombier 	) {			/* Check for the character falling entirely outside */
2967dd7cddfSDavid du Colombier 	/* the clipping region. */
2977dd7cddfSDavid du Colombier 	gx_clip_path *pcpath;
2987dd7cddfSDavid du Colombier 
2997dd7cddfSDavid du Colombier 	if (x >= penum->obox.q.x || x + w <= penum->obox.p.x ||
3007dd7cddfSDavid du Colombier 	    y >= penum->obox.q.y || y + h <= penum->obox.p.y
3017dd7cddfSDavid du Colombier 	    )
3027dd7cddfSDavid du Colombier 	    return 0;		/* nothing to do */
3037dd7cddfSDavid du Colombier 	code = gx_effective_clip_path(pgs, &pcpath);
3047dd7cddfSDavid du Colombier 	if (code < 0)
3057dd7cddfSDavid du Colombier 	    return code;
3067dd7cddfSDavid du Colombier 	gx_make_clip_device(&cdev, gx_cpath_list(pcpath));
3073ff48bf5SDavid du Colombier 	cdev.target = imaging_dev;
3083ff48bf5SDavid du Colombier 	imaging_dev = (gx_device *) & cdev;
3093ff48bf5SDavid du Colombier 	(*dev_proc(imaging_dev, open_device)) (imaging_dev);
3107dd7cddfSDavid du Colombier 	if_debug0('K', "[K](clipping)\n");
3117dd7cddfSDavid du Colombier     }
312*593dc095SDavid du Colombier     gx_set_dev_color(pgs);
3137dd7cddfSDavid du Colombier     /* If an xfont can render this character, use it. */
3147dd7cddfSDavid du Colombier     if (xg != gx_no_xglyph && (xf = cc_pair(cc)->xfont) != 0) {
3157dd7cddfSDavid du Colombier 	int cx = x + fixed2int(cc->offset.x);
3167dd7cddfSDavid du Colombier 	int cy = y + fixed2int(cc->offset.y);
3177dd7cddfSDavid du Colombier 
3187dd7cddfSDavid du Colombier 	/*
3197dd7cddfSDavid du Colombier 	 * Note that we prefer a 1-bit xfont implementation over
3207dd7cddfSDavid du Colombier 	 * a multi-bit cached bitmap.  Eventually we should change
3217dd7cddfSDavid du Colombier 	 * the xfont interface so it can deliver multi-bit bitmaps,
3227dd7cddfSDavid du Colombier 	 * or else implement oversampling for xfonts.
3237dd7cddfSDavid du Colombier 	 */
3247dd7cddfSDavid du Colombier 	if (gs_color_writes_pure(pgs)) {
3257dd7cddfSDavid du Colombier 	    code = (*xf->common.procs->render_char) (xf, xg,
3263ff48bf5SDavid du Colombier 					imaging_dev, cx, cy,
3273ff48bf5SDavid du Colombier 					pdevc->colors.pure, 0);
3287dd7cddfSDavid du Colombier 	    if_debug8('K', "[K]render_char display: xfont=0x%lx, glyph=0x%lx\n\tdev=0x%lx(%s) x,y=%d,%d, color=0x%lx => %d\n",
3293ff48bf5SDavid du Colombier 		      (ulong) xf, (ulong) xg, (ulong) imaging_dev,
3303ff48bf5SDavid du Colombier 		      imaging_dev->dname, cx, cy,
3317dd7cddfSDavid du Colombier 		      (ulong) pdevc->colors.pure, code);
3327dd7cddfSDavid du Colombier 	    if (code == 0)
333*593dc095SDavid du Colombier 		return_check_interrupt(penum->memory, 0);
3347dd7cddfSDavid du Colombier 	}
3357dd7cddfSDavid du Colombier 	/* Can't render directly.  If we don't have a bitmap yet, */
3367dd7cddfSDavid du Colombier 	/* get it from the xfont now. */
3377dd7cddfSDavid du Colombier 	if (!cc_has_bits(cc)) {
3387dd7cddfSDavid du Colombier 	    gx_device_memory mdev;
3397dd7cddfSDavid du Colombier 
340*593dc095SDavid du Colombier 	    gs_make_mem_mono_device(&mdev, dev->memory, imaging_dev);
3417dd7cddfSDavid du Colombier 	    gx_open_cache_device(&mdev, cc);
3427dd7cddfSDavid du Colombier 	    code = (*xf->common.procs->render_char) (xf, xg,
3437dd7cddfSDavid du Colombier 				       (gx_device *) & mdev, cx - x, cy - y,
3447dd7cddfSDavid du Colombier 						     (gx_color_index) 1, 1);
3457dd7cddfSDavid du Colombier 	    if_debug7('K', "[K]render_char to bits: xfont=0x%lx, glyph=0x%lx\n\tdev=0x%lx(%s) x,y=%d,%d => %d\n",
3467dd7cddfSDavid du Colombier 		      (ulong) xf, (ulong) xg, (ulong) & mdev,
3477dd7cddfSDavid du Colombier 		      mdev.dname, cx - x, cy - y, code);
3487dd7cddfSDavid du Colombier 	    if (code != 0)
349*593dc095SDavid du Colombier 		return_check_interrupt(penum->memory, 1);
3507dd7cddfSDavid du Colombier 	    gx_add_char_bits(cc_pair(cc)->font->dir,
3517dd7cddfSDavid du Colombier 			     cc, &scale_log2_1);
3527dd7cddfSDavid du Colombier 	    /* gx_add_char_bits may change width, height, */
3537dd7cddfSDavid du Colombier 	    /* raster, and/or offset.  It's easiest to */
3547dd7cddfSDavid du Colombier 	    /* start over from the top.  Clear xg so that */
3557dd7cddfSDavid du Colombier 	    /* we don't waste time trying render_char again. */
3567dd7cddfSDavid du Colombier 	    xg = gx_no_xglyph;
3577dd7cddfSDavid du Colombier 	    goto top;
3587dd7cddfSDavid du Colombier 	}
3597dd7cddfSDavid du Colombier     }
3607dd7cddfSDavid du Colombier     /*
3617dd7cddfSDavid du Colombier      * No xfont.  Render from the cached bits.  If the cached bits
3627dd7cddfSDavid du Colombier      * have more than 1 bit of alpha, and the color isn't pure or
3637dd7cddfSDavid du Colombier      * the copy_alpha operation fails, construct a single-bit mask
3647dd7cddfSDavid du Colombier      * by taking the high-order alpha bit.
3657dd7cddfSDavid du Colombier      */
3667dd7cddfSDavid du Colombier     bits = cc_bits(cc);
367*593dc095SDavid du Colombier     /* With 4x2 scale, depth == 3.
368*593dc095SDavid du Colombier      * An example is -dTextAlphaBits=4 comparefiles/fonttest.pdf .
369*593dc095SDavid du Colombier      * We need to map 4 bitmap bits to 2 alpha bits.
370*593dc095SDavid du Colombier      */
371*593dc095SDavid du Colombier     depth = (cc_depth(cc) == 3 ? 2 : cc_depth(cc));
3727dd7cddfSDavid du Colombier     if (dev_proc(orig_dev, fill_mask) != gx_default_fill_mask ||
3737dd7cddfSDavid du Colombier 	!lop_no_S_is_T(pgs->log_op)
3747dd7cddfSDavid du Colombier 	) {
3757dd7cddfSDavid du Colombier 	gx_clip_path *pcpath;
3767dd7cddfSDavid du Colombier 
3777dd7cddfSDavid du Colombier 	code = gx_effective_clip_path(pgs, &pcpath);
3787dd7cddfSDavid du Colombier 	if (code >= 0) {
3797dd7cddfSDavid du Colombier 	    code = (*dev_proc(orig_dev, fill_mask))
3807dd7cddfSDavid du Colombier 		(orig_dev, bits, 0, cc_raster(cc), cc->id,
3817dd7cddfSDavid du Colombier 		 x, y, w, h, pdevc, depth, pgs->log_op, pcpath);
3827dd7cddfSDavid du Colombier 	    if (code >= 0)
3837dd7cddfSDavid du Colombier 		goto done;
3847dd7cddfSDavid du Colombier 	}
3857dd7cddfSDavid du Colombier     } else if (gs_color_writes_pure(pgs)) {
3867dd7cddfSDavid du Colombier 	gx_color_index color = pdevc->colors.pure;
3877dd7cddfSDavid du Colombier 
3887dd7cddfSDavid du Colombier 	if (depth > 1) {
3893ff48bf5SDavid du Colombier 	    code = (*dev_proc(imaging_dev, copy_alpha))
3903ff48bf5SDavid du Colombier 		(imaging_dev, bits, 0, cc_raster(cc), cc->id,
3917dd7cddfSDavid du Colombier 		 x, y, w, h, color, depth);
3927dd7cddfSDavid du Colombier 	    if (code >= 0)
393*593dc095SDavid du Colombier 		return_check_interrupt(penum->memory, 0);
3947dd7cddfSDavid du Colombier 	    /* copy_alpha failed, construct a monobit mask. */
395*593dc095SDavid du Colombier 	    bits = compress_alpha_bits(cc, penum->memory->non_gc_memory);
3967dd7cddfSDavid du Colombier 	    if (bits == 0)
3977dd7cddfSDavid du Colombier 		return 1;	/* VMerror, but recoverable */
3987dd7cddfSDavid du Colombier 	}
3993ff48bf5SDavid du Colombier 	code = (*dev_proc(imaging_dev, copy_mono))
400*593dc095SDavid du Colombier 	    (imaging_dev, bits, 0, bitmap_raster(w), gs_no_id,
401*593dc095SDavid du Colombier 	     x, y, w, h, gx_no_color_index, color);
4027dd7cddfSDavid du Colombier 	goto done;
4037dd7cddfSDavid du Colombier     }
4047dd7cddfSDavid du Colombier     if (depth > 1) {		/* Complex color or fill_mask / copy_alpha failed, */
4057dd7cddfSDavid du Colombier 	/* construct a monobit mask. */
406*593dc095SDavid du Colombier 	bits = compress_alpha_bits(cc, penum->memory->non_gc_memory);
4077dd7cddfSDavid du Colombier 	if (bits == 0)
4087dd7cddfSDavid du Colombier 	    return 1;		/* VMerror, but recoverable */
4097dd7cddfSDavid du Colombier 
4107dd7cddfSDavid du Colombier     } {				/* Use imagemask to render the character. */
411*593dc095SDavid du Colombier 	gs_memory_t *mem = penum->memory->non_gc_memory;
4127dd7cddfSDavid du Colombier 	gs_image_enum *pie =
4137dd7cddfSDavid du Colombier 	    gs_image_enum_alloc(mem, "image_char(image_enum)");
4147dd7cddfSDavid du Colombier 	gs_image_t image;
4157dd7cddfSDavid du Colombier 	int iy;
416*593dc095SDavid du Colombier 	uint used, raster = (bits == cc_bits(cc) ? cc_raster(cc)
417*593dc095SDavid du Colombier 			     : bitmap_raster(cc->width) );
418*593dc095SDavid du Colombier 	int code1;
4197dd7cddfSDavid du Colombier 
4207dd7cddfSDavid du Colombier 	if (pie == 0) {
4217dd7cddfSDavid du Colombier 	    if (bits != cc_bits(cc))
422*593dc095SDavid du Colombier 		gs_free_object(mem, bits,
4237dd7cddfSDavid du Colombier 			       "compress_alpha_bits");
4247dd7cddfSDavid du Colombier 	    return 1;		/* VMerror, but recoverable */
4257dd7cddfSDavid du Colombier 	}
4267dd7cddfSDavid du Colombier 	/* Make a matrix that will place the image */
4277dd7cddfSDavid du Colombier 	/* at (x,y) with no transformation. */
4287dd7cddfSDavid du Colombier 	gs_image_t_init_mask(&image, true);
4297dd7cddfSDavid du Colombier #define mat image.ImageMatrix
4307dd7cddfSDavid du Colombier 	gs_make_translation((floatp) - x, (floatp) - y, &mat);
4317dd7cddfSDavid du Colombier 	gs_matrix_multiply(&ctm_only(pgs), &mat, &mat);
4327dd7cddfSDavid du Colombier #undef mat
4337dd7cddfSDavid du Colombier 	image.Width = w;
4347dd7cddfSDavid du Colombier 	image.Height = h;
4357dd7cddfSDavid du Colombier 	image.adjust = false;
4367dd7cddfSDavid du Colombier 	code = gs_image_init(pie, &image, false, pgs);
4377dd7cddfSDavid du Colombier 	switch (code) {
4387dd7cddfSDavid du Colombier 	    case 1:		/* empty image */
4397dd7cddfSDavid du Colombier 		code = 0;
4407dd7cddfSDavid du Colombier 	    default:
4417dd7cddfSDavid du Colombier 		break;
4427dd7cddfSDavid du Colombier 	    case 0:
4437dd7cddfSDavid du Colombier 		for (iy = 0; iy < h && code >= 0; iy++)
444*593dc095SDavid du Colombier 		    code = gs_image_next(pie, bits + iy * raster,
4457dd7cddfSDavid du Colombier 					 (w + 7) >> 3, &used);
4467dd7cddfSDavid du Colombier 	}
447*593dc095SDavid du Colombier 	code1 = gs_image_cleanup_and_free_enum(pie);
448*593dc095SDavid du Colombier 	if (code >= 0 && code1 < 0)
449*593dc095SDavid du Colombier 	    code = code1;
4507dd7cddfSDavid du Colombier     }
4517dd7cddfSDavid du Colombier   done:if (bits != cc_bits(cc))
452*593dc095SDavid du Colombier 	gs_free_object(penum->memory->non_gc_memory, bits, "compress_alpha_bits");
4537dd7cddfSDavid du Colombier     if (code > 0)
4547dd7cddfSDavid du Colombier 	code = 0;
455*593dc095SDavid du Colombier     return_check_interrupt(penum->memory, code);
4567dd7cddfSDavid du Colombier }
4577dd7cddfSDavid du Colombier 
4587dd7cddfSDavid du Colombier /* ------ Image manipulation ------ */
4597dd7cddfSDavid du Colombier 
4607dd7cddfSDavid du Colombier /*
4617dd7cddfSDavid du Colombier  * Compress a mask with 2 or 4 bits of alpha to a monobit mask.
4627dd7cddfSDavid du Colombier  * Allocate and return the address of the monobit mask.
4637dd7cddfSDavid du Colombier  */
4647dd7cddfSDavid du Colombier private byte *
compress_alpha_bits(const cached_char * cc,gs_memory_t * mem)4657dd7cddfSDavid du Colombier compress_alpha_bits(const cached_char * cc, gs_memory_t * mem)
4667dd7cddfSDavid du Colombier {
4677dd7cddfSDavid du Colombier     const byte *data = cc_const_bits(cc);
4687dd7cddfSDavid du Colombier     uint width = cc->width;
4697dd7cddfSDavid du Colombier     uint height = cc->height;
470*593dc095SDavid du Colombier     /* With 4x2 scale, depth == 3.
471*593dc095SDavid du Colombier      * An example is -dTextAlphaBits=4 comparefiles/fonttest.pdf .
472*593dc095SDavid du Colombier      * We need to map 4 bitmap bits to 2 alpha bits.
473*593dc095SDavid du Colombier      */
474*593dc095SDavid du Colombier     int depth = (cc_depth(cc) == 3 ? 2 : cc_depth(cc));
4757dd7cddfSDavid du Colombier     uint sraster = cc_raster(cc);
476*593dc095SDavid du Colombier     uint sskip = sraster - ((width * depth + 7) >> 3);
4777dd7cddfSDavid du Colombier     uint draster = bitmap_raster(width);
4787dd7cddfSDavid du Colombier     uint dskip = draster - ((width + 7) >> 3);
4797dd7cddfSDavid du Colombier     byte *mask = gs_alloc_bytes(mem, draster * height,
4807dd7cddfSDavid du Colombier 				"compress_alpha_bits");
4817dd7cddfSDavid du Colombier     const byte *sptr = data;
4827dd7cddfSDavid du Colombier     byte *dptr = mask;
4837dd7cddfSDavid du Colombier     uint h;
4847dd7cddfSDavid du Colombier 
4857dd7cddfSDavid du Colombier     if (mask == 0)
4867dd7cddfSDavid du Colombier 	return 0;
4877dd7cddfSDavid du Colombier     for (h = height; h; --h) {
4887dd7cddfSDavid du Colombier 	byte sbit = 0x80;
4897dd7cddfSDavid du Colombier 	byte d = 0;
4907dd7cddfSDavid du Colombier 	byte dbit = 0x80;
4917dd7cddfSDavid du Colombier 	uint w;
4927dd7cddfSDavid du Colombier 
4937dd7cddfSDavid du Colombier 	for (w = width; w; --w) {
4947dd7cddfSDavid du Colombier 	    if (*sptr & sbit)
4957dd7cddfSDavid du Colombier 		d += dbit;
496*593dc095SDavid du Colombier 	    if (!(sbit >>= depth))
4977dd7cddfSDavid du Colombier 		sbit = 0x80, sptr++;
498*593dc095SDavid du Colombier 	    if (!(dbit >>= 1)) {
499*593dc095SDavid du Colombier 		*dptr++ = d;
500*593dc095SDavid du Colombier 		dbit = 0x80, d = 0;
501*593dc095SDavid du Colombier 	    }
5027dd7cddfSDavid du Colombier 	}
5037dd7cddfSDavid du Colombier 	if (dbit != 0x80)
5047dd7cddfSDavid du Colombier 	    *dptr++ = d;
5057dd7cddfSDavid du Colombier 	for (w = dskip; w != 0; --w)
5067dd7cddfSDavid du Colombier 	    *dptr++ = 0;
507*593dc095SDavid du Colombier 	if (sbit != 0x80)
508*593dc095SDavid du Colombier 	    ++sptr;
5097dd7cddfSDavid du Colombier 	sptr += sskip;
5107dd7cddfSDavid du Colombier     }
5117dd7cddfSDavid du Colombier     return mask;
5127dd7cddfSDavid du Colombier }
513