Blob Blame History Raw
/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
/*
 * Copyright (c) 2011  Intel Corporation
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use,
 * copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following
 * conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 */
#include "cairoint.h"
#include "cairo-spans-private.h"
#include "cairo-error-private.h"

#include <stdlib.h>
#include <string.h>
#include <limits.h>

struct quorem {
    int32_t quo;
    int32_t rem;
};

struct edge {
    struct edge *next, *prev;

    int32_t height_left;
    int32_t dir;
    int32_t vertical;

    int32_t dy;
    struct quorem x;
    struct quorem dxdy;
};

/* 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. */
    int32_t ymin, ymax;

    int num_edges;
    struct edge *edges;

    /* 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 edge edges_embedded[32];
};

struct mono_scan_converter {
    struct polygon polygon[1];

    /* Leftmost edge on the current scan line. */
    struct edge head, tail;
    int is_vertical;

    cairo_half_open_span_t *spans;
    cairo_half_open_span_t spans_embedded[64];
    int num_spans;

    /* Clip box. */
    int32_t xmin, xmax;
    int32_t ymin, ymax;
};

#define I(x) _cairo_fixed_integer_round_down(x)

/* Compute the floored division a/b. Assumes / and % perform symmetric
 * division. */
inline static struct quorem
floored_divrem(int a, int b)
{
    struct quorem qr;
    qr.quo = a/b;
    qr.rem = a%b;
    if ((a^b)<0 && qr.rem) {
	qr.quo -= 1;
	qr.rem += b;
    }
    return qr;
}

/* Compute the floored division (x*a)/b. Assumes / and % perform symmetric
 * division. */
static struct quorem
floored_muldivrem(int x, int a, int b)
{
    struct quorem qr;
    long long xa = (long long)x*a;
    qr.quo = xa/b;
    qr.rem = xa%b;
    if ((xa>=0) != (b>=0) && qr.rem) {
	qr.quo -= 1;
	qr.rem += b;
    }
    return qr;
}

static cairo_status_t
polygon_init (struct polygon *polygon, int ymin, int ymax)
{
    unsigned h = ymax - ymin + 1;

    polygon->y_buckets = polygon->y_buckets_embedded;
    if (h > ARRAY_LENGTH (polygon->y_buckets_embedded)) {
	polygon->y_buckets = _cairo_malloc_ab (h, sizeof (struct edge *));
	if (unlikely (NULL == polygon->y_buckets))
	    return _cairo_error (CAIRO_STATUS_NO_MEMORY);
    }
    memset (polygon->y_buckets, 0, h * sizeof (struct edge *));
    polygon->y_buckets[h-1] = (void *)-1;

    polygon->ymin = ymin;
    polygon->ymax = ymax;
    return CAIRO_STATUS_SUCCESS;
}

static void
polygon_fini (struct polygon *polygon)
{
    if (polygon->y_buckets != polygon->y_buckets_embedded)
	free (polygon->y_buckets);

    if (polygon->edges != polygon->edges_embedded)
	free (polygon->edges);
}

static void
_polygon_insert_edge_into_its_y_bucket(struct polygon *polygon,
				       struct edge *e,
				       int y)
{
    struct edge **ptail = &polygon->y_buckets[y - polygon->ymin];
    if (*ptail)
	(*ptail)->prev = e;
    e->next = *ptail;
    e->prev = NULL;
    *ptail = e;
}

