Example #1
0
/* We implement get_params to provide a way to read out the bounding box. */
static int
bbox_get_params(gx_device * dev, gs_param_list * plist)
{
    gx_device_bbox *const bdev = (gx_device_bbox *) dev;
    gs_fixed_rect fbox;
    int code = gx_forward_get_params(dev, plist);
    gs_param_float_array bba;
    float bbox[4];

    if (code < 0)
	return code;
    /*
     * We might be calling get_params before the device has been
     * initialized: in this case, box_proc_data = 0.
     */
    if (bdev->box_proc_data == 0)
	fbox = bdev->bbox;
    else
	BBOX_GET_BOX(bdev, &fbox);
    bbox[0] = fixed2float(fbox.p.x);
    bbox[1] = fixed2float(fbox.p.y);
    bbox[2] = fixed2float(fbox.q.x);
    bbox[3] = fixed2float(fbox.q.y);
    bba.data = bbox, bba.size = 4, bba.persistent = false;
    code = param_write_float_array(plist, "PageBoundingBox", &bba);
    if (code < 0)
        return code;
    code = param_write_bool(plist, "WhiteIsOpaque", &bdev->white_is_opaque);
    return code;
}
Example #2
0
int
gs_currentpoint(gs_state *pgs, gs_point *ppt)
{   gs_fixed_point pt;
    int code = gx_path_current_point(pgs->path, &pt);
    if ( code < 0 ) return code;
    return gs_itransform(pgs, fixed2float(pt.x), fixed2float(pt.y), ppt);
}
Example #3
0
int
gs_upathbbox(gs_state * pgs, gs_rect * pbox, bool include_moveto)
{
    gs_fixed_rect fbox;		/* box in device coordinates */
    gs_rect dbox;
    int code = gx_path_bbox_set(pgs->path, &fbox);

    if (code < 0)
	return code;
    /* If the path ends with a moveto and include_moveto is true, */
    /* include the moveto in the bounding box. */
    if (path_last_is_moveto(pgs->path) && include_moveto) {
	gs_fixed_point pt;

	code = gx_path_current_point_inline(pgs->path, &pt);
        if (code < 0)
	    return code;
	if (pt.x < fbox.p.x)
	    fbox.p.x = pt.x;
	if (pt.y < fbox.p.y)
	    fbox.p.y = pt.y;
	if (pt.x > fbox.q.x)
	    fbox.q.x = pt.x;
	if (pt.y > fbox.q.y)
	    fbox.q.y = pt.y;
    }
    /* Transform the result back to user coordinates. */
    dbox.p.x = fixed2float(fbox.p.x);
    dbox.p.y = fixed2float(fbox.p.y);
    dbox.q.x = fixed2float(fbox.q.x);
    dbox.q.y = fixed2float(fbox.q.y);
    return gs_bbox_transform_inverse(&dbox, &ctm_only(pgs), pbox);
}
Example #4
0
/* May call beginpath, moveto, lineto, closepath, endpath. */
int
gdev_vector_write_polygon(gx_device_vector * vdev, const gs_fixed_point * points,
                          uint count, bool close, gx_path_type_t type)
{
    int code = 0;

    if (type != gx_path_type_none &&
        (code = (*vdev_proc(vdev, beginpath)) (vdev, type)) < 0
        )
        return code;
    if (count > 0) {
        double x = fixed2float(points[0].x) / vdev->scale.x, y = fixed2float(points[0].y) / vdev->scale.y;
        double x_start = x, y_start = y, x_prev, y_prev;
        uint i;

        code = (*vdev_proc(vdev, moveto))
            (vdev, 0.0, 0.0, x, y, type);
        if (code >= 0)
            for (i = 1; i < count && code >= 0; ++i) {
                x_prev = x, y_prev = y;
                code = (*vdev_proc(vdev, lineto))
                    (vdev, x_prev, y_prev,
                     (x = fixed2float(points[i].x) / vdev->scale.x),
                     (y = fixed2float(points[i].y) / vdev->scale.y),
                     type);
            }
        if (code >= 0 && close)
            code = (*vdev_proc(vdev, closepath))
                (vdev, x, y, x_start, y_start, type);
    }
    return (code >= 0 && type != gx_path_type_none ?
            (*vdev_proc(vdev, endpath)) (vdev, type) : code);
}
Example #5
0
/*
 * Put a segment of an enumerated path on the output file.
 * pe_op is assumed to be valid and non-zero.
 */
