Commit message: Bug 696636: New scan converter.

This commit adds a new scan converter implementation designed
to address some of the problems seen with 'busy' paths in the
current implementation.

No 'active line list' is kept, instead we run through the path
generating a buffer of scanline edge intersections. This trades
repeated sorting of the active list against potentially higher
memory usage.

This code has 2 pairs of different variants. Each pair copes
with 'centre of pixel' and 'any part of a pixel' rendering
modes. One pair scan converts to rectangles, another pair to
trapezoids.

These routine does not give identical results to the old code,
partly due to (minimal) rounding issues, but more due to the
fact that the new code is only equivalent to adjust values of
0 or 0.5.

GS uses intermediate adjust values by default (such as 0.3)
when rasterising at low resolution (< 150dpi). In such cases
this new code will cover more than the old code did. It is
hoped that the impact of this is reduced due to the fact we
use freetype for character rendering now.

This new scan converter is disabled by default. To enable it,
set the scanconverter value to be non-zero. This is most easily
achieved by using -dSCANCONVERTERTYPE=1 on the command line.

Runs vastly faster on the cases highlighted in bugs 692351
and 691984.
CVE: CVE-2017-7948
Vulnerability description: Integer overflow in the mark_curve function in Artifex Ghostscript 9.21 allows remote attackers to cause a denial of service (out-of-bounds write and application crash) or possibly have unspecified other impact via a crafted PostScript document.

Project: ghostpdl

gx_filter_edgebuffer_tr(gx_device       * pdev,
                        gx_edgebuffer   * edgebuffer,
                        int               rule)
{
    int i;

#ifdef DEBUG_SCAN_CONVERTER
    gx_edgebuffer_print(edgebuffer);
#endif

    for (i=0; i < edgebuffer->height; i++) {
        int *row      = &edgebuffer->table[edgebuffer->index[i]];
        int  rowlen   = *row++;
        int *rowstart = row;
        int *rowout   = row;

        while (rowlen > 0) {
            int left, lid, right, rid;

            if (rule == gx_rule_even_odd) {
                /* Even Odd */
                left  = *row++;
                lid   = *row++;
                right = *row++;
                rid   = *row++;
                rowlen -= 2;
            } else {
                /* Non-Zero */
                int w;

                left = *row++;
                lid  = *row++;
                w = ((lid&1)-1) | 1;
                rowlen--;
                do {
                    right = *row++;
                    rid   = *row++;
                    rowlen--;
                    w += ((rid&1)-1) | 1;
                } while (w != 0);
            }

            if (right > left) {
                *rowout++ = left;
                *rowout++ = lid;
                *rowout++ = right;
                *rowout++ = rid;
            }
        }
        rowstart[-1] = (rowout-rowstart)>>1;
    }
    return 0;
}

Project: ghostpdl

static int make_table(gx_device     * pdev,
                      gx_path       * path,
                const gs_fixed_rect * ibox,
                      int             intersection_size,
                      fixed           adjust,
                      int           * scanlinesp,
                      int          ** indexp,
                      int          ** tablep)
{
    int            scanlines;
    const subpath *psub;
    int           *index;
    int           *table;
    int            i;
    int            offset, delta;

    *scanlinesp = 0;
    *indexp     = NULL;
    *tablep     = NULL;

    /* Step 1: Make us a table */
    scanlines = ibox->q.y-ibox->p.y;
    /* +1 simplifies the loop below */
    index     = (int *)gs_alloc_bytes(pdev->memory,
                                      (scanlines+1) * sizeof(*index),
                                      "scanc index buffer");
    if (index == NULL)
        return_error(gs_error_VMerror);

    /* Step 1 continued: Blank the index */
    for (i=0; i < scanlines+1; i++) {
        index[i] = 0;
    }

    /* Step 1 continued: Run through the path, filling in the index */
    for (psub = path->first_subpath; psub != 0;) {
        const segment *pseg = (const segment *)psub;
        fixed          ey = pseg->pt.y + adjust;
        fixed          iy = ey;

        while ((pseg = pseg->next) != 0 &&
               pseg->type != s_start
            ) {
            fixed sy = ey;
            ey = pseg->pt.y + adjust;

#ifdef DEBUG_SCAN_CONVERTER
            dlprintf1("%d ", pseg->type);
#endif
            switch (pseg->type) {
                default:
                case s_start: /* Should never happen */
                case s_dash:  /* We should never be seeing a dash here */
                    assert("This should never happen" == NULL);
                    break;
                case s_curve: {
                    const curve_segment *const pcur = (const curve_segment *)pseg;
                    fixed c1y = pcur->p1.y + adjust;
                    fixed c2y = pcur->p2.y + adjust;
                    fixed maxy = sy, miny = sy;
                    int imaxy, iminy;
                    if (miny > c1y)
                        miny = c1y;
                    if (miny > c2y)
                        miny = c2y;
                    if (miny > ey)
                        miny = ey;
                    if (maxy < c1y)
                        maxy = c1y;
                    if (maxy < c2y)
                        maxy = c2y;
                    if (maxy < ey)
                        maxy = ey;
                    iminy = fixed2int(miny) - ibox->p.y;
                    if (iminy < 0)
                        iminy = 0;
                    if (iminy < scanlines) {
                        imaxy = fixed2int(maxy) - ibox->p.y;
                        if (imaxy >= 0) {
                            index[iminy]+=3;
                            if (imaxy < scanlines)
                                index[imaxy+1]-=3;
                        }
                    }
                    break;
                }
                case s_gap:
                case s_line:
                case s_line_close: {
                    fixed miny, maxy;
                    int imaxy, iminy;
                    if (sy == ey)
                        break;
                    if (sy < ey)
                        miny = sy, maxy = ey;
                    else
                        miny = ey, maxy = sy;
                    iminy = fixed2int(miny) - ibox->p.y;
                    if (iminy < 0)
                        iminy = 0;
                    if (iminy < scanlines) {
                        imaxy = fixed2int(maxy) - ibox->p.y;
                        if (imaxy >= 0) {
                            index[iminy]++;
                            if (imaxy < scanlines) {
                                index[imaxy+1]--;
                            }
                        }
                    }
                    break;
                }
            }

            /* And close any segments that need it */
            if (ey != iy) {
                fixed miny, maxy;
                int imaxy, iminy;
                if (iy < ey)
                    miny = iy, maxy = ey;
                else
                    miny = ey, maxy = iy;
                iminy = fixed2int(miny) - ibox->p.y;
                if (iminy < 0)
                    iminy = 0;
                if (iminy < scanlines) {
                    imaxy = fixed2int(maxy) - ibox->p.y;
                    if (imaxy >= 0) {
                        index[iminy]++;
                        if (imaxy < scanlines) {
                            index[imaxy+1]--;
                        }
                    }
                }
            }
        }
#ifdef DEBUG_SCAN_CONVERTER
        dlprintf("\n");
#endif
        psub = (const subpath *)pseg;
    }

    /* Step 1 continued: index now contains a list of deltas (how the
     * number of intersects on line x differs from the number on line x-1).
     * First convert them to be the real number of intersects on that line.
     * Sum these values to get us the total nunber of intersects. Then
     * convert the table to be a list of offsets into the real intersect
     * buffer. */
    offset = 0;
    delta  = 0;
    for (i=0; i < scanlines; i++) {
        delta    += intersection_size*index[i];  /* delta = Num ints on this scanline. */
        index[i]  = offset;                      /* Offset into table for this lines data. */
        offset   += delta+1;                     /* Adjust offset for next line. */
    }

    /* End of step 1: index[i] = offset into table 2 for scanline i's
     * intersection data. offset = Total number of int entries required for
     * table. */

    /* Step 2: Collect the real intersections */
    table = (int *)gs_alloc_bytes(pdev->memory, offset * sizeof(*table),
                                  "scanc intersects buffer");
    if (table == NULL) {
        gs_free_object(pdev->memory, table, "scanc index buffer");
        return_error(gs_error_VMerror);
    }

    /* Step 2 continued: initialise table's data; each scanlines data starts
     * with a count of the number of intersects so far, followed by a record
     * of the intersect points on this scanline. */
    for (i=0; i < scanlines; i++) {
        table[index[i]] = 0;
    }

    *scanlinesp = scanlines;
    *tablep     = table;
    *indexp     = index;

    return 0;
}

Project: ghostpdl

print_active_line(const gs_memory_t *mem, const char *label, const active_line * alp)
{
    dmlprintf5(mem, "[f]%s 0x%lx(%d): x_current=%f x_next=%f\n",
               label, (ulong) alp, alp->direction,
               fixed2float(alp->x_current), fixed2float(alp->x_next));
    dmlprintf5(mem, "    start=(%f,%f) pt_end=0x%lx(%f,%f)\n",
               fixed2float(alp->start.x), fixed2float(alp->start.y),
               (ulong) alp->pseg,
               fixed2float(alp->end.x), fixed2float(alp->end.y));
    dmlprintf2(mem, "    prev=0x%lx next=0x%lx\n",
               (ulong) alp->prev, (ulong) alp->next);
}

Project: ghostpdl