inline static void
polygon_add_edge (struct polygon *polygon,
		  const cairo_edge_t *edge)
{
    struct edge *e;
    cairo_fixed_t dx;
    cairo_fixed_t dy;
    int y, ytop, ybot;
    int ymin = polygon->ymin;
    int ymax = polygon->ymax;

    y = I(edge->top);
    ytop = MAX(y, ymin);

    y = I(edge->bottom);
    ybot = MIN(y, ymax);

    if (ybot <= ytop)
	return;

    e = polygon->edges + polygon->num_edges++;
    e->height_left = ybot - ytop;
    e->dir = edge->dir;

    dx = edge->line.p2.x - edge->line.p1.x;
    dy = edge->line.p2.y - edge->line.p1.y;

    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->dy = 0;
    } else {
	e->vertical = FALSE;
	e->dxdy = floored_muldivrem (dx, CAIRO_FIXED_ONE, dy);
	e->dy = dy;

	e->x = floored_muldivrem (ytop * CAIRO_FIXED_ONE + CAIRO_FIXED_FRAC_MASK/2 - edge->line.p1.y,
				  dx, dy);
	e->x.quo += edge->line.p1.x;
    }
    e->x.rem -= dy;

    _polygon_insert_edge_into_its_y_bucket (polygon, e, ytop);
}

static struct edge *
merge_sorted_edges (struct edge *head_a, struct edge *head_b)
{
    struct edge *head, **next, *prev;
    int32_t x;

    prev = head_a->prev;
    next = &head;
    if (head_a->x.quo <= head_b->x.quo) {
	head = head_a;
    } else {
	head = head_b;
	head_b->prev = prev;
	goto start_with_b;
    }

    do {
	x = head_b->x.quo;
	while (head_a != NULL && head_a->x.quo <= x) {
	    prev = head_a;
	    next = &head_a->next;
	    head_a = head_a->next;
	}

	head_b->prev = prev;
	*next = head_b;
	if (head_a == NULL)
	    return head;

start_with_b:
	x = head_a->x.quo;
	while (head_b != NULL && head_b->x.quo <= x) {
	    prev = head_b;
	    next = &head_b->next;
	    head_b = head_b->next;
	}

	head_a->prev = prev;
	*next = head_a;
	if (head_b == NULL)
	    return head;
    } while (1);
}

static struct edge *
sort_edges (struct edge *list,
	    unsigned int level,
	    struct edge **head_out)
{
    struct edge *head_other, *remaining;
    unsigned int i;

    head_other = list->next;

    if (head_other == NULL) {
	*head_out = list;
	return NULL;
    }

    remaining = head_other->next;
    if (list->x.quo <= head_other->x.quo) {
	*head_out = list;
	head_other->next = NULL;
    } else {
	*head_out = head_other;
	head_other->prev = list->prev;
	head_other->next = list;
	list->prev = head_other;
	list->next = NULL;
    }

    for (i = 0; i < level && remaining; i++) {
	remaining = sort_edges (remaining, i, &head_other);
	*head_out = merge_sorted_edges (*head_out, head_other);
    }

    return remaining;
}

static struct edge *
merge_unsorted_edges (struct edge *head, struct edge *unsorted)
{
    sort_edges (unsorted, UINT_MAX, &unsorted);
    return merge_sorted_edges (head, unsorted);
}

inline static void
active_list_merge_edges (struct mono_scan_converter *c, struct edge *edges)
{
    struct edge *e;

    for (e = edges; c->is_vertical && e; e = e->next)
	c->is_vertical = e->vertical;

    c->head.next = merge_unsorted_edges (c->head.next, edges);
}

inline static void
add_span (struct mono_scan_converter *c, int x1, int x2)
{
    int n;

    if (x1 < c->xmin)
	x1 = c->xmin;
    if (x2 > c->xmax)
	x2 = c->xmax;
    if (x2 <= x1)
	return;

    n = c->num_spans++;
    c->spans[n].x = x1;
    c->spans[n].coverage = 255;

    n = c->num_spans++;
    c->spans[n].x = x2;
    c->spans[n].coverage = 0;
}

