Re: drawing text and GdkPixbuf



Darin Adler wrote:
> 
> Here's the situation. I have a GdkPixbuf. I want to draw some text and
> overlay it on top of the existing stuff in the pixbuf within a particular
> rectangle. How can I do that? Can I create a GdkBitmap and draw into that
> and then composite that into the GdkPixbuf somehow? Is this whole thing just
> a misguided X novice problem?
> 
> Do I have to suck fonts out of the X server like the GnomeCanvasText canvas
> item does?
> 
>     -- Darin
> 

I wrote a hack to do this for a paint program i wrote a long time ago. 
Attatched is a patch for nautilus-gdk-pixbuf-extensions.[ch] that adds a
function to draw text into a gdk_pixbuf.

It works by drawing the text into a pixmap, capturing that information,
matching a given color to what gdk_rgb expects, and coping the bits that
match into the pixbuf.

It would be nice to get rid of gdk_rgb hacks in the patch above.  Try it
and if it does what you want we can simplify it. 

Also, it can be faster by caching a lot of stuff.  Right now gcs are
allocated everytime you draw the text.  This is also to fix once (if!)
the patch actually works for you.

-re
Index: nautilus-gdk-pixbuf-extensions.c
===================================================================
RCS file: /cvs/gnome/nautilus/libnautilus-extensions/nautilus-gdk-pixbuf-extensions.c,v
retrieving revision 1.3
diff -u -r1.3 nautilus-gdk-pixbuf-extensions.c
--- nautilus-gdk-pixbuf-extensions.c	2000/04/13 18:24:19	1.3
+++ nautilus-gdk-pixbuf-extensions.c	2000/05/03 03:16:23
@@ -315,3 +315,325 @@
 		}
 	}
 }
