cairo/util/show-polygon.c
Chris Wilson af9fbd176b Introduce a new compositor architecture
Having spent the last dev cycle looking at how we could specialize the
compositors for various backends, we once again look for the
commonalities in order to reduce the duplication. In part this is
motivated by the idea that spans is a good interface for both the
existent GL backend and pixman, and so they deserve a dedicated
compositor. xcb/xlib target an identical rendering system and so they
should be using the same compositor, and it should be possible to run
that same compositor locally against pixman to generate reference tests.

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>

P.S. This brings massive upheaval (read breakage) I've tried delaying in
order to fix as many things as possible but now this one patch does far,
far, far too much. Apologies in advance for breaking your favourite
backend, but trust me in that the end result will be much better. :)
2011-09-12 08:29:48 +01:00

602 lines
15 KiB
C

#define _GNU_SOURCE
#include <gtk/gtk.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <gdk/gdkkeysyms.h>
#include <math.h>
typedef struct _point {
gdouble x, y;
} point_t;
typedef struct _edge {
point_t p1, p2;
gdouble top, bot;
int dir;
} edge_t;
typedef struct _box {
point_t p1, p2;
} box_t;
typedef struct _polygon {
struct _polygon *next, *prev;
int num_edges;
int size;
edge_t edges[0];
} polygon_t;
typedef struct _PolygonView {
GtkWidget widget;
cairo_surface_t *pixmap;
int pixmap_width, pixmap_height;
box_t extents;
polygon_t *polygons;
double px, py;
gint mag_x, mag_y;
gint mag_size;
gdouble mag_zoom;
gboolean in_mag_drag;
gint mag_drag_x, mag_drag_y;
} PolygonView;
typedef struct _PolygonViewClass {
GtkWidgetClass parent_class;
} PolygonViewClass;
G_DEFINE_TYPE (PolygonView, polygon_view, GTK_TYPE_WIDGET)
double highlight = -1;
static void draw_edges (cairo_t *cr, polygon_t *p, gdouble sf, int dir)
{
int n;
for (n = 0; n < p->num_edges; n++) {
const edge_t *e = &p->edges[n];
if (e->dir != dir)
continue;
cairo_arc (cr, e->p1.x, e->p1.y, 2/sf, 0, 2*M_PI);
cairo_arc (cr, e->p2.x, e->p2.y, 2/sf, 0, 2*M_PI);
cairo_fill (cr);
}
for (n = 0; n < p->num_edges; n++) {
const edge_t *e = &p->edges[n];
if (e->dir != dir)
continue;
cairo_move_to (cr, e->p1.x, e->p1.y);
cairo_line_to (cr, e->p2.x, e->p2.y);
}
cairo_save (cr); {
cairo_identity_matrix (cr);
cairo_set_line_width (cr, 1.);
cairo_stroke (cr);
} cairo_restore (cr);
}
static void draw_polygon (cairo_t *cr, polygon_t *p, gdouble sf)
{
cairo_set_source_rgb (cr, 0.0, 0.0, 1.0);
draw_edges (cr, p, sf, -1);
cairo_set_source_rgb (cr, 1.0, 0.0, 0.0);
draw_edges (cr, p, sf, +1);
}
static cairo_surface_t *
pixmap_create (PolygonView *self, cairo_surface_t *target)
{
cairo_surface_t *surface =
cairo_surface_create_similar (target, CAIRO_CONTENT_COLOR,
self->widget.allocation.width,
self->widget.allocation.height);
cairo_t *cr = cairo_create (surface);
polygon_t *polygon;
gdouble sf_x, sf_y, sf;
gdouble mid, dim;
gdouble x0, y0;
box_t extents;
cairo_set_source_rgb (cr, 1, 1, 1);
cairo_paint (cr);
if (self->polygons == NULL) {
cairo_destroy(cr);
return surface;
}
extents = self->extents;
mid = (extents.p2.x + extents.p1.x) / 2.;
dim = (extents.p2.x - extents.p1.x) / 2. * 1.25;
sf_x = self->widget.allocation.width / dim / 2;
mid = (extents.p2.y + extents.p1.y) / 2.;
dim = (extents.p2.y - extents.p1.y) / 2. * 1.25;
sf_y = self->widget.allocation.height / dim / 2;
sf = MIN (sf_x, sf_y);
mid = (extents.p2.x + extents.p1.x) / 2.;
dim = sf_x / sf * (extents.p2.x - extents.p1.x) / 2. * 1.25;
x0 = mid - dim;
mid = (extents.p2.y + extents.p1.y) / 2.;
dim = sf_y / sf * (extents.p2.y - extents.p1.y) / 2. * 1.25;
y0 = mid - dim;
cairo_save (cr); {
cairo_scale (cr, sf, sf);
cairo_translate (cr, -x0, -y0);
for (polygon = self->polygons; polygon; polygon = polygon->next) {
if (polygon->num_edges == 0)
continue;
draw_polygon (cr, polygon, sf);
}
if (highlight != -1) {
cairo_move_to (cr, extents.p1.x, highlight);
cairo_line_to (cr, extents.p2.x, highlight);
cairo_set_source_rgb (cr, 0, .7, 0);
cairo_save (cr);
cairo_identity_matrix (cr);
cairo_set_line_width (cr, 1.);
cairo_stroke (cr);
cairo_restore (cr);
}
} cairo_restore (cr);
cairo_destroy (cr);
return surface;
}
static void
polygon_view_draw (PolygonView *self, cairo_t *cr)
{
polygon_t *polygon;
gdouble sf_x, sf_y, sf;
gdouble mid, dim;
gdouble x0, y0;
box_t extents;
extents = self->extents;
mid = (extents.p2.x + extents.p1.x) / 2.;
dim = (extents.p2.x - extents.p1.x) / 2. * 1.25;
sf_x = self->widget.allocation.width / dim / 2;
mid = (extents.p2.y + extents.p1.y) / 2.;
dim = (extents.p2.y - extents.p1.y) / 2. * 1.25;
sf_y = self->widget.allocation.height / dim / 2;
sf = MIN (sf_x, sf_y);
mid = (extents.p2.x + extents.p1.x) / 2.;
dim = sf_x / sf * (extents.p2.x - extents.p1.x) / 2. * 1.25;
x0 = mid - dim;
mid = (extents.p2.y + extents.p1.y) / 2.;
dim = sf_y / sf * (extents.p2.y - extents.p1.y) / 2. * 1.25;
y0 = mid - dim;
if (self->pixmap_width != self->widget.allocation.width ||
self->pixmap_height != self->widget.allocation.height)
{
cairo_surface_destroy (self->pixmap);
self->pixmap = pixmap_create (self, cairo_get_target (cr));
self->pixmap_width = self->widget.allocation.width;
self->pixmap_height = self->widget.allocation.height;
}
cairo_set_source_surface (cr, self->pixmap, 0, 0);
cairo_paint (cr);
if (self->polygons == NULL)
return;
/* draw a zoom view of the area around the mouse */
if (1) {
double zoom = self->mag_zoom;
int size = self->mag_size;
int mag_x = self->mag_x;
int mag_y = self->mag_y;
if (1) {
if (self->px + size < self->widget.allocation.width/2)
mag_x = self->px + size/4;
else
mag_x = self->px - size/4 - size;
mag_y = self->py - size/2;
if (mag_y < 0)
mag_y = 0;
if (mag_y + size > self->widget.allocation.height)
mag_y = self->widget.allocation.height - size;
}
cairo_save (cr); {
/* bottom right */
cairo_rectangle (cr, mag_x, mag_y, size, size);
cairo_stroke_preserve (cr);
cairo_set_source_rgb (cr, 1, 1, 1);
cairo_fill_preserve (cr);
cairo_clip (cr);
/* compute roi in extents */
cairo_translate (cr, mag_x + size/2, mag_y + size/2);
cairo_save (cr); {
cairo_scale (cr, zoom, zoom);
cairo_translate (cr, -(self->px / sf + x0), -(self->py /sf + y0));
for (polygon = self->polygons; polygon; polygon = polygon->next) {
if (polygon->num_edges == 0)
continue;
draw_polygon (cr, polygon, zoom);
}
if (highlight != -1) {
cairo_move_to (cr, extents.p1.x, highlight);
cairo_line_to (cr, extents.p2.x, highlight);
cairo_set_source_rgb (cr, 0, .7, 0);
cairo_save (cr);
cairo_identity_matrix (cr);
cairo_set_line_width (cr, 1.);
cairo_stroke (cr);
cairo_restore (cr);
}
} cairo_restore (cr);
/* grid */
cairo_save (cr); {
int i;
cairo_translate (cr,
-zoom*fmod (self->px/sf + x0, 1.),
-zoom*fmod (self->py/sf + y0, 1.));
zoom /= 2;
for (i = -size/2/zoom; i <= size/2/zoom + 1; i+=2) {
cairo_move_to (cr, zoom*i, -size/2);
cairo_line_to (cr, zoom*i, size/2 + zoom);
cairo_move_to (cr, -size/2, zoom*i);
cairo_line_to (cr, size/2 + zoom, zoom*i);
}
zoom *= 2;
cairo_set_source_rgba (cr, .7, .7, .7, .5);
cairo_set_line_width (cr, 1.);
cairo_stroke (cr);
for (i = -size/2/zoom - 1; i <= size/2/zoom + 1; i++) {
cairo_move_to (cr, zoom*i, -size/2);
cairo_line_to (cr, zoom*i, size/2 + zoom);
cairo_move_to (cr, -size/2, zoom*i);
cairo_line_to (cr, size/2 + zoom, zoom*i);
}
cairo_set_source_rgba (cr, .1, .1, .1, .5);
cairo_set_line_width (cr, 2.);
cairo_stroke (cr);
} cairo_restore (cr);
} cairo_restore (cr);
}
}
static gboolean
polygon_view_expose (GtkWidget *w, GdkEventExpose *ev)
{
PolygonView *self = (PolygonView *) w;
cairo_t *cr;
cr = gdk_cairo_create (w->window);
gdk_cairo_region (cr, ev->region);
cairo_clip (cr);
polygon_view_draw (self, cr);
cairo_destroy (cr);
return FALSE;
}
static gboolean
polygon_view_key_press (GtkWidget *w, GdkEventKey *ev)
{
switch (ev->keyval) {
case GDK_Escape:
case GDK_Q:
gtk_main_quit ();
break;
}
return FALSE;
}
static gboolean
polygon_view_button_press (GtkWidget *w, GdkEventButton *ev)
{
PolygonView *self = (PolygonView *) w;
if (ev->x < self->mag_x ||
ev->y < self->mag_y ||
ev->x > self->mag_x + self->mag_size ||
ev->y > self->mag_y + self->mag_size)
{
}
else
{
self->in_mag_drag = TRUE;
self->mag_drag_x = ev->x;
self->mag_drag_y = ev->y;
}
return FALSE;
}
static gboolean
polygon_view_button_release (GtkWidget *w, GdkEventButton *ev)
{
PolygonView *self = (PolygonView *) w;
self->in_mag_drag = FALSE;
return FALSE;
}
static void
polygon_view_update_mouse (PolygonView *self, GdkEventMotion *ev)
{
self->px = ev->x;
self->py = ev->y;
gtk_widget_queue_draw (&self->widget);
}
static void
polygon_view_update_magnifier (PolygonView *self, gint *xy)
{
self->mag_x = xy[0];
self->mag_y = xy[1];
gtk_widget_queue_draw (&self->widget);
}
static gboolean
polygon_view_motion (GtkWidget *w, GdkEventMotion *ev)
{
PolygonView *self = (PolygonView *) w;
if (self->in_mag_drag) {
int xy[2];
xy[0] = self->mag_x + ev->x - self->mag_drag_x;
xy[1] = self->mag_y + ev->y - self->mag_drag_y;
polygon_view_update_magnifier (self, xy);
self->mag_drag_x = ev->x;
self->mag_drag_y = ev->y;
} else if (ev->x < self->mag_x ||
ev->y < self->mag_y ||
ev->x > self->mag_x + self->mag_size ||
ev->y > self->mag_y + self->mag_size)
{
polygon_view_update_mouse (self, ev);
}
return FALSE;
}
static void
polygon_view_realize (GtkWidget *widget)
{
GdkWindowAttr attributes;
GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
attributes.window_type = GDK_WINDOW_CHILD;
attributes.x = widget->allocation.x;
attributes.y = widget->allocation.y;
attributes.width = widget->allocation.width;
attributes.height = widget->allocation.height;
attributes.wclass = GDK_INPUT_OUTPUT;
attributes.visual = gtk_widget_get_visual (widget);
attributes.colormap = gtk_widget_get_colormap (widget);
attributes.event_mask = gtk_widget_get_events (widget) |
GDK_BUTTON_PRESS_MASK |
GDK_BUTTON_RELEASE_MASK |
GDK_KEY_PRESS_MASK |
GDK_KEY_RELEASE_MASK |
GDK_POINTER_MOTION_MASK |
GDK_BUTTON_MOTION_MASK |
GDK_EXPOSURE_MASK;
widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
&attributes,
GDK_WA_X | GDK_WA_Y |
GDK_WA_VISUAL | GDK_WA_COLORMAP);
gdk_window_set_user_data (widget->window, widget);
widget->style = gtk_style_attach (widget->style, widget->window);
gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
}
static void
polygon_view_size_allocate (GtkWidget *w, GdkRectangle *r)
{
PolygonView *self = (PolygonView *) w;
GTK_WIDGET_CLASS (polygon_view_parent_class)->size_allocate (w, r);
self->mag_x = w->allocation.width - self->mag_size - 10;
self->mag_y = w->allocation.height - self->mag_size - 10;
}
static void
polygon_view_finalize (GObject *obj)
{
G_OBJECT_CLASS (polygon_view_parent_class)->finalize (obj);
}
static void
polygon_view_class_init (PolygonViewClass *klass)
{
GObjectClass *object_class = (GObjectClass *) klass;
GtkWidgetClass *widget_class = (GtkWidgetClass *) klass;
object_class->finalize = polygon_view_finalize;
widget_class->realize = polygon_view_realize;
widget_class->size_allocate = polygon_view_size_allocate;
widget_class->expose_event = polygon_view_expose;
widget_class->key_press_event = polygon_view_key_press;
widget_class->button_press_event = polygon_view_button_press;
widget_class->button_release_event = polygon_view_button_release;
widget_class->motion_notify_event = polygon_view_motion;
}
static void
polygon_view_init (PolygonView *self)
{
self->mag_zoom = 64;
self->mag_size = 200;
self->extents.p1.x = G_MAXDOUBLE;
self->extents.p1.y = G_MAXDOUBLE;
self->extents.p2.x = -G_MAXDOUBLE;
self->extents.p2.y = -G_MAXDOUBLE;
GTK_WIDGET_SET_FLAGS (self, GTK_CAN_FOCUS);
}
static polygon_t *
_polygon_add_edge (PolygonView *view, polygon_t *polygon,
point_t *p1, point_t *p2,
gdouble top, gdouble bot, int dir)
{
if (polygon == NULL)
return NULL;
if (top < view->extents.p1.y)
view->extents.p1.y = top;
if (bot > view->extents.p2.y)
view->extents.p2.y = bot;
if (p1->x < view->extents.p1.x)
view->extents.p1.x = p1->x;
if (p1->x > view->extents.p2.x)
view->extents.p2.x = p1->x;
if (p2->x < view->extents.p1.x)
view->extents.p1.x = p2->x;
if (p2->x > view->extents.p2.x)
view->extents.p2.x = p2->x;
if (polygon->num_edges == polygon->size) {
int newsize = 2 * polygon->size;
void *newpolygon;
newpolygon = g_realloc (polygon,
sizeof (polygon_t) + newsize * sizeof (edge_t));
if (newpolygon == NULL)
return polygon;
polygon = newpolygon;
polygon->size = newsize;
if (polygon->next != NULL)
polygon->next->prev = newpolygon;
if (polygon->prev != NULL)
polygon->prev->next = newpolygon;
else
view->polygons = newpolygon;
}
polygon->edges[polygon->num_edges].p1 = *p1;
polygon->edges[polygon->num_edges].p2 = *p2;
polygon->edges[polygon->num_edges].top = top;
polygon->edges[polygon->num_edges].bot = bot;
polygon->edges[polygon->num_edges].dir = dir;
polygon->num_edges++;
return polygon;
}
static polygon_t *
polygon_new (PolygonView *view)
{
polygon_t *t;
t = g_malloc (sizeof (polygon_t) + 128 * sizeof (edge_t));
t->prev = NULL;
t->next = view->polygons;
if (view->polygons)
view->polygons->prev = t;
view->polygons = t;
t->size = 128;
t->num_edges = 0;
return t;
}
int
main (int argc, char **argv)
{
PolygonView *view;
polygon_t *polygon = NULL;
GtkWidget *window;
FILE *file;
char *line = NULL;
size_t len = 0;
gtk_init (&argc, &argv);
view = g_object_new (polygon_view_get_type (), NULL);
file = fopen (argv[1], "r");
if (file != NULL) {
while (getline (&line, &len, file) != -1) {
point_t p1, p2;
double top, bottom;
int dir;
if (strncmp (line, "polygon: ", sizeof("polygon: ")-1) == 0) {
if (polygon && polygon->num_edges) {
g_print ("read polygon with %d edges\n", polygon->num_edges);
polygon = polygon_new (view);
} else if (polygon == NULL)
polygon = polygon_new (view);
} else if (sscanf (line, " [%*d] = [(%lf, %lf), (%lf, %lf)], top=%lf, bottom=%lf, dir=%d", &p1.x, &p1.y, &p2.x, &p2.y, &top, &bottom, &dir) == 7) {
polygon = _polygon_add_edge (view, polygon, &p1, &p2,
top, bottom, dir);
}
}
if (polygon && polygon->num_edges)
g_print ("read polygon with %d edges\n", polygon->num_edges);
g_print ("extents=(%lg, %lg), (%lg, %lg)\n",
view->extents.p1.x, view->extents.p1.y,
view->extents.p2.x, view->extents.p2.y);
fclose (file);
}
if (argc > 2)
highlight = atof (argv[2]);
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
g_signal_connect (window, "delete-event",
G_CALLBACK (gtk_main_quit), NULL);
gtk_widget_set_size_request (window, 800, 800);
gtk_container_add (GTK_CONTAINER (window), &view->widget);
gtk_widget_show_all (window);
gtk_main ();
return 0;
}