gx_general_fill_path(gx_device * pdev, const gs_gstate * pgs,
                     gx_path * ppath, const gx_fill_params * params,
                 const gx_device_color * pdevc, const gx_clip_path * pcpath)
{
    gs_fixed_point adjust;
    gs_logical_operation_t lop = pgs->log_op;
    gs_fixed_rect ibox, bbox, sbox;
    gx_device_clip cdev;
    gx_device *dev = pdev;
    gx_device *save_dev = dev;
    gx_path ffpath;
    gx_path *pfpath;
    int code;
    int max_fill_band = dev->max_fill_band;
#define NO_BAND_MASK ((fixed)(-1) << (sizeof(fixed) * 8 - 1))
    const bool is_character = params->adjust.x == -1; /* See gxgstate.h */
    bool fill_by_trapezoids;
    bool big_path = ppath->subpath_count > 50;
    fill_options fo;
    line_list lst;

    *(const fill_options **)&lst.fo = &fo; /* break 'const'. */
    /*
     * Compute the bounding box before we flatten the path.
     * This can save a lot of time if the path has curves.
     * If the path is neither fully within nor fully outside
     * the quick-check boxes, we could recompute the bounding box
     * and make the checks again after flattening the path,
     * but right now we don't bother.
     */
    gx_path_bbox(ppath, &ibox);
    if (is_character)
        adjust.x = adjust.y = 0;
    else
        adjust = params->adjust;
    lst.contour_count = 0;
    lst.windings = NULL;
    lst.bbox_left = fixed2int(ibox.p.x - adjust.x - fixed_epsilon);
    lst.bbox_width = fixed2int(fixed_ceiling(ibox.q.x + adjust.x)) - lst.bbox_left;
    /* Check the bounding boxes. */
    if_debug6m('f', pdev->memory, "[f]adjust=%g,%g bbox=(%g,%g),(%g,%g)\n",
               fixed2float(adjust.x), fixed2float(adjust.y),
               fixed2float(ibox.p.x), fixed2float(ibox.p.y),
               fixed2float(ibox.q.x), fixed2float(ibox.q.y));
    if (pcpath)
        gx_cpath_inner_box(pcpath, &bbox);
    else
        (*dev_proc(dev, get_clipping_box)) (dev, &bbox);
    if (!rect_within(ibox, bbox)) {
        /*
         * Intersect the path box and the clip bounding box.
         * If the intersection is empty, this fill is a no-op.
         */
        if (pcpath)
            gx_cpath_outer_box(pcpath, &bbox);
        if_debug4m('f', pdev->memory, "   outer_box=(%g,%g),(%g,%g)\n",
                   fixed2float(bbox.p.x), fixed2float(bbox.p.y),
                   fixed2float(bbox.q.x), fixed2float(bbox.q.y));
        rect_intersect(ibox, bbox);
        if (ibox.p.x - adjust.x >= ibox.q.x + adjust.x ||
            ibox.p.y - adjust.y >= ibox.q.y + adjust.y
            ) {                 /* Intersection of boxes is empty! */
            return 0;
        }
        /*
         * The path is neither entirely inside the inner clip box
         * nor entirely outside the outer clip box.
         * If we had to flatten the path, this is where we would
         * recompute its bbox and make the tests again,
         * but we don't bother right now.
         *
         * If there is a clipping path, set up a clipping device.
         */
        if (pcpath) {
            dev = (gx_device *) & cdev;
            gx_make_clip_device_on_stack(&cdev, pcpath, save_dev);
            cdev.max_fill_band = save_dev->max_fill_band;
        }
    }
    /*
     * Compute the proper adjustment values.
     * To get the effect of the any-part-of-pixel rule,
     * we may have to tweak them slightly.
     * NOTE: We changed the adjust_right/above value from 0.5+epsilon
     * to 0.5 in release 5.01; even though this does the right thing
     * in every case we could imagine, we aren't confident that it's
     * correct.  (The old values were definitely incorrect, since they
     * caused 1-pixel-wide/high objects to color 2 pixels even if
     * they fell exactly on pixel boundaries.)
     */
    if (adjust.x == fixed_half)
        fo.adjust_left = fixed_half - fixed_epsilon,
            fo.adjust_right = fixed_half /* + fixed_epsilon */ ;        /* see above */
    else
        fo.adjust_left = fo.adjust_right = adjust.x;
    if (adjust.y == fixed_half)
        fo.adjust_below = fixed_half - fixed_epsilon,
            fo.adjust_above = fixed_half /* + fixed_epsilon */ ;        /* see above */
    else
        fo.adjust_below = fo.adjust_above = adjust.y;
    /* Initialize the active line list. */
    init_line_list(&lst, ppath->memory);
    sbox.p.x = ibox.p.x - adjust.x;
    sbox.p.y = ibox.p.y - adjust.y;
    sbox.q.x = ibox.q.x + adjust.x;
    sbox.q.y = ibox.q.y + adjust.y;
    fo.pdevc = pdevc;
    fo.lop = lop;
    fo.fixed_flat = float2fixed(params->flatness);
    fo.ymin = ibox.p.y;
    fo.ymax = ibox.q.y;
    fo.dev = dev;
    fo.pbox = &sbox;
    fo.rule = params->rule;
    fo.is_spotan = is_spotan_device(dev);
    fo.fill_direct = color_writes_pure(pdevc, lop);
    fo.fill_rect = (fo.fill_direct ? dev_proc(dev, fill_rectangle) : NULL);
    fo.fill_trap = dev_proc(dev, fill_trapezoid);

    /*
     * We have a choice of two different filling algorithms:
     * scan-line-based and trapezoid-based.  They compare as follows:
     *
     *      Scan    Trap
     *      ----    ----
     *       skip   +draw   0-height horizontal lines
     *       slow   +fast   rectangles
     *      +fast    slow   curves
     *      +yes     no     write pixels at most once when adjust != 0
     *
     * Normally we use the scan line algorithm for characters, where curve
     * speed is important, and for non-idempotent RasterOps, where double
     * pixel writing must be avoided, and the trapezoid algorithm otherwise.
     * However, we always use the trapezoid algorithm for rectangles.
     */
    fill_by_trapezoids = (!gx_path_has_curves(ppath) ||
                          params->flatness >= 1.0 || fo.is_spotan);

    if (fill_by_trapezoids && !fo.is_spotan && !lop_is_idempotent(lop)) {
        gs_fixed_rect rbox;

        if (gx_path_is_rectangular(ppath, &rbox)) {
            int x0 = fixed2int_pixround(rbox.p.x - fo.adjust_left);
            int y0 = fixed2int_pixround(rbox.p.y - fo.adjust_below);
            int x1 = fixed2int_pixround(rbox.q.x + fo.adjust_right);
            int y1 = fixed2int_pixround(rbox.q.y + fo.adjust_above);

            return gx_fill_rectangle_device_rop(x0, y0, x1 - x0, y1 - y0,
                                                pdevc, dev, lop);
        }
        if (fo.adjust_left | fo.adjust_right | fo.adjust_below | fo.adjust_above)
            fill_by_trapezoids = false; /* avoid double writing pixels */
    }

    if (!fo.is_spotan && gs_getscanconverter(pdev->memory) >= GS_SCANCONVERTER_EDGEBUFFER) {
        gx_edgebuffer eb = { 0 };
        if (fill_by_trapezoids && !lop_is_idempotent(lop))
            fill_by_trapezoids = 0;
        if (!fill_by_trapezoids)
        {
            if (adjust.x == 0 && adjust.y == 0) {
                code = gx_scan_convert(dev,
                                       ppath,
                                       &ibox,
                                       &eb,
                                       fo.fixed_flat);
                if (code >= 0) {
                    code = gx_filter_edgebuffer(dev,
                                                &eb,
                                                params->rule);
                }
                if (code >= 0) {
                    code = gx_fill_edgebuffer(dev,
                                              pdevc,
                                              &eb,
                                              fo.fill_direct ? -1 : (int)pgs->log_op);
                }
            } else {
                code = gx_scan_convert_app(dev,
                                           ppath,
                                           &ibox,
                                           &eb,
                                           fo.fixed_flat);
                if (code >= 0) {
                    code = gx_filter_edgebuffer_app(dev,
                                                    &eb,
                                                    params->rule);
                }
                if (code >= 0) {
                    code = gx_fill_edgebuffer_app(dev,
                                                  pdevc,
                                                  &eb,
                                                  fo.fill_direct ? -1 : (int)pgs->log_op);
                }
            }
        } else {
            if (adjust.x == 0 && adjust.y == 0) {
                code = gx_scan_convert_tr(dev,
                                          ppath,
                                          &ibox,
                                          &eb,
                                          fo.fixed_flat);
                if (code >= 0) {
                    code = gx_filter_edgebuffer_tr(dev,
                                                   &eb,
                                                    params->rule);
                }
                if (code >= 0) {
                    code = gx_fill_edgebuffer_tr(dev,
                                                 pdevc,
                                                 &eb,
                                                 (int)pgs->log_op);
                }
            } else {
                code = gx_scan_convert_tr_app(dev,
                                              ppath,
                                              &ibox,
                                              &eb,
                                              fo.fixed_flat);
                if (code >= 0) {
                    code = gx_filter_edgebuffer_tr_app(dev,
                                                       &eb,
                                                       params->rule);
                }
                if (code >= 0) {
                    code = gx_fill_edgebuffer_tr_app(dev,
                                                     pdevc,
                                                     &eb,
                                                     (int)pgs->log_op);
                }
            }
        }
        gx_edgebuffer_fin(dev,&eb);
        return code;
    }

    gx_path_init_local(&ffpath, ppath->memory);
    if (!big_path && !gx_path_has_curves(ppath))        /* don't need to flatten */
        pfpath = ppath;
    else if (is_spotan_device(dev))
        pfpath = ppath;
    else if (!big_path && !pgs->accurate_curves && gx_path__check_curves(ppath, pco_small_curves, fo.fixed_flat))
        pfpath = ppath;
    else {
        code = gx_path_copy_reducing(ppath, &ffpath, fo.fixed_flat, NULL,
                                     pco_small_curves | (pgs->accurate_curves ? pco_accurate : 0));
        if (code < 0)
            return code;
        pfpath = &ffpath;
        if (big_path) {
            code = gx_path_merge_contacting_contours(pfpath);
            if (code < 0)
                return code;
        }
    }
    fo.fill_by_trapezoids = fill_by_trapezoids;
    if ((code = add_y_list(pfpath, &lst)) < 0)
        goto nope;
    {
        FILL_LOOP_PROC((*fill_loop));

        /* Some short-sighted compilers won't allow a conditional here.... */
        if (fill_by_trapezoids)
            fill_loop = spot_into_trapezoids;
        else
            fill_loop = spot_into_scan_lines;
        if (gs_currentcpsimode(pgs->memory) && is_character) {
            if (lst.contour_count > countof(lst.local_windings)) {
                lst.windings = (int *)gs_alloc_byte_array(pdev->memory, lst.contour_count,
                                sizeof(int), "gx_general_fill_path");
            } else
                lst.windings = lst.local_windings;
            memset(lst.windings, 0, sizeof(lst.windings[0]) * lst.contour_count);
        }
        code = (*fill_loop)
            (&lst, (max_fill_band == 0 ? NO_BAND_MASK : int2fixed(-max_fill_band)));
        if (lst.windings != NULL && lst.windings != lst.local_windings)
            gs_free_object(pdev->memory, lst.windings, "gx_general_fill_path");
    }
  nope:if (lst.close_count != 0)
        unclose_path(pfpath, lst.close_count);
    free_line_list(&lst);
    if (pfpath != ppath)        /* had to flatten */
        gx_path_free(pfpath, "gx_general_fill_path");
#if defined(DEBUG) && !defined(GS_THREADSAFE)
    if (gs_debug_c('f')) {
        dmlputs(ppath->memory,
                "[f]  # alloc    up  down horiz step slowx  iter  find  band bstep bfill\n");
        dmlprintf5(ppath->memory, " %5ld %5ld %5ld %5ld %5ld",
                   stats_fill.fill, stats_fill.fill_alloc,
                   stats_fill.y_up, stats_fill.y_down,
                   stats_fill.horiz);
        dmlprintf4(ppath->memory, " %5ld %5ld %5ld %5ld",
                   stats_fill.x_step, stats_fill.slow_x,
                   stats_fill.iter, stats_fill.find_y);
        dmlprintf3(ppath->memory, " %5ld %5ld %5ld\n",
                   stats_fill.band, stats_fill.band_step,
                   stats_fill.band_fill);
        dmlputs(ppath->memory,
                "[f]    afill slant shall sfill mqcrs order slowo\n");
        dmlprintf7(ppath->memory, "       %5ld %5ld %5ld %5ld %5ld %5ld %5ld\n",
                   stats_fill.afill, stats_fill.slant,
                   stats_fill.slant_shallow, stats_fill.sfill,
                   stats_fill.mq_cross, stats_fill.order,
                   stats_fill.slow_order);
    }
#endif
    return code;
}

Project: ghostpdl

static void mark_curve(fixed sx, fixed sy, fixed c1x, fixed c1y, fixed c2x, fixed c2y, fixed ex, fixed ey, fixed base_y, fixed height, int *table, int *index, int depth)
{
    fixed ax = (sx + c1x)>>1;
    fixed ay = (sy + c1y)>>1;
    fixed bx = (c1x + c2x)>>1;
    fixed by = (c1y + c2y)>>1;
    fixed cx = (c2x + ex)>>1;
    fixed cy = (c2y + ey)>>1;
    fixed dx = (ax + bx)>>1;
    fixed dy = (ay + by)>>1;
    fixed fx = (bx + cx)>>1;
    fixed fy = (by + cy)>>1;
    fixed gx = (dx + fx)>>1;
    fixed gy = (dy + fy)>>1;

    assert(depth >= 0);
    if (depth == 0)
        mark_line(sx, sy, ex, ey, base_y, height, table, index);
    else {
        depth--;
        mark_curve(sx, sy, ax, ay, dx, dy, gx, gy, base_y, height, table, index, depth);
        mark_curve(gx, gy, fx, fy, cx, cy, ex, ey, base_y, height, table, index, depth);
    }
}

Project: ghostpdl

gx_filter_edgebuffer(gx_device       * pdev,
                     gx_edgebuffer   * edgebuffer,
                     int               rule)
{
    int i;

#ifdef DEBUG_SCAN_CONVERTER
    gx_edgebuffer_print(edgebuffer);
#endif

    for (i=0; i < edgebuffer->height; i++) {
        int *row      = &edgebuffer->table[edgebuffer->index[i]];
        int *rowstart = row;
        int  rowlen   = *row++;
        int *rowout   = row;

        while (rowlen > 0)
        {
            int left, right;

            if (rule == gx_rule_even_odd) {
                /* Even Odd */
                left  = (*row++)&~1;
                right = (*row++)&~1;
                rowlen -= 2;
            } else {
                /* Non-Zero */
                int w;

                left = *row++;
                w = ((left&1)-1) | (left&1);
                rowlen--;
                do {
                    right  = *row++;
                    rowlen--;
                    w += ((right&1)-1) | (right&1);
                } while (w != 0);
                left &= ~1;
                right &= ~1;
            }

            if (right > left) {
                *rowout++ = left;
                *rowout++ = right;
            }
        }
        *rowstart = (rowout-rowstart)-1;
    }
    return 0;
}

Project: ghostpdl

static int make_bbox(gx_path       * path,
               const gs_fixed_rect * clip,
                     gs_fixed_rect * ibox,
                     fixed           adjust)
{
    gs_fixed_rect bbox;
    int           code;

    /* Find the bbox - fixed */
    code = gx_path_bbox(path, &bbox);
    if (code < 0)
        return code;

    if (clip) {
        if (bbox.p.y < clip->p.y)
            bbox.p.y = clip->p.y;
        if (bbox.q.y > clip->q.y)
            bbox.q.y = clip->q.y;
    }

    /* Convert to bbox - int */
    ibox->p.x = fixed2int(bbox.p.x-adjust);
    ibox->p.y = fixed2int(bbox.p.y-adjust);
    ibox->q.x = fixed2int(bbox.q.x-adjust+fixed_1);
    ibox->q.y = fixed2int(bbox.q.y-adjust+fixed_1);

    return 0;
}

Project: ghostpdl

int gx_scan_convert_tr(gx_device     * pdev,
                       gx_path       * path,
                 const gs_fixed_rect * clip,
                       gx_edgebuffer * edgebuffer,
                       fixed           fixed_flat)
{
    gs_fixed_rect  ibox;
    int            scanlines;
    const subpath *psub;
    int           *index;
    int           *table;
    int            i;
    int            code;
    int            id = 0;

    edgebuffer->index = NULL;
    edgebuffer->table = NULL;

    /* Bale out if no actual path. We see this with the clist */
    if (path->first_subpath == NULL)
        return 0;

    code = make_bbox(path, clip, &ibox, fixed_half);
    if (code < 0)
        return code;

    if (ibox.q.y <= ibox.p.y)
        return 0;

    code = make_table(pdev, path, &ibox, 2, fixed_half, &scanlines, &index, &table);
    if (code < 0)
        return code;

    /* Step 3: Now we run through the path, filling in the real
     * values. */
    for (psub = path->first_subpath; psub != 0;) {
        const segment *pseg = (const segment *)psub;
        fixed ex = pseg->pt.x;
        fixed ey = pseg->pt.y;
        fixed ix = ex;
        fixed iy = ey;

        while ((pseg = pseg->next) != 0 &&
               pseg->type != s_start
            ) {
            fixed sx = ex;
            fixed sy = ey;
            ex = pseg->pt.x;
            ey = pseg->pt.y;

            switch (pseg->type) {
                default:
                case s_start: /* Should never happen */
                case s_dash:  /* We should never be seeing a dash here */
                    assert("This should never happen" == NULL);
                    break;
                case s_curve: {
                    const curve_segment *const pcur = (const curve_segment *)pseg;
                    int k = gx_curve_log2_samples(sx, sy, pcur, fixed_flat);

                    mark_curve_tr(sx, sy, pcur->p1.x, pcur->p1.y, pcur->p2.x, pcur->p2.y, ex, ey, ibox.p.y, scanlines, table, index, &id, k);
                    break;
                }
                case s_gap:
                case s_line:
                case s_line_close:
                    if (sy != ey)
                        mark_line_tr(sx, sy, ex, ey, ibox.p.y, scanlines, table, index, ++id);
                    break;
            }
        }
        /* And close any open segments */
        if (iy != ey)
            mark_line_tr(ex, ey, ix, iy, ibox.p.y, scanlines, table, index, ++id);
        psub = (const subpath *)pseg;
    }

    /* Step 2 complete: We now have a complete list of intersection data in
     * table, indexed by index. */

    /* Step 4: Sort the intersects on x */
    for (i=0; i < scanlines; i++) {
        int *row = &table[index[i]];
        int  rowlen = *row++;

        /* Sort the 'rowlen' entries - *must* be faster to do this with
         * custom code, as the function call overhead on the comparisons
         * will be a killer */
        qsort(row, rowlen, 2*sizeof(int), intcmp_tr);
    }

    edgebuffer->base   = ibox.p.y;
    edgebuffer->height = scanlines;
    edgebuffer->xmin   = ibox.p.x;
    edgebuffer->xmax   = ibox.q.x;
    edgebuffer->index  = index;
    edgebuffer->table  = table;

    return 0;
}

