/* <userpath> <matrix> ustrokepath - */ static int zustrokepath(i_ctx_t *i_ctx_p) { gx_path save; gs_matrix saved_matrix; int npop, code = gs_currentmatrix(igs, &saved_matrix); if (code < 0) return code; /* Save and reset the path. */ gx_path_init_local(&save, imemory); gx_path_assign_preserve(&save, igs->path); if ((code = npop = upath_stroke(i_ctx_p, NULL, false)) < 0 || (code = gs_strokepath(igs)) < 0 ) { gx_path_assign_free(igs->path, &save); return code; } /* * If a matrix was specified then restore the previous matrix. */ if (npop > 1) { if ((code = gs_setmatrix(igs, &saved_matrix)) < 0) { gx_path_assign_free(igs->path, &save); return code; } } gx_path_free(&save, "ustrokepath"); pop(npop); return 0; }
/* Start enumerating a path */ int gs_path_enum_copy_init(gs_path_enum * penum, const gs_state * pgs, bool copy) { gs_memory_t *mem = pgs->memory; if (copy) { gx_path *copied_path = gx_path_alloc(mem, "gs_path_enum_init"); int code; if (copied_path == 0) return_error(gs_error_VMerror); code = gx_path_copy(pgs->path, copied_path); if (code < 0) { gx_path_free(copied_path, "gs_path_enum_init"); return code; } gx_path_enum_init(penum, copied_path); penum->copied_path = copied_path; } else { gx_path_enum_init(penum, pgs->path); } penum->memory = mem; gs_currentmatrix(pgs, &penum->mat); return 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; }
/* Intersect a clipping path a shading BBox. */ int gx_dc_pattern2_clip_with_bbox(const gx_device_color * pdevc, gx_device * pdev, gx_clip_path *cpath_local, const gx_clip_path **ppcpath1) { 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; gx_path box_path; gs_memory_t *mem = (*ppcpath1 != NULL ? (*ppcpath1)->path.memory : pdev->memory); int code; gx_path_init_local(&box_path, mem); code = gx_dc_shading_path_add_box(&box_path, pdevc); if (code == gs_error_limitcheck) { /* Ignore huge BBox - bug 689027. */ code = 0; } else { if (code >= 0) { gx_cpath_init_local_shared(cpath_local, *ppcpath1, mem); code = gx_cpath_intersect(cpath_local, &box_path, gx_rule_winding_number, (gs_imager_state *)pinst->saved); *ppcpath1 = cpath_local; } } gx_path_free(&box_path, "gx_default_fill_path(path_bbox)"); } return 0; }
/* Clean up after a pathforall. */ void gs_path_enum_cleanup(gs_path_enum * penum) { if (penum->copied_path != 0) { gx_path_free(penum->copied_path, "gs_path_enum_cleanup"); penum->path = 0; penum->copied_path = 0; } }
/* * Copy the current path, because it was shared. */ static int path_alloc_copy(gx_path * ppath) { gx_path path_new; int code; gx_path_init_local(&path_new, ppath->memory); code = gx_path_copy(ppath, &path_new); if (code < 0) { gx_path_free(&path_new, "path_alloc_copy error"); return code; } ppath->last_charpath_segment = 0; return gx_path_assign_free(ppath, &path_new); }
/* shfill */ int gs_shfill(gs_state * pgs, const gs_shading_t * psh) { /* * shfill is equivalent to filling the current clipping path (or, if * clipping, its bounding box) with the shading, disregarding the * Background if any. In order to produce reasonable high-level output, * we must actually implement this by calling gs_fill rather than * gs_shading_fill_path. However, filling with a shading pattern does * paint the Background, so if necessary, we construct a copy of the * shading with Background removed. */ gs_pattern2_template_t pat; gx_path cpath; gs_matrix imat; gs_client_color cc; gs_color_space cs; gx_device_color devc; int code; gs_pattern2_init(&pat); pat.Shading = psh; gs_make_identity(&imat); code = gs_make_pattern(&cc, (gs_pattern_template_t *)&pat, &imat, pgs, pgs->memory); if (code < 0) return code; code = gs_pattern2_set_shfill(&cc); if (code < 0) return code; gs_cspace_init(&cs, &gs_color_space_type_Pattern, pgs->memory, false); cs.params.pattern.has_base_space = false; code = cs.type->remap_color(&cc, &cs, &devc, (gs_imager_state *)pgs, pgs->device, gs_color_select_texture); if (code >= 0) { gx_path_init_local(&cpath, pgs->memory); code = gx_cpath_to_path(pgs->clip_path, &cpath); if (code >= 0) code = gx_fill_path(&cpath, &devc, pgs, gx_rule_winding_number, fixed_0, fixed_0); gx_path_free(&cpath, "gs_shfill"); } gs_pattern_reference(&cc, -1); return code; }
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; }
int gs_flattenpath(gs_state * pgs) { gx_path *ppath = pgs->path; gx_path fpath; int code; if (!gx_path_has_curves(ppath)) return 0; /* nothing to do */ gx_path_init_local(&fpath, ppath->memory); code = gx_path_add_flattened_accurate(ppath, &fpath, pgs->flatness, pgs->accurate_curves); if (code < 0) { gx_path_free(&fpath, "gs_flattenpath"); return code; } gx_path_assign_free(ppath, &fpath); return 0; }
/* Clip to a list of rectangles. */ int gs_rectclip(gs_state * pgs, const gs_rect * pr, uint count) { int code; gx_path save; gx_path_init_local(&save, pgs->memory); gx_path_assign_preserve(&save, pgs->path); gs_newpath(pgs); if ((code = gs_rectappend_compat(pgs, pr, count, true)) < 0 || (code = gs_clip(pgs)) < 0 ) { gx_path_assign_free(pgs->path, &save); return code; } gx_path_free(&save, "gs_rectclip"); gs_newpath(pgs); return 0; }
int gs_dashpath(gs_state * pgs) { gx_path *ppath; gx_path fpath; int code; if (gs_currentdash_length(pgs) == 0) return 0; /* no dash pattern */ code = gs_flattenpath(pgs); if (code < 0) return code; ppath = pgs->path; gx_path_init_local(&fpath, ppath->memory); code = gx_path_add_dash_expansion(ppath, &fpath, (gs_imager_state *)pgs); if (code < 0) { gx_path_free(&fpath, "gs_dashpath"); return code; } gx_path_assign_free(pgs->path, &fpath); return 0; }
/* * Assign one path to another and free the first path at the same time. * (This may do less work than assign_preserve + free.) */ int gx_path_assign_free(gx_path * ppto, gx_path * ppfrom) { /* * Detect the special case where both paths have non-shared local * segments, since we can avoid allocating new segments in this case. */ if (ppto->segments == &ppto->local_segments && ppfrom->segments == &ppfrom->local_segments && !gx_path_is_shared(ppto) ) { #define fromsegs (&ppfrom->local_segments) #define tosegs (&ppto->local_segments) gs_memory_t *mem = ppto->memory; gx_path_allocation_t allocation = ppto->allocation; rc_free_path_segments_local(tosegs->rc.memory, tosegs, "gx_path_assign_free"); /* We record a bogus reference to fromsegs, which */ /* gx_path_free will undo. */ *ppto = *ppfrom; rc_increment(fromsegs); ppto->segments = tosegs; ppto->memory = mem; ppto->allocation = allocation; #undef fromsegs #undef tosegs } else { /* In all other cases, just do assign + free. */ int code = gx_path_assign_preserve(ppto, ppfrom); if (code < 0) return code; } gx_path_free(ppfrom, "gx_path_assign_free"); return 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; }
/* * Return the effective clipping path of a graphics state. Sometimes this * is the intersection of the clip path and the view clip path; sometimes it * is just the clip path. We aren't sure what the correct algorithm is for * this: for now, we use view clipping unless the current device is a memory * device. This takes care of the most important case, where the current * device is a cache device. */ int gx_effective_clip_path(gs_state * pgs, gx_clip_path ** ppcpath) { gs_id view_clip_id = (pgs->view_clip == 0 || pgs->view_clip->rule == 0 ? gs_no_id : pgs->view_clip->id); if (gs_device_is_memory(pgs->device)) { *ppcpath = pgs->clip_path; return 0; } if (pgs->effective_clip_id == pgs->clip_path->id && pgs->effective_view_clip_id == view_clip_id ) { *ppcpath = pgs->effective_clip_path; return 0; } /* Update the cache. */ if (view_clip_id == gs_no_id) { if (!pgs->effective_clip_shared) gx_cpath_free(pgs->effective_clip_path, "gx_effective_clip_path"); pgs->effective_clip_path = pgs->clip_path; pgs->effective_clip_shared = true; } else { gs_fixed_rect cbox, vcbox; gx_cpath_inner_box(pgs->clip_path, &cbox); gx_cpath_outer_box(pgs->view_clip, &vcbox); if (rect_within(vcbox, cbox)) { if (!pgs->effective_clip_shared) gx_cpath_free(pgs->effective_clip_path, "gx_effective_clip_path"); pgs->effective_clip_path = pgs->view_clip; pgs->effective_clip_shared = true; } else { /* Construct the intersection of the two clip paths. */ int code; gx_clip_path ipath; gx_path vpath; gx_clip_path *npath = pgs->effective_clip_path; if (pgs->effective_clip_shared) { npath = gx_cpath_alloc(pgs->memory, "gx_effective_clip_path"); if (npath == 0) return_error(gs_error_VMerror); } gx_cpath_init_local(&ipath, pgs->memory); code = gx_cpath_assign_preserve(&ipath, pgs->clip_path); if (code < 0) return code; gx_path_init_local(&vpath, pgs->memory); code = gx_cpath_to_path(pgs->view_clip, &vpath); if (code < 0 || (code = gx_cpath_clip(pgs, &ipath, &vpath, gx_rule_winding_number)) < 0 || (code = gx_cpath_assign_free(npath, &ipath)) < 0 ) DO_NOTHING; gx_path_free(&vpath, "gx_effective_clip_path"); gx_cpath_free(&ipath, "gx_effective_clip_path"); if (code < 0) return code; pgs->effective_clip_path = npath; pgs->effective_clip_shared = false; } } pgs->effective_clip_id = pgs->effective_clip_path->id; pgs->effective_view_clip_id = view_clip_id; *ppcpath = pgs->effective_clip_path; return 0; }
static int bbox_stroke_path(gx_device * dev, const gs_imager_state * pis, gx_path * ppath, const gx_stroke_params * params, const gx_drawing_color * pdevc, const gx_clip_path * pcpath) { gx_device_bbox *const bdev = (gx_device_bbox *) dev; gx_device *tdev = bdev->target; /* Skip the call if there is no target. */ int code = (tdev == 0 ? 0 : dev_proc(tdev, stroke_path)(tdev, pis, ppath, params, pdevc, pcpath)); if (!GX_DC_IS_TRANSPARENT(pdevc, bdev)) { gs_fixed_rect ibox; gs_fixed_point expand; if (gx_stroke_path_expansion(pis, ppath, &expand) == 0 && gx_path_bbox(ppath, &ibox) >= 0 ) { /* The fast result is exact. */ adjust_box(&ibox, expand); } else { /* * The result is not exact. Compute an exact result using * strokepath. */ gx_path *spath = gx_path_alloc(pis->memory, "bbox_stroke_path"); int code = 0; if (spath) code = gx_imager_stroke_add(ppath, spath, dev, pis); else code = -1; if (code >= 0) code = gx_path_bbox(spath, &ibox); if (code < 0) { ibox.p.x = ibox.p.y = min_fixed; ibox.q.x = ibox.q.y = max_fixed; } if (spath) gx_path_free(spath, "bbox_stroke_path"); } if (pcpath != NULL && !gx_cpath_includes_rectangle(pcpath, ibox.p.x, ibox.p.y, ibox.q.x, ibox.q.y) ) { /* Let the target do the drawing, but break down the */ /* fill path into pieces for computing the bounding box. */ gx_drawing_color devc; set_nonclient_dev_color(&devc, bdev->black); /* any non-white color will do */ bdev->target = NULL; gx_default_stroke_path(dev, pis, ppath, params, &devc, pcpath); bdev->target = tdev; } else { /* Just use the path bounding box. */ BBOX_ADD_RECT(bdev, ibox.p.x, ibox.p.y, ibox.q.x, ibox.q.y); } } return code; }
void hpgl_do_reset(pcl_state_t * pcs, pcl_reset_type_t type) { /* pgframe.c (Chapter 18) */ hpgl_args_t hpgl_args; if ((type & (pcl_reset_initial | pcl_reset_printer | pcl_reset_cold)) != 0) { if ((type & (pcl_reset_initial | pcl_reset_cold)) != 0) { gx_path_alloc_contained(&pcs->g.polygon.buffer.path, pcs->memory, "hpgl_do_reset polygon buffer"); gs_setlimitclamp(pcs->pgs, true); } else gx_path_new(&pcs->g.polygon.buffer.path); /* provide default anchor point, plot size and picture frame size */ hpgl_default_coordinate_system(pcs); /* we should not have a path at this point but we make sure */ hpgl_clear_current_path(pcs); /* Initialize stick/arc font instances */ hpgl_initialize_stick_fonts(pcs); /* intialize subpolygon started hack flag */ pcs->g.subpolygon_started = false; /* indicates a line down operation has been done in polygon mode */ pcs->g.have_drawn_in_path = false; /* execute only the implicit portion of IN */ hpgl_IN_implicit(pcs); /* we select the default pen 1 here, oddly, IN does not select the default pen even though it sets pen widths and units of measure */ pcs->g.pen.selected = 1; } /* NB check all of these */ if ((type & pcl_reset_page_params) != 0) { /* provide default anchor point, plot size and picture frame size. Oddly HP does not reset the scaling parameters when the page size is changed. */ int scale_type = pcs->g.scaling_type; hpgl_scaling_params_t params = pcs->g.scaling_params; hpgl_default_coordinate_system(pcs); /* restore the scaling parameter. */ pcs->g.scaling_type = scale_type; pcs->g.scaling_params = params; hpgl_args_setup(&hpgl_args); hpgl_IW(&hpgl_args, pcs); hpgl_args_set_int(&hpgl_args, 0); hpgl_PM(&hpgl_args, pcs); hpgl_args_set_int(&hpgl_args, 2); hpgl_PM(&hpgl_args, pcs); hpgl_args_setup(&hpgl_args); hpgl_IP(&hpgl_args, pcs); } if ((type & pcl_reset_picture_frame) != 0) { /* this shouldn't happen. Picture frame side effects are handled directly by the command picture frame command. */ dmprintf(pcs->memory, "PCL reset picture frame received\n"); } if ((type & pcl_reset_overlay) != 0) /* ignore return */ (void)hpgl_reset_overlay(pcs); if ((type & (pcl_reset_plot_size)) != 0) { /* this shouldn't happen. Plot size side effects are handled directly by the command picture frame command. */ dmprintf(pcs->memory, "PCL reset plot received\n"); } if ((type & (pcl_reset_permanent)) != 0) { gx_path_free(&pcs->g.polygon.buffer.path, "hpgl_do_reset polygon buffer"); /* if we have allocated memory for a stick font free the memory */ hpgl_free_stick_fonts(pcs); } return; }
/* shfill */ int gs_shfill(gs_gstate * pgs, const gs_shading_t * psh) { /* * shfill is equivalent to filling the current clipping path (or, if * clipping, its bounding box) with the shading, disregarding the * Background if any. In order to produce reasonable high-level output, * we must implement this by calling gs_fill_path. */ gs_pattern2_template_t pat; gs_matrix imat; gs_client_color cc; gs_color_space *pcs; gx_device_color devc; int code; /* Must install the shading color space to allow check_DeviceN_component_names initialize the color component map. */ /* Don't bother with saving the old color space, color, and cie_joint_caches, because .shfill is always called within gsave-grestore - see gs/lib . */ code = gs_setcolorspace(pgs, psh->params.ColorSpace); if (code < 0) return 0; if (psh->params.cie_joint_caches != NULL) { pgs->cie_joint_caches = psh->params.cie_joint_caches; rc_increment(pgs->cie_joint_caches); } gs_pattern2_init(&pat); pat.Shading = psh; gs_make_identity(&imat); code = gs_make_pattern(&cc, (gs_pattern_template_t *)&pat, &imat, pgs, pgs->memory); if (code < 0) return code; code = gs_pattern2_set_shfill(&cc); if (code < 0) return code; pcs = gs_cspace_alloc(pgs->memory, &gs_color_space_type_Pattern); if (pcs == NULL) return_error(gs_error_VMerror); pcs->params.pattern.has_base_space = false; code = pcs->type->remap_color(&cc, pcs, &devc, pgs, pgs->device, gs_color_select_texture); if (code >= 0) { gx_device *dev = pgs->device; bool need_path = !dev_proc(dev, dev_spec_op)(dev, gxdso_pattern_shfill_doesnt_need_path, NULL, 0); if (need_path) { gx_path cpath; gx_path_init_local(&cpath, pgs->memory); code = gx_cpath_to_path(pgs->clip_path, &cpath); if (code >= 0) code = gx_fill_path(&cpath, &devc, pgs, gx_rule_winding_number, pgs->fill_adjust.x, pgs->fill_adjust.y); gx_path_free(&cpath, "gs_shfill"); } else code = gx_fill_path(NULL, &devc, pgs, gx_rule_winding_number, pgs->fill_adjust.x, pgs->fill_adjust.y); } rc_decrement_cs(pcs, "gs_shfill"); gs_pattern_reference(&cc, -1); return code; }
/* Stroke the current path */ int gs_stroke(gs_state * pgs) { int code; /* * If we're inside a charpath, just merge the current path * into the parent's path. */ if (pgs->in_charpath) { if (pgs->in_charpath == cpm_true_charpath) { /* * A stroke inside a true charpath should do the * equivalent of strokepath. */ code = gs_strokepath(pgs); if (code < 0) return code; } code = gx_path_add_char_path(pgs->show_gstate->path, pgs->path, pgs->in_charpath); } if (gs_is_null_device(pgs->device)) { /* Handle separately to prevent gs_state_color_load. */ gs_newpath(pgs); code = 0; } else { int abits, acode, rcode = 0; /* to distinguish text from vectors we hackly look at the target device 1 bit per component is a cache and this is text else it is a path */ if (gx_device_has_color(gs_currentdevice(pgs))) gs_set_object_tag(pgs, GS_PATH_TAG); else gs_set_object_tag(pgs, GS_TEXT_TAG); /* Here we need to distinguish text from vectors to compute the object tag. Actually we need to know whether this function is called to rasterize a character, or to rasterize a vector graphics to the output device. Currently we assume it works for the bitrgbtags device only, which is a low level device with a 4-component color model. We use the fact that with printers a character is usually being rendered to a 1bpp cache device rather than to the output device. Therefore we hackly look whether the target device "has a color" : either it's a multicomponent color model, or it is not gray (such as a yellow separation). This check has several limitations : 1. It doesn't work with -dNOCACHE. 2. It doesn't work with large characters, which cannot fit into a cache cell and thus they render directly to the output device. 3. It doesn't work for TextAlphaBits=2 or 4. We don't care of this case because text antialiasing usually usn't applied to printers. 4. It doesn't work for things like with "(xyz) true charpath stroke". That's unfortunate, we'd like to improve someday. 5. It doesn't work for high level devices when a Type 3 character is being constructed. This case is not important for low level devices (which a printer is), because low level device doesn't accept Type 3 charproc streams immediately. */ if (gx_device_has_color(gs_currentdevice(pgs))) { gs_set_object_tag(pgs, GS_PATH_TAG); } else { gs_set_object_tag(pgs, GS_TEXT_TAG); } gx_set_dev_color(pgs); code = gs_state_color_load(pgs); if (code < 0) return code; abits = alpha_buffer_bits(pgs); if (abits > 1) { /* * Expand the bounding box by the line width. * This is expensive to compute, so we only do it * if we know we're going to buffer. */ float xxyy = fabs(pgs->ctm.xx) + fabs(pgs->ctm.yy); float xyyx = fabs(pgs->ctm.xy) + fabs(pgs->ctm.yx); float scale = (float)(1 << (abits / 2)); float orig_width = gs_currentlinewidth(pgs); float new_width = orig_width * scale; fixed extra_adjust = float2fixed(max(xxyy, xyyx) * new_width / 2); float orig_flatness = gs_currentflat(pgs); gx_path spath; /* Scale up the line width, dash pattern, and flatness. */ if (extra_adjust < fixed_1) extra_adjust = fixed_1; acode = alpha_buffer_init(pgs, pgs->fill_adjust.x + extra_adjust, pgs->fill_adjust.y + extra_adjust, abits); if (acode < 0) return acode; gs_setlinewidth(pgs, new_width); scale_dash_pattern(pgs, scale); gs_setflat(pgs, orig_flatness * scale); /* * The alpha-buffer device requires that we fill the * entire path as a single unit. */ gx_path_init_local(&spath, pgs->memory); code = gx_stroke_add(pgs->path, &spath, pgs); gs_setlinewidth(pgs, orig_width); scale_dash_pattern(pgs, 1.0 / scale); if (code >= 0) code = gx_fill_path(&spath, pgs->dev_color, pgs, gx_rule_winding_number, pgs->fill_adjust.x, pgs->fill_adjust.y); gs_setflat(pgs, orig_flatness); gx_path_free(&spath, "gs_stroke"); if (acode > 0) rcode = alpha_buffer_release(pgs, code >= 0); } else code = gx_stroke_fill(pgs->path, pgs); if (code >= 0) gs_newpath(pgs); if (code >= 0 && rcode < 0) code = rcode; } return code; }