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