inline static void
row (struct mono_scan_converter *c, unsigned int mask)
{
    struct edge *edge = c->head.next;
    int xstart = INT_MIN, prev_x = INT_MIN;
    int winding = 0;

    c->num_spans = 0;
    while (&c->tail != edge) {
	struct edge *next = edge->next;
	int xend = I(edge->x.quo);

	if (--edge->height_left) {
	    if (!edge->vertical) {
		edge->x.quo += edge->dxdy.quo;
		edge->x.rem += edge->dxdy.rem;
		if (edge->x.rem >= 0) {
		    ++edge->x.quo;
		    edge->x.rem -= edge->dy;
		}
	    }

	    if (edge->x.quo < prev_x) {
		struct edge *pos = edge->prev;
		pos->next = next;
		next->prev = pos;
		do {
		    pos = pos->prev;
		} while (edge->x.quo < pos->x.quo);
		pos->next->prev = edge;
		edge->next = pos->next;
		edge->prev = pos;
		pos->next = edge;
	    } else
		prev_x = edge->x.quo;
	} else {
	    edge->prev->next = next;
	    next->prev = edge->prev;
	}

	winding += edge->dir;
	if ((winding & mask) == 0) {
	    if (I(next->x.quo) > xend + 1) {
		add_span (c, xstart, xend);
		xstart = INT_MIN;
	    }
	} else if (xstart == INT_MIN)
	    xstart = xend;

	edge = next;
    }
}

inline static void dec (struct edge *e, int h)
{
    e->height_left -= h;
    if (e->height_left == 0) {
	e->prev->next = e->next;
	e->next->prev = e->prev;
    }
}

static cairo_status_t
_mono_scan_converter_init(struct mono_scan_converter *c,
			  int xmin, int ymin,
			  int xmax, int ymax)
{
    cairo_status_t status;
    int max_num_spans;

    status = polygon_init (c->polygon, ymin, ymax);
    if  (unlikely (status))
	return status;

    max_num_spans = xmax - xmin + 1;
    if (max_num_spans > ARRAY_LENGTH(c->spans_embedded)) {
	c->spans = _cairo_malloc_ab (max_num_spans,
				     sizeof (cairo_half_open_span_t));
	if (unlikely (c->spans == NULL)) {
	    polygon_fini (c->polygon);
	    return _cairo_error (CAIRO_STATUS_NO_MEMORY);
	}
    } else
	c->spans = c->spans_embedded;

    c->xmin = xmin;
    c->xmax = xmax;
    c->ymin = ymin;
    c->ymax = ymax;

    c->head.vertical = 1;
    c->head.height_left = INT_MAX;
    c->head.x.quo = _cairo_fixed_from_int (_cairo_fixed_integer_part (INT_MIN));
    c->head.prev = NULL;
    c->head.next = &c->tail;
    c->tail.prev = &c->head;
    c->tail.next = NULL;
    c->tail.x.quo = _cairo_fixed_from_int (_cairo_fixed_integer_part (INT_MAX));
    c->tail.height_left = INT_MAX;
    c->tail.vertical = 1;

    c->is_vertical = 1;
    return CAIRO_STATUS_SUCCESS;
}

static void
_mono_scan_converter_fini(struct mono_scan_converter *self)
{
    if (self->spans != self->spans_embedded)
	free (self->spans);

    polygon_fini(self->polygon);
}

static cairo_status_t
mono_scan_converter_allocate_edges(struct mono_scan_converter *c,
				   int num_edges)

{
    c->polygon->num_edges = 0;
    c->polygon->edges = c->polygon->edges_embedded;
    if (num_edges > ARRAY_LENGTH (c->polygon->edges_embedded)) {
	c->polygon->edges = _cairo_malloc_ab (num_edges, sizeof (struct edge));
	if (unlikely (c->polygon->edges == NULL))
	    return _cairo_error (CAIRO_STATUS_NO_MEMORY);
    }

    return CAIRO_STATUS_SUCCESS;
}

static void
mono_scan_converter_add_edge (struct mono_scan_converter *c,
			      const cairo_edge_t *edge)
{
    polygon_add_edge (c->polygon, edge);
}

static void
step_edges (struct mono_scan_converter *c, int count)
{
    struct edge *edge;

    for (edge = c->head.next; edge != &c->tail; edge = edge->next) {
	edge->height_left -= count;
	if (! edge->height_left) {
	    edge->prev->next = edge->next;
	    edge->next->prev = edge->prev;
	}
    }
}