Project: ghostpdl

static void mark_line(fixed sx, fixed sy, fixed ex, fixed ey, int base_y, int height, int *table, int *index)
{
    int delta;
    int isy = fixed2int(sy + fixed_half);
    int iey = fixed2int(ey + fixed_half);
    int dirn = DIRN_UP;

#ifdef DEBUG_SCAN_CONVERTER
    dlprintf2("Marking line from %d to %d\n", isy, iey);
#endif

    if (isy == iey)
        return;
    if (isy > iey) {
        int t;
        t = isy; isy = iey; iey = t;
        t = sx; sx = ex; ex = t;
        dirn = DIRN_DOWN;
    }
    /* So we now have to mark a line of intersects from (sx,sy) to (ex,ey) */
    iey -= isy;
    ex -= sx;
    isy -= base_y;
#ifdef DEBUG_SCAN_CONVERTER
    dlprintf2("    sy=%d ey=%d\n", isy, iey);
#endif
    if (ex >= 0) {
        int x_inc, n_inc, f;

        /* We want to change sx by ex in iey steps. So each step, we add
         * ex/iey to sx. That's x_inc + n_inc/iey.
         */
       x_inc = ex/iey;
       n_inc = ex-(x_inc*iey);
       f     = iey>>1;
       /* Do a half step to start us off */
       sx += x_inc>>1;
       f  -= n_inc>>1;
       if (f < 0) {
           f += iey;
           sx++;
       }
       if (x_inc & 1) {
           f -= n_inc>>2;
           if (f < 0) {
               f += iey;
               sx++;
           }
       }
       delta = iey;
       do {
           int *row;

           if (isy >= 0 && isy < height) {
               row = &table[index[isy]];
               *row = (*row)+1; /* Increment the count */
               row[*row] = (sx&~1) | dirn;
           }
           isy++;
           sx += x_inc;
           f  -= n_inc;
           if (f < 0) {
               f += iey;
               sx++;
           }
        } while (--delta);
    } else {
        int x_dec, n_dec, f;

        ex = -ex;
        /* We want to change sx by ex in iey steps. So each step, we subtract
         * ex/iey from sx. That's x_dec + n_dec/iey.
         */
        x_dec = ex/iey;
        n_dec = ex-(x_dec*iey);
        f     = iey>>1;
        /* Do a half step to start us off */
        sx -= x_dec>>1;
        f  -= n_dec>>1;
        if (f < 0) {
            f += iey;
            sx--;
        }
        if (x_dec & 1) {
            f -= n_dec>>2;
            if (f < 0) {
                f += iey;
                sx--;
            }
        }
        delta = iey;
        do {
            int *row;

            if (isy >= 0 && isy < height) {
                row = &table[index[isy]];
                (*row)++; /* Increment the count */
                row[*row] = (sx&~1) | dirn;
            }
            isy++;
            sx -= x_dec;
            f  -= n_dec;
            if (f < 0) {
                f += iey;
                sx--;
            }
         } while (--delta);
    }
}

Project: ghostpdl

gx_fill_edgebuffer_tr(gx_device       * pdev,
                const gx_device_color * pdevc,
                      gx_edgebuffer   * edgebuffer,
                      int               log_op)
{
    int i, j, code;

    for (i=0; i < edgebuffer->height; ) {
        int *row    = &edgebuffer->table[edgebuffer->index[i]];
        int  rowlen = *row++;
        int *row2;
        int *rowptr;
        int *row2ptr;

        /* See how many scanlines match i */
        for (j = i+1; j < edgebuffer->height; j++) {
            int row2len;

            row2    = &edgebuffer->table[edgebuffer->index[j]];
            row2len = *row2++;
            row2ptr = row2;
            rowptr  = row;

            if (rowlen != row2len)
                break;
            while (row2len > 0) {
                if ((rowptr[1]&~1) != (row2ptr[1]&~1))
                    goto rowdifferent;
                rowptr  += 2;
                row2ptr += 2;
                row2len -= 2;
            }
        }
rowdifferent:{}

        /* So j is the first scanline that doesn't match i */

        if (j == i+1) {
            while (rowlen > 0) {
                int left, right;

                left  = row[0];
                right = row[2];
                row += 4;
                rowlen -= 2;

                left  = fixed2int(left + fixed_half);
                right = fixed2int(right + fixed_half);
                right -= left;
                if (right > 0) {
                    if (log_op < 0)
                        code = dev_proc(pdev, fill_rectangle)(pdev, left, edgebuffer->base+i, right, 1, pdevc->colors.pure);
                    else
                        code = gx_fill_rectangle_device_rop(left, edgebuffer->base+i, right, 1, pdevc, pdev, (gs_logical_operation_t)log_op);
                    if (code < 0)
                        return code;
                }
            }
        } else {
            gs_fixed_edge le;
            gs_fixed_edge re;
            le.start.y = re.start.y = int2fixed(edgebuffer->base+i);
            le.end.y   = re.end.y   = int2fixed(edgebuffer->base+j)-1;
            row2    = &edgebuffer->table[edgebuffer->index[j-1]+1];
            while (rowlen > 0) {
                le.start.x = row[0] - fixed_half;
                re.start.x = row[2] + fixed_half;
                le.end.x   = row2[0] - fixed_half;
                re.end.x   = row2[2] + fixed_half;
                row += 4;
                row2 += 4;
                rowlen -= 2;

                assert(le.start.x >= 0);
                assert(le.end.x >= 0);
                assert(re.start.x >= le.start.x);
                assert(re.end.x >= le.end.x);

                code = dev_proc(pdev, fill_trapezoid)(
                                pdev,
                                &le,
                                &re,
                                le.start.y,
                                le.end.y,
                                0, /* bool swap_axes */
                                pdevc, /*const gx_drawing_color *pdcolor */
                                log_op);
                if (code < 0)
                    return code;
            }
        }

        i = j;
    }
    return 0;
}

Project: ghostpdl

init_contour_cursor(const fill_options * const fo, contour_cursor *q)
{
    if (q->pseg->type == s_curve) {
        curve_segment *s = (curve_segment *)q->pseg;
        fixed ymin = min(min(q->prev->pt.y, s->p1.y), min(s->p2.y, s->pt.y));
        fixed ymax = max(max(q->prev->pt.y, s->p1.y), max(s->p2.y, s->pt.y));
        bool in_band = ymin <= fo->ymax && ymax >= fo->ymin;
        q->crossing = ymin < fo->ymin && ymax >= fo->ymin;
        q->monotonic_y = !in_band ||
            (!q->crossing &&
            ((q->prev->pt.y <= s->p1.y && s->p1.y <= s->p2.y && s->p2.y <= s->pt.y) ||
             (q->prev->pt.y >= s->p1.y && s->p1.y >= s->p2.y && s->p2.y >= s->pt.y)));
        q->monotonic_x =
            ((q->prev->pt.x <= s->p1.x && s->p1.x <= s->p2.x && s->p2.x <= s->pt.x) ||
             (q->prev->pt.x >= s->p1.x && s->p1.x >= s->p2.x && s->p2.x >= s->pt.x));
    } else
        q->monotonic_y = true;
    if (!q->monotonic_y) {
        curve_segment *s = (curve_segment *)q->pseg;
        int k = gx_curve_log2_samples(q->prev->pt.x, q->prev->pt.y, s, fo->fixed_flat);

        if (!gx_flattened_iterator__init(&q->fi, q->prev->pt.x, q->prev->pt.y, s, k))
            return_error(gs_error_rangecheck);
    } else {
        q->dir = compute_dir(fo, q->prev->pt.y, q->pseg->pt.y);
        gx_flattened_iterator__init_line(&q->fi,
            q->prev->pt.x, q->prev->pt.y, q->pseg->pt.x, q->pseg->pt.y); /* fake for curves. */
    }
    q->first_flattened = true;
    return 0;
}

Project: ghostpdl

gx_edgebuffer_print_tr_app(gx_edgebuffer * edgebuffer)
{
    int i;
    int borked = 0;

    dlprintf1("Edgebuffer %x\n", edgebuffer);
    dlprintf4("xmin=%d xmax=%d base=%d height=%d\n",
              edgebuffer->xmin, edgebuffer->xmax, edgebuffer->base, edgebuffer->height);
    for (i=0; i < edgebuffer->height; i++)
    {
        int  offset = edgebuffer->index[i];
        int *row    = &edgebuffer->table[offset];
        int count   = *row++;
        int c       = count;
        int wind    = 0;
        dlprintf3("%x @ %d: %d =", i, offset, count);
        while (count-- > 0) {
            int left  = *row++;
            int lid   = *row++;
            int right = *row++;
            int rid   = *row++;
            int ww    = lid & 1;
            int w     = -ww | 1;
            lid >>= 1;
            wind += w;
            dlprintf5(" (%x:%d,%x:%d)%c", left, lid, right, rid, ww ? 'v' : '^');
        }
        if (wind != 0 || c & 1) {
            dlprintf(" <- BROKEN");
            borked = 1;
        }
        dlprintf("\n");
    }
    if (borked) {
        borked = borked; /* Breakpoint here */
    }
}

Project: ghostpdl

