diff options
Diffstat (limited to 'gs/src/gxhint3.c')
-rw-r--r-- | gs/src/gxhint3.c | 368 |
1 files changed, 195 insertions, 173 deletions
diff --git a/gs/src/gxhint3.c b/gs/src/gxhint3.c index 15f0634d0..f7848b70a 100644 --- a/gs/src/gxhint3.c +++ b/gs/src/gxhint3.c @@ -1,4 +1,4 @@ -/* Copyright (C) 1994, 1995, 1996, 1997 Aladdin Enterprises. All rights reserved. +/* Copyright (C) 1994, 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved. This file is part of Aladdin Ghostscript. @@ -16,7 +16,7 @@ all copies. */ -/* gxhint3.c */ +/*Id: gxhint3.c */ /* Apply hints for Type 1 fonts. */ #include "math_.h" /* for floor in fixed_mult_quo */ #include "gx.h" @@ -24,7 +24,6 @@ #include "gxarith.h" #include "gxfixed.h" #include "gxmatrix.h" -#include "gxchar.h" #include "gxfont.h" #include "gxfont1.h" #include "gxtype1.h" @@ -51,18 +50,20 @@ private void * what we use throughout the Type 1 code; however, "horizontal" and * "vertical" refer to the character space coordinate system. */ -#define hint_vert_lower 1 -#define hint_vert_upper 2 /* must be > lower */ -#define hint_vert (hint_vert_lower | hint_vert_upper) -#define hint_horz_lower 4 -#define hint_horz_upper 8 /* must be > lower */ -#define hint_horz (hint_horz_lower | hint_horz_upper) -#define nearly_axial(dmajor, dminor)\ +#define HINT_VERT_LOWER 1 +#define HINT_VERT_UPPER 2 /* must be > lower */ +#define HINT_VERT (HINT_VERT_LOWER | HINT_VERT_UPPER) +#define HINT_HORZ_LOWER 4 +#define HINT_HORZ_UPPER 8 /* must be > lower */ +#define HINT_HORZ (HINT_HORZ_LOWER | HINT_HORZ_UPPER) +#define NEARLY_AXIAL(dmajor, dminor)\ ((dminor) <= (dmajor) >> 4) -/* Determine which types of hints, if any, are applicable to a given */ -/* line segment. */ -private int near +/* + * Determine which types of hints, if any, are applicable to a given + * line segment. + */ +private int line_hints(const gs_type1_state * pcis, const gs_fixed_point * p0, const gs_fixed_point * p1) { @@ -73,20 +74,19 @@ line_hints(const gs_type1_state * pcis, const gs_fixed_point * p0, int hints; /* - * To figure out which side of the stem we are on, we assume that - * the inside of the filled area is always to the left of the edge, - * i.e., edges moving in -X or +Y in character space are on the - * "upper" side of the stem, while edges moving by +X or -Y are on - * the "lower" side. (See section 3.5 of the Adobe Type 1 Font - * Format book.) + * To figure out which side of the stem we are on, we assume that the + * inside of the filled area is always to the left of the edge, i.e., + * edges moving in -X or +Y in character space are on the "upper" side + * of the stem, while edges moving by +X or -Y are on the "lower" side. + * (See section 3.5 of the Adobe Type 1 Font Format book.) */ /* - * Map the deltas back into character space. This is essentially - * an inverse-distance-transform with the combined matrix, - * but we don't bother to undo the scaling, since it only matters - * for the axiality test and we don't care about situations where - * X and Y scaling are radically different. + * Map the deltas back into character space. This is essentially an + * inverse-distance-transform with the combined matrix, but we don't + * bother to undo the scaling, since it only matters for the axiality + * test and we don't care about situations where X and Y scaling are + * radically different. */ if (xi) dx = -dx; @@ -105,17 +105,17 @@ line_hints(const gs_type1_state * pcis, const gs_fixed_point * p0, * Note that since upper/lower refer to device space, we must * interchange them if the corresponding axis is inverted. */ - if (dy != 0 && nearly_axial(ady, adx)) { - hints = (dy > 0 ? hint_vert_upper : hint_vert_lower); + if (dy != 0 && NEARLY_AXIAL(ady, adx)) { + hints = (dy > 0 ? HINT_VERT_UPPER : HINT_VERT_LOWER); if (xi) - hints ^= (hint_vert_lower | hint_vert_upper); - } else if (dx != 0 && nearly_axial(adx, ady)) { - hints = (dx < 0 ? hint_horz_upper : hint_horz_lower); + hints ^= (HINT_VERT_LOWER | HINT_VERT_UPPER); + } else if (dx != 0 && NEARLY_AXIAL(adx, ady)) { + hints = (dx < 0 ? HINT_HORZ_UPPER : HINT_HORZ_LOWER); if (yi) - hints ^= (hint_horz_lower | hint_horz_upper); + hints ^= (HINT_HORZ_LOWER | HINT_HORZ_UPPER); } else hints = 0; - if_debug7('y', "[y]hint from 0x%lx(%g,%g) to 0x%lx(%g,%g) = %d\n", + if_debug7('y', "[y]hint from 0x%lx(%1.4f,%1.4f) to 0x%lx(%1.4f,%1.4f) = %d\n", (ulong) p0, fixed2float(p0->x), fixed2float(p0->y), (ulong) p1, fixed2float(p1->x), fixed2float(p1->y), hints); @@ -123,57 +123,54 @@ line_hints(const gs_type1_state * pcis, const gs_fixed_point * p0, } /* Apply hints at a point. Optionally return the amount of adjustment. */ -private void near +private void apply_hints_at(gs_type1_state * pcis, int hints, gs_fixed_point * ppt, gs_fixed_point * pdiff) { fixed px = ppt->x, py = ppt->y; - if_debug4('y', "[y]applying hints %d to 0x%lx(%g,%g) ...\n", + if_debug4('y', "[y]applying hints %d to 0x%lx(%1.4f,%1.4f) ...\n", hints, (ulong) ppt, fixed2float(px), fixed2float(py)); - if ((hints & hint_vert) != 0 && + if ((hints & HINT_VERT) != 0 && (pcis->vstem_hints.count & pcis->dotsection_flag) != 0 ) - apply_vstem_hints(pcis, (hints & hint_vert_upper) - - (hints & hint_vert_lower), ppt); - if ((hints & hint_horz) != 0 && + apply_vstem_hints(pcis, (hints & HINT_VERT_UPPER) - + (hints & HINT_VERT_LOWER), ppt); + if ((hints & HINT_HORZ) != 0 && (pcis->hstem_hints.count & pcis->dotsection_flag) != 0 ) - apply_hstem_hints(pcis, (hints & hint_horz_upper) - - (hints & hint_horz_lower), ppt); + apply_hstem_hints(pcis, (hints & HINT_HORZ_UPPER) - + (hints & HINT_HORZ_LOWER), ppt); if (pdiff != NULL) pdiff->x = ppt->x - px, pdiff->y = ppt->y - py; /* Here is where we would round *ppt to the nearest quarter-pixel */ /* if we wanted to. */ - if_debug2('y', "[y] ... => (%g,%g)\n", + if_debug2('y', "[y] ... => (%1.4f,%1.4f)\n", fixed2float(ppt->x), fixed2float(ppt->y)); } -#ifdef DEBUG -private void near -add_hint_diff_proc(gs_fixed_point * ppt, fixed dx, fixed dy) +/* Add a hint delta to a point. */ +#ifndef DEBUG +inline +#endif +private void +add_hint_diff(gs_fixed_point * ppt, gs_fixed_point delta) { - if_debug7('y', "[y]adding diff (%g,%g) to 0x%lx(%g,%g) => (%g,%g)\n", - fixed2float(dx), fixed2float(dy), (ulong) ppt, + if_debug7('y', "[y]adding diff (%1.4f,%1.4f) to 0x%lx(%1.4f,%1.4f) => (%1.4f,%1.4f)\n", + fixed2float(delta.x), fixed2float(delta.y), (ulong) ppt, fixed2float(ppt->x), fixed2float(ppt->y), - fixed2float(ppt->x + dx), - fixed2float(ppt->y + dy)); - ppt->x += dx; - ppt->y += dy; + fixed2float(ppt->x + delta.x), fixed2float(ppt->y + delta.y)); + ppt->x += delta.x; + ppt->y += delta.y; } -#define add_hint_dxdy(pt, dx,dy)\ - add_hint_diff_proc(&(pt), dx, dy) -#else -#define add_hint_dxdy(pt, dx, dy)\ - (pt).x += (dx), (pt).y += (dy) -#endif -#define add_hint_diff(pt, diff)\ - add_hint_dxdy(pt, (diff).x, (diff).y) /* Test whether a line is null. */ -#define line_is_null(p0, p1)\ - (any_abs((p1).x - (p0).x) + any_abs((p1).y - (p0).y) < fixed_epsilon * 4) +inline private bool +line_is_null(gs_fixed_point p0, gs_fixed_point p1) +{ + return (any_abs(p1.x - p0.x) + any_abs(p1.y - p0.y) < fixed_epsilon * 4); +} /* * Adjust the other control points of a curve proportionately when moving @@ -193,7 +190,7 @@ scale_delta(fixed diff, fixed dv, fixed lv, bool nearer) * lv = 0 is implausible, but we have to allow for it. */ if (lv == 0) - return (nearer ? diff : (fixed) 0); + return (nearer ? diff : fixed_0); if (lv < 0) lv = -lv, dv = -dv; if (dv < 0) @@ -203,7 +200,7 @@ scale_delta(fixed diff, fixed dv, fixed lv, bool nearer) * the lv = 0 case. */ if (dv >= lv) - return (nearer ? diff : (fixed) 0); + return (nearer ? diff : fixed_0); else return fixed_mult_quo(diff, dv, lv); } @@ -214,13 +211,14 @@ adjust_curve_start(curve_segment * pcseg, const gs_fixed_point * pdiff) fixed end_x = pcseg->pt.x, end_y = pcseg->pt.y; const segment *prev = pcseg->prev; fixed lx = end_x - (prev->pt.x - dx), ly = end_y - (prev->pt.y - dy); - - add_hint_dxdy(pcseg->p1, - scale_delta(end_x - pcseg->p1.x, dx, lx, true), - scale_delta(end_y - pcseg->p1.y, dy, ly, true)); - add_hint_dxdy(pcseg->p2, - scale_delta(end_x - pcseg->p2.x, dx, lx, false), - scale_delta(end_y - pcseg->p2.y, dy, ly, false)); + gs_fixed_point delta; + + delta.x = scale_delta(end_x - pcseg->p1.x, dx, lx, true); + delta.y = scale_delta(end_y - pcseg->p1.y, dy, ly, true); + add_hint_diff(&pcseg->p1, delta); + delta.x = scale_delta(end_x - pcseg->p2.x, dx, lx, false); + delta.y = scale_delta(end_y - pcseg->p2.y, dy, ly, false); + add_hint_diff(&pcseg->p2, delta); } private void adjust_curve_end(curve_segment * pcseg, const gs_fixed_point * pdiff) @@ -229,13 +227,14 @@ adjust_curve_end(curve_segment * pcseg, const gs_fixed_point * pdiff) const segment *prev = pcseg->prev; fixed start_x = prev->pt.x, start_y = prev->pt.y; fixed lx = pcseg->pt.x - dx - start_x, ly = pcseg->pt.y - dy - start_y; - - add_hint_dxdy(pcseg->p1, - scale_delta(pcseg->p1.x - start_x, dx, lx, false), - scale_delta(pcseg->p1.y - start_y, dy, ly, false)); - add_hint_dxdy(pcseg->p2, - scale_delta(pcseg->p2.x - start_x, dx, lx, true), - scale_delta(pcseg->p2.y - start_y, dy, ly, true)); + gs_fixed_point delta; + + delta.x = scale_delta(pcseg->p1.x - start_x, dx, lx, false); + delta.y = scale_delta(pcseg->p1.y - start_y, dy, ly, false); + add_hint_diff(&pcseg->p1, delta); + delta.x = scale_delta(pcseg->p2.x - start_x, dx, lx, true); + delta.y = scale_delta(pcseg->p2.y - start_y, dy, ly, true); + add_hint_diff(&pcseg->p2, delta); } /* @@ -258,7 +257,7 @@ apply_final_hint(segment * pseg_last, const gs_fixed_point * pdiff) case s_line_close: if (!line_is_null(prev->pt, pseg->pt)) return; - add_hint_diff(prev->pt, *pdiff); + add_hint_diff(&prev->pt, *pdiff); break; default: /* s_start */ return; @@ -267,6 +266,102 @@ apply_final_hint(segment * pseg_last, const gs_fixed_point * pdiff) } /* + * Handle the end of the subpath wrapping around to the start. This is + * ugly, messy code that we should be able to improve, but I neither see how + * to do it nor understand how the IBM Type 1 rasterizer can produce such + * good results without doing anything like this. + * + * This is a separate procedure only for readability: it is only called + * from one place in the next procedure. + */ +private void +apply_wrapped_hints(gs_type1_state * pcis, subpath * psub, segment * pseg, + int hints, gs_fixed_point * pdiff) +{ + /* Some fonts don't use closepath when they should.... */ + fixed ctemp; + bool closed = + (pseg->type == s_line_close || + ((ctemp = pseg->pt.x - psub->pt.x, + any_abs(ctemp) < float2fixed(0.1)) && + (ctemp = pseg->pt.y - psub->pt.y, + any_abs(ctemp) < float2fixed(0.1)))); + segment *const pfirst = psub->next; + int hints_first = pcis->hints_initial; + + if (closed) { + /* + * Apply the union of the hints at both the end (pseg) and the start + * (psub) of the subpath. Note that we have already applied hints + * at the end, and hints_first at the start. However, because of + * hint replacement, the points might differ even if hints == + * hints_first. In this case, the initial hints take priority, + * because the initial segment was laid down first. + */ + int do_x, do_y; + gs_fixed_point diff2; + + if_debug2('y', "[y]closing closed, hints=%d, hints_first=%d\n", + hints, hints_first); + if (pcis->fh.axes_swapped) + do_x = HINT_HORZ, do_y = HINT_VERT; + else + do_x = HINT_VERT, do_y = HINT_HORZ; + { + /* Apply hints_first - hints to the end. */ + int hints_end = hints_first & ~hints; + + diff2.x = + (hints_end & do_x ? + psub->pt.x - pcis->unmoved_start.x : 0); + diff2.y = + (hints_end & do_y ? + psub->pt.y - pcis->unmoved_start.y : 0); + } + { + /* Apply hints - hints_first to the start. */ + int hints_start = hints & ~hints_first; + + pdiff->x = + (hints_start & do_x ? + pseg->pt.x - pcis->unmoved_end.x : 0); + pdiff->y = + (hints_start & do_y ? + pseg->pt.y - pcis->unmoved_end.y : 0); + } + add_hint_diff(&pseg->pt, diff2); + apply_final_hint(pseg, &diff2); + add_hint_diff(&psub->pt, *pdiff); + /* + * Now align the initial and final points, to deal with hint + * replacement. + */ + diff2.x = psub->pt.x - pseg->pt.x; + diff2.y = psub->pt.y - pseg->pt.y; + if (diff2.x || diff2.y) { + /* Force the points to coincide. */ + pseg->pt = psub->pt; + apply_final_hint(pseg, &diff2); + } + } else { + int hints_close = + line_hints(pcis, &pcis->unmoved_end, &pcis->unmoved_start); + + hints_close &= ~(hints | hints_first); + if_debug3('y', "[y]closing open, hints=%d, hints_close=%d, hints_first=%d\n", + hints, hints_close, hints_first); + if (hints_close) { + apply_hints_at(pcis, hints_close, &pseg->pt, pdiff); + apply_final_hint(pseg, pdiff); + apply_hints_at(pcis, hints_close, &psub->pt, pdiff); + } else + pdiff->x = pdiff->y = 0; + } + if (pfirst->type == s_curve) + adjust_curve_start((curve_segment *) pfirst, pdiff); +} + +/* * Apply hints along a subpath. If closing is true, consider the subpath * closed; if not, we may add more to the subpath later. In the latter case, * don't do anything if the subpath is closed, because we already applied @@ -276,12 +371,8 @@ void type1_apply_path_hints(gs_type1_state * pcis, bool closing, gx_path * ppath) { segment *pseg = pcis->hint_next; - -#define pseg_curve ((curve_segment *)pseg) segment *pnext; - -#define pnext_curve ((curve_segment *)pnext) - subpath *psub = ppath->current_subpath; + subpath *const psub = ppath->current_subpath; /* * hints holds the set of hints that have already been applied (if @@ -299,7 +390,8 @@ type1_apply_path_hints(gs_type1_state * pcis, bool closing, gx_path * ppath) */ if (pcis->init_done < 0) return; - if (pseg == 0) { /* Start at the beginning of the subpath. */ + if (pseg == 0) { + /* Start at the beginning of the subpath. */ if (psub == 0) return; if (psub->is_closed && !closing) @@ -316,13 +408,15 @@ type1_apply_path_hints(gs_type1_state * pcis, bool closing, gx_path * ppath) for (; (pnext = pseg->next) != 0; pseg = pnext) { int hints_next; - /* Apply hints to the end of the previous segment (pseg) */ - /* and the beginning of this one (pnext). */ + /* + * Apply hints to the end of the previous segment (pseg) + * and the beginning of this one (pnext). + */ gs_fixed_point dseg; switch (pnext->type) { - case s_curve: - { + case s_curve:{ + curve_segment *const pnext_curve = (curve_segment *) pnext; int hints_first = line_hints(pcis, &pcis->unmoved_end, &pnext_curve->p1) & ~hints; @@ -336,23 +430,22 @@ type1_apply_path_hints(gs_type1_state * pcis, bool closing, gx_path * ppath) dseg.x = dseg.y = 0; diff2.x = pseg->pt.x - pcis->unmoved_end.x; diff2.y = pseg->pt.y - pcis->unmoved_end.y; - hints_next = line_hints(pcis, &pnext_curve->p2, - &pnext->pt); + hints_next = line_hints(pcis, &pnext_curve->p2, &pnext->pt); adjust_curve_start(pnext_curve, &diff2); if (hints_next) { - apply_hints_at(pcis, hints_next, &pnext_curve->p2, - &diff); + apply_hints_at(pcis, hints_next, &pnext_curve->p2, &diff); pcis->unmoved_end = pnext->pt; - add_hint_diff(pnext->pt, diff); + add_hint_diff(&pnext->pt, diff); } else pcis->unmoved_end = pnext->pt; + break; } - break; case s_line_close: /* Undo any initial hints propagated to the end. */ pnext->pt = pcis->unmoved_start; default: /* s_line, s_line_close */ - if (line_is_null(pnext->pt, pcis->unmoved_end)) { /* This is a null line, just move it. */ + if (line_is_null(pnext->pt, pcis->unmoved_end)) { + /* This is a null line, just move it. */ hints_next = hints; dseg.x = dseg.y = 0; /* don't move p2 again */ } else { @@ -371,88 +464,17 @@ type1_apply_path_hints(gs_type1_state * pcis, bool closing, gx_path * ppath) apply_hints_at(pcis, hints_next, &pnext->pt, NULL); } if (pseg->type == s_curve) - adjust_curve_end(pseg_curve, &dseg); + adjust_curve_end((curve_segment *) pseg, &dseg); hints = hints_next; } - if (closing) { /* Handle the end of the subpath wrapping around to the start. */ - /* This is ugly, messy code that we can surely improve. */ - fixed ctemp; - - /* Some fonts don't use closepath when they should.... */ - bool closed = - (pseg->type == s_line_close || - (ctemp = pseg->pt.x - psub->pt.x, - any_abs(ctemp) < float2fixed(0.1)) || - (ctemp = pseg->pt.y - psub->pt.y, - any_abs(ctemp) < float2fixed(0.1))); - segment *pfirst = psub->next; - -#define pfirst_curve ((curve_segment *)pfirst) - int hints_first = pcis->hints_initial; - - if (closed) { /* - * Apply the union of the hints at both the end - * (pseg) and the start (psub) of the subpath. Note - * that we have already applied hints at the end, - * and hints_first at the start. - */ - int do_x, do_y; - gs_fixed_point diff2; - - if (pcis->fh.axes_swapped) - do_x = hint_horz, do_y = hint_vert; - else - do_x = hint_vert, do_y = hint_horz; - - { /* Apply hints_first - hints to the end. */ - int hints_end = hints_first & ~hints; - - diff2.x = - (hints_end & do_x ? - psub->pt.x - pcis->unmoved_start.x : 0); - diff2.y = - (hints_end & do_y ? - psub->pt.y - pcis->unmoved_start.y : 0); - } - - { /* Apply hints - hints_first to the start. */ - int hints_start = hints & ~hints_first; - - diff.x = - (hints_start & do_x ? - pseg->pt.x - pcis->unmoved_end.x : 0); - diff.y = - (hints_start & do_y ? - pseg->pt.y - pcis->unmoved_end.y : 0); - } - - add_hint_diff(pseg->pt, diff2); - apply_final_hint(pseg, &diff2); - add_hint_diff(psub->pt, diff); - } else { - int hints_close = - line_hints(pcis, &pcis->unmoved_end, - &pcis->unmoved_start); - - hints_close &= ~(hints | hints_first); - if (hints_close) { - apply_hints_at(pcis, hints_close, &pseg->pt, &diff); - apply_final_hint(pseg, &diff); - apply_hints_at(pcis, hints_close, &psub->pt, &diff); - } else - diff.x = diff.y = 0; - } - if (pfirst->type == s_curve) - adjust_curve_start(pfirst_curve, &diff); + if (closing) { + apply_wrapped_hints(pcis, psub, pseg, hints, &diff); pcis->hint_next = 0; pcis->hints_pending = 0; -#undef pfirst_curve } else { pcis->hint_next = pseg; pcis->hints_pending = hints; } -#undef pseg_curve -#undef pnext_curve } /* ------ Individual hints ------ */ @@ -475,7 +497,7 @@ apply_vstem_hints(gs_type1_state * pcis, int dy, gs_fixed_point * ppt) const stem_hint *ph = search_hints(&pcis->vstem_hints, *pv); if (ph != 0) { - if_debug3('Y', "[Y]use vstem %d: %g (%s)", + if_debug3('Y', "[Y]use vstem %d: %1.4f (%s)", (int)(ph - &pcis->vstem_hints.data[0]), fixed2float(*pv), (dy == 0 ? "?!" : dy > 0 ? "upper" : "lower")); @@ -486,7 +508,7 @@ apply_vstem_hints(gs_type1_state * pcis, int dy, gs_fixed_point * ppt) } #endif *pv += (dy > 0 ? ph->dv1 : ph->dv0); - if_debug1('Y', " -> %g\n", fixed2float(*pv)); + if_debug1('Y', " -> %1.4f\n", fixed2float(*pv)); } } @@ -497,7 +519,7 @@ apply_hstem_hints(gs_type1_state * pcis, int dx, gs_fixed_point * ppt) const stem_hint *ph = search_hints(&pcis->hstem_hints, *pv); if (ph != 0) { - if_debug3('Y', "[Y]use hstem %d: %g (%s)", + if_debug3('Y', "[Y]use hstem %d: %1.4f (%s)", (int)(ph - &pcis->hstem_hints.data[0]), fixed2float(*pv), (dx == 0 ? "?!" : dx > 0 ? "upper" : "lower")); @@ -508,7 +530,7 @@ apply_hstem_hints(gs_type1_state * pcis, int dx, gs_fixed_point * ppt) } #endif *pv += (dx > 0 ? ph->dv1 : ph->dv0); - if_debug1('Y', " -> %g\n", fixed2float(*pv)); + if_debug1('Y', " -> %1.4f\n", fixed2float(*pv)); } } @@ -519,12 +541,12 @@ search_hints(stem_hint_table * psht, fixed v) const stem_hint *table = &psht->data[0]; const stem_hint *ph = table + psht->current; - if (v >= ph->v0 && v <= ph->v1) + if (v >= ph->v0 && v <= ph->v1 && ph->active) return ph; /* We don't bother with binary or even up/down search, */ /* because there won't be very many hints. */ for (ph = &table[psht->count]; --ph >= table;) - if (v >= ph->v0 && v <= ph->v1) { + if (v >= ph->v0 && v <= ph->v1 && ph->active) { psht->current = ph - table; return ph; } |