static cairo_status_t
mono_scan_converter_render(struct mono_scan_converter *c,
			   unsigned int winding_mask,
			   cairo_span_renderer_t *renderer)
{
    struct polygon *polygon = c->polygon;
    int i, j, h = c->ymax - c->ymin;
    cairo_status_t status;

    for (i = 0; i < h; i = j) {
	j = i + 1;

	if (polygon->y_buckets[i])
	    active_list_merge_edges (c, polygon->y_buckets[i]);

	if (c->is_vertical) {
	    int min_height;
	    struct edge *e;

	    e = c->head.next;
	    min_height = e->height_left;
	    while (e != &c->tail) {
		if (e->height_left < min_height)
		    min_height = e->height_left;
		e = e->next;
	    }

	    while (--min_height >= 1 && polygon->y_buckets[j] == NULL)
		j++;
	    if (j != i + 1)
		step_edges (c, j - (i + 1));
	}

	row (c, winding_mask);
	if (c->num_spans) {
	    status = renderer->render_rows (renderer, c->ymin+i, j-i,
					    c->spans, c->num_spans);
	    if (unlikely (status))
		return status;
	}

	/* XXX recompute after dropping edges? */
	if (c->head.next == &c->tail)
	    c->is_vertical = 1;
    }

    return CAIRO_STATUS_SUCCESS;
}

struct _cairo_mono_scan_converter {
    cairo_scan_converter_t base;

    struct mono_scan_converter converter[1];
    cairo_fill_rule_t fill_rule;
};

typedef struct _cairo_mono_scan_converter cairo_mono_scan_converter_t;

static void
_cairo_mono_scan_converter_destroy (void *converter)
{
    cairo_mono_scan_converter_t *self = converter;
    _mono_scan_converter_fini (self->converter);
    free(self);
}

cairo_status_t
_cairo_mono_scan_converter_add_polygon (void		*converter,
				       const cairo_polygon_t *polygon)
{
    cairo_mono_scan_converter_t *self = converter;
    cairo_status_t status;
    int i;

#if 0
    FILE *file = fopen ("polygon.txt", "w");
    _cairo_debug_print_polygon (file, polygon);
    fclose (file);
#endif

    status = mono_scan_converter_allocate_edges (self->converter,
						 polygon->num_edges);
    if (unlikely (status))
	return status;

    for (i = 0; i < polygon->num_edges; i++)
	 mono_scan_converter_add_edge (self->converter, &polygon->edges[i]);

    return CAIRO_STATUS_SUCCESS;
}

static cairo_status_t
_cairo_mono_scan_converter_generate (void			*converter,
				    cairo_span_renderer_t	*renderer)
{
    cairo_mono_scan_converter_t *self = converter;

    return mono_scan_converter_render (self->converter,
				       self->fill_rule == CAIRO_FILL_RULE_WINDING ? ~0 : 1,
				       renderer);
}

cairo_scan_converter_t *
_cairo_mono_scan_converter_create (int			xmin,
				  int			ymin,
				  int			xmax,
				  int			ymax,
				  cairo_fill_rule_t	fill_rule)
{
    cairo_mono_scan_converter_t *self;
    cairo_status_t status;

    self = malloc (sizeof(struct _cairo_mono_scan_converter));
    if (unlikely (self == NULL)) {
	status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
	goto bail_nomem;
    }

    self->base.destroy = _cairo_mono_scan_converter_destroy;
    self->base.generate = _cairo_mono_scan_converter_generate;

    status = _mono_scan_converter_init (self->converter,
					xmin, ymin, xmax, ymax);
    if (unlikely (status))
	goto bail;

    self->fill_rule = fill_rule;

    return &self->base;

 bail:
    self->base.destroy(&self->base);
 bail_nomem:
    return _cairo_scan_converter_create_in_error (status);
}