mirror of
https://gitlab.freedesktop.org/cairo/cairo.git
synced 2025-12-20 09:20:06 +01:00
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. :)
602 lines
15 KiB
C
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;
|
|
}
|