static void mark_line_tr_app(cursor_tr *cr, fixed sx, fixed sy, fixed ex, fixed ey, int id)
{
    int isy = fixed2int(sy) - cr->base;
    int iey = fixed2int(ey) - cr->base;

    if (sx == ex && sy == ey)
        return;
#ifdef DEBUG_SCAN_CONVERTER
    dlprintf6("Marking line from %x,%x to %x,%x (%x,%x)\n", sx, sy, ex, ey, isy, iey);
#endif

    assert(cr->y == sy && cr->left <= sx && cr->right >= sx && cr->d >= DIRN_UNSET && cr->d <= DIRN_DOWN);

    /* First, deal with lines that don't change scanline.
     * This accommodates horizontal lines. */
    if (isy == iey) {
        if (sy == ey) {
            /* Horzizontal line. Don't change cr->d, don't flush. */
        } else if (sy > ey) {
            /* Falling line, flush if previous was rising */
            if (cr->d == DIRN_UP)
                output_cursor_tr(cr, sx, id);
            cr->d = DIRN_DOWN;
        } else {
            /* Rising line, flush if previous was falling */
            if (cr->d == DIRN_DOWN)
                output_cursor_tr(cr, sx, id);
            cr->d = DIRN_UP;
        }
        if (sx <= ex) {
            if (sx < cr->left) {
                cr->left = sx;
                cr->lid  = id;
            }
            if (ex > cr->right) {
                cr->right = ex;
                cr->rid   = id;
            }
        } else {
            if (ex < cr->left) {
                cr->left = ex;
                cr->lid  = id;
            }
            if (sx > cr->right) {
                cr->right = sx;
                cr->rid   = id;
            }
        }
        cr->y = ey;
    } else if (sy < ey) {
        /* So lines increasing in y. */
        fixed y_steps = ey - sy;
        /* We want to change from sy to ey, which are guaranteed to be on
         * different scanlines. We do this in 3 phases.
         * Phase 1 gets us from sy to the next scanline boundary.
         * Phase 2 gets us all the way to the last scanline boundary.
         * Phase 3 gets us from the last scanline boundary to ey.
         */
        int phase1_y_steps = (fixed_1 - sy) & (fixed_1 - 1);
        int phase3_y_steps = ey & (fixed_1 - 1);

        if (cr->d == DIRN_DOWN)
            output_cursor_tr(cr, sx, id);
        cr->d = DIRN_UP;

        if (sx <= ex) {
            /* Lines increasing in x. */
            int phase1_x_steps, phase3_x_steps;
            fixed x_steps = ex - sx;

            assert(cr->left <= sx);
            /* Phase 1: */
            if (phase1_y_steps) {
                phase1_x_steps = (int)(((int64_t)x_steps * phase1_y_steps + y_steps/2) / y_steps);
                sx += phase1_x_steps;
                if (cr->right <= sx) {
                    cr->right = sx;
                    cr->rid   = id;
                }
                x_steps -= phase1_x_steps;
                /* If phase 1 will move us into a new scanline, then we must
                 * flush it before we move. */
                if (fixed2int(cr->y) != fixed2int(cr->y + phase1_y_steps))
                    output_cursor_tr(cr, sx, id);
                cr->y += phase1_y_steps;
                sy += phase1_y_steps;
                y_steps -= phase1_y_steps;
                if (y_steps == 0)
                    return;
            }

            /* Phase 3: precalculation */
            phase3_x_steps = (int)(((int64_t)x_steps * phase3_y_steps + y_steps/2) / y_steps);
            x_steps -= phase3_x_steps;
            y_steps -= phase3_y_steps;

            /* Phase 2: */
            cr->lid = id;
            cr->rid = id;
            y_steps = fixed2int(y_steps);
            assert(y_steps >= 0);
            if (y_steps) {
                /* We want to change sx by x_steps in y_steps steps.
                 * So each step, we add x_steps/y_steps to sx. That's x_inc + n_inc/y_steps. */
                int x_inc = x_steps/y_steps;
                int n_inc = x_steps - (x_inc * y_steps);
                int f = y_steps/2;
                int d = y_steps;
                while (y_steps) {
                    cr->left = sx;
                    sx += x_inc;
                    f -= n_inc;
                    if (f < 0)
                        f += d, sx++;
                    cr->right = sx;
                    y_steps--;
                    output_cursor_tr(cr, sx, id);
                    cr->y += fixed_1;
                }
            }

            /* Phase 3 */
            cr->left  = sx;
            cr->right = ex;
            cr->y += phase3_y_steps;
        } else {
            /* Lines decreasing in x. */
            int phase1_x_steps, phase3_x_steps;
            fixed x_steps = sx - ex;

            assert(cr->right >= sx);
            /* Phase 1: */
            if (phase1_y_steps) {
                phase1_x_steps = (int)(((int64_t)x_steps * phase1_y_steps + y_steps/2) / y_steps);
                sx -= phase1_x_steps;
                if (cr->left > sx) {
                    cr->left = sx;
                    cr->lid  = id;
                }
                x_steps -= phase1_x_steps;
                /* If phase 1 will move us into a new scanline, then we must
                 * flush it before we move. */
                if (fixed2int(cr->y) != fixed2int(cr->y + phase1_y_steps))
                    output_cursor_tr(cr, sx, id);
                cr->y += phase1_y_steps;
                sy += phase1_y_steps;
                y_steps -= phase1_y_steps;
                if (y_steps == 0)
                    return;
            }

            /* Phase 3: precalculation */
            phase3_x_steps = (int)(((int64_t)x_steps * phase3_y_steps + y_steps/2) / y_steps);
            x_steps -= phase3_x_steps;
            y_steps -= phase3_y_steps;

            /* Phase 2: */
            cr->lid = id;
            cr->rid = id;
            assert((y_steps & (fixed_1 - 1)) == 0);
            y_steps = fixed2int(y_steps);
            assert(y_steps >= 0);
            if (y_steps) {
                /* We want to change sx by x_steps in y_steps steps.
                 * So each step, we sub x_steps/y_steps from sx. That's x_inc + n_inc/ey. */
                int x_inc = x_steps/y_steps;
                int n_inc = x_steps - (x_inc * y_steps);
                int f = y_steps/2;
                int d = y_steps;
                while (y_steps) {
                    cr->right = sx;
                    sx -= x_inc;
                    f -= n_inc;
                    if (f < 0)
                        f += d, sx--;
                    cr->left = sx;
                    y_steps--;
                    output_cursor_tr(cr, sx, id);
                    cr->y += fixed_1;
                }
            }

            /* Phase 3 */
            cr->right = sx;
            cr->left  = ex;
            cr->y += phase3_y_steps;
        }
    } else {
        /* So lines decreasing in y. */
        fixed y_steps = sy - ey;
        /* We want to change from sy to ey, which are guaranteed to be on
         * different scanlines. We do this in 3 phases.
         * Phase 1 gets us from sy to the next scanline boundary.
         * Phase 2 gets us all the way to the last scanline boundary.
         * Phase 3 gets us from the last scanline boundary to ey.
         */
        int phase1_y_steps = sy & (fixed_1 - 1);
        int phase3_y_steps = (fixed_1 - ey) & (fixed_1 - 1);

        if (cr->d == DIRN_UP)
            output_cursor_tr(cr, sx, id);
        cr->d = DIRN_DOWN;

        if (sx <= ex) {
            /* Lines increasing in x. */
            int phase1_x_steps, phase3_x_steps;
            fixed x_steps = ex - sx;

            /* Phase 1: */
            assert(cr->left <= sx);
            if (phase1_y_steps) {
                phase1_x_steps = (int)(((int64_t)x_steps * phase1_y_steps + y_steps/2) / y_steps);
                sx += phase1_x_steps;
                if (cr->right < sx) {
                    cr->right = sx;
                    cr->rid   = id;
                }
                x_steps -= phase1_x_steps;
                /* Phase 1 in a falling line never moves us into a new scanline. */
                sy -= phase1_y_steps;
                cr->y -= phase1_y_steps;
                y_steps -= phase1_y_steps;
                if (y_steps == 0)
                    return;
            }

            /* Phase 3: precalculation */
            phase3_x_steps = (int)(((int64_t)x_steps * phase3_y_steps + y_steps/2) / y_steps);
            x_steps -= phase3_x_steps;
            y_steps -= phase3_y_steps;

            /* Phase 2: */
            assert((y_steps & (fixed_1 - 1)) == 0);
            y_steps = fixed2int(y_steps);
            assert(y_steps >= 0);
            if (y_steps) {
                /* We want to change sx by x_steps in y_steps steps.
                 * So each step, we add x_steps/y_steps to sx. That's x_inc + n_inc/ey. */
                int x_inc = x_steps/y_steps;
                int n_inc = x_steps - (x_inc * y_steps);
                int f = y_steps/2;
                int d = y_steps;
                while (y_steps) {
                    output_cursor_tr(cr, sx, id);
                    sx += x_inc;
                    f -= n_inc;
                    if (f < 0)
                        f += d, sx++;
                    cr->right = sx;
                    y_steps--;
                    cr->y -= fixed_1;
                }
            }

            /* Phase 3 */
            if (phase3_y_steps > 0)
                output_cursor_tr(cr, sx, id);
            cr->left  = sx;
            cr->right = ex;
            cr->y -= phase3_y_steps;
        } else {
            /* Lines decreasing in x. */
            int phase1_x_steps, phase3_x_steps;
            fixed x_steps = sx - ex;

            /* Phase 1: */
            assert(cr->right >= sx);
            if (phase1_y_steps) {
                phase1_x_steps = (int)(((int64_t)x_steps * phase1_y_steps + y_steps/2) / y_steps);
                sx -= phase1_x_steps;
                if (cr->left > sx) {
                    cr->left = sx;
                    cr->lid  = id;
                }
                x_steps -= phase1_x_steps;
                /* Phase 1 in a falling line never moves us into a new scanline. */
                sy -= phase1_y_steps;
                cr->y -= phase1_y_steps;
                y_steps -= phase1_y_steps;
                if (y_steps == 0)
                    return;
            }

            /* Phase 3: precalculation */
            phase3_x_steps = (int)(((int64_t)x_steps * phase3_y_steps + y_steps/2) / y_steps);
            x_steps -= phase3_x_steps;
            y_steps -= phase3_y_steps;

            /* Phase 2: */
            y_steps = fixed2int(y_steps);
            assert(y_steps >= 0);
            if (y_steps) {
                /* We want to change sx by x_steps in y_steps steps.
                 * So each step, we sub x_steps/y_steps from sx. That's x_inc + n_inc/ey. */
                int x_inc = x_steps/y_steps;
                int n_inc = x_steps - (x_inc * y_steps);
                int f = y_steps/2;
                int d = y_steps;
                while (y_steps) {
                    output_cursor_tr(cr, sx, id);
                    sx -= x_inc;
                    f -= n_inc;
                    if (f < 0)
                        f += d, sx--;
                    cr->left = sx;
                    y_steps--;
                    cr->y -= fixed_1;
                }
            }

            /* Phase 3 */
            if (phase3_y_steps > 0)
                output_cursor_tr(cr, sx, id);
            cr->right = sx;
            cr->left  = ex;
            cr->y -= phase3_y_steps;
        }
    }
}

Project: ghostpdl

unclose_path(gx_path * ppath, int count)
{
    subpath *psub;

    for (psub = ppath->first_subpath; count != 0;
         psub = (subpath *) psub->last->next
        )
        if (psub->last == (segment *) & psub->closer) {
            segment *prev = psub->closer.prev, *next = psub->closer.next;

            prev->next = next;
            if (next)
                next->prev = prev;
            psub->last = prev;
            count--;
        }
}

Project: ghostpdl

print_al(const gs_memory_t *mem, const char *label, const active_line * alp)
{
    if (gs_debug_c('F'))
        print_active_line(mem, label, alp);
}

Project: ghostpdl

gx_default_fill_path(gx_device * pdev, const gs_gstate * pgs,
                     gx_path * ppath, const gx_fill_params * params,
                 const gx_device_color * pdevc, const gx_clip_path * pcpath)
{
    int code = 0;

    if (gx_dc_is_pattern2_color(pdevc)
        || pdevc->type == &gx_dc_type_data_ht_colored
        || (gx_dc_is_pattern1_color(pdevc) && gx_pattern_tile_is_clist(pdevc->colors.pattern.p_tile))
        ) {
        /*  Optimization for shading and halftone fill :
            The general filling algorithm subdivides the fill region into
            trapezoid or rectangle subregions and then paints each subregion
            with given color. If the color is complex, it also needs to be subdivided
            into constant color rectangles. In the worst case it gives
            a multiple of numbers of rectangles, which may be too slow.
            A faster processing may be obtained with installing a clipper
            device with the filling path, and then render the complex color
            through it. The speeding up happens due to the clipper device
            is optimised for fast scans through neighbour clipping rectangles.
        */
        /*  We need a single clipping path here, because shadings and
            halftones don't take 2 paths. Compute the clipping path intersection.
        */
        gx_clip_path cpath_intersection, cpath_with_shading_bbox;
        const gx_clip_path *pcpath1, *pcpath2;
        gs_gstate *pgs_noconst = (gs_gstate *)pgs; /* Break const. */

        if (ppath != NULL) {
            code = gx_cpath_init_local_shared_nested(&cpath_intersection, pcpath, pdev->memory, 1);
            if (code < 0)
                return code;
            if (pcpath == NULL) {
                gs_fixed_rect clip_box1;

                (*dev_proc(pdev, get_clipping_box)) (pdev, &clip_box1);
                code = gx_cpath_from_rectangle(&cpath_intersection, &clip_box1);
            }
            if (code >= 0)
                code = gx_cpath_intersect_with_params(&cpath_intersection, ppath, params->rule,
                        pgs_noconst, params);
            pcpath1 = &cpath_intersection;
        } else
            pcpath1 = pcpath;
        pcpath2 = pcpath1;
        if (code >= 0)
            code = gx_dc_pattern2_clip_with_bbox(pdevc, pdev, &cpath_with_shading_bbox, &pcpath1);
        /* Do fill : */
        if (code >= 0) {
            gs_fixed_rect clip_box;
            gs_int_rect cb;
            const gx_rop_source_t *rs = NULL;
            gx_device *dev;
            gx_device_clip cdev;

            gx_cpath_outer_box(pcpath1, &clip_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);
            if (gx_dc_is_pattern2_color(pdevc) &&
                    (*dev_proc(pdev, dev_spec_op))(pdev,
                         gxdso_pattern_handles_clip_path, NULL, 0) > 0) {
                /* A special interaction with clist writer device :
                   pass the intersected clipping path. It uses an unusual call to
                   fill_path with NULL device color. */
                code = (*dev_proc(pdev, fill_path))(pdev, pgs, ppath, params, NULL, pcpath1);
                dev = pdev;
            } else {
                gx_make_clip_device_on_stack(&cdev, pcpath1, pdev);
                dev = (gx_device *)&cdev;
                if ((*dev_proc(pdev, dev_spec_op))(pdev,
                        gxdso_pattern_shading_area, NULL, 0) > 0)
                    set_dev_proc(&cdev, fill_path, pass_shading_area_through_clip_path_device);
                code = 0;
            }
            if (code >= 0)
                code = pdevc->type->fill_rectangle(pdevc,
                        cb.p.x, cb.p.y, cb.q.x - cb.p.x, cb.q.y - cb.p.y,
                        dev, pgs->log_op, rs);
        }
        if (ppath != NULL)
            gx_cpath_free(&cpath_intersection, "shading_fill_cpath_intersection");
        if (pcpath1 != pcpath2)
            gx_cpath_free(&cpath_with_shading_bbox, "shading_fill_cpath_intersection");
    } else {
        code = gx_general_fill_path(pdev, pgs, ppath, params, pdevc, pcpath);
    }
    return code;
}

Project: ghostpdl

free_line_list(line_list *ll)
{
    /* Free any individually allocated active_lines. */
    gs_memory_t *mem = ll->memory;
    active_line *alp;

    while ((alp = ll->active_area) != 0) {
        active_line *next = alp->alloc_next;

        gs_free_object(mem, alp, "active line");
        ll->active_area = next;
    }
}

Project: ghostpdl

gx_fill_edgebuffer(gx_device       * pdev,
             const gx_device_color * pdevc,
                   gx_edgebuffer   * edgebuffer,
                   int               log_op)
{
    int i, code;

    for (i=0; i < edgebuffer->height; i++) {
        int *row    = &edgebuffer->table[edgebuffer->index[i]];
        int  rowlen = *row++;

        while (rowlen > 0) {
            int left, right;

            left  = *row++;
            right = *row++;
            rowlen -= 2;
            left  = fixed2int(left + fixed_half);
            right = fixed2int(right + fixed_half);
            right -= left;
            if (right > 0) {
                if (log_op < 0)
                    code = dev_proc(pdev, fill_rectangle)(pdev, left, edgebuffer->base+i, right, 1, pdevc->colors.pure);
                else
                    code = gx_fill_rectangle_device_rop(left, edgebuffer->base+i, right, 1, pdevc, pdev, (gs_logical_operation_t)log_op);
                if (code < 0)
                    return code;
            }
        }
    }
    return 0;
}

Project: ghostpdl

output_cursor(cursor *cr, fixed x)
{
    int iy = fixed2int(cr->y) - cr->base;
    int *row;
    int count;

    if (iy < 0 || iy >= cr->scanlines) {
        /* Out of range, nothing to do */
    } else if (cr->first) {
        /* Store this one for later, for when we match up */
        cr->save_left  = cr->left;
        cr->save_right = cr->right;
        cr->save_y     = cr->y;
        cr->save_d     = cr->d;
        cr->first      = 0;
    } else {
        /* Put it in the table */
        assert(cr->d != DIRN_UNSET);

        row = &cr->table[cr->index[iy]];
        *row = count = (*row)+1; /* Increment the count */
        row[2 * count-1] = (cr->left&~1) | cr->d;
        row[2 * count  ] = cr->right;
    }
    cr->left  = x;
    cr->right = x;
}