int
gdev_vector_dopath_segment(gdev_vector_dopath_state_t *state, int pe_op,
                           gs_fixed_point vs[3])
{
    gx_device_vector *vdev = state->vdev;
    const gs_matrix *const pmat = &state->scale_mat;
    gs_point vp[3];
    int code;

    switch (pe_op) {
        case gs_pe_moveto:
            code = gs_point_transform_inverse(fixed2float(vs[0].x),
                                       fixed2float(vs[0].y), pmat, &vp[0]);
            if (code < 0)
                return code;
            if (state->first)
                state->start = vp[0], state->first = false;
            code = vdev_proc(vdev, moveto)
                (vdev, 0/*unused*/, 0/*unused*/, vp[0].x, vp[0].y,
                 state->type);
            state->prev = vp[0];
            break;
        case gs_pe_lineto:
        case gs_pe_gapto: /* FIXME */
            code = gs_point_transform_inverse(fixed2float(vs[0].x),
                                       fixed2float(vs[0].y), pmat, &vp[0]);
            if (code < 0)
                return code;
            code = vdev_proc(vdev, lineto)
                (vdev, state->prev.x, state->prev.y, vp[0].x, vp[0].y,
                 state->type);
            state->prev = vp[0];
            break;
        case gs_pe_curveto:
            code = gs_point_transform_inverse(fixed2float(vs[0].x),
                                       fixed2float(vs[0].y), pmat, &vp[0]);
            if (code < 0)
                return code;
            code = gs_point_transform_inverse(fixed2float(vs[1].x),
                                       fixed2float(vs[1].y), pmat, &vp[1]);
            if (code < 0)
                return code;
            gs_point_transform_inverse(fixed2float(vs[2].x),
                                       fixed2float(vs[2].y), pmat, &vp[2]);
            code = vdev_proc(vdev, curveto)
                (vdev, state->prev.x, state->prev.y, vp[0].x, vp[0].y,
                 vp[1].x, vp[1].y, vp[2].x, vp[2].y, state->type);
            state->prev = vp[2];
            break;
        case gs_pe_closepath:
            code = vdev_proc(vdev, closepath)
                (vdev, state->prev.x, state->prev.y, state->start.x,
                 state->start.y, state->type);
            state->prev = state->start;
            break;
        default:		/* can't happen */
            return -1;
    }
    return code;
}
Example #6
0
int
gs_reversepath(gs_state * pgs)
{
    gx_path *ppath = pgs->path;
    gx_path rpath;
    int code;

    gx_path_init_local(&rpath, ppath->memory);
    code = gx_path_copy_reversed(ppath, &rpath);
    if (code < 0) {
	gx_path_free(&rpath, "gs_reversepath");
	return code;
    }
    if (pgs->current_point_valid) {
	/* Not empty. */
	gx_setcurrentpoint(pgs, fixed2float(rpath.position.x), 
				fixed2float(rpath.position.y));
	if (rpath.first_subpath != 0) {
	    pgs->subpath_start.x = fixed2float(rpath.segments->contents.subpath_current->pt.x);
	    pgs->subpath_start.y = fixed2float(rpath.segments->contents.subpath_current->pt.y);
	}
    }
    gx_path_assign_free(ppath, &rpath);
    return 0;
}
Example #7
0
/* Get the metrics (l.s.b. and width) from the Type 1 interpreter. */
void
type1_cis_get_metrics(const gs_type1_state * pcis, double psbw[4])
{
    psbw[0] = fixed2float(pcis->lsb.x);
    psbw[1] = fixed2float(pcis->lsb.y);
    psbw[2] = fixed2float(pcis->width.x);
    psbw[3] = fixed2float(pcis->width.y);
}
Example #8
0
static void
gx_print_segment(const segment * pseg)
{
    double px = fixed2float(pseg->pt.x);
    double py = fixed2float(pseg->pt.y);
    char out[80];

    sprintf(out, "0x%lx<0x%lx,0x%lx>:%u",
         (ulong) pseg, (ulong) pseg->prev, (ulong) pseg->next, pseg->notes);
    switch (pseg->type) {
        case s_start:{
                const subpath *const psub = (const subpath *)pseg;

                dprintf5("   %1.4f %1.4f moveto\t%% %s #curves=%d last=0x%lx\n",
                         px, py, out, psub->curve_count, (ulong) psub->last);
                break;
            }
        case s_curve:{
                const curve_segment *const pcur = (const curve_segment *)pseg;

                dprintf7("   %1.4f %1.4f %1.4f %1.4f %1.4f %1.4f curveto\t%% %s\n",
                         fixed2float(pcur->p1.x), fixed2float(pcur->p1.y),
                         fixed2float(pcur->p2.x), fixed2float(pcur->p2.y),
                         px, py, out);
                break;
            }
        case s_line:
            dprintf3("   %1.4f %1.4f lineto\t%% %s\n", px, py, out);
            break;
        case s_gap:
            dprintf3("   %1.4f %1.4f gapto\t%% %s\n", px, py, out);
            break;
        case s_dash:{
                const dash_segment *const pd = (const dash_segment *)pseg;

                dprintf5("   %1.4f %1.4f %1.4f  %1.4f dash\t%% %s\n",
                         fixed2float(pd->pt.x), fixed2float(pd->pt.y),
                         fixed2float(pd->tangent.x),fixed2float(pd->tangent.y),
                         out);
                break;
            }
        case s_line_close:{
                const line_close_segment *const plc =
                (const line_close_segment *)pseg;

                dprintf4("   closepath\t%% %s %1.4f %1.4f 0x%lx\n",
                         out, px, py, (ulong) (plc->sub));
                break;
            }
        default:
            dprintf4("   %1.4f %1.4f <type 0x%x>\t%% %s\n",
                     px, py, pseg->type, out);
    }
}
Example #9
0
int
gx_setcurrentpoint_from_path(gs_imager_state *pis, gx_path *path)
{
    gs_point pt;

    pt.x = fixed2float(path->position.x);
    pt.y = fixed2float(path->position.y);
    gx_setcurrentpoint(pis, pt.x, pt.y);
    pis->current_point_valid = true;
    return 0;
}
Example #10
0
/* Print a matrix */
static void
trace_matrix_fixed(const gs_matrix_fixed * pmat)
{
    trace_matrix((const gs_matrix *)pmat);
    if (pmat->txy_fixed_valid) {
        dprintf2("\t\tt_fixed: [%6g %6g]\n",
                 fixed2float(pmat->tx_fixed),
                 fixed2float(pmat->ty_fixed));
    } else {
        dputs("\t\tt_fixed not valid\n");
    }
}
Example #11
0
/* Make a gs_matrix_fixed from a gs_matrix. */
int
gs_matrix_fixed_from_matrix(gs_matrix_fixed *pfmat, const gs_matrix *pmat)
{
    *(gs_matrix *)pfmat = *pmat;
    if (f_fits_in_fixed(pmat->tx) && f_fits_in_fixed(pmat->ty)) {
        pfmat->tx = fixed2float(pfmat->tx_fixed = float2fixed(pmat->tx));
        pfmat->ty = fixed2float(pfmat->ty_fixed = float2fixed(pmat->ty));
        pfmat->txy_fixed_valid = true;
    } else {
        pfmat->txy_fixed_valid = false;
    }
    return 0;
}
Example #12
0
/* Record the side bearing and character width. */
int
gs_type1_sbw(gs_type1_state * pcis, fixed lsbx, fixed lsby, fixed wx, fixed wy)
{
    if (!pcis->sb_set)
	pcis->lsb.x = lsbx, pcis->lsb.y = lsby,
	    pcis->sb_set = true;	/* needed for accented chars */
    if (!pcis->width_set)
	pcis->width.x = wx, pcis->width.y = wy,
	    pcis->width_set = true;
    if_debug4('1', "[1]sb=(%g,%g) w=(%g,%g)\n",
	      fixed2float(pcis->lsb.x), fixed2float(pcis->lsb.y),
	      fixed2float(pcis->width.x), fixed2float(pcis->width.y));
    return 0;
}
Example #13
0
int main(int argc, char* argv[])
{
	int i;
	for (i = 1000; i > 0; --i) {
		float f1 = fixed2float(fixsqrt(int2fixed(i)));
		float f2 = sqrtf(i);
		float diff = f1 - f2;
		printf("%d\t%.2f\t%.2f\t%.2f\n", i, f1, f2, diff);
		assert(fabsf(diff) < fixed2float(2));
	}
	TIMEIT(1000, for (i = 1000; i > 0; --i) fixsqrt(int2fixed(i)));
	system("pause");
	return 0;
}
Example #14
0
static inline int 
gs_distance_transform_compat(floatp x, floatp y, const gs_matrix_fixed *m, gs_point *pt)
{
#if !PRECISE_CURRENTPOINT
    gs_fixed_point p;
    int code = gs_distance_transform2fixed(m, x, y, &p);

    if (code < 0)
	return code;
    pt->x = fixed2float(p.x);
    pt->y = fixed2float(p.y);
    return 0;
#else
    return gs_distance_transform(x, y, (const gs_matrix *)m, pt);
#endif
}
Example #15
0
void
xps_bounds_in_user_space(xps_context_t *ctx, gs_rect *ubox)
{
    gx_clip_path *clip_path;
    gs_rect dbox;
    int code;

    code = gx_effective_clip_path(ctx->pgs, &clip_path);
    if (code < 0)
        gs_warn("gx_effective_clip_path failed");

    dbox.p.x = fixed2float(clip_path->outer_box.p.x);
    dbox.p.y = fixed2float(clip_path->outer_box.p.y);
    dbox.q.x = fixed2float(clip_path->outer_box.q.x);
    dbox.q.y = fixed2float(clip_path->outer_box.q.y);
    gs_bbox_transform_inverse(&dbox, &ctm_only(ctx->pgs), ubox);
}
Example #16
0
/* Set the bounding box for the current path. */
int
gs_setbbox(gs_state * pgs, double llx, double lly, double urx, double ury)
{
    gs_rect ubox, dbox;
    gs_fixed_rect obox, bbox;
    gx_path *ppath = pgs->path;
    int code;

    if (llx > urx || lly > ury)
        return_error(gs_error_rangecheck);
    /* Transform box to device coordinates. */
    ubox.p.x = llx;
    ubox.p.y = lly;
    ubox.q.x = urx;
    ubox.q.y = ury;
    if ((code = gs_bbox_transform(&ubox, &ctm_only(pgs), &dbox)) < 0)
        return code;
    /* Round the corners in opposite directions. */
    /* Because we can't predict the magnitude of the dbox values, */
    /* we add/subtract the slop after fixing. */
    if (dbox.p.x < fixed2float(min_fixed + box_rounding_slop_fixed) ||
        dbox.p.y < fixed2float(min_fixed + box_rounding_slop_fixed) ||
        dbox.q.x >= fixed2float(max_fixed - box_rounding_slop_fixed + fixed_epsilon) ||
        dbox.q.y >= fixed2float(max_fixed - box_rounding_slop_fixed + fixed_epsilon)
        )
        return_error(gs_error_limitcheck);
    bbox.p.x =
        (fixed) floor(dbox.p.x * fixed_scale) - box_rounding_slop_fixed;
    bbox.p.y =
        (fixed) floor(dbox.p.y * fixed_scale) - box_rounding_slop_fixed;
    bbox.q.x =
        (fixed) ceil(dbox.q.x * fixed_scale) + box_rounding_slop_fixed;
    bbox.q.y =
        (fixed) ceil(dbox.q.y * fixed_scale) + box_rounding_slop_fixed;
    if (gx_path_bbox_set(ppath, &obox) >= 0) {	/* Take the union of the bboxes. */
        ppath->bbox.p.x = min(obox.p.x, bbox.p.x);
        ppath->bbox.p.y = min(obox.p.y, bbox.p.y);
        ppath->bbox.q.x = max(obox.q.x, bbox.q.x);
        ppath->bbox.q.y = max(obox.q.y, bbox.q.y);
    } else {			/* empty path *//* Just set the bbox. */
        ppath->bbox = bbox;
    }
    ppath->bbox_set = 1;
    return 0;
}
Example #17
0
int
gs_clippath(gs_state * pgs)
{
    gx_path cpath;
    int code;

    gx_path_init_local(&cpath, pgs->path->memory);
    code = gx_cpath_to_path(pgs->clip_path, &cpath);
    if (code >= 0) {
	code = gx_path_assign_free(pgs->path, &cpath);
	pgs->current_point.x = fixed2float(pgs->path->position.x);
	pgs->current_point.y = fixed2float(pgs->path->position.y);
	pgs->current_point_valid = true;
    }
    if (code < 0)
	gx_path_free(&cpath, "gs_clippath");
    return code;
}
Example #18
0
/* Read back the bounding box in 1/72" units. */
void
gx_device_bbox_bbox(gx_device_bbox * dev, gs_rect * pbbox)
{
    gs_fixed_rect bbox;

    BBOX_GET_BOX(dev, &bbox);
    if (bbox.p.x > bbox.q.x || bbox.p.y > bbox.q.y) {
	/* Nothing has been written on this page. */
	pbbox->p.x = pbbox->p.y = pbbox->q.x = pbbox->q.y = 0;
    } else {
	gs_rect dbox;
	gs_matrix mat;

	dbox.p.x = fixed2float(bbox.p.x);
	dbox.p.y = fixed2float(bbox.p.y);
	dbox.q.x = fixed2float(bbox.q.x);
	dbox.q.y = fixed2float(bbox.q.y);
	gs_deviceinitialmatrix((gx_device *)dev, &mat);
	gs_bbox_transform_inverse(&dbox, &mat, pbbox);
    }
}
Example #19
0
/* otherwise, return the element type. */
int
gs_path_enum_next(gs_path_enum * penum, gs_point ppts[3])
{
    gs_fixed_point fpts[3];
    int pe_op = gx_path_enum_next(penum, fpts);
    int code;

    switch (pe_op) {
	case 0:		/* all done */
	case gs_pe_closepath:
	    break;
	case gs_pe_curveto:
	    if ((code = gs_point_transform_inverse(
						      fixed2float(fpts[1].x),
						      fixed2float(fpts[1].y),
					      &penum->mat, &ppts[1])) < 0 ||
		(code = gs_point_transform_inverse(
						      fixed2float(fpts[2].x),
						      fixed2float(fpts[2].y),
						&penum->mat, &ppts[2])) < 0)
		return code;
	    /* falls through */
	case gs_pe_moveto:
	case gs_pe_lineto:
	    if ((code = gs_point_transform_inverse(
						      fixed2float(fpts[0].x),
						      fixed2float(fpts[0].y),
						&penum->mat, &ppts[0])) < 0)
		return code;
	default:		/* error */
	    break;
    }
    return pe_op;
}
Example #20
0
/* Print a path */
void
gx_path_print(const gx_path * ppath)
{
    const segment *pseg = (const segment *)ppath->first_subpath;

    dmlprintf5(ppath->memory,
               " %% state_flags=%d subpaths=%d, curves=%d, point=(%f,%f)\n",
               ppath->state_flags, ppath->subpath_count, ppath->curve_count,
               fixed2float(ppath->position.x),
               fixed2float(ppath->position.y));
    dmlprintf5(ppath->memory," %% box=(%f,%f),(%f,%f) last=0x%lx\n",
               fixed2float(ppath->bbox.p.x), fixed2float(ppath->bbox.p.y),
               fixed2float(ppath->bbox.q.x), fixed2float(ppath->bbox.q.y),
               (ulong) ppath->box_last);
    dmlprintf4(ppath->memory,
               " %% segments=0x%lx (refct=%ld, first=0x%lx, current=0x%lx)\n",
               (ulong) ppath->segments, (long)ppath->segments->rc.ref_count,
               (ulong) ppath->segments->contents.subpath_first,
               (ulong) ppath->segments->contents.subpath_current);
    while (pseg) {
        dmlputs(ppath->memory,"");
        gx_print_segment(ppath->memory, pseg);
        pseg = pseg->next;
    }
}
Example #21
0
static int
svg_dorect(gx_device_vector *vdev, fixed x0, fixed y0,
           fixed x1, fixed y1, gx_path_type_t type)
{
    gx_device_svg *svg = (gx_device_svg *)vdev;
    char line[300];

    /* hack single-page output */
    if (svg->page_count)
        return 0;

    dprintf("svg_dorect ");
    svg_print_path_type(svg, type);
    dprintf("\n");

    svg_write_state(svg);

    if (type & gx_path_type_clip) {
        svg_write(svg, "<clipPath>\n");
    }

    sprintf(line, "<rect x='%lf' y='%lf' width='%lf' height='%lf'",
            fixed2float(x0), fixed2float(y0),
            fixed2float(x1 - x0), fixed2float(y1 - y0));
    svg_write(svg, line);
    /* override the inherited stroke attribute if we're not stroking */
    if (!(type & gx_path_type_stroke) && svg->strokecolor)
        svg_write(svg, " stroke='none'");
    /* override the inherited fill attribute if we're not filling */
    if (!(type & gx_path_type_fill) && svg->fillcolor)
        svg_write(svg, " fill='none'");
    svg_write(svg, "/>\n");

    if (type & gx_path_type_clip) {
        svg_write(svg, "</clipPath>\n");
    }

    return 0;
}
Example #22
0
void
pcl_mark_page_for_current_pos(pcl_state_t *pcs)
{
    /* nothing to do */
    if ( pcs->page_marked )
        return;

    /* convert current point to device space and check if it is inside
       device rectangle for the page */
    {
        gs_fixed_rect page_bbox_fixed = pcs->xfm_state.dev_print_rect;
        gs_rect page_bbox_float;
        gs_point current_pt, dev_pt;

        page_bbox_float.p.x = fixed2float(page_bbox_fixed.p.x);
        page_bbox_float.p.y = fixed2float(page_bbox_fixed.p.y);
        page_bbox_float.q.x = fixed2float(page_bbox_fixed.q.x);
        page_bbox_float.q.y = fixed2float(page_bbox_fixed.q.y);

        if ( gs_currentpoint(pcs->pgs, &current_pt) < 0 ) {
             dprintf("Not expected to fail\n" );
             return;
        }

        if ( gs_transform(pcs->pgs, current_pt.x, current_pt.y, &dev_pt) ) {
             dprintf("Not expected to fail\n" );
             return;
        }

        /* half-open lower - not sure this is correct */
        if ( dev_pt.x >= page_bbox_float.p.x &&
             dev_pt.y >= page_bbox_float.p.y &&
             dev_pt.x < page_bbox_float.q.x &&
             dev_pt.y < page_bbox_float.q.y )
            pcs->page_marked = true;

    }
}
Example #23
0
/* Compute the stroked outline of the current path */
int
gs_strokepath(gs_state * pgs)
{
    gx_path spath;
    int code;

    gx_path_init_local(&spath, pgs->path->memory);
    code = gx_stroke_add(pgs->path, &spath, pgs);
    if (code < 0) {
	gx_path_free(&spath, "gs_strokepath");
	return code;
    }
    pgs->device->sgr.stroke_stored = false;
    code = gx_path_assign_free(pgs->path, &spath);
    if (code < 0)
	return code;
    /* NB: needs testing with PCL */
    if (CPSI_mode && gx_path_is_void(pgs->path))
        pgs->current_point_valid = false;
    else
        gx_setcurrentpoint(pgs, fixed2float(spath.position.x), fixed2float(spath.position.y));
    return 0;

}
Example #24
0
/* Used by gschar.c to prepare for a BuildChar or BuildGlyph procedure. */
int
gx_translate_to_fixed(register gs_state * pgs, fixed px, fixed py)
{
    double fpx = fixed2float(px);
    double fdx = fpx - pgs->ctm.tx;
    double fpy = fixed2float(py);
    double fdy = fpy - pgs->ctm.ty;
    fixed dx, dy;
    int code;

    if (pgs->ctm.txy_fixed_valid) {
        dx = float2fixed(fdx);
        dy = float2fixed(fdy);
        code = gx_path_translate(pgs->path, dx, dy);
        if (code < 0)
            return code;
        if (pgs->char_tm_valid && pgs->char_tm.txy_fixed_valid)
            pgs->char_tm.tx_fixed += dx,
                pgs->char_tm.ty_fixed += dy;
    } else {
        if (!gx_path_is_null(pgs->path))
            return_error(gs_error_limitcheck);
    }
    pgs->ctm.tx = fpx;
    pgs->ctm.tx_fixed = px;
    pgs->ctm.ty = fpy;
    pgs->ctm.ty_fixed = py;
    pgs->ctm.txy_fixed_valid = true;
    pgs->ctm_inverse_valid = false;
    if (pgs->char_tm_valid) {	/* Update char_tm now, leaving it valid. */
        pgs->char_tm.tx += fdx;
        pgs->char_tm.ty += fdy;
    }
#ifdef DEBUG
    if (gs_debug_c('x')) {
        dlprintf2("[x]translate_to_fixed %g, %g:\n",
                  fixed2float(px), fixed2float(py));
        trace_ctm(pgs);
        dlprintf("[x]   char_tm:\n");
        trace_matrix_fixed(&pgs->char_tm);
    }
#endif
    gx_setcurrentpoint(pgs, fixed2float(pgs->ctm.tx_fixed), fixed2float(pgs->ctm.ty_fixed));
    pgs->current_point_valid = true;
    return 0;
}
Example #25
0
int
gs_type1_glyph_info(gs_font *font, gs_glyph glyph, const gs_matrix *pmat,
		    int members, gs_glyph_info_t *info)
{
    gs_font_type1 *const pfont = (gs_font_type1 *)font;
    gs_type1_data *const pdata = &pfont->data;
    int wmode = ((members & GLYPH_INFO_WIDTH1) != 0);
    int piece_members = members & (GLYPH_INFO_NUM_PIECES | GLYPH_INFO_PIECES);
    int width_members = (members & ((GLYPH_INFO_WIDTH0 << wmode) | (GLYPH_INFO_VVECTOR0 << wmode)));
    int default_members = members & ~(piece_members | GLYPH_INFO_WIDTHS |
				     GLYPH_INFO_VVECTOR0 | GLYPH_INFO_VVECTOR1 |
				     GLYPH_INFO_OUTLINE_WIDTHS);
    int code = 0;
    gs_glyph_data_t gdata;

    if (default_members) {
	code = gs_default_glyph_info(font, glyph, pmat, default_members, info);

	if (code < 0)
	    return code;
    } else
	info->members = 0;

    if (default_members == members)
	return code;		/* all done */

    gdata.memory = pfont->memory;
    if ((code = pdata->procs.glyph_data(pfont, glyph, &gdata)) < 0)
	return code;		/* non-existent glyph */

    if (piece_members) {
	code = gs_type1_glyph_pieces(pfont, &gdata, members, info);
	if (code < 0)
	    return code;
	info->members |= piece_members;
    }

    if (width_members) {
	gx_path path;
	/*
	 * Interpret the CharString until we get to the [h]sbw.
	 */
	gs_imager_state gis;
	gs_type1_state cis;
	int value;

	/* Initialize just enough of the imager state. */
	if (pmat)
	    gs_matrix_fixed_from_matrix(&gis.ctm, pmat);
	else {
	    gs_matrix imat;

	    gs_make_identity(&imat);
	    gs_matrix_fixed_from_matrix(&gis.ctm, &imat);
	}
	gis.flatness = 0;
	code = gs_type1_interp_init(&cis, &gis, NULL /* no path needed */,
				    NULL, NULL, true, 0, pfont);
	if (code < 0)
	    return code;
	cis.no_grid_fitting = true;
	gx_path_init_bbox_accumulator(&path);
	cis.path = &path;
	code = pdata->interpret(&cis, &gdata, &value);
	switch (code) {
	case 0:		/* done with no [h]sbw, error */
	                /* Adobe interpreters ignore the error! */
	    info->width[wmode].x = 0;
	    info->width[wmode].y = 0;
	    info->v.x = 0;
	    info->v.y = 0;
	    break;
	default:		/* code < 0, error */
	    return code;
	case type1_result_callothersubr:	/* unknown OtherSubr */
	    return_error(gs_error_rangecheck); /* can't handle it */
	case type1_result_sbw:
	    info->width[wmode].x = fixed2float(cis.width.x);
	    info->width[wmode].y = fixed2float(cis.width.y);
	    info->v.x = fixed2float(cis.lsb.x);
	    info->v.y = fixed2float(cis.lsb.y);
	    break;
	}
	info->members |= width_members | (GLYPH_INFO_VVECTOR0 << wmode);
    }

    gs_glyph_data_free(&gdata, "gs_type1_glyph_info");
    return code;
}
Example #26
0
irender_proc_t
gs_image_class_1_simple(gx_image_enum * penum)
{
    irender_proc_t rproc;
    fixed ox = dda_current(penum->dda.pixel0.x);
    fixed oy = dda_current(penum->dda.pixel0.y);

    if (penum->use_rop || penum->spp != 1 || penum->bps != 1)
        return 0;
    switch (penum->posture) {
        case image_portrait:
            {			/* Use fast portrait algorithm. */
                long dev_width =
                    fixed2long_pixround(ox + penum->x_extent.x) -
                    fixed2long_pixround(ox);

                if (dev_width != penum->rect.w) {
                    /*
                     * Add an extra align_bitmap_mod of padding so that
                     * we can align scaled rows with the device.
                     */
                    long line_size =
                        bitmap_raster(any_abs(dev_width)) + align_bitmap_mod;

                    if (penum->adjust != 0 || line_size > max_uint)
                        return 0;
                    /* Must buffer a scan line. */
                    penum->line_width = any_abs(dev_width);
                    penum->line_size = (uint) line_size;
                    penum->line = gs_alloc_bytes(penum->memory,
                                            penum->line_size, "image line");
                    if (penum->line == 0) {
                        gx_default_end_image(penum->dev,
                                             (gx_image_enum_common_t *)penum,
                                             false);
                        return 0;
                    }
                }
                if_debug2('b', "[b]render=simple, unpack=copy; rect.w=%d, dev_width=%ld\n",
                          penum->rect.w, dev_width);
                rproc = image_render_simple;
                break;
            }
        case image_landscape:
            {			/* Use fast landscape algorithm. */
                long dev_width =
                    fixed2long_pixround(oy + penum->x_extent.y) -
                    fixed2long_pixround(oy);
                long line_size =
                    (dev_width = any_abs(dev_width),
                     bitmap_raster(dev_width) * 8 +
                     ROUND_UP(dev_width, 8) * align_bitmap_mod);

                if ((dev_width != penum->rect.w && penum->adjust != 0) ||
                    line_size > max_uint
                    )
                    return 0;
                /* Must buffer a group of 8N scan lines. */
                penum->line_width = dev_width;
                penum->line_size = (uint) line_size;
                penum->line = gs_alloc_bytes(penum->memory,
                                             penum->line_size, "image line");
                if (penum->line == 0) {
                    gx_default_end_image(penum->dev,
                                         (gx_image_enum_common_t *) penum,
                                         false);
                    return 0;
                }
                penum->xi_next = penum->line_xy = fixed2int_var_rounded(ox);
                if_debug3('b', "[b]render=landscape, unpack=copy; rect.w=%d, dev_width=%ld, line_size=%ld\n",
                          penum->rect.w, dev_width, line_size);
                rproc = image_render_landscape;
                /* Precompute values needed for rasterizing. */
                penum->dxy =
                    float2fixed(penum->matrix.xy +
                                fixed2float(fixed_epsilon) / 2);
                break;
            }
        default:
            return 0;
    }
    /* Precompute values needed for rasterizing. */
    penum->dxx =
        float2fixed(penum->matrix.xx + fixed2float(fixed_epsilon) / 2);
    /*
     * We don't want to spread the samples, but we have to reset unpack_bps
     * to prevent the buffer pointer from being incremented by 8 bytes per
     * input byte.
     */
    penum->unpack = sample_unpack_copy;
    penum->unpack_bps = 8;
    if (penum->use_mask_color) {
        /*
         * Set the masked color as 'no_color' to make it transparent
         *  according to the mask color range and the decoding.
         */
        penum->masked = true;
        if (penum->mask_color.values[0] == 1) {
            /* if v0 == 1, 1 is transparent since v1 must be == 1 to be a valid range */
            set_nonclient_dev_color(penum->map[0].inverted ? penum->icolor0 : penum->icolor1,
                        gx_no_color_index);
        } else if (penum->mask_color.values[1] == 0) {
            /* if v1 == 0, 0 is transparent since v0 must be == 0 to be a valid range */
            set_nonclient_dev_color(penum->map[0].inverted ? penum->icolor1 : penum->icolor0,
                        gx_no_color_index);
        } else {
            /*
             * The only other possible in-range value is v0 = 0, v1 = 1.
             * The image is completely transparent!
             */
            rproc = image_render_skip;
        }
        penum->map[0].decoding = sd_none;
    }
    return rproc;
}
Example #27
0
static int
charstring_execchar_aux(i_ctx_t *i_ctx_p, gs_text_enum_t *penum, gs_font *pfont)
{
    os_ptr op = osp;
    gs_font_base *const pbfont = (gs_font_base *) pfont;
    gs_font_type1 *const pfont1 = (gs_font_type1 *) pfont;
    const gs_type1_data *pdata;
    gs_type1exec_state cxs;
    gs_type1_state *const pcis = &cxs.cis;
    gs_rect FontBBox = pfont1->FontBBox;
    int code;

    if (penum->current_font->FontType == ft_CID_encrypted) {
        if (FontBBox.q.x <= FontBBox.p.x && FontBBox.q.y <= FontBBox.p.y) {
            gs_font_cid0 *pfcid0 = (gs_font_cid0 *)penum->current_font;

            FontBBox = pfcid0->FontBBox;
        }
    }

    pdata = &pfont1->data;
    /*
     * Any reasonable implementation would execute something like
     *    1 setmiterlimit 0 setlinejoin 0 setlinecap
     * here, but the Adobe implementations don't.
     *
     * If this is a stroked font, set the stroke width.
     */
    if (pfont->PaintType)
        gs_setlinewidth(igs, pfont->StrokeWidth);
    check_estack(3);		/* for continuations */
    /*
     * Execute the definition of the character.
     */
    if (r_is_proc(op))
        return zchar_exec_char_proc(i_ctx_p);
    /*
     * The definition must be a Type 1 CharString.
     * Note that we do not require read access: this is deliberate.
     */
    check_type(*op, t_string);
    if (r_size(op) <= max(pdata->lenIV, 0))
        return_error(e_invalidfont);
    /*
     * In order to make character oversampling work, we must
     * set up the cache before calling .type1addpath.
     * To do this, we must get the bounding box from the FontBBox,
     * and the width from the CharString or the Metrics.
     * If the FontBBox isn't valid, we can't do any of this.
     */

    if ((penum->FontBBox_as_Metrics2.x == 0 &&
         penum->FontBBox_as_Metrics2.y == 0) ||
        gs_rootfont(igs)->WMode == 0 ) {
        code = zchar_get_metrics(pbfont, op - 1, cxs.sbw);
        if (code < 0)
            return code;
        cxs.present = code;
        cxs.use_FontBBox_as_Metrics2 = false;
    }  else {  /* pass here if FontType==9,11 && WMode==1*/
        cxs.sbw[0] = penum->FontBBox_as_Metrics2.x / 2;
        cxs.sbw[1] = penum->FontBBox_as_Metrics2.y;
        cxs.sbw[2] = 0;
        cxs.sbw[3] = -penum->FontBBox_as_Metrics2.x; /* Sic! */
        cxs.use_FontBBox_as_Metrics2 = true;
        cxs.present = metricsNone;
    }
    /* Establish a current point. */
    code = gs_moveto(igs, 0.0, 0.0);
    if (code < 0)
        return code;
    code = type1_exec_init(pcis, penum, igs, pfont1);
    if (code < 0)
        return code;
    gs_type1_set_callback_data(pcis, &cxs);
    if (FontBBox.q.x > FontBBox.p.x &&
        FontBBox.q.y > FontBBox.p.y
        ) {
        /* The FontBBox appears to be valid. */
        op_proc_t exec_cont = 0;

        cxs.char_bbox = pfont1->FontBBox;
        code = type1exec_bbox(i_ctx_p, penum, &cxs, pfont, &exec_cont);
        if (code >= 0 && exec_cont != 0)
            code = (*exec_cont)(i_ctx_p);
        return code;
    } else {
        /* The FontBBox is not valid */
        const ref *opstr = op;
        ref other_subr;
        const gs_matrix * pctm = &ctm_only(igs);

        /* First, check for singular CTM */
        if (pctm->xx * pctm->yy == pctm->xy * pctm->yx) {
           /* The code below won't be able to find the FontBBox but we
            * don't need it anyway. Set an empty box and consider it valid.
            */
            op_proc_t exec_cont = 0;

            cxs.char_bbox.p.x = 0;
            cxs.char_bbox.p.y = 0;
            cxs.char_bbox.q.x = 0;
            cxs.char_bbox.q.y = 0;
            code = type1exec_bbox(i_ctx_p, penum, &cxs, pfont, &exec_cont);
            if (code >= 0 && exec_cont != 0)
                code = (*exec_cont)(i_ctx_p);
            return code;
        }
        /* Now we create the path first, then do the setcachedevice.
         * If we are oversampling (in this case, only for anti-
         * aliasing, not just to improve quality), we have to
         * create the path twice, since we can't know the
         * oversampling factor until after setcachedevice.
         */
        switch (cxs.present) {
            case metricsSideBearingAndWidth: {
                gs_point pt;

                pt.x = cxs.sbw[0], pt.y = cxs.sbw[1];
                gs_type1_set_lsb(pcis, &pt);
            }
            /* fall through */
            case metricsWidthOnly: {
                gs_point pt;

                pt.x = cxs.sbw[2], pt.y = cxs.sbw[3];
                gs_type1_set_width(pcis, &pt);
            }
        }

        /* Continue interpreting. */
      icont:
        code = type1_continue_dispatch(i_ctx_p, &cxs, opstr, &other_subr, 4);
        op = osp;		/* OtherSubrs might change it */
        switch (code) {
            case 0:		/* all done */
                return nobbox_finish(i_ctx_p, &cxs);
            default:		/* code < 0, error */
                return code;
            case type1_result_callothersubr:	/* unknown OtherSubr */
                return type1_call_OtherSubr(i_ctx_p, &cxs, nobbox_continue,
                                            &other_subr);
            case type1_result_sbw:	/* [h]sbw, just continue */
                switch (cxs.present) {
                    case metricsNone:
                        cxs.sbw[0] = fixed2float(pcis->lsb.x);
                        cxs.sbw[1] = fixed2float(pcis->lsb.y);
                    /* fall through */
                    case metricsWidthOnly:
                        cxs.sbw[2] = fixed2float(pcis->width.x);
                        cxs.sbw[3] = fixed2float(pcis->width.y);
                }
                opstr = 0;
                goto icont;
        }
    }
}
Example #28
0
int
gslt_outline_font_glyph(gs_state *pgs, gslt_font_t *xf, int gid, gslt_outline_walker_t *walk)
{
    gs_text_params_t params;
    gs_text_enum_t *textenum;
    gs_matrix matrix;
    segment *seg;
    curve_segment *cseg;

    gs_gsave(pgs);
    gs_make_identity(&matrix);
    gs_setmatrix(pgs, &matrix);
    gs_scale(pgs, 1000.0, 1000.0); /* otherwise we hit serious precision problems with fixpoint math */

    /* set gstate params */
    gs_setfont(pgs, xf->font); /* set pgs->font and invalidate existing charmatrix */
    gs_make_identity(&matrix);
    gs_setcharmatrix(pgs, &matrix); /* set the charmatrix to identity */

    /* reset the path */
    gs_newpath(pgs);
    gs_moveto(pgs, 0.0, 0.0);

    /* draw the glyph, in charpath mode */
    params.operation = TEXT_FROM_SINGLE_GLYPH | TEXT_DO_FALSE_CHARPATH | TEXT_RETURN_WIDTH;
    params.data.d_glyph = gid;
    params.size = 1;

    if (gs_text_begin(pgs, &params, xf->font->memory, &textenum) != 0)
        return gs_throw(-1, "cannot gs_text_begin()");
    if (gs_text_process(textenum) != 0)
        return gs_throw(-1, "cannot gs_text_process()");
    gs_text_release(textenum, "gslt font outline");

    /* walk the resulting path */
    seg = (segment*)pgs->path->first_subpath;
    while (seg)
    {
        switch (seg->type)
        {
        case s_start:
            walk->moveto(walk->user,
                    fixed2float(seg->pt.x) * 0.001,
                    fixed2float(seg->pt.y) * 0.001);
            break;
        case s_line:
            walk->lineto(walk->user,
                    fixed2float(seg->pt.x) * 0.001,
                    fixed2float(seg->pt.y) * 0.001);
            break;
        case s_line_close:
            walk->closepath(walk->user);
            break;
        case s_curve:
            cseg = (curve_segment*)seg;
            walk->curveto(walk->user,
                    fixed2float(cseg->p1.x) * 0.001,
                    fixed2float(cseg->p1.y) * 0.001,
                    fixed2float(cseg->p2.x) * 0.001,
                    fixed2float(cseg->p2.y) * 0.001,
                    fixed2float(seg->pt.x) * 0.001,
                    fixed2float(seg->pt.y) * 0.001);
            break;
        }
        seg = seg->next;
    }

    /* and toss it away... */
    gs_newpath(pgs);

    gs_grestore(pgs);
    return 0;
}
Example #29
0
static int
subpath_expand_dashes(const subpath * psub, gx_path * ppath,
		   const gs_imager_state * pis, const gx_dash_params * dash)
{
    const float *pattern = dash->pattern;
    int count, index;
    bool ink_on;
    double elt_length;
    fixed x0 = psub->pt.x, y0 = psub->pt.y;
    fixed x, y;
    const segment *pseg;
    int wrap = (dash->init_ink_on && psub->is_closed ? -1 : 0);
    int drawing = wrap;
    segment_notes notes = ~sn_not_first;
    const gx_line_params *pgs_lp = gs_currentlineparams_inline(pis);
    bool zero_length = true;
    int code;

    if ((code = gx_path_add_point(ppath, x0, y0)) < 0)
	return code;
    /*
     * To do the right thing at the beginning of a closed path, we have
     * to skip any initial line, and then redo it at the end of the
     * path.  Drawing = -1 while skipping, 0 while drawing normally, and
     * 1 on the second round.  Note that drawing != 0 implies ink_on.
     */
  top:count = dash->pattern_size;
    ink_on = dash->init_ink_on;
    index = dash->init_index;
    elt_length = dash->init_dist_left;
    x = x0, y = y0;
    pseg = (const segment *)psub;
    while ((pseg = pseg->next) != 0 && pseg->type != s_start) {
	fixed sx = pseg->pt.x, sy = pseg->pt.y;
	fixed udx = sx - x, udy = sy - y;
	double length, dx, dy;
	double scale = 1;
	double left;

	if (!(udx | udy)) {	/* degenerate */
	    if (pgs_lp->dot_length == 0 &&
		pgs_lp->cap != gs_cap_round) {
		/* From PLRM, stroke operator :
		   If a subpath is degenerate (consists of a single-point closed path 
		   or of two or more points at the same coordinates), 
		   stroke paints it only if round line caps have been specified */
		if (zero_length || pseg->type != s_line_close)
		    continue;
	    }
	    dx = 0, dy = 0, length = 0;
	} else {
  	    gs_point d;

	    zero_length = false;
	    dx = udx, dy = udy;	/* scaled as fixed */
	    gs_imager_idtransform(pis, dx, dy, &d);
	    length = hypot(d.x, d.y) * (1.0 / fixed_1);
	    if (gs_imager_currentdashadapt(pis)) {
		double reps = length / dash->pattern_length;

		scale = reps / ceil(reps);
		/* Ensure we're starting at the start of a */
		/* repetition.  (This shouldn't be necessary, */
		/* but it is.) */
		count = dash->pattern_size;
		ink_on = dash->init_ink_on;
		index = dash->init_index;
		elt_length = dash->init_dist_left * scale;
	    }
	}
	left = length;
	while (left > elt_length) {	/* We are using up the line segment. */
	    double fraction = elt_length / length;
	    fixed fx = (fixed) (dx * fraction);
	    fixed fy = (fixed) (dy * fraction);
	    fixed nx = x + fx;
	    fixed ny = y + fy;

	    if (ink_on) {
		if (drawing >= 0) {
		    if (left >= elt_length && any_abs(fx) + any_abs(fy) < fixed_half)
			code = gx_path_add_dash_notes(ppath, nx, ny, udx, udy, 
				notes & pseg->notes);
		    else
			code = gx_path_add_line_notes(ppath, nx, ny,
						  notes & pseg->notes);
		}
		notes |= sn_not_first;
	    } else {
		if (drawing > 0)	/* done */
		    return 0;
		code = gx_path_add_point(ppath, nx, ny);
		notes &= ~sn_not_first;
		drawing = 0;
	    }
	    if (code < 0)
		return code;
	    left -= elt_length;
	    ink_on = !ink_on;
	    if (++index == count)
		index = 0;
	    elt_length = pattern[index] * scale;
	    x = nx, y = ny;
	}
	elt_length -= left;
	/* Handle the last dash of a segment. */
      on:if (ink_on) {
	    if (drawing >= 0) {
		if (pseg->type == s_line_close && drawing > 0)
		    code = gx_path_close_subpath_notes(ppath,
						 notes & pseg->notes);
		else if (any_abs(sx - x) + any_abs(sy - y) < fixed_half)
		    code = gx_path_add_dash_notes(ppath, sx, sy, udx, udy, 
			    notes & pseg->notes);
		else
		    code = gx_path_add_line_notes(ppath, sx, sy,
					notes & pseg->notes);
		notes |= sn_not_first;
	    }
	} else {
	    code = gx_path_add_point(ppath, sx, sy);
	    notes &= ~sn_not_first;
	    if (elt_length < fixed2float(fixed_epsilon) &&
		(pseg->next == 0 || pseg->next->type == s_start || elt_length == 0)) {		
				/*
				 * Ink is off, but we're within epsilon of the end
				 * of the dash element.  
				 * "Stretch" a little so we get a dot.
				 * Also if the next dash pattern is zero length,
				 * use the last segment orientation.
				 */
		double elt_length1;

		if (code < 0)
		    return code;
		if (++index == count)
		    index = 0;
		elt_length1 = pattern[index] * scale;
		if (pseg->next == 0 || pseg->next->type == s_start) {
		    elt_length = elt_length1;
		    left = 0;
		    ink_on = true;
		    goto on;
		}
		/* Looking ahead one dash pattern element.
		   If it is zero length, apply to the current segment 
		   (at its end). */
		if (elt_length1 == 0) {
		    left = 0;
		    code = gx_path_add_dash_notes(ppath, sx, sy, udx, udy,
				    notes & pseg->notes);
		    if (++index == count)
			index = 0;
		    elt_length = pattern[index] * scale;
		    ink_on = false;
		} else if (--index == 0) {
		    /* Revert lookahead. */
		    index = count - 1;
		}
	    }
	    if (drawing > 0)	/* done */
		return code;
	    drawing = 0;
	}
	if (code < 0)
	    return code;
	x = sx, y = sy;
    }
    /* Check for wraparound. */
    if (wrap && drawing <= 0) {	/* We skipped some initial lines. */
	/* Go back and do them now. */
	drawing = 1;
	goto top;
    }
    return 0;
}
Example #30
0
/* this procedure is exported for the benefit of gsicc.c */
int
gx_cie_real_remap_finish(cie_cached_vector3 vec3, frac * pconc,
                         const gs_imager_state * pis,
                         const gs_color_space *pcs)
{
    const gs_cie_render *pcrd = pis->cie_render;
    const gx_cie_joint_caches *pjc = pis->cie_joint_caches;
    const gs_const_string *table = pcrd->RenderTable.lookup.table;
    int tabc[3];		/* indices for final EncodeABC lookup */

    /* Apply DecodeLMN, MatrixLMN(decode), and MatrixPQR. */
    if (!pjc->skipDecodeLMN)
        cie_lookup_map3(&vec3 /* LMN => PQR */, &pjc->DecodeLMN,
                        "Decode/MatrixLMN+MatrixPQR");

    /* Apply TransformPQR, MatrixPQR', and MatrixLMN(encode). */
    if (!pjc->skipPQR)
        cie_lookup_map3(&vec3 /* PQR => LMN */, &pjc->TransformPQR,
                        "Transform/Matrix'PQR+MatrixLMN");

    /* Apply EncodeLMN and MatrixABC(encode). */
    if (!pjc->skipEncodeLMN)
        cie_lookup_map3(&vec3 /* LMN => ABC */, &pcrd->caches.EncodeLMN,
                        "EncodeLMN+MatrixABC");

    /* MatrixABCEncode includes the scaling of the EncodeABC */
    /* cache index. */
#define SET_TABC(i, t)\
  BEGIN\
    tabc[i] = cie_cached2int(vec3 /*ABC*/.t - pcrd->EncodeABC_base[i],\
                             _cie_interpolate_bits);\
    if ((uint)tabc[i] > (gx_cie_cache_size - 1) << _cie_interpolate_bits)\
        tabc[i] = (tabc[i] < 0 ? 0 :\
                   (gx_cie_cache_size - 1) << _cie_interpolate_bits);\
  END
    SET_TABC(0, u);
    SET_TABC(1, v);
    SET_TABC(2, w);
#undef SET_TABC
    if (table == 0) {
        /*
         * No further transformation.
         * The final mapping step includes both restriction to
         * the range [0..1] and conversion to fracs.
         */
#define EABC(i)\
  cie_interpolate_fracs(pcrd->caches.EncodeABC[i].fixeds.fracs.values, tabc[i])
        pconc[0] = EABC(0);
        pconc[1] = EABC(1);
        pconc[2] = EABC(2);
#undef EABC
        return 3;
    } else {
        /*
         * Use the RenderTable.
         */
        int m = pcrd->RenderTable.lookup.m;

#define RT_LOOKUP(j, i) pcrd->caches.RenderTableT[j].fracs.values[i]
#ifdef CIE_RENDER_TABLE_INTERPOLATE

        /*
         * The final mapping step includes restriction to the
         * ranges [0..dims[c]] as ints with interpolation bits.
         */
        fixed rfix[3];
        const int s = _fixed_shift - _cie_interpolate_bits;

#define EABC(i)\
  cie_interpolate_fracs(pcrd->caches.EncodeABC[i].fixeds.ints.values, tabc[i])
#define FABC(i, s)\
  ((s) > 0) ? (EABC(i) << (s)) : (EABC(i) >> -(s))
        rfix[0] = FABC(0, s);
        rfix[1] = FABC(1, s);
        rfix[2] = FABC(2, s);
#undef FABC
#undef EABC
        if_debug6('c', "[c]ABC=%g,%g,%g => iabc=%g,%g,%g\n",
                  cie_cached2float(vec3.u), cie_cached2float(vec3.v),
                  cie_cached2float(vec3.w), fixed2float(rfix[0]),
                  fixed2float(rfix[1]), fixed2float(rfix[2]));
        gx_color_interpolate_linear(rfix, &pcrd->RenderTable.lookup,
                                    pconc);
        if_debug3('c', "[c]  interpolated => %g,%g,%g\n",
                  frac2float(pconc[0]), frac2float(pconc[1]),
                  frac2float(pconc[2]));
        if (!pcrd->caches.RenderTableT_is_identity) {
            /* Map the interpolated values. */
#define frac2cache_index(v) frac2bits(v, gx_cie_log2_cache_size)
            pconc[0] = RT_LOOKUP(0, frac2cache_index(pconc[0]));
            pconc[1] = RT_LOOKUP(1, frac2cache_index(pconc[1]));
            pconc[2] = RT_LOOKUP(2, frac2cache_index(pconc[2]));
            if (m > 3)
                pconc[3] = RT_LOOKUP(3, frac2cache_index(pconc[3]));
#undef frac2cache_index
        }

#else /* !CIE_RENDER_TABLE_INTERPOLATE */

        /*
         * The final mapping step includes restriction to the ranges
         * [0..dims[c]], plus scaling of the indices in the strings.
         */
#define RI(i)\
  pcrd->caches.EncodeABC[i].ints.values[tabc[i] >> _cie_interpolate_bits]
        int ia = RI(0);
        int ib = RI(1);		/* pre-multiplied by m * NC */
        int ic = RI(2);		/* pre-multiplied by m */
        const byte *prtc = table[ia].data + ib + ic;

        /* (*pcrd->RenderTable.T)(prtc, m, pcrd, pconc); */

        if_debug6('c', "[c]ABC=%g,%g,%g => iabc=%d,%d,%d\n",
                  cie_cached2float(vec3.u), cie_cached2float(vec3.v),
                  cie_cached2float(vec3.w), ia, ib, ic);
        if (pcrd->caches.RenderTableT_is_identity) {
            pconc[0] = byte2frac(prtc[0]);
            pconc[1] = byte2frac(prtc[1]);
            pconc[2] = byte2frac(prtc[2]);
            if (m > 3)
                pconc[3] = byte2frac(prtc[3]);
        } else {
#if gx_cie_log2_cache_size == 8
#  define byte2cache_index(b) (b)
#else
# if gx_cie_log2_cache_size > 8
#  define byte2cache_index(b)\
    ( ((b) << (gx_cie_log2_cache_size - 8)) +\
      ((b) >> (16 - gx_cie_log2_cache_size)) )
# else				/* < 8 */
#  define byte2cache_index(b) ((b) >> (8 - gx_cie_log2_cache_size))
# endif
#endif
            pconc[0] = RT_LOOKUP(0, byte2cache_index(prtc[0]));
            pconc[1] = RT_LOOKUP(1, byte2cache_index(prtc[1]));
            pconc[2] = RT_LOOKUP(2, byte2cache_index(prtc[2]));
            if (m > 3)
                pconc[3] = RT_LOOKUP(3, byte2cache_index(prtc[3]));
#undef byte2cache_index
        }

#endif /* !CIE_RENDER_TABLE_INTERPOLATE */
#undef RI
#undef RT_LOOKUP
        return m;
    }
}