summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'base/gxdtfill.h')
-rw-r--r--base/gxdtfill.h401
1 files changed, 401 insertions, 0 deletions
diff --git a/base/gxdtfill.h b/base/gxdtfill.h
new file mode 100644
index 00000000..f5eece33
--- /dev/null
+++ b/base/gxdtfill.h
@@ -0,0 +1,401 @@
+/* Copyright (C) 2001-2019 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 1305 Grant Avenue - Suite 200, Novato,
+ CA 94945, U.S.A., +1(415)492-9861, for further information.
+*/
+
+
+/* Configurable algorithm for filling a trapezoid */
+
+/*
+ * Since we need several statically defined variants of this agorithm,
+ * we store it in .h file and include several times into gdevddrw.c and
+ * into gxfill.h . Configuration flags (macros) are :
+ *
+ * GX_FILL_TRAPEZOID - a name of method
+ * CONTIGUOUS_FILL - prevent dropouts in narrow trapezoids
+ * SWAP_AXES - assume swapped axes
+ * FILL_DIRECT - See LOOP_FILL_RECTANGLE_DIRECT.
+ * LINEAR_COLOR - Fill with a linear color.
+ * EDGE_TYPE - a type of edge structure.
+ * FILL_ATTRS - operation attributes.
+ */
+
+/*
+ * Fill a trapezoid. left.start => left.end and right.start => right.end
+ * define the sides; ybot and ytop define the top and bottom. Requires:
+ * {left,right}->start.y <= ybot <= ytop <= {left,right}->end.y.
+ * Lines where left.x >= right.x will not be drawn. Thanks to Paul Haeberli
+ * for an early floating point version of this algorithm.
+ */
+
+/*
+ * With CONTIGUOUS_FILL is off,
+ * this algorithm paints pixels, which centers fall between
+ * the left and the right side of the trapezoid, excluding the
+ * right side (see PLRM3, 7.5. Scan conversion details).
+ * Particularly 0-width trapezoids are not painted.
+ *
+ * Similarly, it paints pixels, which centers
+ * fall between ybot and ytop, excluding ytop.
+ * Particularly 0-height trapezoids are not painted.
+ *
+ * With CONTIGUOUS_FILL is on, it paints a contigous area,
+ * adding a minimal number of pixels outside the trapezoid.
+ * Particularly it may paint pixels on the right and on the top sides,
+ * if they are necessary for the contiguity.
+ *
+ * With LINEAR_COLOR returns 1 if the gradient arithmetics overflows..
+ */
+
+/*
+We must paint pixels with index i such that
+
+ Xl <= i + 0.5 < Xr
+
+The condition is is equivalent to
+
+ Xl - 0.5 <= i < Xr - 0.5
+
+which is equivalent to
+
+ (is_integer(Xl - 0.5) ? Xl - 0.5 : ceil(Xl - 0.5)) <= i <
+ (is_integer(Xr - 0.5) ? Xr - 0.5 : floor(Xr - 0.5) + 1)
+
+(the last '+1" happens due to the strong comparizon '<')
+which is equivalent to
+
+ ceil(Xl - 0.5) <= i < ceil(Xr - 0.5)
+
+trap_line represents the intersection coordinate as a rational value :
+
+ Xl = xl + e - fl
+ Xr = xr + e - fr
+
+Where 'e' is 'fixed_epsilon', 0.5 is 'fixed_half', and fl == l.fx / l.h, fr == - r.fx / r.h,
+e <= fl < 0, e <= fr < 0.
+Let
+
+ xl' := xl + 0.5
+ xr' := xr + 0.5
+
+Then
+
+ xl = xl' - 0.5
+ xr = xr' - 0.5
+
+ Xl = xl' - 0.5 + e - fl
+ Xr = xr' - 0.5 + e - fr
+
+ ceil(xl' - 0.5 + e - fl - 0.5) <= i < ceil(xr' - 0.5 + e - fr - 0.5)
+
+which is equivalent to
+
+ ceil(xl' + e - fl) - 1 <= i < ceil(xr' + e - fr) - 1
+
+which is equivalent to
+
+ (is_integer(xl' + e - fl) ? xl' + e - fl - 1 : ceil(xl' + e - fl) - 1) <= i <
+ (is_integer(xr' + e - fr) ? xr' + e - fr - 1 : ceil(xr' + e - fr) - 1)
+
+which is equivalent to
+
+ (is_integer(xl' + e - fl) ? xl' + e - fl - 1 : floor(xl' + e - fl)) <= i <
+ (is_integer(xr' + e - fr) ? xr' + e - fr - 1 : floor(xr' + e - fr))
+
+which is equivalent to
+
+ (is_integer(xl') && e == fl ? xl' - 1 : floor(xl' + e - fl)) <= i <
+ (is_integer(xr') && e == fr ? xr' - 1 : floor(xr' + e - fr))
+
+Note that e != fl ==> floor(xl' + e - fl) == floor(xl') due to e - fl < LeastSignificantBit(xl') ;
+ e == fl ==> floor(xl' + e - fl) == floor(xl') due to e - fl == 0;
+
+thus the condition is is equivalent to
+
+ (is_integer(xl') && e == fl ? xl' - 1 : floor(xl')) <= i <
+ (is_integer(xr') && e == fr ? xr' - 1 : floor(xr'))
+
+It is computed with the macro 'rational_floor'.
+
+*/
+
+#if defined(GX_FILL_TRAPEZOID) && defined(EDGE_TYPE)
+
+GX_FILL_TRAPEZOID (gx_device * dev, const EDGE_TYPE * left,
+ const EDGE_TYPE * right, fixed ybot, fixed ytop, int flags,
+ const gx_device_color * pdevc, FILL_ATTRS fa)
+{
+ const fixed ymin = fixed_pixround(ybot) + fixed_half;
+ const fixed ymax = fixed_pixround(ytop);
+
+ if (ymin >= ymax)
+ return 0; /* no scan lines to sample */
+ {
+ int iy = fixed2int_var(ymin);
+ const int iy1 = fixed2int_var(ymax);
+ trap_line l, r;
+ register int rxl, rxr;
+#if !LINEAR_COLOR
+ int ry;
+#endif
+ const fixed
+ x0l = left->start.x, x1l = left->end.x, x0r = right->start.x,
+ x1r = right->end.x, dxl = x1l - x0l, dxr = x1r - x0r;
+ const fixed /* partial pixel offset to first line to sample */
+ ysl = ymin - left->start.y, ysr = ymin - right->start.y;
+ fixed fxl;
+ int code;
+# if CONTIGUOUS_FILL
+ const bool peak0 = ((flags & 1) != 0);
+ const bool peak1 = ((flags & 2) != 0);
+ int peak_y0 = ybot + fixed_half;
+ int peak_y1 = ytop - fixed_half;
+# endif
+# if LINEAR_COLOR
+ int num_components = dev->color_info.num_components;
+ frac31 lgc[GX_DEVICE_COLOR_MAX_COMPONENTS];
+ int32_t lgf[GX_DEVICE_COLOR_MAX_COMPONENTS];
+ int32_t lgnum[GX_DEVICE_COLOR_MAX_COMPONENTS];
+ frac31 rgc[GX_DEVICE_COLOR_MAX_COMPONENTS];
+ int32_t rgf[GX_DEVICE_COLOR_MAX_COMPONENTS];
+ int32_t rgnum[GX_DEVICE_COLOR_MAX_COMPONENTS];
+ frac31 xgc[GX_DEVICE_COLOR_MAX_COMPONENTS];
+ int32_t xgf[GX_DEVICE_COLOR_MAX_COMPONENTS];
+ int32_t xgnum[GX_DEVICE_COLOR_MAX_COMPONENTS];
+ trap_gradient lg, rg, xg;
+# else
+ gx_color_index cindex = pdevc->colors.pure;
+ dev_proc_fill_rectangle((*fill_rect)) =
+ dev_proc(dev, fill_rectangle);
+# endif
+
+ if_debug2m('z', dev->memory, "[z]y=[%d,%d]\n", iy, iy1);
+
+ l.h = left->end.y - left->start.y;
+ r.h = right->end.y - right->start.y;
+ l.x = x0l + (fixed_half - fixed_epsilon);
+ r.x = x0r + (fixed_half - fixed_epsilon);
+#if !LINEAR_COLOR
+ ry = iy;
+#endif
+
+/*
+ * Free variables of FILL_TRAP_RECT:
+ * SWAP_AXES, pdevc, dev, fa
+ * Free variables of FILL_TRAP_RECT_DIRECT:
+ * SWAP_AXES, fill_rect, dev, cindex
+ */
+#define FILL_TRAP_RECT_INDIRECT(x,y,w,h)\
+ (SWAP_AXES ? gx_fill_rectangle_device_rop(y, x, h, w, pdevc, dev, fa) :\
+ gx_fill_rectangle_device_rop(x, y, w, h, pdevc, dev, fa))
+#define FILL_TRAP_RECT_DIRECT(x,y,w,h)\
+ (SWAP_AXES ? (*fill_rect)(dev, y, x, h, w, cindex) :\
+ (*fill_rect)(dev, x, y, w, h, cindex))
+
+#if LINEAR_COLOR
+# define FILL_TRAP_RECT(x,y,w,h)\
+ (!(w) ? 0 : dev_proc(dev, fill_linear_color_scanline)(dev, fa, x, y, w, xg.c, xg.f, xg.num, xg.den))
+#else
+# define FILL_TRAP_RECT(x,y,w,h)\
+ (FILL_DIRECT ? FILL_TRAP_RECT_DIRECT(x,y,w,h) : FILL_TRAP_RECT_INDIRECT(x,y,w,h))
+#endif
+
+ /* Compute the dx/dy ratios. */
+
+ /*
+ * Compute the x offsets at the first scan line to sample. We need
+ * to be careful in computing ys# * dx#f {/,%} h# because the
+ * multiplication may overflow. We know that all the quantities
+ * involved are non-negative, and that ys# is usually less than 1 (as
+ * a fixed, of course); this gives us a cheap conservative check for
+ * overflow in the multiplication.
+ */
+#define YMULT_QUO(ys, tl)\
+ (ys < fixed_1 && tl.df < YMULT_LIMIT ? ys * tl.df / tl.h :\
+ fixed_mult_quo(ys, tl.df, tl.h))
+
+#if CONTIGUOUS_FILL
+/*
+ * If left and right boundary round to same pixel index,
+ * we would not paing the scan and would get a dropout.
+ * Check for this case and choose one of two pixels
+ * which is closer to the "axis". We need to exclude
+ * 'peak' because it would paint an excessive pixel.
+ */
+#define SET_MINIMAL_WIDTH(ixl, ixr, l, r) \
+ if (ixl == ixr) \
+ if ((!peak0 || iy >= peak_y0) && (!peak1 || iy <= peak_y1)) {\
+ fixed x = int2fixed(ixl) + fixed_half;\
+ if (x - l.x < r.x - x)\
+ ++ixr;\
+ else\
+ --ixl;\
+ }
+
+#define CONNECT_RECTANGLES(ixl, ixr, rxl, rxr, iy, ry, adj1, adj2, fill)\
+ if (adj1 < adj2) {\
+ if (iy - ry > 1) {\
+ code = fill(rxl, ry, rxr - rxl, iy - ry - 1);\
+ if (code < 0)\
+ goto xit;\
+ ry = iy - 1;\
+ }\
+ adj1 = adj2 = (adj2 + adj2) / 2;\
+ }
+
+#else
+#define SET_MINIMAL_WIDTH(ixl, ixr, l, r) DO_NOTHING
+#define CONNECT_RECTANGLES(ixl, ixr, rxl, rxr, iy, ry, adj1, adj2, fill) DO_NOTHING
+#endif
+ if (fixed_floor(l.x) == fixed_pixround(x1l)) {
+ /* Left edge is vertical, we don't need to increment. */
+ l.di = 0, l.df = 0;
+ fxl = 0;
+ } else {
+ compute_dx(&l, dxl, ysl);
+ fxl = YMULT_QUO(ysl, l);
+ l.x += fxl;
+ }
+ if (fixed_floor(r.x) == fixed_pixround(x1r)) {
+ /* Right edge is vertical. If both are vertical, */
+ /* we have a rectangle. */
+# if !LINEAR_COLOR
+ if (l.di == 0 && l.df == 0) {
+ rxl = fixed2int_var(l.x);
+ rxr = fixed2int_var(r.x);
+ SET_MINIMAL_WIDTH(rxl, rxr, l, r);
+ code = FILL_TRAP_RECT(rxl, ry, rxr - rxl, iy1 - ry);
+ goto xit;
+ }
+# endif
+ r.di = 0, r.df = 0;
+ }
+ /*
+ * The test for fxl != 0 is required because the right edge might
+ * cross some pixel centers even if the left edge doesn't.
+ */
+ else if (dxr == dxl && fxl != 0) {
+ if (l.di == 0)
+ r.di = 0, r.df = l.df;
+ else
+ compute_dx(&r, dxr, ysr);
+ if (ysr == ysl && r.h == l.h)
+ r.x += fxl;
+ else
+ r.x += YMULT_QUO(ysr, r);
+ } else {
+ compute_dx(&r, dxr, ysr);
+ r.x += YMULT_QUO(ysr, r);
+ }
+ /* Compute one line's worth of dx/dy. */
+ compute_ldx(&l, ysl);
+ compute_ldx(&r, ysr);
+ /* We subtracted fixed_epsilon from l.x, r.x to simplify rounding
+ when the rational part is zero. Now add it back to get xl', xr' */
+ l.x += fixed_epsilon;
+ r.x += fixed_epsilon;
+# if LINEAR_COLOR
+# ifdef DEBUG
+ if (check_gradient_overflow(left, right)) {
+ /* The caller must care of.
+ Checking it here looses some performance with triangles. */
+ return_error(gs_error_unregistered);
+ }
+# endif
+ lg.c = lgc;
+ lg.f = lgf;
+ lg.num = lgnum;
+ rg.c = rgc;
+ rg.f = rgf;
+ rg.num = rgnum;
+ xg.c = xgc;
+ xg.f = xgf;
+ xg.num = xgnum;
+ code = init_gradient(&lg, fa, left, right, &l, ymin, num_components);
+ if (code < 0)
+ return code;
+ code = init_gradient(&rg, fa, right, left, &r, ymin, num_components);
+ if (code < 0)
+ return code;
+
+# endif
+
+#define rational_floor(tl)\
+ fixed2int_var(fixed_is_int(tl.x) && tl.xf == -tl.h ? tl.x - fixed_1 : tl.x)
+#define STEP_LINE(ix, tl)\
+ tl.x += tl.ldi;\
+ if ( (tl.xf += tl.ldf) >= 0 ) tl.xf -= tl.h, tl.x++;\
+ ix = rational_floor(tl)
+
+ rxl = rational_floor(l);
+ rxr = rational_floor(r);
+ SET_MINIMAL_WIDTH(rxl, rxr, l, r);
+ while (LINEAR_COLOR ? 1 : ++iy != iy1) {
+# if LINEAR_COLOR
+ if (rxl != rxr) {
+ code = set_x_gradient(&xg, &lg, &rg, &l, &r, rxl, rxr, num_components);
+ if (code < 0)
+ goto xit;
+ code = FILL_TRAP_RECT(rxl, iy, rxr - rxl, 1);
+ if (code < 0)
+ goto xit;
+ }
+ if (++iy == iy1)
+ break;
+ STEP_LINE(rxl, l);
+ STEP_LINE(rxr, r);
+ step_gradient(&lg, num_components);
+ step_gradient(&rg, num_components);
+# else
+ register int ixl, ixr;
+
+ STEP_LINE(ixl, l);
+ STEP_LINE(ixr, r);
+ SET_MINIMAL_WIDTH(ixl, ixr, l, r);
+ if (ixl != rxl || ixr != rxr) {
+ CONNECT_RECTANGLES(ixl, ixr, rxl, rxr, iy, ry, rxr, ixl, FILL_TRAP_RECT);
+ CONNECT_RECTANGLES(ixl, ixr, rxl, rxr, iy, ry, ixr, rxl, FILL_TRAP_RECT);
+ code = FILL_TRAP_RECT(rxl, ry, rxr - rxl, iy - ry);
+ if (code < 0)
+ goto xit;
+ rxl = ixl, rxr = ixr, ry = iy;
+ }
+# endif
+ }
+# if !LINEAR_COLOR
+ code = FILL_TRAP_RECT(rxl, ry, rxr - rxl, iy - ry);
+# else
+ code = 0;
+# endif
+#undef STEP_LINE
+#undef SET_MINIMAL_WIDTH
+#undef CONNECT_RECTANGLES
+#undef FILL_TRAP_RECT
+#undef FILL_TRAP_RECT_DIRECT
+#undef FILL_TRAP_RECT_INRECT
+#undef YMULT_QUO
+xit: if (code < 0 && FILL_DIRECT)
+ return_error(code);
+ return_if_interrupt(dev->memory);
+ return code;
+ }
+}
+
+#undef GX_FILL_TRAPEZOID
+#undef CONTIGUOUS_FILL
+#undef SWAP_AXES
+#undef FLAGS_TYPE
+
+#else
+int dummy;
+#endif