Project: ghostpdl

gx_filter_edgebuffer_tr_app(gx_device       * pdev,
                            gx_edgebuffer   * edgebuffer,
                            int               rule)
{
    int i;
    int marked_id = 0;

#ifdef DEBUG_SCAN_CONVERTER
    gx_edgebuffer_print_tr_app(edgebuffer);
#endif

    for (i=0; i < edgebuffer->height; i++) {
        int *row      = &edgebuffer->table[edgebuffer->index[i]];
        int  rowlen   = *row++;
        int *rowstart = row;
        int *rowout   = row;
        int  ll, llid, lr, lrid, rlid, rr, rrid, wind, marked_to;

        /* Avoid double setting pixels, by keeping where we have marked to. */
        marked_to = 0;
        while (rowlen > 0) {
            if (rule == gx_rule_even_odd) {
                /* Even Odd */
                ll   = *row++;
                llid = (*row++)>>1;
                lr   = *row++;
                lrid = *row++;
                rowlen--;
                wind = 1;

                /* We will fill solidly from ll to at least lr, possibly further */
                assert(rowlen > 0);
                do {
                    (void)row++; /* rl not needed here */
                    rlid = *row++>>1;
                    rr   = *row++;
                    rrid = *row++;
                    rowlen--;
                    if (rr > lr) {
                        lr   = rr;
                        lrid = rrid;
                    }
                    wind ^= 1;
                    if (wind == 0)
                        break;
                } while (rowlen > 0);
            } else {
                /* Non-Zero */
                int w;

                ll   = *row++;
                llid = *row++;
                lr   = *row++;
                lrid = *row++;
                wind = -(llid&1) | 1;
                llid >>= 1;
                rowlen--;

                assert(rowlen > 0);
                do {
                    (void)row++; /* rl not needed */
                    rlid = *row++;
                    rr   = *row++;
                    rrid = *row++;
                    w = -(rlid&1) | 1;
                    rlid >>= 1;
                    rowlen--;
                    if (rr > lr) {
                        lr   = rr;
                        lrid = rrid;
                    }
                    wind += w;
                    if (wind == 0)
                        break;
                } while (rowlen > 0);
            }

            if (lr < 0)
                continue;

            if (marked_to > ll) {
                if (rowout == rowstart) {
                    ll   = marked_to;
                    llid = --marked_id;
                } else {
                    rowout -= 4;
                    ll   = rowout[0];
                    llid = rowout[1];
                }
            }

            if (lr > ll) {
                *rowout++ = ll;
                *rowout++ = llid;
                *rowout++ = lr;
                *rowout++ = lrid;
                marked_to = ll;
            }
        }
        rowstart[-1] = (rowout - rowstart)>>2;
    }
    return 0;
}

Project: ghostpdl

start_al_pair(line_list *ll, contour_cursor *q, contour_cursor *p)
{
    int code;

    if (q->monotonic_y)
        code = add_y_line(q->prev, q->pseg, DIR_DOWN, ll);
    else
        code = add_y_curve_part(ll, q->prev, q->pseg, DIR_DOWN, &q->fi,
                            !q->first_flattened, false, q->monotonic_x);
    if (code < 0)
        return code;
    if (p->monotonic_y)
        code = add_y_line(p->prev, p->pseg, DIR_UP, ll);
    else
        code = add_y_curve_part(ll, p->prev, p->pseg, DIR_UP, &p->fi,
                            p->more_flattened, false, p->monotonic_x);
    return code;
}

Project: ghostpdl

init_line_list(line_list *ll, gs_memory_t * mem)
{
    ll->memory = mem;
    ll->active_area = 0;
    ll->next_active = ll->local_active;
    ll->limit = ll->next_active + MAX_LOCAL_ACTIVE;
    ll->close_count = 0;
    ll->y_list = 0;
    ll->y_line = 0;
    ll->h_list0 = ll->h_list1 = 0;

    ll->x_head.prev = NULL;
    /* Bug 695234: Initialise the following to pacify valgrind */
    ll->x_head.start.x = 0;
    ll->x_head.start.y = 0;
    ll->x_head.end.x = 0;
    ll->x_head.end.y = 0;

    /* Do not initialize ll->bbox_left, ll->bbox_width - they were set in advance. */
    INCR(fill);
}

Project: ghostpdl

start_al_pair_from_min(line_list *ll, contour_cursor *q)
{
    int dir, code;
    const fill_options * const fo = ll->fo;

    /* q stands at the first segment, which isn't last. */
    do {
        code = gx_flattened_iterator__next(&q->fi);
        if (code < 0)
            return code;
        q->more_flattened = code;
        dir = compute_dir(fo, q->fi.ly0, q->fi.ly1);
        if (q->fi.ly0 > fo->ymax && ll->y_break > q->fi.y0)
            ll->y_break = q->fi.ly0;
        if (q->fi.ly1 > fo->ymax && ll->y_break > q->fi.ly1)
            ll->y_break = q->fi.ly1;
        if (q->fi.ly0 >= fo->ymin) {
            if (dir == DIR_UP && ll->main_dir == DIR_DOWN) {
                code = add_y_curve_part(ll, q->prev, q->pseg, DIR_DOWN, &q->fi,
                                        true, true, q->monotonic_x);
                if (code < 0)
                    return code;
                code = add_y_curve_part(ll, q->prev, q->pseg, DIR_UP, &q->fi,
                                        q->more_flattened, false, q->monotonic_x);
                if (code < 0)
                    return code;
            } else if (q->fi.ly1 < fo->ymin) {
                code = add_y_curve_part(ll, q->prev, q->pseg, DIR_DOWN, &q->fi,
                                        true, false, q->monotonic_x);
                if (code < 0)
                    return code;
            }
        } else if (q->fi.ly1 >= fo->ymin) {
            code = add_y_curve_part(ll, q->prev, q->pseg, DIR_UP, &q->fi,
                                    q->more_flattened, false, q->monotonic_x);
            if (code < 0)
                return code;
        }
        q->first_flattened = false;
        q->dir = dir;
        if (dir == DIR_DOWN || dir == DIR_UP)
            ll->main_dir = dir;
    } while(q->more_flattened);
    /* q stands at the last segment. */
    return 0;
    /* note : it doesn't depend on the number of curve minimums,
       which may vary due to arithmetic errors. */
}

Project: ghostpdl

static int intcmp(const void *a, const void *b)
{
    return *((int*)a) - *((int *)b);
}

Project: ghostpdl

static void mark_curve_tr(fixed sx, fixed sy, fixed c1x, fixed c1y, fixed c2x, fixed c2y, fixed ex, fixed ey, fixed base_y, fixed height, int *table, int *index, int *id, int depth)
{
    fixed ax = (sx + c1x)>>1;
    fixed ay = (sy + c1y)>>1;
    fixed bx = (c1x + c2x)>>1;
    fixed by = (c1y + c2y)>>1;
    fixed cx = (c2x + ex)>>1;
    fixed cy = (c2y + ey)>>1;
    fixed dx = (ax + bx)>>1;
    fixed dy = (ay + by)>>1;
    fixed fx = (bx + cx)>>1;
    fixed fy = (by + cy)>>1;
    fixed gx = (dx + fx)>>1;
    fixed gy = (dy + fy)>>1;

    assert(depth >= 0);
    if (depth == 0) {
        *id += 1;
        mark_line_tr(sx, sy, ex, ey, base_y, height, table, index, *id);
    } else {
        depth--;
        mark_curve_tr(sx, sy, ax, ay, dx, dy, gx, gy, base_y, height, table, index, id, depth);
        mark_curve_tr(gx, gy, fx, fy, cx, cy, ex, ey, base_y, height, table, index, id, depth);
    }
}

Project: ghostpdl

static int edgecmp_tr(const void *a, const void *b)
{
    int left  = ((int*)a)[0];
    int right = ((int*)b)[0];
    if (left != right)
        return left - right;
    left = ((int*)a)[1] - ((int*)b)[1];
    if (left != 0)
        return left;
    left = ((int*)a)[2] - ((int*)b)[2];
    if (left != 0)
        return left;
    return ((int*)a)[3] - ((int*)b)[3];
}

Project: ghostpdl

flush_cursor_tr(cursor_tr *cr, fixed x, int id)
{
    /* This should only happen if we were entirely out of bounds */
    if (cr->first)
        return;

    /* Merge save into current if we can */
    if (fixed2int(cr->y) == fixed2int(cr->save_y) &&
        (cr->d == cr->save_d || cr->save_d == DIRN_UNSET)) {
        if (cr->left > cr->save_left) {
            cr->left = cr->save_left;
            cr->lid  = cr->save_lid;
        }
        if (cr->right < cr->save_right) {
            cr->right = cr->save_right;
            cr->rid = cr->save_rid;
        }
        output_cursor_tr(cr, x, id);
        return;
    }

    /* Merge not possible */
    output_cursor_tr(cr, x, id);
    cr->left  = cr->save_left;
    cr->lid   = cr->save_lid;
    cr->right = cr->save_right;
    cr->rid   = cr->save_rid;
    cr->y     = cr->save_y;
    if (cr->save_d != -1)
        cr->d = cr->save_d;
    output_cursor_tr(cr, x, id);
}

Project: ghostpdl

gx_fill_edgebuffer_tr_app(gx_device       * pdev,
                    const gx_device_color * pdevc,
                          gx_edgebuffer   * edgebuffer,
                          int               log_op)
{
    int i, j, code;

    for (i=0; i < edgebuffer->height; ) {
        int *row    = &edgebuffer->table[edgebuffer->index[i]];
        int  rowlen = *row++;
        int *row2;
        int *rowptr;
        int *row2ptr;

        /* See how many scanlines match i */
        for (j = i+1; j < edgebuffer->height; j++) {
            int row2len;

            row2    = &edgebuffer->table[edgebuffer->index[j]];
            row2len = *row2++;
            row2ptr = row2;
            rowptr  = row;

            if (rowlen != row2len)
                break;
            while (row2len > 0) {
                if (rowptr[1] != row2ptr[1] || rowptr[3] != row2ptr[3])
                    goto rowdifferent;
                rowptr  += 4;
                row2ptr += 4;
                row2len--;
            }
        }
rowdifferent:{}

        /* So j is the first scanline that doesn't match i */

        /* The first scanline is always sent as rectangles */
        while (rowlen > 0) {
            int left  = row[0];
            int right = row[2];
            row += 4;
            left = fixed2int(left);
            right = fixed2int(right + fixed_1 - 1);
            rowlen--;

            right -= left;
            if (right > 0) {
                if (log_op < 0)
                    code = dev_proc(pdev, fill_rectangle)(pdev, left, edgebuffer->base+i, right, 1, pdevc->colors.pure);
                else
                    code = gx_fill_rectangle_device_rop(left, edgebuffer->base+i, right, 1, pdevc, pdev, (gs_logical_operation_t)log_op);
                if (code < 0)
                    return code;
            }
        }

        /* The middle section (all but the first and last
         * scanlines) can be sent as a trapezoid. */
        if (i + 2 < j) {
            gs_fixed_edge le;
            gs_fixed_edge re;
            fixed ybot = int2fixed(edgebuffer->base+i+1);
            fixed ytop = int2fixed(edgebuffer->base+j-1);
            row    = &edgebuffer->table[edgebuffer->index[i]];
            rowlen = *row++;
            row2    = &edgebuffer->table[edgebuffer->index[j-1]];
            row2++;
            while (rowlen > 0) {
                /* The fill rules used by fill_trap state that if a
                 * pixel centre is touched by a boundary, the pixel
                 * will be filled iff the boundary is horizontal and
                 * the filled region is above it, or the boundary is
                 * not horizontal, and the filled region is to the
                 * right of it.
                 *
                 * Therefore, we move the left edge back by just less
                 * than half, so ...00 goes to ...81 and therefore
                 * does not cause an extra pixel to get filled.
                 *
                 * We move the right edge forward by half, so ...00
                 * goes to ...80 and therefore does not cause an extra
                 * pixel to get filled.
                 */
                le.start.x = row[0] - (fixed_half-1);
                re.start.x = row[2] + fixed_half;
                le.end.x   = row2[0] - (fixed_half-1);
                re.end.x   = row2[2] + fixed_half;
                row += 4;
                row2 += 4;
                rowlen--;

                if (le.start.x <= le.end.x) {
                    le.start.y = ybot - fixed_1 + fixed_half;
                    le.end.y   = ytop           + fixed_half;
                } else {
                    le.start.y = ybot           - fixed_half;
                    le.end.y   = ytop + fixed_1 - fixed_half;
                }

                if (re.start.x <= re.end.x) {
                    re.start.y = ybot           - fixed_half;
                    re.end.y   = ytop + fixed_1 - fixed_half;
                } else {
                    re.start.y = ybot - fixed_1 + fixed_half;
                    re.end.y   = ytop           + fixed_half;
                }

                assert(le.start.x >= -fixed_half);
                assert(le.end.x >= -fixed_half);
                assert(re.start.x >= le.start.x);
                assert(re.end.x >= le.end.x);
                assert(le.start.y <= ybot);
                assert(re.start.y <= ybot);
                assert(le.end.y >= ytop);
                assert(re.end.y >= ytop);

                code = dev_proc(pdev, fill_trapezoid)(
                                pdev,
                                &le,
                                &re,
                                ybot,
                                ytop,
                                0, /* bool swap_axes */
                                pdevc, /*const gx_drawing_color *pdcolor */
                                log_op);
                if (code < 0)
                    return code;
            }
        }

        if (i + 1 < j)
        {
            /* The last scanline is always sent as rectangles */
            row    = &edgebuffer->table[edgebuffer->index[j-1]];
            rowlen = *row++;
            while (rowlen > 0) {
                int left  = row[0];
                int right = row[2];
                row += 4;
                left = fixed2int(left);
                right = fixed2int(right + fixed_1 - 1);
                rowlen--;

                right -= left;
                if (right > 0) {
                    if (log_op < 0)
                        code = dev_proc(pdev, fill_rectangle)(pdev, left, edgebuffer->base+j-1, right, 1, pdevc->colors.pure);
                    else
                        code = gx_fill_rectangle_device_rop(left, edgebuffer->base+j-1, right, 1, pdevc, pdev, (gs_logical_operation_t)log_op);
                    if (code < 0)
                        return code;
                }
            }
        }
        i = j;
    }
    return 0;
}

