/* * 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; }
/* We take the trouble to do this efficiently in the simple cases. */ int gs_rectfill(gs_state * pgs, const gs_rect * pr, uint count) { const gs_rect *rlist = pr; gx_clip_path *pcpath; uint rcount = count; int code; gx_device * pdev = pgs->device; gx_device_color *pdc = gs_currentdevicecolor_inline(pgs); const gs_imager_state *pis = (const gs_imager_state *)pgs; bool hl_color_available = gx_hld_is_hl_color_available(pis, pdc); bool hl_color = (hl_color_available && dev_proc(pdev, dev_spec_op)(pdev, gxdso_supports_hlcolor, NULL, 0)); bool center_of_pixel = (pgs->fill_adjust.x == 0 && pgs->fill_adjust.y == 0); /* Processing a fill object operation */ dev_proc(pgs->device, set_graphics_type_tag)(pgs->device, GS_PATH_TAG); code = gx_set_dev_color(pgs); if (code != 0) return code; if ((is_fzero2(pgs->ctm.xy, pgs->ctm.yx) || is_fzero2(pgs->ctm.xx, pgs->ctm.yy)) && gx_effective_clip_path(pgs, &pcpath) >= 0 && clip_list_is_rectangle(gx_cpath_list(pcpath)) && (hl_color || pdc->type == gx_dc_type_pure || pdc->type == gx_dc_type_ht_binary || pdc->type == gx_dc_type_ht_colored) && gs_state_color_load(pgs) >= 0 && (*dev_proc(pdev, get_alpha_bits)) (pdev, go_graphics) <= 1 && (!pgs->overprint || !pgs->effective_overprint_mode) ) { uint i; gs_fixed_rect clip_rect; gx_cpath_inner_box(pcpath, &clip_rect); /* We should never plot anything for an empty clip rectangle */ if ((clip_rect.p.x >= clip_rect.q.x) && (clip_rect.p.y >= clip_rect.q.y)) return 0; for (i = 0; i < count; ++i) { gs_fixed_point p, q; gs_fixed_rect draw_rect; if (gs_point_transform2fixed(&pgs->ctm, pr[i].p.x, pr[i].p.y, &p) < 0 || gs_point_transform2fixed(&pgs->ctm, pr[i].q.x, pr[i].q.y, &q) < 0 ) { /* Switch to the slow algorithm. */ goto slow; } draw_rect.p.x = min(p.x, q.x); draw_rect.p.y = min(p.y, q.y); draw_rect.q.x = max(p.x, q.x); draw_rect.q.y = max(p.y, q.y); if (hl_color) { rect_intersect(draw_rect, clip_rect); /* We do pass on 0 extant rectangles to high level devices. It isn't clear how a client and an output device should interact if one uses a center of pixel algorithm and the other uses any part of pixel. For now we punt and just pass the high level rectangle on without adjustment. */ if (draw_rect.p.x <= draw_rect.q.x && draw_rect.p.y <= draw_rect.q.y) { code = dev_proc(pdev, fill_rectangle_hl_color)(pdev, &draw_rect, pis, pdc, pcpath); if (code < 0) return code; } } else { int x, y, w, h; rect_intersect(draw_rect, clip_rect); if (center_of_pixel) { draw_rect.p.x = fixed_rounded(draw_rect.p.x); draw_rect.p.y = fixed_rounded(draw_rect.p.y); draw_rect.q.x = fixed_rounded(draw_rect.q.x); draw_rect.q.y = fixed_rounded(draw_rect.q.y); } else { /* any part of pixel rule - touched */ draw_rect.p.x = fixed_floor(draw_rect.p.x); draw_rect.p.y = fixed_floor(draw_rect.p.y); draw_rect.q.x = fixed_ceiling(draw_rect.q.x); draw_rect.q.y = fixed_ceiling(draw_rect.q.y); } x = fixed2int(draw_rect.p.x); y = fixed2int(draw_rect.p.y); w = fixed2int(draw_rect.q.x) - x; h = fixed2int(draw_rect.q.y) - y; /* clients that use the "any part of pixel" rule also fill 0 areas. This is true of current graphics library clients but not a general rule. */ if (!center_of_pixel) { if (w == 0) w = 1; /* yes Adobe Acrobat 8, seems to back up the y coordinate when the width is 0, sigh. */ if (h == 0) { y--; h = 1; } } if (gx_fill_rectangle(x, y, w, h, pdc, pgs) < 0) goto slow; } } return 0; slow:rlist = pr + i; rcount = count - i; } { bool do_save = !gx_path_is_null(pgs->path); if (do_save) { if ((code = gs_gsave(pgs)) < 0) return code; gs_newpath(pgs); } if ((code = gs_rectappend(pgs, rlist, rcount)) < 0 || (code = gs_fill(pgs)) < 0 ) DO_NOTHING; if (do_save) gs_grestore(pgs); else if (code < 0) gs_newpath(pgs); } return code; }
static int bbox_fill_path(gx_device * dev, const gs_imager_state * pis, gx_path * ppath, const gx_fill_params * params, const gx_device_color * pdevc, const gx_clip_path * pcpath) { gx_device_bbox *const bdev = (gx_device_bbox *) dev; gx_device *tdev = bdev->target; dev_proc_fill_path((*fill_path)) = (tdev == 0 ? dev_proc(&gs_null_device, fill_path) : dev_proc(tdev, fill_path)); int code; if (ppath == NULL) { /* A special handling of shfill with no path. */ gs_fixed_rect ibox; gs_fixed_point adjust; if (pcpath == NULL) return 0; gx_cpath_inner_box(pcpath, &ibox); adjust = params->adjust; adjust_box(&ibox, adjust); BBOX_ADD_RECT(bdev, ibox.p.x, ibox.p.y, ibox.q.x, ibox.q.y); return 0; } else if (!GX_DC_IS_TRANSPARENT(pdevc, bdev) && !gx_path_is_void(ppath)) { gs_fixed_rect ibox; gs_fixed_point adjust; if (gx_path_bbox(ppath, &ibox) < 0) return 0; adjust = params->adjust; adjust_box(&ibox, adjust); /* * If the path lies within the already accumulated box, just draw * on the target. */ if (BBOX_IN_RECT(bdev, &ibox)) return fill_path(tdev, pis, ppath, params, pdevc, pcpath); /* * If the target uses the default algorithm, just draw on the * bbox device. */ if (tdev != 0 && fill_path == gx_default_fill_path) return fill_path(dev, pis, ppath, params, pdevc, pcpath); /* Draw on the target now. */ code = fill_path(tdev, pis, ppath, params, pdevc, pcpath); if (code < 0) return code; 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; code = gx_default_fill_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; } else return fill_path(tdev, pis, ppath, params, pdevc, pcpath); }
/* We take the trouble to do this efficiently in the simple cases. */ int gs_rectfill(gs_state * pgs, const gs_rect * pr, uint count) { const gs_rect *rlist = pr; gx_clip_path *pcpath; uint rcount = count; int code; gx_device * pdev = pgs->device; gx_device_color *pdc = pgs->dev_color; const gs_imager_state *pis = (const gs_imager_state *)pgs; bool hl_color_available = gx_hld_is_hl_color_available(pis, pdc); gs_fixed_rect empty = {{0, 0}, {0, 0}}; bool hl_color = (hl_color_available && dev_proc(pdev, fill_rectangle_hl_color)(pdev, &empty, pis, pdc, NULL) == 0); gx_set_dev_color(pgs); if ((is_fzero2(pgs->ctm.xy, pgs->ctm.yx) || is_fzero2(pgs->ctm.xx, pgs->ctm.yy)) && gx_effective_clip_path(pgs, &pcpath) >= 0 && clip_list_is_rectangle(gx_cpath_list(pcpath)) && (hl_color || pdc->type == gx_dc_type_pure || pdc->type == gx_dc_type_ht_binary || pdc->type == gx_dc_type_ht_colored /* DeviceN todo: add wts case */) && gs_state_color_load(pgs) >= 0 && (*dev_proc(pdev, get_alpha_bits)) (pdev, go_graphics) <= 1 && (!pgs->overprint || !pgs->effective_overprint_mode) ) { uint i; gs_fixed_rect clip_rect; gx_cpath_inner_box(pcpath, &clip_rect); for (i = 0; i < count; ++i) { gs_fixed_point p, q; gs_fixed_rect draw_rect; if (gs_point_transform2fixed(&pgs->ctm, pr[i].p.x, pr[i].p.y, &p) < 0 || gs_point_transform2fixed(&pgs->ctm, pr[i].q.x, pr[i].q.y, &q) < 0 ) { /* Switch to the slow algorithm. */ goto slow; } draw_rect.p.x = min(p.x, q.x); draw_rect.p.y = min(p.y, q.y); draw_rect.q.x = max(p.x, q.x); draw_rect.q.y = max(p.y, q.y); if (hl_color) { rect_intersect(draw_rect, clip_rect); if (draw_rect.p.x < draw_rect.q.x && draw_rect.p.y < draw_rect.q.y) { code = dev_proc(pdev, fill_rectangle_hl_color)(pdev, &draw_rect, pis, pdc, pcpath); if (code < 0) return code; } } else { int x, y, w, h; draw_rect.p.x -= pgs->fill_adjust.x; draw_rect.p.y -= pgs->fill_adjust.x; draw_rect.q.x += pgs->fill_adjust.x; draw_rect.q.y += pgs->fill_adjust.x; rect_intersect(draw_rect, clip_rect); x = fixed2int_pixround(draw_rect.p.x); y = fixed2int_pixround(draw_rect.p.y); w = fixed2int_pixround(draw_rect.q.x) - x; h = fixed2int_pixround(draw_rect.q.y) - y; if (w > 0 && h > 0) if (gx_fill_rectangle(x, y, w, h, pdc, pgs) < 0) goto slow; } } return 0; slow:rlist = pr + i; rcount = count - i; } { bool do_save = !gx_path_is_null(pgs->path); if (do_save) { if ((code = gs_gsave(pgs)) < 0) return code; gs_newpath(pgs); } if ((code = gs_rectappend(pgs, rlist, rcount)) < 0 || (code = gs_fill(pgs)) < 0 ) DO_NOTHING; if (do_save) gs_grestore(pgs); else if (code < 0) gs_newpath(pgs); } return code; }