+
+/* Access the individual RGB components */
+#define NAUTILUS_COLOR_GET_R(_rgba) (((_rgba) >> 16) & 0xff)
+#define NAUTILUS_COLOR_GET_G(_rgba) (((_rgba) >> 8) & 0xff)
+#define NAUTILUS_COLOR_GET_B(_rgba) (((_rgba) >> 0) & 0xff)
+#define NAUTILUS_COLOR_GET_A(_rgba) (((_rgba) >> 24) & 0xff)
+
+#define NAUTILUS_COLOR_EQUAL_RGB(_a, _b)			\
+( (NAUTILUS_COLOR_GET_R (_a) == NAUTILUS_COLOR_GET_R (_b)) &&	\
+  (NAUTILUS_COLOR_GET_G (_a) == NAUTILUS_COLOR_GET_G (_b)) &&	\
+  (NAUTILUS_COLOR_GET_B (_a) == NAUTILUS_COLOR_GET_B (_b)))
+
+/* Pack RGB values into a 32 bit word.  Assume A=255 */
+#define NAUTILUS_COLOR_PACK_RGB(_r, _g, _b)	\
+(  (255 << 24) |				\
+  ((_r) << 16) |				\
+  ((_g) <<  8) |				\
+  ((_b) <<  0) )
+
+#define PIXBUF_POKE_RGB(_offset, _color)		\
+G_STMT_START {						\
+*((_offset) + 0) = NAUTILUS_COLOR_GET_R (_color);	\
+*((_offset) + 1) = NAUTILUS_COLOR_GET_G (_color);	\
+*((_offset) + 2) = NAUTILUS_COLOR_GET_B (_color);	\
+} G_STMT_END
+
+#define NAUTILUS_COLOR_WHITE	      NAUTILUS_COLOR_PACK_RGB (255, 255, 255)
+
+static GdkGC * nautilus_gdk_create_drawing_gc                  (GdkWindow   *window,
+								guint32      color);
+static guint32 nautilus_gdk_rgb_get_rendered_value_for_rgb     (GdkWindow   *window,
+								GdkColormap *colormap,
+								guint32      value);
+static void    nautilus_gdk_pixbuf_copy_bits_from_gdk_drawable (GdkPixbuf   *pixbuf,
+								GdkDrawable *drawable,
+								GdkWindow   *window,
+								GdkColormap *colormap,
+								int          source_x,
+								int          source_y,
+								int          dest_x,
+								int          dest_y,
+								int          width,
+								int          height,
+								guint32      color);
+
+/**
+ * nautilus_gdk_create_drawing_gc
+ * @window: A window (or drawable)
+ * @color:  RGB packed color to use for gdk_rgb drawing.
+ *
+ * Create a drawing gc given an rgb packed color.
+ * Returns a newly allocated gc.
+ **/
+static GdkGC *
+nautilus_gdk_create_drawing_gc (GdkWindow *window, guint32 color)
+{
+	GdkGC *drawing_gc;
+	
+	g_return_val_if_fail (window != NULL, NULL);
+
+ 	drawing_gc = gdk_gc_new (window);
+
+ 	gdk_gc_set_function (drawing_gc, GDK_COPY);
+
+	gdk_rgb_gc_set_foreground (drawing_gc, color);
+
+	return drawing_gc;
+}
+
+/**
+ * nautilus_gdk_rgb_get_rendered_value_for_rgb
+ * @window: A window (or drawable)
+ * @colormap:  A colormap that matches the above window.
+ * @guint32:  A RGB packed color.
+ *
+ * gdk_rgb doesnt always use RGB values as given.  Sometimes it will munge them
+ * to conform to a color cube or limit color usage.  This function can be used to
+ * find out exactly what the value that gdk_rgb ends up using is.
+ * Returns a RGB packed color value as known to gdk_rgb.
+ **/
+static guint32
+nautilus_gdk_rgb_get_rendered_value_for_rgb (GdkWindow *window, GdkColormap *colormap, guint32 value)
+{
+	GdkPixmap	*pixmap;
+	GdkGC		*gc;
+	GdkPixbuf	*capture;
+	guchar		*pixels;
+	guint32		rv;
+	guchar		*p;
+
+	pixmap = gdk_pixmap_new (window, 1, 1, -1);
+
+	g_assert (pixmap != NULL);
+	
+	gc = nautilus_gdk_create_drawing_gc (window, value);
+
+	g_assert (gc != NULL);
+
+	gdk_draw_point (window, gc, 0, 0);
+
+	capture = gdk_pixbuf_get_from_drawable (NULL, window, colormap, 0, 0, 0, 0, 1, 1);
+
+	g_assert (capture != NULL);
+
+	pixels = gdk_pixbuf_get_pixels (capture);
+
+	p = pixels;
+
+	rv = NAUTILUS_COLOR_PACK_RGB (*(p + 0), *(p + 1), *(p + 2));
+
+	gdk_pixbuf_unref (capture);
+	gdk_gc_unref (gc);
+	gdk_pixmap_unref (pixmap);
+
+	return rv;
+}
+
+/**
+ * nautilus_gdk_rgb_get_rendered_value_for_rgb
+ * @pixbuf: A gdk_pixbuf to munge.
+ * @drawable:  The drawable to copy into the pixbuf.
+ * @source_x:  Source X coordinate on the drawable.
+ * @source_y:  Source Y coordinate on the drawable.
+ * @dest_x:    Dest X coordinate on the pixbuf.
+ * @dest_y:    Dest Y coordinate on the pixbuf.
+ * @width:     Copy width.
+ * @height:    Copy height.
+ * @color:     The color to to copy from the drawable.
+ *
+ * Copy bits of a specific color from a drawable to a gdk-pixbuf.
+ **/
+static void
+nautilus_gdk_pixbuf_copy_bits_from_gdk_drawable (GdkPixbuf	*pixbuf,
+						 GdkDrawable	*drawable,
+						 GdkWindow	*window,
+						 GdkColormap	*colormap,
+						 int		source_x,
+						 int		source_y,
+						 int		dest_x,
+						 int		dest_y,
+						 int		width,
+						 int		height,
+						 guint32	color)
+{
+	GdkPixbuf	*capture;
+	guint32		rendered_color;
+	int		capture_x;
+	int		capture_y;
+
+	guchar		*capture_pixels;
+	guint		capture_rowstride;
+	guint		capture_pixel_offset;
+
+	guchar		*dest_pixels;
+	guint		dest_rowstride;
+	guint		dest_pixel_offset;
+	guint		dest_width;
+	guint		dest_height;
+
+	g_return_if_fail (pixbuf != NULL);
+	g_return_if_fail (drawable != NULL);
+	g_return_if_fail (window != NULL);
+	g_return_if_fail (colormap != NULL);
+	g_return_if_fail (width > 0);
+	g_return_if_fail (height > 0);
+
+	capture = gdk_pixbuf_get_from_drawable (NULL,
+						drawable,
+						colormap,
+						source_x,
+						source_y,
+						0,
+						0,
+						width,
+						height);
+
+	g_assert (capture != NULL);
+
+	capture_pixels = gdk_pixbuf_get_pixels (capture);
+	capture_rowstride = gdk_pixbuf_get_rowstride (capture);
+	capture_pixel_offset = gdk_pixbuf_get_has_alpha (capture) ? 4 : 3;
+
+	dest_pixels = gdk_pixbuf_get_pixels (pixbuf);
+	dest_rowstride = gdk_pixbuf_get_rowstride (pixbuf);
+	dest_pixel_offset = gdk_pixbuf_get_has_alpha (pixbuf) ? 4 : 3;
+	dest_width = gdk_pixbuf_get_width (pixbuf);
+	dest_height = gdk_pixbuf_get_height (pixbuf);
+
+	rendered_color = nautilus_gdk_rgb_get_rendered_value_for_rgb (window, colormap, color);
+
+	for (capture_y = 0; capture_y < height; capture_y++)
+	{
+		guchar *row_offset = capture_pixels + capture_y * capture_rowstride;
+
+		for (capture_x = 0; capture_x < width; capture_x++)
+		{
+			guchar *offset = row_offset + capture_x * capture_pixel_offset;
+
+ 			guint32 peek = NAUTILUS_COLOR_PACK_RGB (*(offset + 0),
+								*(offset + 1),
+								*(offset + 2));
+
+ 			if (NAUTILUS_COLOR_EQUAL_RGB (peek, rendered_color))
+			{
+				int x = dest_x + capture_x;
+				int y = dest_y + capture_y;
+
+				if (x < dest_width && y < dest_height)
+				{
+					guchar *dest_offset = dest_pixels + y * dest_rowstride + x * dest_pixel_offset;
+					
+					PIXBUF_POKE_RGB (dest_offset, rendered_color);
+				}
+			}
+		}
+	}
+	
+	gdk_pixbuf_unref (capture);
+}
+
+/**
+ * nautilus_gdk_pixbuf_draw_text
+ * @pixbuf:        A gdk_pixbuf to munge.
+ * @font:          Font to use.
+ * @window:        Window where pixbuf will eventually be copied to.
+ * @colormap:      Colormap for the above window.
+ * @x:             X coordinate for text drawing.
+ * @y:	           Y coordinate for text drawing.
+ * @text:          The text.
+ * @text_length:   The length of the text in characters.
+ * @color:         The color to draw the text in.
+ *
+ * Draw text into a gdk_pixbuf using the given coordinates, font and color.
+ *
+ * The window and colormap parameters are needed so that all rendering 
+ * operations occur using values that are coherent with what gdk_rgb 
+ * uses.
+ **/
+void
+nautilus_gdk_pixbuf_draw_text (GdkPixbuf	*pixbuf,
+			       GdkFont		*font,
+			       GdkWindow	*window,
+			       GdkColormap	*colormap,
+			       int		x,
+			       int		y,
+			       const char	*text,
+			       guint		text_length,
+			       guint32		color)
+{
+	GdkPixmap		*pixmap;
+	GdkGC			*text_gc;
+	GdkGC			*clear_gc;
+	int			width;
+	int			height;
+	int			lbearing;
+	int			rbearing;
+	int			ascent;
+	int			descent;
+
+	g_return_if_fail (pixbuf != NULL);
+	g_return_if_fail (font != NULL);
+	g_return_if_fail (window != NULL);
+	g_return_if_fail (colormap != NULL);
+	g_return_if_fail (text != NULL);
+
+	gdk_text_extents (font,
+			  text,
+			  strlen (text),
+			  &lbearing,
+			  &rbearing,
+			  &width,
+			  &ascent,
+			  &descent);
+	
+	height = ascent + descent;
+
+	g_assert (width > 0);
+	g_assert (height > 0);
+
+	pixmap = gdk_pixmap_new (window, width, height, -1);
+
+	text_gc = nautilus_gdk_create_drawing_gc (window, color);
+	
+	g_assert (text_gc != NULL);
+
+	clear_gc = nautilus_gdk_create_drawing_gc (window, NAUTILUS_COLOR_WHITE);
+
+	g_assert (clear_gc != NULL);
+
+	gdk_draw_rectangle (pixmap,
+			    clear_gc,
+			    TRUE,
+			    0,
+			    0,
+			    width,
+			    height);
+				    
+	gdk_draw_text (pixmap,
+		       font,
+		       text_gc,
+		       0,
+		       ascent,
+		       text,
+		       strlen (text));
+
+	nautilus_gdk_pixbuf_copy_bits_from_gdk_drawable (pixbuf,
+							 pixmap,
+							 window,
+							 colormap,
+							 0,
+							 0,
+							 x,
+							 y,
+							 width,
+							 height,
+							 color);
+
+	gdk_gc_unref (text_gc);
+	gdk_gc_unref (clear_gc);
+	gdk_pixmap_unref (pixmap);
+}
+
Index: nautilus-gdk-pixbuf-extensions.h
===================================================================
RCS file: /cvs/gnome/nautilus/libnautilus-extensions/nautilus-gdk-pixbuf-extensions.h,v
retrieving revision 1.1
diff -u -r1.1 nautilus-gdk-pixbuf-extensions.h
--- nautilus-gdk-pixbuf-extensions.h	2000/04/04 01:00:06	1.1
+++ nautilus-gdk-pixbuf-extensions.h	2000/05/03 03:16:23
@@ -38,15 +38,18 @@
 void                      nautilus_gdk_pixbuf_list_unref               (GList                      *pixbuf_list);
 void                      nautilus_gdk_pixbuf_list_free                (GList                      *pixbuf_list);
 
