int gs_matrix_invert_to_double(const gs_matrix * pm, gs_matrix_double * pmr) { /* We have to be careful about fetch/store order, */ /* because pm might be the same as pmr. */ if (is_xxyy(pm)) { if (is_fzero(pm->xx) || is_fzero(pm->yy)) return_error(gs_error_undefinedresult); pmr->tx = -(pmr->xx = 1.0 / pm->xx) * pm->tx; pmr->xy = 0.0; pmr->yx = 0.0; pmr->ty = -(pmr->yy = 1.0 / pm->yy) * pm->ty; } else { double mxx = pm->xx, myy = pm->yy, mxy = pm->xy, myx = pm->yx; double mtx = pm->tx, mty = pm->ty; double det = (mxx * myy) - (mxy * myx); /* * We are doing the math as floats instead of doubles to reproduce * the results in page 1 of CET 10-09.ps */ if (det == 0) return_error(gs_error_undefinedresult); pmr->xx = myy / det; pmr->xy = -mxy / det; pmr->yx = -myx / det; pmr->yy = mxx / det; pmr->tx = (((mty * myx) - (mtx * myy))) / det; pmr->ty = (((mtx * mxy) - (mty * mxx))) / det; } return 0; }
/* Since this is used heavily, we check for shortcuts. */ int gs_matrix_multiply(const gs_matrix * pm1, const gs_matrix * pm2, gs_matrix * pmr) { double xx1 = pm1->xx, yy1 = pm1->yy; double tx1 = pm1->tx, ty1 = pm1->ty; double xx2 = pm2->xx, yy2 = pm2->yy; double xy2 = pm2->xy, yx2 = pm2->yx; if (is_xxyy(pm1)) { pmr->tx = tx1 * xx2 + pm2->tx; pmr->ty = ty1 * yy2 + pm2->ty; if (is_fzero(xy2)) pmr->xy = 0; else pmr->xy = xx1 * xy2, pmr->ty += tx1 * xy2; pmr->xx = xx1 * xx2; if (is_fzero(yx2)) pmr->yx = 0; else pmr->yx = yy1 * yx2, pmr->tx += ty1 * yx2; pmr->yy = yy1 * yy2; } else { double xy1 = pm1->xy, yx1 = pm1->yx; pmr->xx = xx1 * xx2 + xy1 * yx2; pmr->xy = xx1 * xy2 + xy1 * yy2; pmr->yy = yx1 * xy2 + yy1 * yy2; pmr->yx = yx1 * xx2 + yy1 * yx2; pmr->tx = tx1 * xx2 + ty1 * yx2 + pm2->tx; pmr->ty = tx1 * xy2 + ty1 * yy2 + pm2->ty; } return 0; }
/* Return gs_error_undefinedresult if the matrix is not invertible. */ int gs_point_transform_inverse(floatp x, floatp y, const gs_matrix * pmat, gs_point * ppt) { if (is_xxyy(pmat)) { if (is_fzero(pmat->xx) || is_fzero(pmat->yy)) return_error(gs_error_undefinedresult); ppt->x = (x - pmat->tx) / pmat->xx; ppt->y = (y - pmat->ty) / pmat->yy; return 0; } else if (is_xyyx(pmat)) { if (is_fzero(pmat->xy) || is_fzero(pmat->yx)) return_error(gs_error_undefinedresult); ppt->x = (y - pmat->ty) / pmat->xy; ppt->y = (x - pmat->tx) / pmat->yx; return 0; } else { /* There are faster ways to do this, */ /* but we won't implement one unless we have to. */ gs_matrix imat; int code = gs_matrix_invert(pmat, &imat); if (code < 0) return code; return gs_point_transform(x, y, &imat, ppt); } }
/* * Determine the flatness for rendering a character in an outline font. * This may be less than the flatness in the imager state. * The second argument is the default scaling for the font: 0.001 for * Type 1 fonts, 1.0 for TrueType fonts. */ double gs_char_flatness(const gs_imager_state *pis, floatp default_scale) { /* * Set the flatness to a value that is likely to produce reasonably * good-looking curves, regardless of its current value in the * graphics state. If the character is very small, set the flatness * to zero, which will produce very accurate curves. */ double cxx = fabs(pis->ctm.xx), cyy = fabs(pis->ctm.yy); if (is_fzero(cxx) || (cyy < cxx && !is_fzero(cyy))) cxx = cyy; if (!is_xxyy(&pis->ctm)) { double cxy = fabs(pis->ctm.xy), cyx = fabs(pis->ctm.yx); if (is_fzero(cxx) || (cxy < cxx && !is_fzero(cxy))) cxx = cxy; if (is_fzero(cxx) || (cyx < cxx && !is_fzero(cyx))) cxx = cyx; } /* Correct for the default scaling. */ cxx *= 0.001 / default_scale; /* Don't let the flatness be worse than the default. */ if (cxx > pis->flatness) cxx = pis->flatness; /* If the character is tiny, force accurate curves. */ if (cxx < 0.2) cxx = 0; return cxx; }
/* Check whether color is a shading with BBox. */ int gx_dc_pattern2_is_rectangular_cell(const gx_device_color * pdevc, gx_device * pdev, gs_fixed_rect *rect) { if (gx_dc_is_pattern2_color(pdevc) && gx_dc_pattern2_color_has_bbox(pdevc) && (*dev_proc(pdev, dev_spec_op))(pdev, gxdso_pattern_shading_area, NULL, 0) == 0) { gs_pattern2_instance_t *pinst = (gs_pattern2_instance_t *)pdevc->ccolor.pattern; const gs_shading_t *psh = pinst->templat.Shading; gs_fixed_point p, q; if (is_xxyy(&ctm_only(pinst->saved))) if (psh->params.have_BBox) { int code = gs_point_transform2fixed(&pinst->saved->ctm, psh->params.BBox.p.x, psh->params.BBox.p.y, &p); if (code < 0) return code; code = gs_point_transform2fixed(&pinst->saved->ctm, psh->params.BBox.q.x, psh->params.BBox.q.y, &q); if (code < 0) return code; if (p.x > q.x) { p.x ^= q.x; q.x ^= p.x; p.x ^= q.x; } if (p.y > q.y) { p.y ^= q.y; q.y ^= p.y; p.y ^= q.y; } rect->p = p; rect->q = q; return 1; } } return 0; }
/* Invert a matrix. Return gs_error_undefinedresult if not invertible. */ int gs_matrix_invert(const gs_matrix *pm, gs_matrix *pmr) { /* We have to be careful about fetch/store order, */ /* because pm might be the same as pmr. */ if ( is_xxyy(pm) ) { if ( is_fzero(pm->xx) || is_fzero(pm->yy) ) return_error(gs_error_undefinedresult); pmr->tx = - (pmr->xx = 1.0 / pm->xx) * pm->tx; pmr->xy = 0.0; pmr->yx = 0.0; pmr->ty = - (pmr->yy = 1.0 / pm->yy) * pm->ty; } else { double det = pm->xx * pm->yy - pm->xy * pm->yx; double mxx = pm->xx, mtx = pm->tx; if ( det == 0 ) return_error(gs_error_undefinedresult); pmr->xx = pm->yy / det; pmr->xy = - pm->xy / det; pmr->yx = - pm->yx / det; pmr->yy = mxx / det; /* xx is already changed */ pmr->tx = - (mtx * pmr->xx + pm->ty * pmr->yx); pmr->ty = - (mtx * pmr->xy + pm->ty * pmr->yy); /* tx ditto */ } return 0; }
/* from Ghostscript */ int matrix_invert(const MATRIX *pm, MATRIX *pmr) { /* We have to be careful about fetch/store order, */ /* because pm might be the same as pmr. */ if ( is_xxyy(pm) ) { if ( is_fzero(pm->xx) || is_fzero(pm->yy) ) return -1 ; pmr->tx = (float)(- (pmr->xx = (float)(1.0 / pm->xx)) * pm->tx); pmr->xy = 0.0; pmr->yx = 0.0; pmr->ty = (float)(- (pmr->yy = (float)(1.0 / pm->yy)) * pm->ty); } else { double det = pm->xx * pm->yy - pm->xy * pm->yx; double mxx = pm->xx, mtx = pm->tx; if ( det == 0 ) return -1 ; pmr->xx = (float)(pm->yy / det); pmr->xy = (float)(- pm->xy / det); pmr->yx = (float)(- pm->yx / det); pmr->yy = (float)(mxx / det); /* xx is already changed */ pmr->tx = (float)(- (mtx * pmr->xx + pm->ty * pmr->yx)); pmr->ty = (float)(- (mtx * pmr->xy + pm->ty * pmr->yy)); /* tx ditto */ } return 0; }
/* Test whether an image has a default ImageMatrix. */ bool gx_image_matrix_is_default(const gs_data_image_t *pid) { return (is_xxyy(&pid->ImageMatrix) && pid->ImageMatrix.xx == pid->Width && pid->ImageMatrix.yy == -pid->Height && is_fzero(pid->ImageMatrix.tx) && pid->ImageMatrix.ty == pid->Height); }
/* * Compute the scale for transforming the line width and dash pattern for a * stroke operation, and, if necessary to handle anisotropic scaling, a full * transformation matrix to be inverse-applied to the path elements as well. * Return 0 if only scaling, 1 if a full matrix is needed. */ int gdev_vector_stroke_scaling(const gx_device_vector *vdev, const gs_imager_state *pis, double *pscale, gs_matrix *pmat) { bool set_ctm = true; double scale = 1; /* * If the CTM is not uniform, stroke width depends on angle. * We'd like to avoid resetting the CTM, so we check for uniform * CTMs explicitly. Note that in PDF, unlike PostScript, it is * the CTM at the time of the stroke operation, not the CTM at * the time the path was constructed, that is used for transforming * the points of the path; so if we have to reset the CTM, we must * do it before constructing the path, and inverse-transform all * the coordinates. */ if (is_xxyy(&pis->ctm)) { scale = fabs(pis->ctm.xx); set_ctm = fabs(pis->ctm.yy) != scale; } else if (is_xyyx(&pis->ctm)) { scale = fabs(pis->ctm.xy); set_ctm = fabs(pis->ctm.yx) != scale; } else if ((pis->ctm.xx == pis->ctm.yy && pis->ctm.xy == -pis->ctm.yx) || (pis->ctm.xx == -pis->ctm.yy && pis->ctm.xy == pis->ctm.yx) ) { scale = hypot(pis->ctm.xx, pis->ctm.xy); set_ctm = false; } if (set_ctm) { /* * Adobe Acrobat Reader has limitations on the maximum user * coordinate value. If we scale the matrix down too far, the * coordinates will get too big: limit the scale factor to prevent * this from happening. (This does no harm for other output * formats.) */ double mxx = pis->ctm.xx / vdev->scale.x, mxy = pis->ctm.xy / vdev->scale.y, myx = pis->ctm.yx / vdev->scale.x, myy = pis->ctm.yy / vdev->scale.y; scale = 0.5 * (fabs(mxx) + fabs(mxy) + fabs(myx) + fabs(myy)); pmat->xx = mxx / scale, pmat->xy = mxy / scale; pmat->yx = myx / scale, pmat->yy = myy / scale; pmat->tx = pmat->ty = 0; } *pscale = scale; return (int)set_ctm; }
/* Invert a matrix. Return gs_error_undefinedresult if not invertible. */ int gs_matrix_invert(const gs_matrix * pm, gs_matrix * pmr) { /* We have to be careful about fetch/store order, */ /* because pm might be the same as pmr. */ if (is_xxyy(pm)) { if (is_fzero(pm->xx) || is_fzero(pm->yy)) return_error(gs_error_undefinedresult); pmr->tx = -(pmr->xx = 1.0 / pm->xx) * pm->tx; pmr->xy = 0.0; pmr->yx = 0.0; pmr->ty = -(pmr->yy = 1.0 / pm->yy) * pm->ty; } else { float mxx = pm->xx, myy = pm->yy, mxy = pm->xy, myx = pm->yx; float mtx = pm->tx, mty = pm->ty; /* we declare det as double since on at least some computer (i.e. peeves) declaring it as a float results in different values for pmr depending on whether or not optimization is turned on. I believe this is caused by the compiler keeping the det value in an internal register when optimization is enable. As evidence of this if you add a debugging statement to print out det the optimized code acts the same as the unoptimized code. declearing det as double does not change the CET 10-09.ps output. */ double det = (float)(mxx * myy) - (float)(mxy * myx); /* * We are doing the math as floats instead of doubles to reproduce * the results in page 1 of CET 10-09.ps */ if (det == 0) return_error(gs_error_undefinedresult); pmr->xx = myy / det; pmr->xy = -mxy / det; pmr->yx = -myx / det; pmr->yy = mxx / det; pmr->tx = (((float)(mty * myx) - (float)(mtx * myy))) / det; pmr->ty = (((float)(mtx * mxy) - (float)(mty * mxx))) / det; } return 0; }
/* Return gs_error_undefinedresult if the matrix is not invertible. */ int gs_distance_transform_inverse(floatp dx, floatp dy, const gs_matrix * pmat, gs_point * pdpt) { if (is_xxyy(pmat)) { if (is_fzero(pmat->xx) || is_fzero(pmat->yy)) return_error(gs_error_undefinedresult); pdpt->x = dx / pmat->xx; pdpt->y = dy / pmat->yy; } else if (is_xyyx(pmat)) { if (is_fzero(pmat->xy) || is_fzero(pmat->yx)) return_error(gs_error_undefinedresult); pdpt->x = dy / pmat->xy; pdpt->y = dx / pmat->yx; } else { double det = pmat->xx * pmat->yy - pmat->xy * pmat->yx; if (det == 0) return_error(gs_error_undefinedresult); pdpt->x = (dx * pmat->yy - dy * pmat->yx) / det; pdpt->y = (dy * pmat->xx - dx * pmat->xy) / det; } return 0; }
int gdev_vector_dopath(gx_device_vector *vdev, const gx_path * ppath, gx_path_type_t type, const gs_matrix *pmat) { bool do_close = (type & (gx_path_type_stroke | gx_path_type_always_close)) != 0; gs_fixed_rect rbox; gx_path_rectangular_type rtype = gx_path_is_rectangular(ppath, &rbox); gs_path_enum cenum; gdev_vector_dopath_state_t state; gs_fixed_point line_start, line_end; bool incomplete_line = false; bool need_moveto = false; int code; gdev_vector_dopath_init(&state, vdev, type, pmat); /* * if the path type is stroke, we only recognize closed * rectangles; otherwise, we recognize all rectangles. * Note that for stroking with a transformation, we can't use dorect, * which requires (untransformed) device coordinates. */ if (rtype != prt_none && (!(type & gx_path_type_stroke) || rtype == prt_closed) && (pmat == 0 || is_xxyy(pmat) || is_xyyx(pmat)) && (state.scale_mat.xx == 1.0 && state.scale_mat.yy == 1.0 && is_xxyy(&state.scale_mat) && is_fzero2(state.scale_mat.tx, state.scale_mat.ty)) ) { gs_point p, q; gs_point_transform_inverse((double)rbox.p.x, (double)rbox.p.y, &state.scale_mat, &p); gs_point_transform_inverse((double)rbox.q.x, (double)rbox.q.y, &state.scale_mat, &q); code = vdev_proc(vdev, dorect)(vdev, (fixed)p.x, (fixed)p.y, (fixed)q.x, (fixed)q.y, type); if (code >= 0) return code; /* If the dorect proc failed, use a general path. */ } code = vdev_proc(vdev, beginpath)(vdev, type); if (code < 0) return code; gx_path_enum_init(&cenum, ppath); for (;;) { gs_fixed_point vs[3]; int pe_op = gx_path_enum_next(&cenum, vs); sw: if (type & gx_path_type_optimize) { opt: /* RJW: We fail to optimize gaptos */ if (pe_op == gs_pe_lineto) { if (!incomplete_line) { line_end = vs[0]; incomplete_line = true; continue; } /* * Merge collinear horizontal or vertical line segments * going in the same direction. */ if (vs[0].x == line_end.x) { if (vs[0].x == line_start.x && coord_between(line_start.y, line_end.y, vs[0].y) ) { line_end.y = vs[0].y; continue; } } else if (vs[0].y == line_end.y) { if (vs[0].y == line_start.y && coord_between(line_start.x, line_end.x, vs[0].x) ) { line_end.x = vs[0].x; continue; } } } if (incomplete_line) { if (need_moveto) { /* see gs_pe_moveto case */ code = gdev_vector_dopath_segment(&state, gs_pe_moveto, &line_start); if (code < 0) return code; need_moveto = false; } code = gdev_vector_dopath_segment(&state, gs_pe_lineto, &line_end); if (code < 0) return code; line_start = line_end; incomplete_line = false; goto opt; } } switch (pe_op) { case 0: /* done */ done: code = vdev_proc(vdev, endpath)(vdev, type); return (code < 0 ? code : 0); case gs_pe_curveto: if (need_moveto) { /* see gs_pe_moveto case */ code = gdev_vector_dopath_segment(&state, gs_pe_moveto, &line_start); if (code < 0) return code; need_moveto = false; } line_start = vs[2]; goto draw; case gs_pe_moveto: /* * A bug in Acrobat Reader 4 causes it to draw a single pixel * for a fill with an isolated moveto. If we're doing a fill * without a stroke, defer emitting a moveto until we know that * the subpath has more elements. */ line_start = vs[0]; if (!(type & gx_path_type_stroke) && (type & gx_path_type_fill)) { need_moveto = true; continue; } goto draw; case gs_pe_lineto: case gs_pe_gapto: if (need_moveto) { /* see gs_pe_moveto case */ code = gdev_vector_dopath_segment(&state, gs_pe_moveto, &line_start); if (code < 0) return code; need_moveto = false; } line_start = vs[0]; goto draw; case gs_pe_closepath: if (need_moveto) { /* see gs_pe_moveto case */ need_moveto = false; continue; } if (!do_close) { pe_op = gx_path_enum_next(&cenum, vs); if (pe_op == 0) goto done; code = gdev_vector_dopath_segment(&state, gs_pe_closepath, vs); if (code < 0) return code; goto sw; } /* falls through */ draw: code = gdev_vector_dopath_segment(&state, pe_op, vs); if (code < 0) return code; } incomplete_line = false; /* only needed if optimizing */ } }
static int pdf_pattern(gx_device_pdf *pdev, const gx_drawing_color *pdc, const gx_color_tile *p_tile, const gx_color_tile *m_tile, cos_stream_t *pcs_image, pdf_resource_t **ppres) { pdf_resource_t *pres; int code = pdf_alloc_resource(pdev, resourcePattern, pdc->mask.id, ppres, 0L); cos_stream_t *pcos; cos_dict_t *pcd; cos_dict_t *pcd_Resources = cos_dict_alloc(pdev, "pdf_pattern(Resources)"); const gx_color_tile *tile = (p_tile ? p_tile : m_tile); const gx_strip_bitmap *btile = (p_tile ? &p_tile->tbits : &m_tile->tmask); bool mask = p_tile == 0; gs_point step; gs_matrix smat; if (code < 0) return code; if (!tile_size_ok(pdev, p_tile, m_tile)) return_error(gs_error_limitcheck); /* * We currently can't handle Patterns whose X/Y step isn't parallel * to the coordinate axes. */ if (is_xxyy(&tile->step_matrix)) step.x = tile->step_matrix.xx, step.y = tile->step_matrix.yy; else if (is_xyyx(&tile->step_matrix)) step.x = tile->step_matrix.yx, step.y = tile->step_matrix.xy; else return_error(gs_error_rangecheck); if (pcd_Resources == 0) return_error(gs_error_VMerror); gs_make_identity(&smat); smat.xx = btile->rep_width / (pdev->HWResolution[0] / 72.0); smat.yy = btile->rep_height / (pdev->HWResolution[1] / 72.0); smat.tx = tile->step_matrix.tx / (pdev->HWResolution[0] / 72.0); smat.ty = tile->step_matrix.ty / (pdev->HWResolution[1] / 72.0); pres = *ppres; { cos_dict_t *pcd_XObject = cos_dict_alloc(pdev, "pdf_pattern(XObject)"); char key[MAX_REF_CHARS + 3]; cos_value_t v; if (pcd_XObject == 0) return_error(gs_error_VMerror); sprintf(key, "/R%ld", pcs_image->id); COS_OBJECT_VALUE(&v, pcs_image); if ((code = cos_dict_put(pcd_XObject, (byte *)key, strlen(key), &v)) < 0 || (code = cos_dict_put_c_key_object(pcd_Resources, "/XObject", COS_OBJECT(pcd_XObject))) < 0 ) return code; } if ((code = cos_dict_put_c_strings(pcd_Resources, "/ProcSet", (mask ? "[/PDF/ImageB]" : "[/PDF/ImageC]"))) < 0) return code; cos_become(pres->object, cos_type_stream); pcos = (cos_stream_t *)pres->object; pcd = cos_stream_dict(pcos); if ((code = cos_dict_put_c_key_int(pcd, "/PatternType", 1)) < 0 || (code = cos_dict_put_c_key_int(pcd, "/PaintType", (mask ? 2 : 1))) < 0 || (code = cos_dict_put_c_key_int(pcd, "/TilingType", tile->tiling_type)) < 0 || (code = cos_dict_put_c_key_object(pcd, "/Resources", COS_OBJECT(pcd_Resources))) < 0 || (code = cos_dict_put_c_strings(pcd, "/BBox", "[0 0 1 1]")) < 0 || (code = cos_dict_put_matrix(pcd, "/Matrix", &smat)) < 0 || (code = cos_dict_put_c_key_real(pcd, "/XStep", step.x / btile->rep_width)) < 0 || (code = cos_dict_put_c_key_real(pcd, "/YStep", step.y / btile->rep_height)) < 0 ) { return code; } { char buf[MAX_REF_CHARS + 6 + 1]; /* +6 for /R# Do\n */ sprintf(buf, "/R%ld Do\n", pcs_image->id); cos_stream_add_bytes(pcos, (const byte *)buf, strlen(buf)); } return 0; }