[gnome-desktop/randr-transformations: 1/2] RANDR - Add API for projective transformations



commit 26e42397e5d16b15b82e96595e4d70d972b37b6b
Author: David Reveman <dreveman novell com>
Date:   Tue Jan 26 15:10:20 2010 -0600

    RANDR - Add API for projective transformations
    
    Signed-off-by: Federico Mena Quintero <federico novell com>

 libgnome-desktop/gnome-rr-config.c            |  148 ++++++++++-----
 libgnome-desktop/gnome-rr.c                   |  251 ++++++++++++++++++++++++-
 libgnome-desktop/libgnomeui/gnome-rr-config.h |    1 +
 libgnome-desktop/libgnomeui/gnome-rr.h        |   19 ++
 4 files changed, 363 insertions(+), 56 deletions(-)
---
diff --git a/libgnome-desktop/gnome-rr-config.c b/libgnome-desktop/gnome-rr-config.c
index 6a8f8ae..19705bf 100644
--- a/libgnome-desktop/gnome-rr-config.c
+++ b/libgnome-desktop/gnome-rr-config.c
@@ -98,6 +98,19 @@ struct Parser
     GQueue *		stack;
 };
 
+static void
+init_transform (GnomeRRTransform *transform)
+{
+    int i, j;
+
+    for (i = 0; i < 3; i++)
+	for (j = 0; j < 3; j++)
+	    if (i == j)
+		transform->transform[i][j] = 1.0;
+	    else
+		transform->transform[i][j] = 0.0;
+}
+
 static int
 parse_int (const char *text)
 {
@@ -168,6 +181,7 @@ handle_start_element (GMarkupParseContext *context,
 
 	parser->output = g_new0 (GnomeOutputInfo, 1);
 	parser->output->rotation = 0;
+	init_transform (&parser->output->transform);
 	
 	for (i = 0; attr_names[i] != NULL; ++i)
 	{
@@ -352,6 +366,30 @@ handle_text (GMarkupParseContext *context,
 	    parser->output->primary = TRUE;
 	}
     }
+    else if (stack_is (parser, "transform", "output", "configuration", TOPLEVEL_ELEMENT, NULL))
+    {
+	float transform[3][3];
+
+	if (sscanf (text,
+		    "[%f,%f,%f,%f,%f,%f,%f,%f,%f]",
+		    &transform[0][0],
+		    &transform[0][1],
+		    &transform[0][2],
+		    &transform[1][0],
+		    &transform[1][1],
+		    &transform[1][2],
+		    &transform[2][0],
+		    &transform[2][1],
+		    &transform[2][2]) == 9)
+	{
+	    int i, j;
+
+	    for (i = 0; i < 3; i++)
+		for (j = 0; j < 3; j++)
+		    parser->output->transform.transform[i][j] =
+			transform[i][j];
+	}
+    }
     else
     {
 	/* Ignore other properties so we can expand the format in the future */
@@ -472,6 +510,7 @@ gnome_rr_config_new_current (GnomeRRScreen *screen)
 	    output->height = -1;
 	    output->rate = -1;
 	    output->rotation = GNOME_RR_ROTATION_0;
+	    init_transform (&output->transform);
 	}
 	else
 	{
@@ -513,6 +552,7 @@ gnome_rr_config_new_current (GnomeRRScreen *screen)
 		output->height = gnome_rr_mode_get_height (mode);
 		output->rate = gnome_rr_mode_get_freq (mode);
 		output->rotation = gnome_rr_crtc_get_current_rotation (crtc);
+		gnome_rr_crtc_get_current_transform (crtc, &output->transform);
 
 		if (output->x == 0 && output->y == 0) {
 			if (clone_width == -1) {
@@ -762,6 +802,10 @@ output_equal (GnomeOutputInfo *output1, GnomeOutputInfo *output2)
 	
 	if (output1->rotation != output2->rotation)
 	    return FALSE;
+
+	if (memcmp (&output1->transform, &output2->transform,
+		    sizeof (GnomeRRTransform)) != 0)
+	    return FALSE;
     }
 
     return TRUE;
@@ -852,6 +896,7 @@ make_outputs (GnomeRRConfig *config)
 	    new->width = first_on->width;
 	    new->height = first_on->height;
 	    new->rotation = first_on->rotation;
+	    new->transform = first_on->transform;
 	    new->x = 0;
 	    new->y = 0;
 	}
@@ -997,6 +1042,11 @@ emit_configuration (GnomeRRConfig *config,
 		string, "          <reflect_y>%s</reflect_y>\n", get_reflect_y (output->rotation));
             g_string_append_printf (
                 string, "          <primary>%s</primary>\n", yes_no (output->primary));
+	    g_string_append_printf (
+		string, "          <transform>[%f,%f,%f,%f,%f,%f,%f,%f,%f]</transform>\n",
+		output->transform.transform[0][0], output->transform.transform[0][1], output->transform.transform[0][2],
+		output->transform.transform[1][0], output->transform.transform[1][1], output->transform.transform[1][2],
+		output->transform.transform[2][0], output->transform.transform[2][1], output->transform.transform[2][2]);
 	}
 	
 	g_string_append_printf (string, "      </output>\n");
@@ -1369,6 +1419,7 @@ struct CrtcInfo
     int        x;
     int        y;
     GnomeRRRotation rotation;
+    GnomeRRTransform transform;
     GPtrArray *outputs;
 };
 
@@ -1404,13 +1455,15 @@ crtc_assignment_assign (CrtcAssignment   *assign,
 			int               y,
 			GnomeRRRotation   rotation,
                         gboolean          primary,
+			GnomeRRTransform *transform,
 			GnomeRROutput    *output)
 {
     CrtcInfo *info = g_hash_table_lookup (assign->info, crtc);
 
     if (!gnome_rr_crtc_can_drive_output (crtc, output) ||
 	!gnome_rr_output_supports_mode (output, mode)  ||
-	!gnome_rr_crtc_supports_rotation (crtc, rotation))
+	!gnome_rr_crtc_supports_rotation (crtc, rotation) ||
+	!gnome_rr_crtc_supports_transform (crtc, transform))
     {
 	return FALSE;
     }
@@ -1421,6 +1474,9 @@ crtc_assignment_assign (CrtcAssignment   *assign,
 	    info->x == x		&&
 	    info->y == y		&&
 	    info->rotation == rotation  &&
+	    memcmp (&info->transform,
+		    transform,
+		    sizeof (GnomeRRTransform)) == 0 &&
 	    can_clone (info, output))
 	{
 	    g_ptr_array_add (info->outputs, output);
@@ -1441,6 +1497,7 @@ crtc_assignment_assign (CrtcAssignment   *assign,
 	info->x = x;
 	info->y = y;
 	info->rotation = rotation;
+	info->transform = *transform;
 	info->outputs = g_ptr_array_new ();
 	
 	g_ptr_array_add (info->outputs, output);
@@ -1505,6 +1562,15 @@ configure_crtc (gpointer key,
     if (state->has_error)
 	return;
 
+    if (gnome_rr_crtc_supports_transform (crtc, &info->transform))
+    {
+	if (!gnome_rr_crtc_set_transform (crtc, &info->transform))
+	{
+	    state->has_error = TRUE;
+	    return;
+	}
+    }
+
     if (!gnome_rr_crtc_set_config_with_time (crtc,
 					     state->timestamp,
 					     info->x, info->y,
@@ -1516,31 +1582,6 @@ configure_crtc (gpointer key,
 	state->has_error = TRUE;
 }
 
-static gboolean
-mode_is_rotated (CrtcInfo *info)
-{
-    if ((info->rotation & GNOME_RR_ROTATION_270)		||
-	(info->rotation & GNOME_RR_ROTATION_90))
-    {
-	return TRUE;
-    }
-    return FALSE;
-}
-
-static gboolean
-crtc_is_rotated (GnomeRRCrtc *crtc)
-{
-    GnomeRRRotation r = gnome_rr_crtc_get_current_rotation (crtc);
-
-    if ((r & GNOME_RR_ROTATION_270)		||
-	(r & GNOME_RR_ROTATION_90))
-    {
-	return TRUE;
-    }
-
-    return FALSE;
-}
-
 /* Check whether the given set of settings can be used
  * at the same time -- ie. whether there is an assignment
  * of CRTC's to outputs.
@@ -1595,6 +1636,7 @@ real_assign_crtcs (GnomeRRScreen *screen,
 			    output->x, output->y,
 			    output->rotation,
                             output->primary,
+			    &output->transform,
 			    gnome_rr_output))
 		    {
 			if (real_assign_crtcs (screen, outputs + 1, assignment))
@@ -1635,20 +1677,21 @@ get_required_virtual_size (CrtcAssignment *assign, int *width, int *height)
     {
 	GnomeRRCrtc *crtc = list->data;
 	CrtcInfo *info = g_hash_table_lookup (assign->info, crtc);
-	int w, h;
+	int x, y, w, h;
+	int x1, y1, x2, y2;
 
-	w = gnome_rr_mode_get_width (info->mode);
-	h = gnome_rr_mode_get_height (info->mode);
-	
-	if (mode_is_rotated (info))
-	{
-	    int tmp = h;
-	    h = w;
-	    w = tmp;
-	}
-	
-	*width = MAX (*width, info->x + w);
-	*height = MAX (*height, info->y + h);
+	gnome_rr_mode_get_geometry (info->mode,
+				    info->rotation,
+				    &info->transform,
+				    &x1, &y1, &x2, &y2);
+
+	x = info->x + x1;
+	y = info->y + y1;
+	w = x2 - x1;
+	h = y2 - y1;
+
+	*width = MAX (*width, x + w);
+	*height = MAX (*height, y + h);
     }
 
     g_list_free (active_crtcs);
@@ -1745,23 +1788,28 @@ crtc_assignment_apply (CrtcAssignment *assign, guint32 timestamp, GError **error
     {
 	GnomeRRCrtc *crtc = all_crtcs[i];
 	GnomeRRMode *mode = gnome_rr_crtc_get_current_mode (crtc);
-	int x, y;
+	GnomeRRRotation rotation = gnome_rr_crtc_get_current_rotation (crtc);
+	GnomeRRTransform transform;
+
+	gnome_rr_crtc_get_current_transform (crtc, &transform);
 
 	if (mode)
 	{
-	    int w, h;
+	    int x, y, w, h;
+	    int x1, y1, x2, y2;
+
+	    gnome_rr_mode_get_geometry (mode,
+					rotation,
+					&transform,
+					&x1, &y1, &x2, &y2);
+
 	    gnome_rr_crtc_get_position (crtc, &x, &y);
 
-	    w = gnome_rr_mode_get_width (mode);
-	    h = gnome_rr_mode_get_height (mode);
+	    x += x1;
+	    y += y1;
+	    w = x2 - x1;
+	    h = y2 - y1;
 
-	    if (crtc_is_rotated (crtc))
-	    {
-		int tmp = h;
-		h = w;
-		w = tmp;
-	    }
-	    
 	    if (x + w > width || y + h > height || !g_hash_table_lookup (assign->info, crtc))
 	    {
 		if (!gnome_rr_crtc_set_config_with_time (crtc, timestamp, 0, 0, NULL, GNOME_RR_ROTATION_0, NULL, 0, error))
diff --git a/libgnome-desktop/gnome-rr.c b/libgnome-desktop/gnome-rr.c
index 6f23fbb..a56c02a 100644
--- a/libgnome-desktop/gnome-rr.c
+++ b/libgnome-desktop/gnome-rr.c
@@ -28,7 +28,9 @@
 #include <glib/gi18n-lib.h>
 #include "libgnomeui/gnome-rr.h"
 #include <string.h>
+#include <math.h>
 #include <X11/Xlib.h>
+#include <X11/extensions/Xrender.h>
 #include <X11/extensions/Xrandr.h>
 #include <gtk/gtk.h>
 #include <gdk/gdkx.h>
@@ -71,10 +73,11 @@ struct GnomeRRCrtc
     GnomeRROutput **	possible_outputs;
     int			x;
     int			y;
-    
     GnomeRRRotation	current_rotation;
     GnomeRRRotation	rotations;
     int			gamma_size;
+    GnomeRRTransform	current_transform;
+    GnomeRRTransform	pending_transform;
 };
 
 struct GnomeRRMode
@@ -911,7 +914,7 @@ output_initialize (GnomeRROutput *output, XRRScreenResources *res, GError **erro
 	DISPLAY (output), res, output->id);
     GPtrArray *a;
     int i;
-    
+
 #if 0
     g_print ("Output %lx Timestamp: %u\n", output->id, (guint32)info->timestamp);
 #endif
@@ -973,7 +976,7 @@ output_initialize (GnomeRROutput *output, XRRScreenResources *res, GError **erro
     
     /* Edid data */
     output->edid_data = read_edid_data (output);
-    
+
     XRRFreeOutputInfo (info);
 
     return TRUE;
@@ -1276,7 +1279,31 @@ gnome_rr_crtc_set_config_with_time (GnomeRRCrtc      *crtc,
 	for (i = 0; i < n_outputs; ++i)
 	    g_array_append_val (output_ids, outputs[i]->id);
     }
-    
+
+#if RANDR_MAJOR > 1 || RANDR_MINOR >= 3
+    if (memcmp (&crtc->current_transform,
+		&crtc->pending_transform,
+		sizeof (GnomeRRTransform)) != 0)
+    {
+	XTransform xtransform;
+	int        j;
+
+	for (i = 0; i < 3; i++)
+	    for (j = 0; j < 3; j++)
+		xtransform.matrix[i][j] =
+		    XDoubleToFixed (crtc->pending_transform.transform[i][j]);
+
+        gdk_error_trap_push ();
+	XRRSetCrtcTransform (DISPLAY (crtc), crtc->id,
+			     &xtransform,
+			     "bilinear",
+			     NULL,
+			     0);
+        XSync (DISPLAY (crtc), FALSE);
+        gdk_error_trap_pop ();
+    }
+#endif
+
     status = XRRSetCrtcConfig (DISPLAY (crtc), info->resources, crtc->id,
 			       timestamp, 
 			       x, y,
@@ -1393,7 +1420,11 @@ crtc_initialize (GnomeRRCrtc        *crtc,
     XRRCrtcInfo *info = XRRGetCrtcInfo (DISPLAY (crtc), res, crtc->id);
     GPtrArray *a;
     int i;
-    
+
+#if RANDR_MAJOR > 1 || RANDR_MINOR >= 3
+    XRRCrtcTransformAttributes *attr;
+#endif
+
 #if 0
     g_print ("CRTC %lx Timestamp: %u\n", crtc->id, (guint32)info->timestamp);
 #endif
@@ -1445,7 +1476,34 @@ crtc_initialize (GnomeRRCrtc        *crtc,
     /* Rotations */
     crtc->current_rotation = gnome_rr_rotation_from_xrotation (info->rotation);
     crtc->rotations = gnome_rr_rotation_from_xrotation (info->rotations);
-    
+
+#if RANDR_MAJOR > 1 || RANDR_MINOR >= 3
+    if (XRRGetCrtcTransform (DISPLAY (crtc), crtc->id, &attr) && attr) {
+	int j;
+
+	for (i = 0; i < 3; i++)
+	    for (j = 0; j < 3; j++)
+		crtc->current_transform.transform[i][j] =
+		    XFixedToDouble (attr->currentTransform.matrix[i][j]);
+
+	XFree (attr);
+    }
+    else
+#endif
+    {
+	crtc->current_transform.transform[0][0] = 1.0;
+	crtc->current_transform.transform[0][1] = 0.0;
+	crtc->current_transform.transform[0][2] = 0.0;
+	crtc->current_transform.transform[1][0] = 0.0;
+	crtc->current_transform.transform[1][1] = 1.0;
+	crtc->current_transform.transform[1][2] = 0.0;
+	crtc->current_transform.transform[2][0] = 0.0;
+	crtc->current_transform.transform[2][1] = 0.0;
+	crtc->current_transform.transform[2][2] = 1.0;
+    }
+
+    crtc->pending_transform = crtc->current_transform;
+
     XRRFreeCrtcInfo (info);
 
     /* get an store gamma size */
@@ -1592,3 +1650,184 @@ gnome_rr_crtc_get_gamma (GnomeRRCrtc *crtc, int *size,
     return TRUE;
 }
 
+typedef struct {
+    int	    x, y, width, height;
+} rectangle_t;
+
+typedef struct {
+    int	    x1, y1, x2, y2;
+} box_t;
+
+typedef struct {
+    int	    x, y;
+} point_t;
+
+static gboolean
+transform_point (GnomeRRTransform *transform, double *xp, double *yp)
+{
+    double  vector[3];
+    double  result[3];
+    int	    i, j;
+    double  v;
+
+    vector[0] = *xp;
+    vector[1] = *yp;
+    vector[2] = 1;
+    for (j = 0; j < 3; j++)
+    {
+	v = 0;
+	for (i = 0; i < 3; i++)
+	    v += (transform->transform[j][i] * vector[i]);
+	if (v > 32767 || v < -32767)
+	    return FALSE;
+	result[j] = v;
+    }
+    if (!result[2])
+	return FALSE;
+    for (j = 0; j < 2; j++)
+	vector[j] = result[j] / result[2];
+    *xp = vector[0];
+    *yp = vector[1];
+    return TRUE;
+}
+
+static void
+path_bounds (GnomeRRTransform *transform, point_t *points, int npoints, box_t *box)
+{
+    int	    i;
+    box_t   point;
+
+    for (i = 0; i < npoints; i++) {
+	double	x, y;
+	x = points[i].x;
+	y = points[i].y;
+	transform_point (transform, &x, &y);
+	point.x1 = floor (x);
+	point.y1 = floor (y);
+	point.x2 = ceil (x);
+	point.y2 = ceil (y);
+	if (i == 0)
+	    *box = point;
+	else {
+	    if (point.x1 < box->x1) box->x1 = point.x1;
+	    if (point.y1 < box->y1) box->y1 = point.y1;
+	    if (point.x2 > box->x2) box->x2 = point.x2;
+	    if (point.y2 > box->y2) box->y2 = point.y2;
+	}
+    }
+}
+
+static int
+mode_height (GnomeRRMode *mode, GnomeRRRotation rotation)
+{
+    switch (rotation & 0xf) {
+    case GNOME_RR_ROTATION_0:
+    case GNOME_RR_ROTATION_180:
+	return gnome_rr_mode_get_height (mode);
+    case GNOME_RR_ROTATION_90:
+    case GNOME_RR_ROTATION_270:
+	return gnome_rr_mode_get_width (mode);
+    default:
+	return 0;
+    }
+}
+
+static int
+mode_width (GnomeRRMode *mode, GnomeRRRotation rotation)
+{
+    switch (rotation & 0xf) {
+    case GNOME_RR_ROTATION_0:
+    case GNOME_RR_ROTATION_180:
+	return gnome_rr_mode_get_width (mode);
+    case GNOME_RR_ROTATION_90:
+    case GNOME_RR_ROTATION_270:
+	return gnome_rr_mode_get_height (mode);
+    default:
+	return 0;
+    }
+}
+
+static void
+mode_get_geometry (GnomeRRMode *mode,
+		   GnomeRRRotation rotation,
+		   GnomeRRTransform *transform,
+		   box_t *bounds)
+{
+    point_t rect[4];
+    int	width = mode_width (mode, rotation);
+    int height = mode_height (mode, rotation);
+
+    rect[0].x = 0;
+    rect[0].y = 0;
+    rect[1].x = width;
+    rect[1].y = 0;
+    rect[2].x = width;
+    rect[2].y = height;
+    rect[3].x = 0;
+    rect[3].y = height;
+
+    path_bounds (transform, rect, 4, bounds);
+}
+
+void
+gnome_rr_mode_get_geometry (GnomeRRMode           *mode,
+			    GnomeRRRotation        rotation,
+			    GnomeRRTransform      *transform,
+			    int                   *x1,
+			    int                   *y1,
+			    int                   *x2,
+			    int                   *y2)
+{
+    box_t bounds = { 0 };
+
+    mode_get_geometry (mode, rotation, transform, &bounds);
+
+    *x1 = bounds.x1;
+    *y1 = bounds.y1;
+    *x2 = bounds.x2;
+    *y2 = bounds.y2;
+}
+
+void
+gnome_rr_crtc_get_current_transform (GnomeRRCrtc           *crtc,
+				     GnomeRRTransform      *transform)
+{
+    *transform = crtc->current_transform;
+}
+
+gboolean
+gnome_rr_crtc_supports_transform (GnomeRRCrtc           *crtc,
+				  GnomeRRTransform      *transform)
+{
+    const static GnomeRRTransform identity = {
+	{
+	    { 1.0, 0.0, 0.0 },
+	    { 0.0, 1.0, 0.0 },
+	    { 0.0, 0.0, 1.0 }
+	}
+    };
+
+    if (memcmp (transform, &identity, sizeof (identity)) == 0)
+	return TRUE;
+
+#if RANDR_MAJOR > 1 || RANDR_MINOR >= 3
+    int	major, minor;
+
+    XRRQueryVersion (DISPLAY (crtc), &major, &minor);
+    if (major > 1 || (major == 1 && minor >= 3))
+	return TRUE;
+#endif
+
+    return FALSE;
+}
+
+gboolean
+gnome_rr_crtc_set_transform (GnomeRRCrtc           *crtc,
+			     GnomeRRTransform      *transform)
+{
+    if (!gnome_rr_crtc_supports_transform (crtc, transform))
+	return FALSE;
+
+    crtc->pending_transform = *transform;
+    return TRUE;
+}
diff --git a/libgnome-desktop/libgnomeui/gnome-rr-config.h b/libgnome-desktop/libgnomeui/gnome-rr-config.h
index d55a38c..1a402d1 100644
--- a/libgnome-desktop/libgnomeui/gnome-rr-config.h
+++ b/libgnome-desktop/libgnomeui/gnome-rr-config.h
@@ -52,6 +52,7 @@ struct GnomeOutputInfo
     int			x;
     int			y;
     GnomeRRRotation	rotation;
+    GnomeRRTransform    transform;
 
     gboolean		connected; /* whether the output is physically connected to a monitor */
     char		vendor[4];
diff --git a/libgnome-desktop/libgnomeui/gnome-rr.h b/libgnome-desktop/libgnomeui/gnome-rr.h
index c43bd8f..122c3ac 100644
--- a/libgnome-desktop/libgnomeui/gnome-rr.h
+++ b/libgnome-desktop/libgnomeui/gnome-rr.h
@@ -48,6 +48,11 @@ typedef enum
     GNOME_RR_REFLECT_Y =	(1 << 5)
 } GnomeRRRotation;
 
+typedef struct
+{
+    double transform[3][3];
+} GnomeRRTransform;
+
 /* Error codes */
 
 #define GNOME_RR_ERROR (gnome_rr_error_quark ())
@@ -125,6 +130,14 @@ guint32         gnome_rr_mode_get_id               (GnomeRRMode           *mode)
 guint           gnome_rr_mode_get_width            (GnomeRRMode           *mode);
 guint           gnome_rr_mode_get_height           (GnomeRRMode           *mode);
 int             gnome_rr_mode_get_freq             (GnomeRRMode           *mode);
+void            gnome_rr_mode_get_geometry         (GnomeRRMode           *mode,
+						    GnomeRRRotation        rotation,
+						    GnomeRRTransform      *transform,
+						    int                   *x1,
+						    int                   *y1,
+						    int                   *x2,
+						    int                   *y2);
+
 
 /* GnomeRRCrtc */
 guint32         gnome_rr_crtc_get_id               (GnomeRRCrtc           *crtc);
@@ -159,6 +172,12 @@ GnomeRRRotation gnome_rr_crtc_get_current_rotation (GnomeRRCrtc           *crtc)
 GnomeRRRotation gnome_rr_crtc_get_rotations        (GnomeRRCrtc           *crtc);
 gboolean        gnome_rr_crtc_supports_rotation    (GnomeRRCrtc           *crtc,
 						    GnomeRRRotation        rotation);
+void            gnome_rr_crtc_get_current_transform (GnomeRRCrtc           *crtc,
+						     GnomeRRTransform      *transform);
+gboolean        gnome_rr_crtc_supports_transform   (GnomeRRCrtc           *crtc,
+						    GnomeRRTransform      *transform);
+gboolean        gnome_rr_crtc_set_transform        (GnomeRRCrtc           *crtc,
+						    GnomeRRTransform      *transform);
 
 gboolean        gnome_rr_crtc_get_gamma            (GnomeRRCrtc           *crtc,
 						    int                   *size,



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]