+
 /* Loading a GdkPixbuf with a URI. */
 GdkPixbuf *               nautilus_gdk_pixbuf_load                     (const char                 *uri);
 
+
 /* Same thing async. */
 NautilusPixbufLoadHandle *nautilus_gdk_pixbuf_load_async               (const char                 *uri,
 									NautilusPixbufLoadCallback  callback,
 									gpointer                    callback_data);
 void                      nautilus_cancel_gdk_pixbuf_load              (NautilusPixbufLoadHandle   *handle);
 
+
 /* Draw a GdkPixbuf tiled. */
 void                      nautilus_gdk_pixbuf_render_to_drawable_tiled (GdkPixbuf                  *pixbuf,
 									GdkDrawable                *drawable,
@@ -55,5 +58,14 @@
 									GdkRgbDither                dither,
 									int                         x_dither,
 									int                         y_dither);
+void                      nautilus_gdk_pixbuf_draw_text                (GdkPixbuf                  *pixbuf,
+									GdkFont                    *font,
+									GdkWindow                  *window,
+									GdkColormap                *colormap,
+									int                         x,
+									int                         y,
+									const char                 *text,
+									guint                       text_length,
+									guint32                     color);
 
 #endif /* NAUTILUS_GDK_PIXBUF_EXTENSIONS_H */


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