summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobin Watts <Robin.Watts@artifex.com>2021-12-22 19:05:45 +0000
committerRobin Watts <Robin.Watts@artifex.com>2022-01-04 15:11:54 +0000
commit07cfc24d532beadf23d50effa3b8a0bca45b849d (patch)
tree28de15b17a8a3eaa3822c2c651c18165838cd8d0
parent585da5043b8a3aafc1ca361c56336a980d38b1f2 (diff)
downloadghostpdl-07cfc24d532beadf23d50effa3b8a0bca45b849d.tar.gz
Rework the optimisation for stroked patterns.ghostpdl-9.56.0-test-base-4
Until recently, if we were asked to stroke a pattern we would perform the stroke, and then for each segment of the stroke do the fill with the pattern. This meant that we decomposed shadings many times. As an optimisation, we tried to perform the same trick as used by the fill code; namely to perform a single rectangle fill, and to clip that. That way the decomposition only happens once. Unfortunately the mechanism we used to do that had problems. We convert the stroke to a path to be filled and then called the 'fill with a pattern' code on that. The conversion of the stroke to a path to be filled would be slightly different in many cases, and in particular prevents the 'thin_line' code ever being triggered. This produces nasty thickening of strokes. So, we recode the optimisation here. We use a clip path accumulator device, and stroke to that. That gives us an exact match. We then fill that.
-rw-r--r--base/gsptype2.c2
-rw-r--r--base/gxstroke.c73
2 files changed, 52 insertions, 23 deletions
diff --git a/base/gsptype2.c b/base/gsptype2.c
index b9ef6a05c..9a6b7d838 100644
--- a/base/gsptype2.c
+++ b/base/gsptype2.c
@@ -222,7 +222,7 @@ gs_pattern2_set_color(const gs_client_color * pcc, gs_gstate * pgs)
/* Fill a rectangle with a PatternType 2 color. */
/* WARNING: This function doesn't account the shading BBox
- to allow the clipent to optimize the clipping
+ to allow the client to optimize the clipping
with changing the order of clip paths and rects.
The client must clip with the shading BBox before calling this function. */
static int
diff --git a/base/gxstroke.c b/base/gxstroke.c
index 04f59a6e5..c7d8e5696 100644
--- a/base/gxstroke.c
+++ b/base/gxstroke.c
@@ -36,6 +36,7 @@
#include "gzcpath.h"
#include "gxpaint.h"
#include "gsstate.h" /* for gs_currentcpsimode */
+#include "gzacpath.h"
/* RJW: There appears to be a difference in the xps and postscript models
* (at least in as far as Microsofts implementation of xps and Acrobats of
@@ -332,35 +333,63 @@ static int join_under_pie(gx_path *, pl_ptr, pl_ptr, bool);
int
gx_default_stroke_path_shading_or_pattern(gx_device * pdev,
- const gs_gstate * pgs,
+ const gs_gstate * pgs_orig,
gx_path * ppath,
const gx_stroke_params * params,
const gx_drawing_color * pdevc,
const gx_clip_path * pcpath)
{
- gx_path spath;
- gx_fill_params fparams;
+ gs_gstate *pgs = (gs_gstate *)pgs_orig; /* Nasty cast away const! */
+ gs_logical_operation_t save_lop = gs_current_logical_op_inline(pgs);
+ gx_device_cpath_accum adev;
+ gx_device_color devc;
+ gx_clip_path stroke_as_clip_path;
int code;
- /* Override the stroke params to be 'traditional'. This is
- * required to solve some issues with the 'stroke to a path'
- * mechanism used by gx_default_stroke_path_shading_or_pattern,
- * where we can otherwise get differences in the 'underjoins'.
- * of the stroked path. */
- gx_stroke_params params2 = *params;
- params2.traditional = 1;
-
- fparams.flatness = params->flatness;
- fparams.adjust.x = pgs->fill_adjust.x;
- fparams.adjust.y = pgs->fill_adjust.x;
- fparams.rule = gx_rule_winding_number;
- gx_path_init_local(&spath, pgs->memory);
- code = gx_stroke_path_only(ppath, &spath, pdev, pgs, &params2,
- NULL, NULL);
+
+ /* We want to make a image of the stroke as a clip path, so
+ * create an empty structure on the stack. */
+ code = gx_cpath_init_local_shared_nested(&stroke_as_clip_path, NULL, pdev->memory, 1);
+ if (code < 0)
+ return code;
+ /* Now we make an accumulator device that will fill that out. */
+ gx_cpath_accum_begin(&adev, stroke_as_clip_path.path.memory, false);
+ set_nonclient_dev_color(&devc, 0); /* arbitrary, but not transparent */
+ gs_set_logical_op_inline(pgs, lop_default);
+ /* Stroke the path to the accumulator. */
+ code = gx_stroke_path_only(ppath, NULL, (gx_device *)&adev, pgs, params,
+ &devc, pcpath);
+ /* Now extract the accumulated path into stroke_as_clip_path. */
+ if (code < 0 || (code = gx_cpath_accum_end(&adev, &stroke_as_clip_path)) < 0)
+ gx_cpath_accum_discard(&adev);
+ gs_set_logical_op_inline(pgs, save_lop);
if (code >= 0)
- code = gx_default_fill_path_shading_or_pattern(pdev, pgs,
- &spath, &fparams,
- pdevc, pcpath);
- gx_path_free(&spath, "gs_stroke");
+ {
+ /* Now, fill a rectangle with the original color through that
+ * clip path. */
+ gs_fixed_rect clip_box, shading_box;
+ gs_int_rect cb;
+ gx_device_clip cdev;
+
+ gx_cpath_outer_box(&stroke_as_clip_path, &clip_box);
+ /* This is horrid. If the pdevc is a shading color, then the
+ * fill_rectangle routine requires us to have intersected it
+ * with the shading rectangle first. If we don't do this,
+ * ps3fts/470-01.ps goes wrong. */
+ if (gx_dc_is_pattern2_color(pdevc) &&
+ gx_dc_pattern2_get_bbox(pdevc, &shading_box) > 0)
+ {
+ rect_intersect(clip_box, shading_box);
+ }
+ cb.p.x = fixed2int_pixround(clip_box.p.x);
+ cb.p.y = fixed2int_pixround(clip_box.p.y);
+ cb.q.x = fixed2int_pixround(clip_box.q.x);
+ cb.q.y = fixed2int_pixround(clip_box.q.y);
+ gx_make_clip_device_on_stack(&cdev, &stroke_as_clip_path, pdev);
+ code = pdevc->type->fill_rectangle(pdevc,
+ cb.p.x, cb.p.y, cb.q.x - cb.p.x, cb.q.y - cb.p.y,
+ (gx_device *)&cdev, pgs->log_op, NULL);
+ }
+ gx_cpath_free(&stroke_as_clip_path, "gx_default_stroke_path_shading_or_pattern");
return code;
}