Project: ghostpdl

gx_edgebuffer_print_app(gx_edgebuffer * edgebuffer)
{
    int i;
    int borked = 0;

    dlprintf1("Edgebuffer %x\n", edgebuffer);
    dlprintf4("xmin=%d xmax=%d base=%d height=%d\n",
              edgebuffer->xmin, edgebuffer->xmax, edgebuffer->base, edgebuffer->height);
    for (i=0; i < edgebuffer->height; i++) {
        int  offset = edgebuffer->index[i];
        int *row    = &edgebuffer->table[offset];
        int count   = *row++;
        int c       = count;
        int wind    = 0;
        dlprintf3("%x @ %d: %d =", i, offset, count);
        while (count-- > 0) {
            int left  = *row++;
            int right = *row++;
            int w     = -(left&1) | 1;
            wind += w;
            dlprintf3(" (%x,%x)%c", left&~1, right, left&1 ? 'v' : '^');
        }
        if (wind != 0 || c & 1) {
            dlprintf(" <- BROKEN");
            borked = 1;
        }
        dlprintf("\n");
    }
    if (borked) {
        borked = borked; /* Breakpoint here */
    }
}

Project: ghostpdl

flush_cursor(cursor *cr, fixed x)
{
    /* This should only happen if we were entirely out of bounds */
    if (cr->first)
        return;

    /* Merge save into current if we can */
    if (fixed2int(cr->y) == fixed2int(cr->save_y) &&
        (cr->d == cr->save_d || cr->save_d == DIRN_UNSET)) {
        if (cr->left > cr->save_left)
            cr->left = cr->save_left;
        if (cr->right < cr->save_right)
            cr->right = cr->save_right;
        output_cursor(cr, x);
        return;
    }

    /* Merge not possible */
    output_cursor(cr, x);
    cr->left  = cr->save_left;
    cr->right = cr->save_right;
    cr->y     = cr->save_y;
    if (cr->save_d != -1)
        cr->d = cr->save_d;
    output_cursor(cr, x);
}

Project: ghostpdl

pass_shading_area_through_clip_path_device(gx_device * pdev, const gs_gstate * pgs,
                     gx_path * ppath, const gx_fill_params * params,
                 const gx_device_color * pdevc, const gx_clip_path * pcpath)
{
    if (pdevc == NULL) {
        gx_device_clip *cdev = (gx_device_clip *)pdev;

        return dev_proc(cdev->target, fill_path)(cdev->target, pgs, ppath, params, pdevc, pcpath);
    }
    /* We know that tha clip path device implements fill_path with default proc. */
    return gx_default_fill_path(pdev, pgs, ppath, params, pdevc, pcpath);
}

Project: ghostpdl

gx_fill_edgebuffer_app(gx_device       * pdev,
                 const gx_device_color * pdevc,
                       gx_edgebuffer   * edgebuffer,
                       int               log_op)
{
    int i, code;

    for (i=0; i < edgebuffer->height; i++) {
        int *row    = &edgebuffer->table[edgebuffer->index[i]];
        int  rowlen = *row++;
        int  left, right;

        while (rowlen > 0) {
            left  = *row++;
            right = *row++;
            left  = fixed2int(left);
            right = fixed2int(right + fixed_1 - 1);
            rowlen -= 2;

            right -= left;
            if (right > 0) {
                if (log_op < 0)
                    code = dev_proc(pdev, fill_rectangle)(pdev, left, edgebuffer->base+i, right, 1, pdevc->colors.pure);
                else
                    code = gx_fill_rectangle_device_rop(left, edgebuffer->base+i, right, 1, pdevc, pdev, (gs_logical_operation_t)log_op);
                if (code < 0)
                    return code;
            }
        }
    }
    return 0;
}

Project: ghostpdl

gx_edgebuffer_print_tr(gx_edgebuffer * edgebuffer)
{
    int i;

    dlprintf1("Edgebuffer %x\n", edgebuffer);
    dlprintf4("xmin=%d xmax=%d base=%d height=%d\n",
              edgebuffer->xmin, edgebuffer->xmax, edgebuffer->base, edgebuffer->height);
    for (i=0; i < edgebuffer->height; i++)
    {
        int  offset = edgebuffer->index[i];
        int *row    = &edgebuffer->table[offset];
        int count   = *row++;
        dlprintf3("%d @ %d: %d =", i, offset, count);
        while (count-- > 0) {
            int e  = *row++;
            int id = *row++;
            dlprintf3(" %d%c%d", e, id&1 ? 'v' : '^', id>>1);
        }
        dlprintf("\n");
    }
}

Project: ghostpdl

is_spotan_device(gx_device * dev)
{
    /* Use open_device procedure to identify the type of the device
     * instead of the standard gs_object_type() because gs_cpath_accum_device
     * is allocaded on the stack i.e. has no block header with a descriptor
     * but has dev->memory set like a heap-allocated device.
     */
    return dev->procs.open_device == san_open;
}

Project: ghostpdl

static int intcmp_tr(const void *a, const void *b)
{
    int left  = ((int*)a)[0];
    int right = ((int*)b)[0];
    if (left != right)
        return left - right;
    return ((int*)a)[1] - ((int*)b)[1];
}

Project: ghostpdl

int gx_scan_convert_tr_app(gx_device     * pdev,
                           gx_path       * path,
                     const gs_fixed_rect * clip,
                           gx_edgebuffer * edgebuffer,
                           fixed           fixed_flat)
{
    gs_fixed_rect  ibox;
    int            scanlines;
    const subpath *psub;
    int           *index;
    int           *table;
    int            i;
    cursor_tr      cr;
    int            code;
    int            id = 0;

    edgebuffer->index = NULL;
    edgebuffer->table = NULL;

    /* Bale out if no actual path. We see this with the clist */
    if (path->first_subpath == NULL)
        return 0;

    code = make_bbox(path, clip, &ibox, 0);
    if (code < 0)
        return code;

    if (ibox.q.y <= ibox.p.y)
        return 0;

    code = make_table(pdev, path, &ibox, 4, 0, &scanlines, &index, &table);
    if (code < 0)
        return code;

    /* Step 2 continued: Now we run through the path, filling in the real
     * values. */
    cr.scanlines = scanlines;
    cr.index     = index;
    cr.table     = table;
    cr.base      = ibox.p.y;
    for (psub = path->first_subpath; psub != 0;) {
        const segment *pseg = (const segment *)psub;
        fixed ex = pseg->pt.x;
        fixed ey = pseg->pt.y;
        fixed ix = ex;
        fixed iy = ey;
        fixed sx, sy;

        cr.left = cr.right = ex;
        cr.lid = cr.rid = id+1;
        cr.y = ey;
        cr.d = -1;
        cr.first = 1;

        while ((pseg = pseg->next) != 0 &&
               pseg->type != s_start
            ) {
            sx = ex;
            sy = ey;
            ex = pseg->pt.x;
            ey = pseg->pt.y;

            switch (pseg->type) {
                default:
                case s_start: /* Should never happen */
                case s_dash:  /* We should never be seeing a dash here */
                    assert("This should never happen" == NULL);
                    break;
                case s_curve: {
                    const curve_segment *const pcur = (const curve_segment *)pseg;
                    int k = gx_curve_log2_samples(sx, sy, pcur, fixed_flat);

                    mark_curve_tr_app(&cr, sx, sy, pcur->p1.x, pcur->p1.y, pcur->p2.x, pcur->p2.y, ex, ey, k, &id);
                    break;
                }
                case s_gap:
                case s_line:
                case s_line_close:
                    mark_line_tr_app(&cr, sx, sy, ex, ey, ++id);
                    break;
            }
        }
        /* And close any open segments */
        mark_line_tr_app(&cr, ex, ey, ix, iy, ++id);
        flush_cursor_tr(&cr, ex, id);
        psub = (const subpath *)pseg;
    }

    /* Step 2 complete: We now have a complete list of intersection data in
     * table, indexed by index. */

    /* Step 3: Sort the intersects on x */
    for (i=0; i < scanlines; i++) {
        int *row = &table[index[i]];
        int  rowlen = *row++;

        /* Sort the 'rowlen' entries - *must* be faster to do this with
         * custom code, as the function call overhead on the comparisons
         * will be a killer */
        qsort(row, rowlen, 4*sizeof(int), edgecmp_tr);
    }

    edgebuffer->base   = ibox.p.y;
    edgebuffer->height = scanlines;
    edgebuffer->xmin   = ibox.p.x;
    edgebuffer->xmax   = ibox.q.x;
    edgebuffer->index  = index;
    edgebuffer->table  = table;

    return 0;
}

Project: ghostpdl

static void mark_curve_tr_app(cursor_tr *cr, fixed sx, fixed sy, fixed c1x, fixed c1y, fixed c2x, fixed c2y, fixed ex, fixed ey, int depth, int *id)
{
        int ax = (sx + c1x)>>1;
        int ay = (sy + c1y)>>1;
        int bx = (c1x + c2x)>>1;
        int by = (c1y + c2y)>>1;
        int cx = (c2x + ex)>>1;
        int cy = (c2y + ey)>>1;
        int dx = (ax + bx)>>1;
        int dy = (ay + by)>>1;
        int fx = (bx + cx)>>1;
        int fy = (by + cy)>>1;
        int gx = (dx + fx)>>1;
        int gy = (dy + fy)>>1;

        assert(depth >= 0);
        if (depth == 0) {
            *id += 1;
            mark_line_tr_app(cr, sx, sy, ex, ey, *id);
        } else {
            depth--;
            mark_curve_tr_app(cr, sx, sy, ax, ay, dx, dy, gx, gy, depth, id);
            mark_curve_tr_app(cr, gx, gy, fx, fy, cx, cy, ex, ey, depth, id);
        }
}

Project: ghostpdl

gx_edgebuffer_init(gx_edgebuffer * edgebuffer)
{
    edgebuffer->base   = 0;
    edgebuffer->height = 0;
    edgebuffer->index  = NULL;
    edgebuffer->table  = NULL;
}

Project: ghostpdl

make_al(line_list *ll)
{
    active_line *alp = ll->next_active;

    if (alp == ll->limit) {     /* Allocate separately */
        alp = gs_alloc_struct(ll->memory, active_line,
                              &st_active_line, "active line");
        if (alp == 0)
            return NULL;
        alp->alloc_next = ll->active_area;
        ll->active_area = alp;
        INCR(fill_alloc);
    } else
        ll->next_active++;
    alp->contour_count = ll->contour_count;
    return alp;
}

Project: ghostpdl

static void mark_curve_app(cursor *cr, fixed sx, fixed sy, fixed c1x, fixed c1y, fixed c2x, fixed c2y, fixed ex, fixed ey, int depth)
{
        int ax = (sx + c1x)>>1;
        int ay = (sy + c1y)>>1;
        int bx = (c1x + c2x)>>1;
        int by = (c1y + c2y)>>1;
        int cx = (c2x + ex)>>1;
        int cy = (c2y + ey)>>1;
        int dx = (ax + bx)>>1;
        int dy = (ay + by)>>1;
        int fx = (bx + cx)>>1;
        int fy = (by + cy)>>1;
        int gx = (dx + fx)>>1;
        int gy = (dy + fy)>>1;

        assert(depth >= 0);
        if (depth == 0)
            mark_line_app(cr, sx, sy, ex, ey);
        else {
            depth--;
            mark_curve_app(cr, sx, sy, ax, ay, dx, dy, gx, gy, depth);
            mark_curve_app(cr, gx, gy, fx, fy, cx, cy, ex, ey, depth);
        }
}

Project: ghostpdl

output_cursor_tr(cursor_tr *cr, fixed x, int id)
{
    int iy = fixed2int(cr->y) - cr->base;
    int *row;
    int count;

    if (iy < 0 || iy >= cr->scanlines) {
        /* Nothing to do */
    }
    else if (cr->first) {
        /* Save it for later in case we join up */
        cr->save_left  = cr->left;
        cr->save_lid   = cr->lid;
        cr->save_right = cr->right;
        cr->save_rid   = cr->rid;
        cr->save_y     = cr->y;
        cr->save_d     = cr->d;
        cr->first      = 0;
    } else {
        /* Enter it into the table */
        assert(cr->d != DIRN_UNSET);

        row = &cr->table[cr->index[iy]];
        *row = count = (*row)+1; /* Increment the count */
        row[4 * count - 3] = cr->left;
        row[4 * count - 2] = cr->d | (cr->lid<<1);
        row[4 * count - 1] = cr->right;
        row[4 * count    ] = cr->rid;
    }
    cr->left  = x;
    cr->lid   = id;
    cr->right = x;
    cr->rid   = id;
}

Project: ghostpdl

insert_y_line(line_list *ll, active_line *alp)
{
    active_line *yp = ll->y_line;
    active_line *nyp;
    fixed y_start = alp->start.y;

    if (yp == 0) {
        alp->next = alp->prev = 0;
        ll->y_list = alp;
    } else if (y_start >= yp->start.y) {        /* Insert the new line after y_line */
        while (INCR_EXPR(y_up),
               ((nyp = yp->next) != NULL &&
                y_start > nyp->start.y)
            )
            yp = nyp;
        alp->next = nyp;
        alp->prev = yp;
        yp->next = alp;
        if (nyp)
            nyp->prev = alp;
    } else {            /* Insert the new line before y_line */
        while (INCR_EXPR(y_down),
               ((nyp = yp->prev) != NULL &&
                y_start < nyp->start.y)
            )
            yp = nyp;
        alp->prev = nyp;
        alp->next = yp;
        yp->prev = alp;
        if (nyp)
            nyp->next = alp;
        else
            ll->y_list = alp;
    }
    ll->y_line = alp;
    print_al(ll->memory, "add ", alp);
}

