[PATCH 5/6] clients: image: use cairo for getting image

Tiago Vignatti vignatti at freedesktop.org
Fri Dec 16 07:59:41 PST 2011


From: Tiago Vignatti <tiago.vignatti at intel.com>

It caches the image using load_jpeg at initialization and the draw handler
does the repaint with the correct size. I set nearest-neighbor filtering on
cairo which gives a better effect when resizing.

Images now are scaled-up correctly without ugly black background.

Signed-off-by: Tiago Vignatti <tiago.vignatti at intel.com>
---
 clients/image.c |  138 +++++++++++++++----------------------------------------
 1 files changed, 37 insertions(+), 101 deletions(-)

diff --git a/clients/image.c b/clients/image.c
index b12a360..a1920db 100644
--- a/clients/image.c
+++ b/clients/image.c
@@ -38,121 +38,41 @@
 #include <wayland-client.h>
 
 #include "window.h"
+#include "cairo-util.h"
 
 struct image {
 	struct window *window;
 	struct display *display;
 	uint32_t key;
 	gchar *filename;
+	cairo_surface_t *c_image;
 };
 
 static void
-set_source_pixbuf(cairo_t         *cr,
-		  const GdkPixbuf *pixbuf,
-		  double           src_x,
-		  double           src_y,
-		  double           src_width,
-		  double           src_height)
+image_load(struct image *image)
 {
-	gint width = gdk_pixbuf_get_width (pixbuf);
-	gint height = gdk_pixbuf_get_height (pixbuf);
-	guchar *gdk_pixels = gdk_pixbuf_get_pixels (pixbuf);
-	int gdk_rowstride = gdk_pixbuf_get_rowstride (pixbuf);
-	int n_channels = gdk_pixbuf_get_n_channels (pixbuf);
-	int cairo_stride;
-	guchar *cairo_pixels;
-	cairo_format_t format;
-	cairo_surface_t *surface;
-	int j;
-
-	if (n_channels == 3)
-		format = CAIRO_FORMAT_RGB24;
+	if (image->filename)
+		image->c_image = load_jpeg(image->filename);
 	else
-		format = CAIRO_FORMAT_ARGB32;
-
-	surface = cairo_image_surface_create(format, width, height);
-	if (cairo_surface_status(surface)) {
-		cairo_set_source_surface(cr, surface, 0, 0);
-		return;
-	}
-
-	cairo_stride = cairo_image_surface_get_stride(surface);
-	cairo_pixels = cairo_image_surface_get_data(surface);
-
-	for (j = height; j; j--) {
-		guchar *p = gdk_pixels;
-		guchar *q = cairo_pixels;
-
-		if (n_channels == 3) {
-			guchar *end = p + 3 * width;
-
-			while (p < end) {
-#if G_BYTE_ORDER == G_LITTLE_ENDIAN
-				q[0] = p[2];
-				q[1] = p[1];
-				q[2] = p[0];
-#else
-				q[1] = p[0];
-				q[2] = p[1];
-				q[3] = p[2];
-#endif
-				p += 3;
-				q += 4;
-			}
-		} else {
-			guchar *end = p + 4 * width;
-			guint t1,t2,t3;
-
-#define MULT(d,c,a,t) G_STMT_START { t = c * a + 0x7f; d = ((t >> 8) + t) >> 8; } G_STMT_END
-
-			while (p < end) {
-#if G_BYTE_ORDER == G_LITTLE_ENDIAN
-				MULT(q[0], p[2], p[3], t1);
-				MULT(q[1], p[1], p[3], t2);
-				MULT(q[2], p[0], p[3], t3);
-				q[3] = p[3];
-#else
-				q[0] = p[3];
-				MULT(q[1], p[0], p[3], t1);
-				MULT(q[2], p[1], p[3], t2);
-				MULT(q[3], p[2], p[3], t3);
-#endif
-
-				p += 4;
-				q += 4;
-			}
-#undef MULT
-		}
-
-		gdk_pixels += gdk_rowstride;
-		cairo_pixels += cairo_stride;
-	}
-	cairo_surface_mark_dirty(surface);
-
-	cairo_set_source_surface(cr, surface,
-				 src_x + .5 * (src_width - width),
-				 src_y + .5 * (src_height - height));
-	cairo_surface_destroy(surface);
+		fprintf(stderr, "failed loading image\n");
 }
 
 static void
 image_draw(struct image *image)
 {
 	struct rectangle allocation;
-	GdkPixbuf *pb;
 	cairo_t *cr;
 	cairo_surface_t *surface;
+	cairo_pattern_t *pattern;
+	cairo_matrix_t matrix;
+	double sx, sy;
+	int width, height;
 
 	window_draw(image->window);
 
 	window_get_child_allocation(image->window, &allocation);
-
-	pb = gdk_pixbuf_new_from_file_at_size(image->filename,
-					      allocation.width,
-					      allocation.height,
-					      NULL);
-	if (pb == NULL)
-		return;
+	width = allocation.width;
+	height = allocation.height;
 
 	surface = window_get_surface(image->window);
 	cr = cairo_create(surface);
@@ -164,17 +84,23 @@ image_draw(struct image *image)
 	cairo_translate(cr, allocation.x, allocation.y);
 
 	cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
-	cairo_set_source_rgba(cr, 0, 0, 0, 1);
+	cairo_set_source_rgba(cr, 0.0, 0.0, 0.2, 1.0);
 	cairo_paint(cr);
-	set_source_pixbuf(cr, pb,
-			  0, 0,
-			  allocation.width, allocation.height);
+
+	pattern = cairo_pattern_create_for_surface(image->c_image);
+	sx = (double) cairo_image_surface_get_width(image->c_image) / width;
+	sy = (double) cairo_image_surface_get_height(image->c_image) / height;
+	cairo_matrix_init_scale(&matrix, sx, sy);
+	cairo_pattern_set_filter(pattern, CAIRO_FILTER_NEAREST);
+	cairo_pattern_set_matrix(pattern, &matrix);
+	cairo_set_source(cr, pattern);
+	cairo_pattern_destroy (pattern);
+
 	cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
 	cairo_paint(cr);
 
-	g_object_unref(pb);
-
 	cairo_pop_group_to_source(cr);
+
 	cairo_paint(cr);
 	cairo_destroy(cr);
 
@@ -229,12 +155,20 @@ image_create(struct display *display, uint32_t key, const char *filename)
 	window_set_redraw_handler(image->window, redraw_handler);
 	window_set_keyboard_focus_handler(image->window,
 					  keyboard_focus_handler);
-
+	image_load(image);
 	image_draw(image);
 
 	return image;
 }
 
+static void
+image_destroy(struct image *image)
+{
+	if (image->c_image)
+		cairo_surface_destroy(image->c_image);
+	free(image);
+}
+
 static int
 filter(const struct dirent *entry)
 {
@@ -285,6 +219,7 @@ int
 main(int argc, char *argv[])
 {
 	struct display *d;
+	struct image *image;
 	int i;
 
 	d = display_create(&argc, &argv, option_entries);
@@ -295,12 +230,13 @@ main(int argc, char *argv[])
 
 	/* if not enough args, pick a random image from the system */
 	if (argc == 1)
-		image_create (d, 1, get_image_random());
+		image = image_create (d, 1, get_image_random());
 	else
 		for (i = 1; i < argc; i++)
-			image_create (d, i, argv[i]);
+			image = image_create (d, i, argv[i]);
 
 	display_run(d);
 
+	image_destroy(image);
 	return 0;
 }
-- 
1.7.5.4



More information about the wayland-devel mailing list