Revision adaf08b4d37764b483e33845f47e320e197eca4b authored by David Keeler on 28 September 2012, 17:27:03 UTC, committed by David Keeler on 28 September 2012, 17:27:03 UTC
1 parent 3613fde
Raw File
clip-invariant.patch
diff --git a/src/cairo-gl-surface.c b/src/cairo-gl-surface.c
index 2acc8b5..019249e 100644
--- a/src/cairo-gl-surface.c
+++ b/src/cairo-gl-surface.c
@@ -2012,13 +2012,14 @@ typedef struct _cairo_gl_surface_span_renderer {
 
     cairo_gl_composite_setup_t setup;
 
+    int xmin, xmax;
+
     cairo_operator_t op;
     cairo_antialias_t antialias;
 
     cairo_gl_surface_t *dst;
     cairo_region_t *clip;
 
-    cairo_composite_rectangles_t composite_rectangles;
     GLuint vbo;
     void *vbo_base;
     unsigned int vbo_size;
@@ -2049,11 +2050,11 @@ _cairo_gl_span_renderer_flush (cairo_gl_surface_span_renderer_t *renderer)
 	    cairo_region_get_rectangle (renderer->clip, i, &rect);
 
 	    glScissor (rect.x, rect.y, rect.width, rect.height);
-	    glDrawArrays (GL_LINES, 0, count);
+	    glDrawArrays (GL_QUADS, 0, count);
 	}
 	glDisable (GL_SCISSOR_TEST);
     } else {
-	glDrawArrays (GL_LINES, 0, count);
+	glDrawArrays (GL_QUADS, 0, count);
     }
 }
 