Project: ghostpdl

compute_dir(const fill_options *fo, fixed y0, fixed y1)
{
    if (max(y0, y1) < fo->ymin)
        return DIR_OUT_OF_Y_RANGE;
    if (min(y0, y1) > fo->ymax)
        return DIR_OUT_OF_Y_RANGE;
    return (y0 < y1 ? DIR_UP :
            y0 > y1 ? DIR_DOWN : DIR_HORIZONTAL);
}

Project: ghostpdl

int gx_scan_convert(gx_device     * pdev,
                    gx_path       * path,
              const gs_fixed_rect * clip,
                    gx_edgebuffer * edgebuffer,
                    fixed           fixed_flat)
{
    gs_fixed_rect  ibox;
    int            scanlines;
    const subpath *psub;
    int           *index;
    int           *table;
    int            i;
    int            code;

    edgebuffer->index = NULL;
    edgebuffer->table = NULL;

    /* Bale out if no actual path. We see this with the clist */
    if (path->first_subpath == NULL)
        return 0;

    code = make_bbox(path, clip, &ibox, fixed_half);
    if (code < 0)
        return code;

    if (ibox.q.y <= ibox.p.y)
        return 0;

    code = make_table(pdev, path, &ibox, 1, fixed_half, &scanlines, &index, &table);
    if (code < 0)
        return code;

    /* Step 2 continued: Now we run through the path, filling in the real
     * values. */
    for (psub = path->first_subpath; psub != 0;) {
        const segment *pseg = (const segment *)psub;
        fixed ex = pseg->pt.x;
        fixed ey = pseg->pt.y;
        fixed ix = ex;
        fixed iy = ey;

        while ((pseg = pseg->next) != 0 &&
               pseg->type != s_start
            ) {
            fixed sx = ex;
            fixed sy = ey;
            ex = pseg->pt.x;
            ey = pseg->pt.y;

            switch (pseg->type) {
                default:
                case s_start: /* Should never happen */
                case s_dash:  /* We should never be seeing a dash here */
                    assert("This should never happen" == NULL);
                    break;
                case s_curve: {
                    const curve_segment *const pcur = (const curve_segment *)pseg;
                    int k = gx_curve_log2_samples(sx, sy, pcur, fixed_flat);

                    mark_curve(sx, sy, pcur->p1.x, pcur->p1.y, pcur->p2.x, pcur->p2.y, ex, ey, ibox.p.y, scanlines, table, index, k);
                    break;
                }
                case s_gap:
                case s_line:
                case s_line_close:
                    if (sy != ey)
                        mark_line(sx, sy, ex, ey, ibox.p.y, scanlines, table, index);
                    break;
            }
        }
        /* And close any open segments */
        if (iy != ey)
            mark_line(ex, ey, ix, iy, ibox.p.y, scanlines, table, index);
        psub = (const subpath *)pseg;
    }

    /* Step 2 complete: We now have a complete list of intersection data in
     * table, indexed by index. */

    /* Step 3: Sort the intersects on x */
    for (i=0; i < scanlines; i++) {
        int *row = &table[index[i]];
        int  rowlen = *row++;

        /* Sort the 'rowlen' entries - *must* be faster to do this with
         * custom code, as the function call overhead on the comparisons
         * will be a killer */
        qsort(row, rowlen, sizeof(int), intcmp);
    }

    edgebuffer->base   = ibox.p.y;
    edgebuffer->height = scanlines;
    edgebuffer->xmin   = ibox.p.x;
    edgebuffer->xmax   = ibox.q.x;
    edgebuffer->index  = index;
    edgebuffer->table  = table;

    return 0;
}

Project: ghostpdl

add_y_curve_part(line_list *ll, segment *s0, segment *s1, int dir,
    gx_flattened_iterator *fi, bool more1, bool step_back, bool monotonic_x)
{
    active_line *alp = make_al(ll);
    int code;

    if (alp == NULL)
        return_error(gs_error_VMerror);
    alp->pseg = (dir == DIR_UP ? s1 : s0);
    alp->direction = dir;
    alp->fi = *fi;
    alp->more_flattened = more1;
    if (dir != DIR_UP && more1)
        gx_flattened_iterator__switch_to_backscan(&alp->fi, more1);
    if (step_back) {
        do {
            code = gx_flattened_iterator__prev(&alp->fi);
            if (code < 0)
                return code;
            alp->more_flattened = code;
            if (compute_dir(ll->fo, alp->fi.ly0, alp->fi.ly1) != 2)
                break;
        } while (alp->more_flattened);
    }
    code = step_al(alp, false);
    if (code < 0)
        return code;
    alp->monotonic_y = false;
    alp->monotonic_x = monotonic_x;
    insert_y_line(ll, alp);
    return 0;
}

Project: ghostpdl

gx_filter_edgebuffer_app(gx_device       * pdev,
                         gx_edgebuffer   * edgebuffer,
                         int               rule)
{
    int i;

#ifdef DEBUG_SCAN_CONVERTER
    gx_edgebuffer_print_app(edgebuffer);
#endif

    for (i=0; i < edgebuffer->height; i++) {
        int *row      = &edgebuffer->table[edgebuffer->index[i]];
        int  rowlen   = *row++;
        int *rowstart = row;
        int *rowout   = row;
        int  ll, lr, rl, rr, wind, marked_to;

        /* Avoid double setting pixels, by keeping where we have marked to. */
        marked_to = 0;
        while (rowlen > 0) {
            if (rule == gx_rule_even_odd) {
                /* Even Odd */
                ll = (*row++)&~1;
                lr = (*row++);
                rowlen--;
                wind = 1;

                /* We will fill solidly from ll to at least lr, possibly further */
                assert(rowlen > 0);
                do {
                    rl = (*row++)&~1;
                    rr = (*row++);
                    rowlen--;
                    if (rr > lr)
                        lr = rr;
                    wind ^= 1;
                    if (wind == 0)
                        break;
                } while (rowlen > 0);
            } else {
                /* Non-Zero */
                int w;

                ll = *row++;
                lr = *row++;
                wind = -(ll&1) | 1;
                ll &= ~1;
                rowlen--;

                assert(rowlen > 0);
                do {
                    rl = (*row++);
                    rr = (*row++);
                    w = -(rl&1) | 1;
                    rl &= ~1;
                    rowlen--;
                    if (rr > lr)
                        lr = rr;
                    wind += w;
                    if (wind == 0)
                        break;
                } while (rowlen > 0);
            }

            if (marked_to > ll) {
                if (rowout == rowstart)
                    ll = marked_to;
                else {
                    rowout -= 2;
                    ll = *rowout;
                }
            }

            if (lr > ll) {
                *rowout++ = ll;
                *rowout++ = lr;
                marked_to = ll;
            }
        }
        rowstart[-1] = rowout - rowstart;
    }
    return 0;
}

Project: ghostpdl

add_y_line(const segment * prev_lp, const segment * lp, int dir, line_list *ll)
{
    return add_y_line_aux(prev_lp, lp, &lp->pt, &prev_lp->pt, dir, ll);
}

Project: ghostpdl

static void mark_line_app(cursor *cr, fixed sx, fixed sy, fixed ex, fixed ey)
{
    int isy = fixed2int(sy) - cr->base;
    int iey = fixed2int(ey) - cr->base;

    if (sx == ex && sy == ey)
        return;
#ifdef DEBUG_SCAN_CONVERTER
    dlprintf6("Marking line from %x,%x to %x,%x (%x,%x)\n", sx, sy, ex, ey, isy, iey);
#endif

    assert(cr->y == sy && cr->left <= sx && cr->right >= sx && cr->d >= DIRN_UNSET && cr->d <= DIRN_DOWN);

    /* First, deal with lines that don't change scanline.
     * This accommodates horizontal lines. */
    if (isy == iey) {
        if (sy == ey) {
            /* Horzizontal line. Don't change cr->d, don't flush. */
        } else if (sy > ey) {
            /* Falling line, flush if previous was rising */
            if (cr->d == DIRN_UP)
                output_cursor(cr, sx);
            cr->d = DIRN_DOWN;
        } else {
            /* Rising line, flush if previous was falling */
            if (cr->d == DIRN_DOWN)
                output_cursor(cr, sx);
            cr->d = DIRN_UP;
        }
        if (sx <= ex) {
            if (sx < cr->left)
                cr->left = sx;
            if (ex > cr->right)
                cr->right = ex;
        } else {
            if (ex < cr->left)
                cr->left = ex;
            if (sx > cr->right)
                cr->right = sx;
        }
        cr->y = ey;
    } else if (sy < ey) {
        /* So lines increasing in y. */
        fixed y_steps = ey - sy;
        /* We want to change from sy to ey, which are guaranteed to be on
         * different scanlines. We do this in 3 phases.
         * Phase 1 gets us from sy to the next scanline boundary.
         * Phase 2 gets us all the way to the last scanline boundary.
         * Phase 3 gets us from the last scanline boundary to ey.
         */
        int phase1_y_steps = (fixed_1 - sy) & (fixed_1 - 1);
        int phase3_y_steps = ey & (fixed_1 - 1);

        if (cr->d == DIRN_DOWN)
            output_cursor(cr, sx);
        cr->d = DIRN_UP;

        if (sx <= ex) {
            /* Lines increasing in x. */
            int phase1_x_steps, phase3_x_steps;
            fixed x_steps = ex - sx;

            assert(cr->left <= sx);
            /* Phase 1: */
            if (phase1_y_steps) {
                phase1_x_steps = (int)(((int64_t)x_steps * phase1_y_steps + y_steps/2) / y_steps);
                sx += phase1_x_steps;
                if (cr->right < sx)
                    cr->right = sx;
                x_steps -= phase1_x_steps;
                /* If phase 1 will move us into a new scanline, then we must
                 * flush it before we move. */
                if (fixed2int(cr->y) != fixed2int(cr->y + phase1_y_steps))
                    output_cursor(cr, sx);
                cr->y += phase1_y_steps;
                sy += phase1_y_steps;
                y_steps -= phase1_y_steps;
                if (y_steps == 0)
                    return;
            }

            /* Phase 3: precalculation */
            phase3_x_steps = (int)(((int64_t)x_steps * phase3_y_steps + y_steps/2) / y_steps);
            x_steps -= phase3_x_steps;
            y_steps -= phase3_y_steps;

            /* Phase 2: */
            y_steps = fixed2int(y_steps);
            assert(y_steps >= 0);
            if (y_steps) {
                /* We want to change sx by x_steps in y_steps steps.
                 * So each step, we add x_steps/y_steps to sx. That's x_inc + n_inc/y_steps. */
                int x_inc = x_steps/y_steps;
                int n_inc = x_steps - (x_inc * y_steps);
                int f = y_steps/2;
                int d = y_steps;
                while (y_steps) {
                    cr->left = sx;
                    sx += x_inc;
                    f -= n_inc;
                    if (f < 0)
                        f += d, sx++;
                    cr->right = sx;
                    y_steps--;
                    output_cursor(cr, sx);
                    cr->y += fixed_1;
                }
            }

            /* Phase 3 */
            cr->left  = sx;
            cr->right = ex;
            cr->y += phase3_y_steps;
        } else {
            /* Lines decreasing in x. */
            int phase1_x_steps, phase3_x_steps;
            fixed x_steps = sx - ex;

            assert(cr->right >= sx);
            /* Phase 1: */
            if (phase1_y_steps) {
                phase1_x_steps = (int)(((int64_t)x_steps * phase1_y_steps + y_steps/2) / y_steps);
                sx -= phase1_x_steps;
                if (cr->left > sx)
                    cr->left = sx;
                x_steps -= phase1_x_steps;
                /* If phase 1 will move us into a new scanline, then we must
                 * flush it before we move. */
                if (fixed2int(cr->y) != fixed2int(cr->y + phase1_y_steps))
                    output_cursor(cr, sx);
                cr->y += phase1_y_steps;
                sy += phase1_y_steps;
                y_steps -= phase1_y_steps;
                if (y_steps == 0)
                    return;
            }

            /* Phase 3: precalculation */
            phase3_x_steps = (int)(((int64_t)x_steps * phase3_y_steps + y_steps/2) / y_steps);
            x_steps -= phase3_x_steps;
            y_steps -= phase3_y_steps;

            /* Phase 2: */
            assert((y_steps & (fixed_1 - 1)) == 0);
            y_steps = fixed2int(y_steps);
            assert(y_steps >= 0);
            if (y_steps) {
                /* We want to change sx by x_steps in y_steps steps.
                 * So each step, we sub x_steps/y_steps from sx. That's x_inc + n_inc/ey. */
                int x_inc = x_steps/y_steps;
                int n_inc = x_steps - (x_inc * y_steps);
                int f = y_steps/2;
                int d = y_steps;
                while (y_steps) {
                    cr->right = sx;
                    sx -= x_inc;
                    f -= n_inc;
                    if (f < 0)
                        f += d, sx--;
                    cr->left = sx;
                    y_steps--;
                    output_cursor(cr, sx);
                    cr->y += fixed_1;
                }
            }

            /* Phase 3 */
            cr->right = sx;
            cr->left  = ex;
            cr->y += phase3_y_steps;
        }
    } else {
        /* So lines decreasing in y. */
        fixed y_steps = sy - ey;
        /* We want to change from sy to ey, which are guaranteed to be on
         * different scanlines. We do this in 3 phases.
         * Phase 1 gets us from sy to the next scanline boundary.
         * Phase 2 gets us all the way to the last scanline boundary.
         * Phase 3 gets us from the last scanline boundary to ey.
         */
        int phase1_y_steps = sy & (fixed_1 - 1);
        int phase3_y_steps = (fixed_1 - ey) & (fixed_1 - 1);

        if (cr->d == DIRN_UP)
            output_cursor(cr, sx);
        cr->d = DIRN_DOWN;

        if (sx <= ex) {
            /* Lines increasing in x. */
            int phase1_x_steps, phase3_x_steps;
            fixed x_steps = ex - sx;

            /* Phase 1: */
            assert(cr->left <= sx);
            if (phase1_y_steps) {
                phase1_x_steps = (int)(((int64_t)x_steps * phase1_y_steps + y_steps/2) / y_steps);
                sx += phase1_x_steps;
                if (cr->right < sx)
                    cr->right = sx;
                x_steps -= phase1_x_steps;
                /* Phase 1 in a falling line never moves us into a new scanline. */
                sy -= phase1_y_steps;
                cr->y -= phase1_y_steps;
                y_steps -= phase1_y_steps;
                if (y_steps == 0)
                    return;
            }

            /* Phase 3: precalculation */
            phase3_x_steps = (int)(((int64_t)x_steps * phase3_y_steps + y_steps/2) / y_steps);
            x_steps -= phase3_x_steps;
            y_steps -= phase3_y_steps;

            /* Phase 2: */
            assert((y_steps & (fixed_1 - 1)) == 0);
            y_steps = fixed2int(y_steps);
            assert(y_steps >= 0);
            if (y_steps) {
                /* We want to change sx by x_steps in y_steps steps.
                 * So each step, we add x_steps/y_steps to sx. That's x_inc + n_inc/ey. */
                int x_inc = x_steps/y_steps;
                int n_inc = x_steps - (x_inc * y_steps);
                int f = y_steps/2;
                int d = y_steps;
                while (y_steps) {
                    output_cursor(cr, sx);
                    sx += x_inc;
                    f -= n_inc;
                    if (f < 0)
                        f += d, sx++;
                    cr->right = sx;
                    y_steps--;
                    cr->y -= fixed_1;
                }
            }

            /* Phase 3 */
            if (phase3_y_steps > 0)
                output_cursor(cr, sx);
            cr->left  = sx;
            cr->right = ex;
            cr->y -= phase3_y_steps;
        } else {
            /* Lines decreasing in x. */
            int phase1_x_steps, phase3_x_steps;
            fixed x_steps = sx - ex;

            /* Phase 1: */
            assert(cr->right >= sx);
            if (phase1_y_steps) {
                phase1_x_steps = (int)(((int64_t)x_steps * phase1_y_steps + y_steps/2) / y_steps);
                sx -= phase1_x_steps;
                if (cr->left > sx)
                    cr->left = sx;
                x_steps -= phase1_x_steps;
                /* Phase 1 in a falling line never moves us into a new scanline. */
                sy -= phase1_y_steps;
                cr->y -= phase1_y_steps;
                y_steps -= phase1_y_steps;
                if (y_steps == 0)
                    return;
            }

            /* Phase 3: precalculation */
            phase3_x_steps = (int)(((int64_t)x_steps * phase3_y_steps + y_steps/2) / y_steps);
            x_steps -= phase3_x_steps;
            y_steps -= phase3_y_steps;

            /* Phase 2: */
            y_steps = fixed2int(y_steps);
            assert(y_steps >= 0);
            if (y_steps) {
                /* We want to change sx by x_steps in y_steps steps.
                 * So each step, we sub x_steps/y_steps from sx. That's x_inc + n_inc/ey. */
                int x_inc = x_steps/y_steps;
                int n_inc = x_steps - (x_inc * y_steps);
                int f = y_steps/2;
                int d = y_steps;
                while (y_steps) {
                    output_cursor(cr, sx);
                    sx -= x_inc;
                    f -= n_inc;
                    if (f < 0)
                        f += d, sx--;
                    cr->left = sx;
                    y_steps--;
                    cr->y -= fixed_1;
                }
            }

            /* Phase 3 */
            if (phase3_y_steps > 0)
                output_cursor(cr, sx);
            cr->right = sx;
            cr->left  = ex;
            cr->y -= phase3_y_steps;
        }
    }
}

Project: ghostpdl

gx_edgebuffer_fin(gx_device     * pdev,
                  gx_edgebuffer * edgebuffer)
{
    gs_free_object(pdev->memory, edgebuffer->table, "scanc intersects buffer");
    gs_free_object(pdev->memory, edgebuffer->index, "scanc index buffer");
    edgebuffer->index = NULL;
    edgebuffer->table = NULL;
}

Project: ghostpdl

int gx_scan_convert_app(gx_device     * pdev,
                        gx_path       * path,
                  const gs_fixed_rect * clip,
                        gx_edgebuffer * edgebuffer,
                        fixed           fixed_flat)
{
    gs_fixed_rect  ibox;
    int            scanlines;
    const subpath *psub;
    int           *index;
    int           *table;
    int            i;
    cursor         cr;
    int            code;

    edgebuffer->index = NULL;
    edgebuffer->table = NULL;

    /* Bale out if no actual path. We see this with the clist */
    if (path->first_subpath == NULL)
        return 0;

    code = make_bbox(path, clip, &ibox, 0);
    if (code < 0)
        return code;

    if (ibox.q.y <= ibox.p.y)
        return 0;

    code = make_table(pdev, path, &ibox, 2, 0, &scanlines, &index, &table);
    if (code < 0)
        return code;

    /* Step 2 continued: Now we run through the path, filling in the real
     * values. */
    cr.scanlines = scanlines;
    cr.index     = index;
    cr.table     = table;
    cr.base      = ibox.p.y;
    for (psub = path->first_subpath; psub != 0;) {
        const segment *pseg = (const segment *)psub;
        fixed ex = pseg->pt.x;
        fixed ey = pseg->pt.y;
        fixed ix = ex;
        fixed iy = ey;
        fixed sx, sy;

        cr.left = cr.right = ex;
        cr.y = ey;
        cr.d = -1;
        cr.first = 1;

        while ((pseg = pseg->next) != 0 &&
               pseg->type != s_start
            ) {
            sx = ex;
            sy = ey;
            ex = pseg->pt.x;
            ey = pseg->pt.y;

            switch (pseg->type) {
                default:
                case s_start: /* Should never happen */
                case s_dash:  /* We should never be seeing a dash here */
                    assert("This should never happen" == NULL);
                    break;
                case s_curve: {
                    const curve_segment *const pcur = (const curve_segment *)pseg;
                    int k = gx_curve_log2_samples(sx, sy, pcur, fixed_flat);

                    mark_curve_app(&cr, sx, sy, pcur->p1.x, pcur->p1.y, pcur->p2.x, pcur->p2.y, ex, ey, k);
                    break;
                }
                case s_gap:
                case s_line:
                case s_line_close:
                    mark_line_app(&cr, sx, sy, ex, ey);
                    break;
            }
        }
        /* And close any open segments */
        mark_line_app(&cr, ex, ey, ix, iy);
        flush_cursor(&cr, ex);
        psub = (const subpath *)pseg;
    }

    /* Step 2 complete: We now have a complete list of intersection data in
     * table, indexed by index. */

    /* Step 3: Sort the intersects on x */
    for (i=0; i < scanlines; i++) {
        int *row = &table[index[i]];
        int  rowlen = *row++;

        /* Sort the 'rowlen' entries - *must* be faster to do this with
         * custom code, as the function call overhead on the comparisons
         * will be a killer */
        qsort(row, rowlen, 2*sizeof(int), edgecmp);
    }

    edgebuffer->base   = ibox.p.y;
    edgebuffer->height = scanlines;
    edgebuffer->xmin   = ibox.p.x;
    edgebuffer->xmax   = ibox.q.x;
    edgebuffer->index  = index;
    edgebuffer->table  = table;

    return 0;
}

Project: ghostpdl

static void mark_line_tr(fixed sx, fixed sy, fixed ex, fixed ey, int base_y, int height, int *table, int *index, int id)
{
    int delta;
    int isy = fixed2int(sy + fixed_half);
    int iey = fixed2int(ey + fixed_half);
    int dirn = DIRN_UP;

#ifdef DEBUG_SCAN_CONVERTER
    dlprintf2("Marking line from %d to %d\n", isy, iey);
#endif

    if (isy == iey)
        return;
    if (isy > iey) {
        int t;
        t = isy; isy = iey; iey = t;
        t = sx; sx = ex; ex = t;
        dirn = DIRN_DOWN;
    }
    id = (id<<1) | dirn;
    /* So we now have to mark a line of intersects from (sx,sy) to (ex,ey) */
    iey -= isy;
    ex -= sx;
    isy -= base_y;
#ifdef DEBUG_SCAN_CONVERTER
    dlprintf2("    sy=%d ey=%d\n", isy, iey);
#endif
    if (ex >= 0) {
        int x_inc, n_inc, f;

        /* We want to change sx by ex in iey steps. So each step, we add
         * ex/iey to sx. That's x_inc + n_inc/iey.
         */
       x_inc = ex/iey;
       n_inc = ex-(x_inc*iey);
       f     = iey>>1;
       /* Do a half step to start us off */
       sx += x_inc>>1;
       f  -= n_inc>>1;
       if (f < 0) {
           f += iey;
           sx++;
       }
       if (x_inc & 1) {
           f -= n_inc>>2;
           if (f < 0) {
               f += iey;
               sx++;
           }
       }
       delta = iey;
       do {
           int *row;

           if (isy >= 0 && isy < height) {
               row = &table[index[isy]];
               *row = (*row)+1; /* Increment the count */
               row[*row * 2 - 1] = sx;
               row[*row * 2    ] = id;
           }
           isy++;
           sx += x_inc;
           f  -= n_inc;
           if (f < 0) {
               f += iey;
               sx++;
           }
        }
        while (--delta);
    } else {
        int x_dec, n_dec, f;

        ex = -ex;
        /* We want to change sx by ex in iey steps. So each step, we subtract
         * ex/iey from sx. That's x_dec + n_dec/iey.
         */
        x_dec = ex/iey;
        n_dec = ex-(x_dec*iey);
        f     = iey>>1;
        /* Do a half step to start us off */
        sx -= x_dec>>1;
        f  -= n_dec>>1;
        if (f < 0) {
            f += iey;
            sx--;
        }
        if (x_dec & 1) {
            f -= n_dec>>2;
            if (f < 0)
            {
                f += iey;
                sx--;
            }
        }
        delta = iey;
        do {
            int *row;

            if (isy >= 0 && isy < height) {
                row = &table[index[isy]];
                (*row)++; /* Increment the count */
                row[*row * 2 - 1] = sx;
                row[*row * 2    ] = id;
            }
            isy++;
            sx -= x_dec;
            f  -= n_dec;
            if (f < 0) {
                f += iey;
                sx--;
            }
         }
         while (--delta);
    }
}

Project: ghostpdl

static int edgecmp(const void *a, const void *b)
{
    int left  = ((int*)a)[0]&~1;
    int right = ((int*)b)[0]&~1;
    if (left != right)
        return left - right;
    return ((int*)a)[1] - ((int*)b)[1];
}

Project: ghostpdl

gx_edgebuffer_print(gx_edgebuffer * edgebuffer)
{
    int i;

    dlprintf1("Edgebuffer %x\n", edgebuffer);
    dlprintf4("xmin=%d xmax=%d base=%d height=%d\n",
              edgebuffer->xmin, edgebuffer->xmax, edgebuffer->base, edgebuffer->height);
    for (i=0; i < edgebuffer->height; i++) {
        int  offset = edgebuffer->index[i];
        int *row    = &edgebuffer->table[offset];
        int count   = *row++;
        dlprintf3("%d @ %d: %d =", i, offset, count);
        while (count-- > 0) {
            int v = *row++;
            dlprintf2(" %d:%d", v&~1, v&1);
        }
        dlprintf("\n");
    }
}

Project: ghostpdl

x_order(const active_line *lp1, const active_line *lp2)
{
    bool s1;

    INCR(order);
    if (!lp1 || !lp2 || lp1->x_current < lp2->x_current)
        return -1;
    else if (lp1->x_current > lp2->x_current)
        return 1;
    /*
     * We need to compare the slopes of the lines.  Start by
     * checking one fast case, where the slopes have opposite signs.
     */
    if ((s1 = lp1->start.x < lp1->end.x) != (lp2->start.x < lp2->end.x))
        return (s1 ? 1 : -1);
    /*
     * We really do have to compare the slopes.  Fortunately, this isn't
     * needed very often.  We want the sign of the comparison
     * dx1/dy1 - dx2/dy2, or (since we know dy1 and dy2 are positive)
     * dx1 * dy2 - dx2 * dy1.  In principle, we can't simply do this using
     * doubles, since we need complete accuracy and doubles don't have
     * enough fraction bits.  However, with the usual 20+12-bit fixeds and
     * 64-bit doubles, both of the factors would have to exceed 2^15
     * device space pixels for the result to be inaccurate, so we
     * simply disregard this possibility.  ****** FIX SOMEDAY ******
     */
    INCR(slow_order);
    {
        fixed dx1 = lp1->end.x - lp1->start.x,
            dy1 = lp1->end.y - lp1->start.y;
        fixed dx2 = lp2->end.x - lp2->start.x,
            dy2 = lp2->end.y - lp2->start.y;
        double diff = (double)dx1 * dy2 - (double)dx2 * dy1;

        return (diff < 0 ? -1 : diff > 0 ? 1 : 0);
    }
}

Project: ghostpdl

print_line_list(const gs_memory_t *mem, const active_line * flp)
{
    const active_line *lp;

    for (lp = flp; lp != 0; lp = lp->next) {
        fixed xc = lp->x_current, xn = lp->x_next;

        dmlprintf3(mem, "[f]0x%lx(%d): x_current/next=%g",
                  (ulong) lp, lp->direction,
                  fixed2float(xc));
        if (xn != xc)
            dmprintf1(mem, "/%g", fixed2float(xn));
        dmputc(mem, '\n');
    }
}