@@ -2134,72 +2135,87 @@ _cairo_gl_emit_span_vertex (cairo_gl_surface_span_renderer_t *renderer,
 
 static void
 _cairo_gl_emit_span (cairo_gl_surface_span_renderer_t *renderer,
-		     int x1, int x2, int y, uint8_t alpha)
+		     int x, int y1, int y2,
+		     uint8_t alpha)
 {
     float *vertices = _cairo_gl_span_renderer_get_vbo (renderer, 2);
 
-    _cairo_gl_emit_span_vertex (renderer, x1, y, alpha, vertices);
-    _cairo_gl_emit_span_vertex (renderer, x2, y, alpha,
+    _cairo_gl_emit_span_vertex (renderer, x, y1, alpha, vertices);
+    _cairo_gl_emit_span_vertex (renderer, x, y2, alpha,
 			       vertices + renderer->vertex_size / 4);
 }
 
-/* Emits the contents of the span renderer rows as GL_LINES with the span's
- * alpha.
- *
- * Unlike the image surface, which is compositing into a temporary, we emit
- * coverage even for alpha == 0, in case we're using an unbounded operator.
- * But it means we avoid having to do the fixup.
- */
+static void
+_cairo_gl_emit_rectangle (cairo_gl_surface_span_renderer_t *renderer,
+			  int x1, int y1,
+			  int x2, int y2,
+			  int coverage)
+{
+    _cairo_gl_emit_span (renderer, x1, y1, y2, coverage);
+    _cairo_gl_emit_span (renderer, x2, y2, y1, coverage);
+}
+
 static cairo_status_t
-_cairo_gl_surface_span_renderer_render_row (
-    void				*abstract_renderer,
-    int					 y,
-    const cairo_half_open_span_t	*spans,
-    unsigned				 num_spans)
+_cairo_gl_render_bounded_spans (void *abstract_renderer,
+				int y, int height,
+				const cairo_half_open_span_t *spans,
+				unsigned num_spans)
 {
     cairo_gl_surface_span_renderer_t *renderer = abstract_renderer;
-    int xmin = renderer->composite_rectangles.mask.x;
-    int xmax = xmin + renderer->composite_rectangles.width;
-    int prev_x = xmin;
-    int prev_alpha = 0;
-    unsigned i;
-    int x_translate;
-
-    /* Make sure we're within y-range. */
-    if (y < renderer->composite_rectangles.mask.y ||
-	y >= renderer->composite_rectangles.mask.y +
-	renderer->composite_rectangles.height)
+
+    if (num_spans == 0)
 	return CAIRO_STATUS_SUCCESS;
 
-    x_translate = renderer->composite_rectangles.dst.x -
-	renderer->composite_rectangles.mask.x;
-    y += renderer->composite_rectangles.dst.y -
-	renderer->composite_rectangles.mask.y;
+    do {
+	if (spans[0].coverage) {
+	    _cairo_gl_emit_rectangle (renderer,
+				      spans[0].x, y,
+				      spans[1].x, y + height,
+				      spans[0].coverage);
+	}
 
-    /* Find the first span within x-range. */
-    for (i=0; i < num_spans && spans[i].x < xmin; i++) {}
-    if (i>0)
-	prev_alpha = spans[i-1].coverage;
+	spans++;
+    } while (--num_spans > 1);
 
-    /* Set the intermediate spans. */
-    for (; i < num_spans; i++) {
-	int x = spans[i].x;
+    return CAIRO_STATUS_SUCCESS;
+}
 
-	if (x >= xmax)
-	    break;
+static cairo_status_t
+_cairo_gl_render_unbounded_spans (void *abstract_renderer,
+				  int y, int height,
+				  const cairo_half_open_span_t *spans,
+				  unsigned num_spans)
+{
+    cairo_gl_surface_span_renderer_t *renderer = abstract_renderer;
 
-	_cairo_gl_emit_span (renderer,
-			     prev_x + x_translate, x + x_translate, y,
-			     prev_alpha);
+    if (num_spans == 0) {
+	_cairo_gl_emit_rectangle (renderer,
+				  renderer->xmin, y,
+				  renderer->xmax, y + height,
+				  0);
+	return CAIRO_STATUS_SUCCESS;
+    }
 
-	prev_x = x;
-	prev_alpha = spans[i].coverage;
+    if (spans[0].x != renderer->xmin) {
+	_cairo_gl_emit_rectangle (renderer,
+				  renderer->xmin, y,
+				  spans[0].x, y + height,
+				  0);
     }
 
-    if (prev_x < xmax) {
-	_cairo_gl_emit_span (renderer,
-			     prev_x + x_translate, xmax + x_translate, y,
-			     prev_alpha);
+    do {
+	_cairo_gl_emit_rectangle (renderer,
+				  spans[0].x, y,
+				  spans[1].x, y + height,
+				  spans[0].coverage);
+	spans++;
+    } while (--num_spans > 1);
+
+    if (spans[0].x != renderer->xmax) {
+	_cairo_gl_emit_rectangle (renderer,
+				  spans[0].x, y,
+				  renderer->xmax, y + height,
+				  0);
     }
 
     return CAIRO_STATUS_SUCCESS;
@@ -2274,8 +2290,6 @@ _cairo_gl_surface_create_span_renderer (cairo_operator_t	 op,
     cairo_gl_surface_t *dst = abstract_dst;
     cairo_gl_surface_span_renderer_t *renderer;
     cairo_status_t status;
-    int width = rects->width;
-    int height = rects->height;
     cairo_surface_attributes_t *src_attributes;
     GLenum err;
 
diff --git a/src/cairo-image-surface.c b/src/cairo-image-surface.c
index 48d8013..d52979d 100644
--- a/src/cairo-image-surface.c
+++ b/src/cairo-image-surface.c
@@ -1390,11 +1390,13 @@ typedef struct _cairo_image_surface_span_renderer {
     const cairo_pattern_t *pattern;
     cairo_antialias_t antialias;
 
+    uint8_t *mask_data;
+    uint32_t mask_stride;
+
     cairo_image_surface_t *src;
     cairo_surface_attributes_t src_attributes;
     cairo_image_surface_t *mask;
     cairo_image_surface_t *dst;
-
     cairo_composite_rectangles_t composite_rectangles;
 } cairo_image_surface_span_renderer_t;
 
@@ -1403,66 +1405,46 @@ _cairo_image_surface_span_render_row (
     int                                  y,
     const cairo_half_open_span_t        *spans,
     unsigned                             num_spans,
-    cairo_image_surface_t               *mask,
-    const cairo_composite_rectangles_t  *rects)
+    uint8_t				*data,
+    uint32_t				 stride)
 {
-    int xmin = rects->mask.x;
-    int xmax = xmin + rects->width;
     uint8_t *row;
-    int prev_x = xmin;
-    int prev_alpha = 0;
     unsigned i;
 
-    /* Make sure we're within y-range. */
-    y -= rects->mask.y;
-    if (y < 0 || y >= rects->height)
+    if (num_spans == 0)
 	return;
 
-    row = (uint8_t*)(mask->data) + y*(size_t)mask->stride - xmin;
-
-    /* Find the first span within x-range. */
-    for (i=0; i < num_spans && spans[i].x < xmin; i++) {}
-    if (i>0)
-	prev_alpha = spans[i-1].coverage;
-
-    /* Set the intermediate spans. */
-    for (; i < num_spans; i++) {
-	int x = spans[i].x;
-
-	if (x >= xmax)
-	    break;
-
-	if (prev_alpha != 0) {
-	    /* We implement setting rendering the most common single
-	     * pixel wide span case to avoid the overhead of a memset
-	     * call.  Open coding setting longer spans didn't show a
-	     * noticeable improvement over memset. */
-	    if (x == prev_x + 1) {
-		row[prev_x] = prev_alpha;
-	    }
-	    else {
-		memset(row + prev_x, prev_alpha, x - prev_x);
-	    }
+    row = data + y * stride;
+    for (i = 0; i < num_spans - 1; i++) {
+	if (! spans[i].coverage)
+	    continue;
+
+	/* We implement setting the most common single pixel wide
+	 * span case to avoid the overhead of a memset call.
+	 * Open coding setting longer spans didn't show a
+	 * noticeable improvement over memset.
+	 */
+	if (spans[i+1].x == spans[i].x + 1) {
+	    row[spans[i].x] = spans[i].coverage;
+	} else {
+	    memset (row + spans[i].x,
+		    spans[i].coverage,
+		    spans[i+1].x - spans[i].x);
 	}
-
-	prev_x = x;
-	prev_alpha = spans[i].coverage;
-    }
-
-    if (prev_alpha != 0 && prev_x < xmax) {
-	memset(row + prev_x, prev_alpha, xmax - prev_x);
     }
 }
 
 static cairo_status_t
-_cairo_image_surface_span_renderer_render_row (
+_cairo_image_surface_span_renderer_render_rows (
     void				*abstract_renderer,
     int					 y,
+    int					 height,
     const cairo_half_open_span_t	*spans,
     unsigned				 num_spans)
 {
     cairo_image_surface_span_renderer_t *renderer = abstract_renderer;
-    _cairo_image_surface_span_render_row (y, spans, num_spans, renderer->mask, &renderer->composite_rectangles);
+    while (height--)
+	_cairo_image_surface_span_render_row (y++, spans, num_spans, renderer->mask_data, renderer->mask_stride);
     return CAIRO_STATUS_SUCCESS;
 }
 
@@ -1517,11 +1499,11 @@ _cairo_image_surface_span_renderer_finish (void *abstract_renderer)
 		&dst->base,
 		src_attributes,
 		src->width, src->height,
-		rects->width, rects->height,
+		width, height,
 		rects->src.x, rects->src.y,
 		0, 0,		/* mask.x, mask.y */
 		rects->dst.x, rects->dst.y,
-		rects->width, rects->height,
+		width, height,
 		dst->clip_region);
 	}
     }
@@ -1567,7 +1549,7 @@ _cairo_image_surface_create_span_renderer (cairo_operator_t	 op,
 
     renderer->base.destroy = _cairo_image_surface_span_renderer_destroy;
     renderer->base.finish = _cairo_image_surface_span_renderer_finish;
-    renderer->base.render_row = _cairo_image_surface_span_renderer_render_row;
+    renderer->base.render_rows = _cairo_image_surface_span_renderer_render_rows;
     renderer->op = op;
     renderer->pattern = pattern;
     renderer->antialias = antialias;
@@ -1604,6 +1586,9 @@ _cairo_image_surface_create_span_renderer (cairo_operator_t	 op,
 	_cairo_image_surface_span_renderer_destroy (renderer);
 	return _cairo_span_renderer_create_in_error (status);
     }
+
+    renderer->mask_data = renderer->mask->data - rects->mask.x - rects->mask.y * renderer->mask->stride;
+    renderer->mask_stride = renderer->mask->stride;
     return &renderer->base;
 }
 
diff --git a/src/cairo-spans-private.h b/src/cairo-spans-private.h
index e29a567..af3b38c 100644
--- a/src/cairo-spans-private.h
+++ b/src/cairo-spans-private.h
@@ -47,26 +47,24 @@ typedef struct _cairo_half_open_span {
  * surfaces if they want to composite spans instead of trapezoids. */
 typedef struct _cairo_span_renderer cairo_span_renderer_t;
 struct _cairo_span_renderer {
+    /* Private status variable. */
+    cairo_status_t status;
+
     /* Called to destroy the renderer. */
     cairo_destroy_func_t	destroy;
 
-    /* Render the spans on row y of the source by whatever compositing
-     * method is required.  The function should ignore spans outside
-     * the bounding box set by the init() function. */
-    cairo_status_t (*render_row)(
-	void				*abstract_renderer,
-	int				 y,
-	const cairo_half_open_span_t	*coverages,
-	unsigned			 num_coverages);
+    /* Render the spans on row y of the destination by whatever compositing
+     * method is required. */
+    cairo_warn cairo_status_t
+    (*render_rows) (void *abstract_renderer,
+		    int y, int height,
+		    const cairo_half_open_span_t	*coverages,
+		    unsigned num_coverages);
 
     /* Called after all rows have been rendered to perform whatever
      * final rendering step is required.  This function is called just
      * once before the renderer is destroyed. */
-    cairo_status_t (*finish)(
-	void		      *abstract_renderer);
-
-    /* Private status variable. */
-    cairo_status_t status;
+    cairo_status_t (*finish) (void *abstract_renderer);
 };
 
 /* Scan converter interface. */
diff --git a/src/cairo-spans.c b/src/cairo-spans.c
index af3b85f..69894c1 100644
--- a/src/cairo-spans.c
+++ b/src/cairo-spans.c
@@ -275,13 +275,15 @@ _cairo_scan_converter_create_in_error (cairo_status_t status)
 }
 
 static cairo_status_t
-_cairo_nil_span_renderer_render_row (
+_cairo_nil_span_renderer_render_rows (
     void				*abstract_renderer,
     int					 y,
+    int					 height,
     const cairo_half_open_span_t	*coverages,
     unsigned				 num_coverages)
 {
     (void) y;
+    (void) height;
     (void) coverages;
     (void) num_coverages;
     return _cairo_span_renderer_status (abstract_renderer);
@@ -310,7 +312,7 @@ _cairo_span_renderer_set_error (
 	ASSERT_NOT_REACHED;
     }
     if (renderer->status == CAIRO_STATUS_SUCCESS) {
-	renderer->render_row = _cairo_nil_span_renderer_render_row;
+	renderer->render_rows = _cairo_nil_span_renderer_render_rows;
 	renderer->finish = _cairo_nil_span_renderer_finish;
 	renderer->status = error;
     }
diff --git a/src/cairo-tor-scan-converter.c b/src/cairo-tor-scan-converter.c
index 29262c2..2b9fb1b 100644
--- a/src/cairo-tor-scan-converter.c
+++ b/src/cairo-tor-scan-converter.c
@@ -128,27 +128,29 @@ blit_with_span_renderer(
     cairo_span_renderer_t	*span_renderer,
     struct pool			*span_pool,
     int				 y,
+    int				 height,
     int				 xmin,
     int				 xmax);
 
 static glitter_status_t
-blit_empty_with_span_renderer (cairo_span_renderer_t *renderer, int y);
+blit_empty_with_span_renderer (cairo_span_renderer_t *renderer, int y, int height);
 
 #define GLITTER_BLIT_COVERAGES_ARGS \
 	cairo_span_renderer_t *span_renderer, \
 	struct pool *span_pool
 
-#define GLITTER_BLIT_COVERAGES(cells, y, xmin, xmax) do {		\
+#define GLITTER_BLIT_COVERAGES(cells, y, height,xmin, xmax) do {	\
     cairo_status_t status = blit_with_span_renderer (cells,		\
 						     span_renderer,	\
 						     span_pool,		\
-						     y, xmin, xmax);	\
+						     y, height,		\
+						     xmin, xmax);	\
     if (unlikely (status))						\
 	return status;							\
 } while (0)
 
-#define GLITTER_BLIT_COVERAGES_EMPTY(y, xmin, xmax) do {		\
-    cairo_status_t status = blit_empty_with_span_renderer (span_renderer, y); \
+#define GLITTER_BLIT_COVERAGES_EMPTY(y, height, xmin, xmax) do {		\
+    cairo_status_t status = blit_empty_with_span_renderer (span_renderer, y, height); \
     if (unlikely (status))						\
 	return status;							\
 } while (0)
@@ -309,8 +311,8 @@ typedef int grid_area_t;
 #define UNROLL3(x) x x x
 
 struct quorem {
-    int quo;
-    int rem;
+    int32_t quo;
+    int32_t rem;
 };
 
 /* Header for a chunk of memory in a memory pool. */
@@ -382,6 +384,7 @@ struct edge {
     /* Original sign of the edge: +1 for downwards, -1 for upwards
      * edges.  */
     int dir;
+    int vertical;
 };
 
 /* Number of subsample rows per y-bucket. Must be GRID_Y. */
@@ -389,18 +392,28 @@ struct edge {
 
 #define EDGE_Y_BUCKET_INDEX(y, ymin) (((y) - (ymin))/EDGE_Y_BUCKET_HEIGHT)
 
+struct bucket {
+    /* Unsorted list of edges starting within this bucket. */
+    struct edge *edges;
+
+    /* Set to non-zero if there are edges starting strictly within the
+     * bucket. */
+    unsigned     have_inside_edges;
+};
+
 /* A collection of sorted and vertically clipped edges of the polygon.
  * Edges are moved from the polygon to an active list while scan
  * converting. */
 struct polygon {
-    /* The vertical clip extents. */
+    /* The clip extents. */
+    grid_scaled_x_t xmin, xmax;
     grid_scaled_y_t ymin, ymax;
 
     /* Array of edges all starting in the same bucket.	An edge is put
      * into bucket EDGE_BUCKET_INDEX(edge->ytop, polygon->ymin) when
      * it is added to the polygon. */
-    struct edge **y_buckets;
-    struct edge *y_buckets_embedded[64];
+    struct bucket *y_buckets;
+    struct bucket  y_buckets_embedded[64];
 
     struct {
 	struct pool base[1];
@@ -702,7 +715,6 @@ static void
 cell_list_fini(struct cell_list *cells)
 {
     pool_fini (cells->cell_pool.base);
-    cell_list_init (cells);
 }
 
 /* Empty the cell list.  This is called at the start of every pixel
@@ -715,6 +727,26 @@ cell_list_reset (struct cell_list *cells)
     pool_reset (cells->cell_pool.base);
 }
 
+static struct cell *
+cell_list_alloc (struct cell_list *cells,
+		 struct cell **cursor,
+		 struct cell *tail,
+		 int x)
+{
+    struct cell *cell;
+
+    cell = pool_alloc (cells->cell_pool.base, sizeof (struct cell));
+    if (unlikely (NULL == cell))
+	return NULL;
+
+    *cursor = cell;
+    cell->next = tail;
+    cell->x = x;
+    cell->uncovered_area = 0;
+    cell->covered_height = 0;
+    return cell;
+}
+
 /* Find a cell at the given x-coordinate.  Returns %NULL if a new cell
  * needed to be allocated but couldn't be.  Cells must be found with
  * non-decreasing x-coordinate until the cell list is rewound using
@@ -737,22 +769,10 @@ cell_list_find (struct cell_list *cells, int x)
     }
     cells->cursor = cursor;
 
-    if (tail->x == x) {
+    if (tail->x == x)
 	return tail;
-    } else {
-	struct cell *cell;
-
-	cell = pool_alloc (cells->cell_pool.base, sizeof (struct cell));
-	if (unlikely (NULL == cell))
-	    return NULL;
 
-	*cursor = cell;
-	cell->next = tail;
-	cell->x = x;
-	cell->uncovered_area = 0;
-	cell->covered_height = 0;
-	return cell;
-    }
+    return cell_list_alloc (cells, cursor, tail, x);
 }
 
 /* Find two cells at x1 and x2.	 This is exactly equivalent
@@ -832,9 +852,8 @@ cell_list_find_pair(struct cell_list *cells, int x1, int x2)
 /* Add an unbounded subpixel span covering subpixels >= x to the
  * coverage cells. */
 static glitter_status_t
-cell_list_add_unbounded_subspan(
-    struct cell_list *cells,
-    grid_scaled_x_t x)
+cell_list_add_unbounded_subspan (struct cell_list *cells,
+				 grid_scaled_x_t x)
 {
     struct cell *cell;
     int ix, fx;
@@ -907,20 +926,24 @@ cell_list_render_edge(
     struct edge *edge,
     int sign)
 {
-    struct quorem x1 = edge->x;
-    struct quorem x2 = x1;
     grid_scaled_y_t y1, y2, dy;
     grid_scaled_x_t dx;
     int ix1, ix2;
     grid_scaled_x_t fx1, fx2;
 
-    x2.quo += edge->dxdy_full.quo;
-    x2.rem += edge->dxdy_full.rem;
-    if (x2.rem >= 0) {
-	++x2.quo;
-	x2.rem -= edge->dy;
+    struct quorem x1 = edge->x;
+    struct quorem x2 = x1;
+
+    if (! edge->vertical) {
+	x2.quo += edge->dxdy_full.quo;
+	x2.rem += edge->dxdy_full.rem;
+	if (x2.rem >= 0) {
+	    ++x2.quo;
+	    x2.rem -= edge->dy;
+	}
+
+	edge->x = x2;
     }
-    edge->x = x2;
 
     GRID_X_TO_INT_FRAC(x1.quo, ix1, fx1);
     GRID_X_TO_INT_FRAC(x2.quo, ix2, fx2);
@@ -1026,6 +1049,7 @@ static void
 polygon_init (struct polygon *polygon)
 {
     polygon->ymin = polygon->ymax = 0;
+    polygon->xmin = polygon->xmax = 0;
     polygon->y_buckets = polygon->y_buckets_embedded;
     pool_init (polygon->edge_pool.base,
 	       8192 - sizeof (struct _pool_chunk),
@@ -1045,10 +1069,11 @@ polygon_fini (struct polygon *polygon)
  * receive new edges and clip them to the vertical range
  * [ymin,ymax). */
 static glitter_status_t
-polygon_reset(
-    struct polygon *polygon,
-    grid_scaled_y_t ymin,
-    grid_scaled_y_t ymax)
+polygon_reset (struct polygon *polygon,
+	       grid_scaled_x_t xmin,
+	       grid_scaled_x_t xmax,
+	       grid_scaled_y_t ymin,
+	       grid_scaled_y_t ymax)
 {
     unsigned h = ymax - ymin;
     unsigned num_buckets = EDGE_Y_BUCKET_INDEX(ymax + EDGE_Y_BUCKET_HEIGHT-1,
@@ -1065,14 +1090,16 @@ polygon_reset(
     polygon->y_buckets =  polygon->y_buckets_embedded;
     if (num_buckets > ARRAY_LENGTH (polygon->y_buckets_embedded)) {
 	polygon->y_buckets = _cairo_malloc_ab (num_buckets,
-					       sizeof (struct edge *));
+					       sizeof (struct bucket));
 	if (unlikely (NULL == polygon->y_buckets))
 	    goto bail_no_mem;
     }
-    memset (polygon->y_buckets, 0, num_buckets * sizeof (struct edge *));
+    memset (polygon->y_buckets, 0, num_buckets * sizeof (struct bucket));
 
     polygon->ymin = ymin;
     polygon->ymax = ymax;
+    polygon->xmin = xmin;
+    polygon->xmax = xmax;
     return GLITTER_STATUS_SUCCESS;
 
  bail_no_mem:
@@ -1086,10 +1113,13 @@ _polygon_insert_edge_into_its_y_bucket(
     struct polygon *polygon,
     struct edge *e)
 {
-    unsigned ix = EDGE_Y_BUCKET_INDEX(e->ytop, polygon->ymin);
-    struct edge **ptail = &polygon->y_buckets[ix];
+    unsigned j = e->ytop - polygon->ymin;
+    unsigned ix = j / EDGE_Y_BUCKET_HEIGHT;
+    unsigned offset = j % EDGE_Y_BUCKET_HEIGHT;
+    struct edge **ptail = &polygon->y_buckets[ix].edges;
     e->next = *ptail;
     *ptail = e;
+    polygon->y_buckets[ix].have_inside_edges |= offset;
 }
 
 inline static glitter_status_t
@@ -1115,30 +1145,53 @@ polygon_add_edge (struct polygon *polygon,
     dx = edge->line.p2.x - edge->line.p1.x;
     dy = edge->line.p2.y - edge->line.p1.y;
     e->dy = dy;
-    e->dxdy = floored_divrem (dx, dy);
-
-    if (ymin <= edge->top)
-	ytop = edge->top;
-    else
-	ytop = ymin;
-    if (ytop == edge->line.p1.y) {
-	e->x.quo = edge->line.p1.x;
-	e->x.rem = 0;
-    } else {
-	e->x = floored_muldivrem (ytop - edge->line.p1.y, dx, dy);
-	e->x.quo += edge->line.p1.x;
-    }
-
     e->dir = edge->dir;
+
+    ytop = edge->top >= ymin ? edge->top : ymin;
+    ybot = edge->bottom <= ymax ? edge->bottom : ymax;
     e->ytop = ytop;
-    ybot = edge->bottom < ymax ? edge->bottom : ymax;
     e->height_left = ybot - ytop;
 
-    if (e->height_left >= GRID_Y) {
-	e->dxdy_full = floored_muldivrem (GRID_Y, dx, dy);
-    } else {
+    if (dx == 0) {
+	e->vertical = TRUE;
+	e->x.quo = edge->line.p1.x;
+	e->x.rem = 0;
+	e->dxdy.quo = 0;
+	e->dxdy.rem = 0;
 	e->dxdy_full.quo = 0;
 	e->dxdy_full.rem = 0;
+
+	/* Drop edges to the right of the clip extents. */
+	if (e->x.quo >= polygon->xmax)
+	    return GLITTER_STATUS_SUCCESS;
+
+	/* Offset vertical edges at the left side of the clip extents
+	 * to just shy of the left side.  We depend on this when
+	 * checking for possible intersections within the clip
+	 * rectangle. */
+	if (e->x.quo <= polygon->xmin) {
+	    e->x.quo = polygon->xmin - 1;
+	}
+    } else {
+	e->vertical = FALSE;
+	e->dxdy = floored_divrem (dx, dy);
+	if (ytop == edge->line.p1.y) {
+	    e->x.quo = edge->line.p1.x;
+	    e->x.rem = 0;
+	} else {
+	    e->x = floored_muldivrem (ytop - edge->line.p1.y, dx, dy);
+	    e->x.quo += edge->line.p1.x;
+	}
+
+	if (e->x.quo >= polygon->xmax && e->dxdy.quo >= 0)
+	    return GLITTER_STATUS_SUCCESS;
+
+	if (e->height_left >= GRID_Y) {
+	    e->dxdy_full = floored_muldivrem (GRID_Y, dx, dy);
+	} else {
+	    e->dxdy_full.quo = 0;
+	    e->dxdy_full.rem = 0;
+	}
     }
 
     _polygon_insert_edge_into_its_y_bucket (polygon, e);
@@ -1161,31 +1214,30 @@ active_list_init(struct active_list *active)
     active_list_reset(active);
 }
 
-static void
-active_list_fini(
-    struct active_list *active)
-{
-    active_list_reset(active);
-}
-
 /* Merge the edges in an unsorted list of edges into a sorted
  * list. The sort order is edges ascending by edge->x.quo.  Returns
  * the new head of the sorted list. */
 static struct edge *
 merge_unsorted_edges(struct edge *sorted_head, struct edge *unsorted_head)
 {
-    struct edge *head = unsorted_head;
     struct edge **cursor = &sorted_head;
     int x;
 
-    while (NULL != head) {
+    if (sorted_head == NULL) {
+	sorted_head = unsorted_head;
+	unsorted_head = unsorted_head->next;
+	sorted_head->next = NULL;
+	if (unsorted_head == NULL)
+	    return sorted_head;
+    }
+
+    do {
+	struct edge *next = unsorted_head->next;
 	struct edge *prev = *cursor;
-	struct edge *next = head->next;
-	x = head->x.quo;
 
-	if (NULL == prev || x < prev->x.quo) {
+	x = unsorted_head->x.quo;
+	if (x < prev->x.quo)
 	    cursor = &sorted_head;
-	}
 
 	while (1) {
 	    UNROLL3({
@@ -1196,26 +1248,29 @@ merge_unsorted_edges(struct edge *sorted_head, struct edge *unsorted_head)
 	    });
 	}
 
-	head->next = *cursor;
-	*cursor = head;
+	unsorted_head->next = *cursor;
+	*cursor = unsorted_head;
+	unsorted_head = next;
+    } while (unsorted_head != NULL);
 
-	head = next;
-    }
     return sorted_head;
 }
 
 /* Test if the edges on the active list can be safely advanced by a
  * full row without intersections or any edges ending. */
 inline static int
-active_list_can_step_full_row(
-    struct active_list *active)
+active_list_can_step_full_row (struct active_list *active,
+			       grid_scaled_x_t     xmin)
 {
+    const struct edge *e;
+    grid_scaled_x_t prev_x = INT_MIN;
+
     /* Recomputes the minimum height of all edges on the active
      * list if we have been dropping edges. */
     if (active->min_height <= 0) {
-	struct edge *e = active->head;
 	int min_height = INT_MAX;
 
+	e = active->head;
 	while (NULL != e) {
 	    if (e->height_left < min_height)
 		min_height = e->height_left;
@@ -1225,27 +1280,38 @@ active_list_can_step_full_row(
 	active->min_height = min_height;
     }
 
-    /* Check for intersections only if no edges end during the next
-     * row. */
-    if (active->min_height >= GRID_Y) {
-	grid_scaled_x_t prev_x = INT_MIN;
-	struct edge *e = active->head;
-	while (NULL != e) {
-	    struct quorem x = e->x;
+    if (active->min_height < GRID_Y)
+	return 0;
 
+    /* Check for intersections as no edges end during the next row. */
+    e = active->head;
+    while (NULL != e) {
+	struct quorem x = e->x;
+
+	if (! e->vertical) {
 	    x.quo += e->dxdy_full.quo;
 	    x.rem += e->dxdy_full.rem;
 	    if (x.rem >= 0)
 		++x.quo;
+	}
 
-	    if (x.quo <= prev_x)
+	/* There's may be an intersection if the edge sort order might
+	 * change. */
+	if (x.quo <= prev_x) {
+	    /* Ignore intersections to the left of the clip extents.
+	     * This assumes that all vertical edges on or at the left
+	     * side of the clip rectangle have been shifted slightly
+	     * to the left in polygon_add_edge(). */
+	    if (prev_x >= xmin || x.quo >= xmin || e->x.quo >= xmin)
 		return 0;
+	}
+	else {
 	    prev_x = x.quo;
-	    e = e->next;
 	}
-	return 1;
+	e = e->next;
     }
-    return 0;
+
+    return 1;
 }
 
 /* Merges edges on the given subpixel row from the polygon to the
@@ -1261,7 +1327,7 @@ active_list_merge_edges_from_polygon(
     unsigned ix = EDGE_Y_BUCKET_INDEX(y, polygon->ymin);
     int min_height = active->min_height;
     struct edge *subrow_edges = NULL;
-    struct edge **ptail = &polygon->y_buckets[ix];
+    struct edge **ptail = &polygon->y_buckets[ix].edges;
 
     while (1) {
 	struct edge *tail = *ptail;
@@ -1277,8 +1343,10 @@ active_list_merge_edges_from_polygon(
 	    ptail = &tail->next;
 	}
     }
-    active->head = merge_unsorted_edges(active->head, subrow_edges);
-    active->min_height = min_height;
+    if (subrow_edges) {
+	active->head = merge_unsorted_edges(active->head, subrow_edges);
+	active->min_height = min_height;
+    }
 }
 
 /* Advance the edges on the active list by one subsample row by
@@ -1439,11 +1507,13 @@ apply_nonzero_fill_rule_and_step_edges (struct active_list *active,
 		}
 	    }
 
-	    right_edge->x.quo += right_edge->dxdy_full.quo;
-	    right_edge->x.rem += right_edge->dxdy_full.rem;
-	    if (right_edge->x.rem >= 0) {
-		++right_edge->x.quo;
-		right_edge->x.rem -= right_edge->dy;
+	    if (! right_edge->vertical) {
+		right_edge->x.quo += right_edge->dxdy_full.quo;
+		right_edge->x.rem += right_edge->dxdy_full.rem;
+		if (right_edge->x.rem >= 0) {
+		    ++right_edge->x.quo;
+		    right_edge->x.rem -= right_edge->dy;
+		}
 	    }
 	}
 
@@ -1472,6 +1542,7 @@ apply_evenodd_fill_rule_and_step_edges (struct active_list *active,
     left_edge = *cursor;
     while (NULL != left_edge) {
 	struct edge *right_edge;
+	int winding = left_edge->dir;
 
 	left_edge->height_left -= GRID_Y;
 	if (left_edge->height_left)
@@ -1490,17 +1561,22 @@ apply_evenodd_fill_rule_and_step_edges (struct active_list *active,
 	    else
 		*cursor = right_edge->next;
 
+	    winding += right_edge->dir;
+	    if ((winding & 1) == 0) {
 	    if (right_edge->next == NULL ||
 		right_edge->next->x.quo != right_edge->x.quo)
 	    {
 		break;
 	    }
+	    }
 
-	    right_edge->x.quo += right_edge->dxdy_full.quo;
-	    right_edge->x.rem += right_edge->dxdy_full.rem;
-	    if (right_edge->x.rem >= 0) {
-		++right_edge->x.quo;
-		right_edge->x.rem -= right_edge->dy;
+	    if (! right_edge->vertical) {
+		right_edge->x.quo += right_edge->dxdy_full.quo;
+		right_edge->x.rem += right_edge->dxdy_full.rem;
+		if (right_edge->x.rem >= 0) {
+		    ++right_edge->x.quo;
+		    right_edge->x.rem -= right_edge->dy;
+		}
 	    }
 	}
 
@@ -1537,8 +1613,14 @@ blit_span(
     }
 }
 
-#define GLITTER_BLIT_COVERAGES(coverages, y, xmin, xmax) \
-	blit_cells(coverages, raster_pixels + (y)*raster_stride, xmin, xmax)
+#define GLITTER_BLIT_COVERAGES(coverages, y, height, xmin, xmax) \
+    do { \
+	int __y = y; \
+	int __h = height; \
+	do { \
+	    blit_cells(coverages, raster_pixels + (__y)*raster_stride, xmin, xmax); \
+	} while (--__h); \
+    } while (0)
 
 static void
 blit_cells(
@@ -1597,7 +1679,6 @@ static void
 _glitter_scan_converter_fini(glitter_scan_converter_t *converter)
 {
     polygon_fini(converter->polygon);
-    active_list_fini(converter->active);
     cell_list_fini(converter->coverages);
     converter->xmin=0;
     converter->ymin=0;
@@ -1641,7 +1722,7 @@ glitter_scan_converter_reset(
 
     active_list_reset(converter->active);
     cell_list_reset(converter->coverages);
-    status = polygon_reset(converter->polygon, ymin, ymax);
+    status = polygon_reset(converter->polygon, xmin, xmax, ymin, ymax);
     if (status)
 	return status;
 
@@ -1711,19 +1792,48 @@ glitter_scan_converter_add_edge (glitter_scan_converter_t *converter,
 #endif
 
 #ifndef GLITTER_BLIT_COVERAGES_EMPTY
-# define GLITTER_BLIT_COVERAGES_EMPTY(y, xmin, xmax)
+# define GLITTER_BLIT_COVERAGES_EMPTY(y0, y1, xmin, xmax)
 #endif
 
+static cairo_bool_t
+active_list_is_vertical (struct active_list *active)
+{
+    struct edge *e;
+
+    for (e = active->head; e != NULL; e = e->next) {
+	if (! e->vertical)
+	    return FALSE;
+    }
+
+    return TRUE;
+}
+
+static void
+step_edges (struct active_list *active, int count)
+{
+    struct edge **cursor = &active->head;
+    struct edge *edge;
+
+    for (edge = *cursor; edge != NULL; edge = *cursor) {
+	edge->height_left -= GRID_Y * count;
+	if (edge->height_left)
+	    cursor = &edge->next;
+	else
+	    *cursor = edge->next;
+    }
+}
+
 I glitter_status_t
 glitter_scan_converter_render(
     glitter_scan_converter_t *converter,
     int nonzero_fill,
     GLITTER_BLIT_COVERAGES_ARGS)
 {
-    int i;
+    int i, j;
     int ymax_i = converter->ymax / GRID_Y;
     int ymin_i = converter->ymin / GRID_Y;
     int xmin_i, xmax_i;
+    grid_scaled_x_t xmin = converter->xmin;
     int h = ymax_i - ymin_i;
     struct polygon *polygon = converter->polygon;
     struct cell_list *coverages = converter->coverages;
@@ -1738,22 +1848,28 @@ glitter_scan_converter_render(
     GLITTER_BLIT_COVERAGES_BEGIN;
 
     /* Render each pixel row. */
-    for (i=0; i<h; i++) {
+    for (i = 0; i < h; i = j) {
 	int do_full_step = 0;
 	glitter_status_t status = 0;
 
+	j = i + 1;
+
 	/* Determine if we can ignore this row or use the full pixel
 	 * stepper. */
-	if (GRID_Y == EDGE_Y_BUCKET_HEIGHT && ! polygon->y_buckets[i]) {
+	if (polygon->y_buckets[i].edges == NULL) {
 	    if (! active->head) {
-		GLITTER_BLIT_COVERAGES_EMPTY (i+ymin_i, xmin_i, xmax_i);
+		for (; j < h && ! polygon->y_buckets[j].edges; j++)
+		    ;
+		GLITTER_BLIT_COVERAGES_EMPTY (i+ymin_i, j-i, xmin_i, xmax_i);
 		continue;
 	    }
-
-	    do_full_step = active_list_can_step_full_row (active);
+	    do_full_step = active_list_can_step_full_row (active, xmin);
+	}
+	else if (! polygon->y_buckets[i].have_inside_edges) {
+	    grid_scaled_y_t y = (i+ymin_i)*GRID_Y;
+	    active_list_merge_edges_from_polygon (active, y, polygon);
+	    do_full_step = active_list_can_step_full_row (active, xmin);
 	}
-
-	cell_list_reset (coverages);
 
 	if (do_full_step) {
 	    /* Step by a full pixel row's worth. */
@@ -1764,8 +1880,20 @@ glitter_scan_converter_render(
 		status = apply_evenodd_fill_rule_and_step_edges (active,
 								 coverages);
 	    }
+
+	    if (active_list_is_vertical (active)) {
+		while (j < h &&
+		       polygon->y_buckets[j].edges == NULL &&
+		       active->min_height >= 2*GRID_Y)
+		{
+		    active->min_height -= GRID_Y;
+		    j++;
+		}
+		if (j != i + 1)
+		    step_edges (active, j - (i + 1));
+	    }
 	} else {
-	    /* Subsample this row. */
+	    /* Supersample this row. */
 	    grid_scaled_y_t suby;
 	    for (suby = 0; suby < GRID_Y; suby++) {
 		grid_scaled_y_t y = (i+ymin_i)*GRID_Y + suby;
@@ -1787,13 +1915,13 @@ glitter_scan_converter_render(
 	if (unlikely (status))
 	    return status;
 
-	GLITTER_BLIT_COVERAGES(coverages, i+ymin_i, xmin_i, xmax_i);
+	GLITTER_BLIT_COVERAGES(coverages, i+ymin_i, j-i, xmin_i, xmax_i);
+	cell_list_reset (coverages);
 
-	if (! active->head) {
+	if (! active->head)
 	    active->min_height = INT_MAX;
-	} else {
+	else
 	    active->min_height -= GRID_Y;
-	}
     }
 
     /* Clean up the coverage blitter. */
@@ -1807,21 +1935,20 @@ glitter_scan_converter_render(
  * scan converter subclass. */
 
 static glitter_status_t
-blit_with_span_renderer(
-    struct cell_list *cells,
-    cairo_span_renderer_t *renderer,
-    struct pool *span_pool,
-    int y,
-    int xmin,
-    int xmax)
+blit_with_span_renderer (struct cell_list *cells,
+			 cairo_span_renderer_t *renderer,
+			 struct pool *span_pool,
+			 int y, int height,
+			 int xmin, int xmax)
 {
     struct cell *cell = cells->head;
     int prev_x = xmin;
     int cover = 0;
     cairo_half_open_span_t *spans;
     unsigned num_spans;
+
     if (cell == NULL)
-	return CAIRO_STATUS_SUCCESS;
+	return blit_empty_with_span_renderer (renderer, y, height);
 
     /* Skip cells to the left of the clip region. */
     while (cell != NULL && cell->x < xmin) {
@@ -1833,12 +1960,12 @@ blit_with_span_renderer(
     /* Count number of cells remaining. */
     {
 	struct cell *next = cell;
-	num_spans = 0;
-	while (next) {
+	num_spans = 1;
+	while (next != NULL) {
 	    next = next->next;
 	    ++num_spans;
 	}
-	num_spans = 2*num_spans + 1;
+	num_spans = 2*num_spans;
     }
 
     /* Allocate enough spans for the row. */
@@ -1853,6 +1980,7 @@ blit_with_span_renderer(
     for (; cell != NULL; cell = cell->next) {
 	int x = cell->x;
 	int area;
+
 	if (x >= xmax)
 	    break;
 
@@ -1872,20 +2000,26 @@ blit_with_span_renderer(
 	prev_x = x+1;
     }
 
-    if (prev_x < xmax) {
+    if (prev_x <= xmax) {
 	spans[num_spans].x = prev_x;
 	spans[num_spans].coverage = GRID_AREA_TO_ALPHA (cover);
 	++num_spans;
     }
 
+    if (prev_x < xmax && cover) {
+	spans[num_spans].x = xmax;
+	spans[num_spans].coverage = 0;
+	++num_spans;
+    }
+
     /* Dump them into the renderer. */
-    return renderer->render_row (renderer, y, spans, num_spans);
+    return renderer->render_rows (renderer, y, height, spans, num_spans);
 }
 
 static glitter_status_t
-blit_empty_with_span_renderer (cairo_span_renderer_t *renderer, int y)
+blit_empty_with_span_renderer (cairo_span_renderer_t *renderer, int y, int height)
 {
-    return renderer->render_row (renderer, y, NULL, 0);
+    return renderer->render_rows (renderer, y, height, NULL, 0);
 }
 
 struct _cairo_tor_scan_converter {
diff --git a/src/cairo-win32-surface.c b/src/cairo-win32-surface.c
index 82d1cf5..d4575a3 100644
--- a/src/cairo-win32-surface.c
+++ b/src/cairo-win32-surface.c
@@ -1954,6 +1954,9 @@ typedef struct _cairo_win32_surface_span_renderer {
     const cairo_pattern_t *pattern;
     cairo_antialias_t antialias;
 
+    uint8_t *mask_data;
+    uint32_t mask_stride;
+
     cairo_image_surface_t *mask;
     cairo_win32_surface_t *dst;
     cairo_region_t *clip_region;
@@ -1962,14 +1965,16 @@ typedef struct _cairo_win32_surface_span_renderer {
 } cairo_win32_surface_span_renderer_t;
 
 static cairo_status_t
-_cairo_win32_surface_span_renderer_render_row (
+_cairo_win32_surface_span_renderer_render_rows (
     void				*abstract_renderer,
     int					 y,
+    int					 height,
     const cairo_half_open_span_t	*spans,
     unsigned				 num_spans)
 {
     cairo_win32_surface_span_renderer_t *renderer = abstract_renderer;
-    _cairo_image_surface_span_render_row (y, spans, num_spans, renderer->mask, &renderer->composite_rectangles);
+    while (height--)
+	_cairo_image_surface_span_render_row (y++, spans, num_spans, renderer->mask_data, renderer->mask_stride);
     return CAIRO_STATUS_SUCCESS;
 }
 
@@ -2066,8 +2071,7 @@ _cairo_win32_surface_create_span_renderer (cairo_operator_t	 op,
 
     renderer->base.destroy = _cairo_win32_surface_span_renderer_destroy;
     renderer->base.finish = _cairo_win32_surface_span_renderer_finish;
-    renderer->base.render_row =
-	_cairo_win32_surface_span_renderer_render_row;
+    renderer->base.render_rows = _cairo_win32_surface_span_renderer_render_rows;
     renderer->op = op;
     renderer->pattern = pattern;
     renderer->antialias = antialias;
@@ -2088,6 +2092,9 @@ _cairo_win32_surface_create_span_renderer (cairo_operator_t	 op,
 	_cairo_win32_surface_span_renderer_destroy (renderer);
 	return _cairo_span_renderer_create_in_error (status);
     }
+
+    renderer->mask_data = renderer->mask->data - rects->mask.x - rects->mask.y * renderer->mask->stride;
+    renderer->mask_stride = renderer->mask->stride;
     return &renderer->base;
 }
 
diff --git a/src/cairo-xlib-display.c b/src/cairo-xlib-display.c
index a7a40b8..566d9fb 100644
--- a/src/cairo-xlib-display.c
+++ b/src/cairo-xlib-display.c
@@ -407,6 +407,10 @@ _cairo_xlib_display_get (Display *dpy,
 	display->buggy_pad_reflect = TRUE;
     }
 
+    /* gradients don't seem to work */
+    display->buggy_gradients = TRUE;
+
+
     /* XXX workaround; see https://bugzilla.mozilla.org/show_bug.cgi?id=413583 */
     /* If buggy_repeat_force == -1, then initialize.
      *    - set to -2, meaning "nothing was specified", and we trust the above detection.
diff --git a/src/cairoint.h b/src/cairoint.h
index 58850ab..1cdf6ff 100644
--- a/src/cairoint.h
+++ b/src/cairoint.h
@@ -2257,8 +2257,8 @@ cairo_private void
 _cairo_image_surface_span_render_row (int				 y,
 				      const cairo_half_open_span_t	 *spans,
 				      unsigned				 num_spans,
-				      cairo_image_surface_t              *mask,
-				      const cairo_composite_rectangles_t *rects);
+				      uint8_t				*data,
+				      uint32_t				 stride);
 
 cairo_private cairo_image_transparency_t
 _cairo_image_analyze_transparency (cairo_image_surface_t      *image);
back to top