From d21f55fe9add35bb708d260841ce047c2458a7ca Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Fri, 27 Mar 2009 16:54:10 +0000 Subject: [PATCH] =?UTF-8?q?Bug=20437533=20=E2=80=93=20Implement=20draw=5Fs?= =?UTF-8?q?hape=20PangoRenderer=20method?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 2009-03-27 Behdad Esfahbod Bug 437533 – Implement draw_shape PangoRenderer method * gdk/gdkpango.c (gdk_pango_renderer_draw_shape), (gdk_pango_renderer_class_init): Implement draw_shape * demos/gtk-demo/rotated_text.c (fancy_shape_renderer), (create_fancy_attr_list_for_layout), (rotated_text_expose_event), (do_rotated_text): 1) Port to pangocairo 2) Also show a rotated label 3) Install a custom shape renderer on both. The second one goes through gdkpango and hence tests the patch above. It's working. svn path=/trunk/; revision=22592 --- ChangeLog | 15 +++ demos/gtk-demo/rotated_text.c | 217 ++++++++++++++++++++++++---------- gdk/gdkpango.c | 47 ++++++++ 3 files changed, 218 insertions(+), 61 deletions(-) diff --git a/ChangeLog b/ChangeLog index a1268729a9..adfb0e1d0a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,18 @@ +2009-03-27 Behdad Esfahbod + + Bug 437533 – Implement draw_shape PangoRenderer method + + * gdk/gdkpango.c (gdk_pango_renderer_draw_shape), + (gdk_pango_renderer_class_init): Implement draw_shape + + * demos/gtk-demo/rotated_text.c (fancy_shape_renderer), + (create_fancy_attr_list_for_layout), (rotated_text_expose_event), + (do_rotated_text): + 1) Port to pangocairo + 2) Also show a rotated label + 3) Install a custom shape renderer on both. The second one goes + through gdkpango and hence tests the patch above. It's working. + 2009-03-26 Stef Walter * gtk/gtkfilechooserentry.c diff --git a/demos/gtk-demo/rotated_text.c b/demos/gtk-demo/rotated_text.c index 550101eda4..b32bd2880d 100644 --- a/demos/gtk-demo/rotated_text.c +++ b/demos/gtk-demo/rotated_text.c @@ -1,101 +1,173 @@ /* Rotated Text * - * This demo shows how to use GDK and Pango to draw rotated and transformed - * text. The use of GdkPangoRenderer in this example is a somewhat advanced - * technique; most applications can simply use gdk_draw_layout(). We use - * it here mostly because that allows us to work in user coordinates - that is, - * coordinates prior to the application of the transformation matrix, rather - * than device coordinates. + * This demo shows how to use PangoCairo to draw rotated and transformed + * text. The right pane shows a rotated GtkLabel widget. * - * As of GTK+-2.6, the ability to draw transformed and anti-aliased graphics - * as shown in this example is only present for text. With GTK+-2.8, a new - * graphics system called "Cairo" will be introduced that provides these - * capabilities and many more for all types of graphics. + * In both cases, a custom PangoCairo shape renderer is installed to draw + * a red heard using cairo drawing operations instead of the Unicode heart + * character. */ -#include + #include +#include static GtkWidget *window = NULL; +#define HEART "♥" +const char text[] = "I ♥ GTK+"; + +static void +fancy_shape_renderer (cairo_t *cr, + PangoAttrShape *attr, + gboolean do_path, + gpointer data) +{ + double x, y; + cairo_get_current_point (cr, &x, &y); + cairo_translate (cr, x, y); + + cairo_scale (cr, + (double) attr->ink_rect.width / PANGO_SCALE, + (double) attr->ink_rect.height / PANGO_SCALE); + + switch (GPOINTER_TO_UINT (attr->data)) + { + case 0x2665: /* U+2665 BLACK HEART SUIT */ + { + cairo_move_to (cr, .5, .0); + cairo_line_to (cr, .9, -.4); + cairo_curve_to (cr, 1.1, -.8, .5, -.9, .5, -.5); + cairo_curve_to (cr, .5, -.9, -.1, -.8, .1, -.4); + cairo_close_path (cr); + } + break; + } + + if (!do_path) { + cairo_set_source_rgb (cr, 1., 0., 0.); + cairo_fill (cr); + } +} + +PangoAttrList * +create_fancy_attr_list_for_layout (PangoLayout *layout) +{ + PangoAttrList *attrs; + PangoFontMetrics *metrics; + int ascent; + PangoRectangle ink_rect, logical_rect; + const char *p; + + /* Get font metrics and prepare fancy shape size */ + metrics = pango_context_get_metrics (pango_layout_get_context (layout), + pango_layout_get_font_description (layout), + NULL); + ascent = pango_font_metrics_get_ascent (metrics); + logical_rect.x = 0; + logical_rect.width = ascent; + logical_rect.y = -ascent; + logical_rect.height = ascent; + ink_rect = logical_rect; + pango_font_metrics_unref (metrics); + + /* Set fancy shape attributes for all hearts */ + attrs = pango_attr_list_new (); + for (p = text; (p = strstr (p, HEART)); p += strlen (HEART)) + { + PangoAttribute *attr; + + attr = pango_attr_shape_new_with_data (&ink_rect, + &logical_rect, + GUINT_TO_POINTER (g_utf8_get_char (p)), + NULL, NULL); + + attr->start_index = p - text; + attr->end_index = attr->start_index + strlen (HEART); + + pango_attr_list_insert (attrs, attr); + } + + return attrs; +} + static gboolean rotated_text_expose_event (GtkWidget *widget, GdkEventExpose *event, gpointer data) { #define RADIUS 150 -#define N_WORDS 10 -#define FONT "Sans Bold 27" - - PangoRenderer *renderer; - PangoMatrix matrix = PANGO_MATRIX_INIT; +#define N_WORDS 5 +#define FONT "Serif 18" + PangoContext *context; PangoLayout *layout; PangoFontDescription *desc; + cairo_t *cr; + cairo_pattern_t *pattern; + + PangoAttrList *attrs; + int width = widget->allocation.width; int height = widget->allocation.height; double device_radius; int i; - /* Get the default renderer for the screen, and set it up for drawing */ - renderer = gdk_pango_renderer_get_default (gtk_widget_get_screen (widget)); - gdk_pango_renderer_set_drawable (GDK_PANGO_RENDERER (renderer), widget->window); - gdk_pango_renderer_set_gc (GDK_PANGO_RENDERER (renderer), widget->style->black_gc); - - /* Set up a transformation matrix so that the user space coordinates for - * the centered square where we draw are [-RADIUS, RADIUS], [-RADIUS, RADIUS] - * We first center, then change the scale */ + /* Create a cairo context and set up a transformation matrix so that the user + * space coordinates for the centered square where we draw are [-RADIUS, RADIUS], + * [-RADIUS, RADIUS]. + * We first center, then change the scale. */ + cr = gdk_cairo_create (widget->window); device_radius = MIN (width, height) / 2.; - pango_matrix_translate (&matrix, - device_radius + (width - 2 * device_radius) / 2, - device_radius + (height - 2 * device_radius) / 2); - pango_matrix_scale (&matrix, device_radius / RADIUS, device_radius / RADIUS); + cairo_translate (cr, + device_radius + (width - 2 * device_radius) / 2, + device_radius + (height - 2 * device_radius) / 2); + cairo_scale (cr, device_radius / RADIUS, device_radius / RADIUS); - /* Create a PangoLayout, set the font and text */ + /* Create and a subtle gradient source and use it. */ + pattern = cairo_pattern_create_linear (-RADIUS, -RADIUS, RADIUS, RADIUS); + cairo_pattern_add_color_stop_rgb (pattern, 0., .5, .0, .0); + cairo_pattern_add_color_stop_rgb (pattern, 1., .0, .0, .5); + cairo_set_source (cr, pattern); + + /* Create a PangoContext and set up our shape renderer */ context = gtk_widget_create_pango_context (widget); + pango_cairo_context_set_shape_renderer (context, + fancy_shape_renderer, + NULL, NULL); + + /* Create a PangoLayout, set the text, font, and attributes */ layout = pango_layout_new (context); - pango_layout_set_text (layout, "Text", -1); + pango_layout_set_text (layout, text, -1); desc = pango_font_description_from_string (FONT); pango_layout_set_font_description (layout, desc); - pango_font_description_free (desc); + + attrs = create_fancy_attr_list_for_layout (layout); + pango_layout_set_attributes (layout, attrs); + pango_attr_list_unref (attrs); /* Draw the layout N_WORDS times in a circle */ for (i = 0; i < N_WORDS; i++) { - GdkColor color; - PangoMatrix rotated_matrix = matrix; int width, height; - double angle = (360. * i) / N_WORDS; - /* Gradient from red at angle == 60 to blue at angle == 300 */ - color.red = 65535 * (1 + cos ((angle - 60) * G_PI / 180.)) / 2; - color.green = 0; - color.blue = 65535 - color.red; - - gdk_pango_renderer_set_override_color (GDK_PANGO_RENDERER (renderer), - PANGO_RENDER_PART_FOREGROUND, &color); - - pango_matrix_rotate (&rotated_matrix, angle); - - pango_context_set_matrix (context, &rotated_matrix); - /* Inform Pango to re-layout the text with the new transformation matrix */ - pango_layout_context_changed (layout); + pango_cairo_update_layout (cr, layout); - pango_layout_get_size (layout, &width, &height); - pango_renderer_draw_layout (renderer, layout, - - width / 2, - RADIUS * PANGO_SCALE); + pango_layout_get_pixel_size (layout, &width, &height); + cairo_move_to (cr, - width / 2, - RADIUS * .9); + pango_cairo_show_layout (cr, layout); + + /* Rotate for the next turn */ + cairo_rotate (cr, G_PI*2 / N_WORDS); } - /* Clean up default renderer, since it is shared */ - gdk_pango_renderer_set_override_color (GDK_PANGO_RENDERER (renderer), - PANGO_RENDER_PART_FOREGROUND, NULL); - gdk_pango_renderer_set_drawable (GDK_PANGO_RENDERER (renderer), NULL); - gdk_pango_renderer_set_gc (GDK_PANGO_RENDERER (renderer), NULL); - /* free the objects we created */ + pango_font_description_free (desc); g_object_unref (layout); g_object_unref (context); + cairo_pattern_destroy (pattern); + cairo_destroy (cr); return FALSE; } @@ -103,21 +175,30 @@ rotated_text_expose_event (GtkWidget *widget, GtkWidget * do_rotated_text (GtkWidget *do_widget) { - GtkWidget *drawing_area; - if (!window) { + GtkWidget *box; + GtkWidget *drawing_area; + GtkWidget *label; + PangoLayout *layout; + PangoAttrList *attrs; + const GdkColor white = { 0, 0xffff, 0xffff, 0xffff }; window = gtk_window_new (GTK_WINDOW_TOPLEVEL); gtk_window_set_screen (GTK_WINDOW (window), gtk_widget_get_screen (do_widget)); gtk_window_set_title (GTK_WINDOW (window), "Rotated Text"); - + gtk_window_set_default_size (GTK_WINDOW (window), 4 * RADIUS, 2 * RADIUS); g_signal_connect (window, "destroy", G_CALLBACK (gtk_widget_destroyed), &window); + box = gtk_hbox_new (TRUE, 0); + gtk_container_add (GTK_CONTAINER (window), box); + + /* Add a drawing area */ + drawing_area = gtk_drawing_area_new (); - gtk_container_add (GTK_CONTAINER (window), drawing_area); + gtk_container_add (GTK_CONTAINER (box), drawing_area); /* This overrides the background color from the theme */ gtk_widget_modify_bg (drawing_area, GTK_STATE_NORMAL, &white); @@ -125,7 +206,21 @@ do_rotated_text (GtkWidget *do_widget) g_signal_connect (drawing_area, "expose-event", G_CALLBACK (rotated_text_expose_event), NULL); - gtk_window_set_default_size (GTK_WINDOW (window), 2 * RADIUS, 2 * RADIUS); + /* And a label */ + + label = gtk_label_new (text); + gtk_container_add (GTK_CONTAINER (box), label); + + gtk_label_set_angle (label, 45); + + /* Set up fancy stuff on the label */ + layout = gtk_label_get_layout (label); + pango_cairo_context_set_shape_renderer (pango_layout_get_context (layout), + fancy_shape_renderer, + NULL, NULL); + attrs = create_fancy_attr_list_for_layout (layout); + gtk_label_set_attributes (label, attrs); + pango_attr_list_unref (attrs); } if (!GTK_WIDGET_VISIBLE (window)) diff --git a/gdk/gdkpango.c b/gdk/gdkpango.c index 302eb9d3e0..a38640374b 100644 --- a/gdk/gdkpango.c +++ b/gdk/gdkpango.c @@ -311,6 +311,52 @@ gdk_pango_renderer_draw_error_underline (PangoRenderer *renderer, (double)width / PANGO_SCALE, (double)height / PANGO_SCALE); } +static void +gdk_pango_renderer_draw_shape (PangoRenderer *renderer, + PangoAttrShape *attr, + int x, + int y) +{ + GdkPangoRenderer *gdk_renderer = GDK_PANGO_RENDERER (renderer); + GdkPangoRendererPrivate *priv = gdk_renderer->priv; + PangoLayout *layout; + PangoCairoShapeRendererFunc shape_renderer; + gpointer shape_renderer_data; + cairo_t *cr; + double dx = (double)x / PANGO_SCALE, dy = (double)y / PANGO_SCALE; + + layout = pango_renderer_get_layout (renderer); + + if (!layout) + return; + + shape_renderer = pango_cairo_context_get_shape_renderer (pango_layout_get_context (layout), + &shape_renderer_data); + + if (!shape_renderer) + return; + + cr = get_cairo_context (gdk_renderer, PANGO_RENDER_PART_FOREGROUND); + + cairo_save (cr); + + if (priv->embossed) + { + cairo_save (cr); + emboss_context (gdk_renderer, cr); + + cairo_move_to (cr, dx, dy); + shape_renderer (cr, attr, FALSE, shape_renderer_data); + + cairo_restore (cr); + } + + cairo_move_to (cr, dx, dy); + shape_renderer (cr, attr, FALSE, shape_renderer_data); + + cairo_restore (cr); +} + static void gdk_pango_renderer_part_changed (PangoRenderer *renderer, PangoRenderPart part) @@ -474,6 +520,7 @@ gdk_pango_renderer_class_init (GdkPangoRendererClass *klass) renderer_class->draw_glyphs = gdk_pango_renderer_draw_glyphs; renderer_class->draw_rectangle = gdk_pango_renderer_draw_rectangle; renderer_class->draw_error_underline = gdk_pango_renderer_draw_error_underline; + renderer_class->draw_shape = gdk_pango_renderer_draw_shape; renderer_class->part_changed = gdk_pango_renderer_part_changed; renderer_class->begin = gdk_pango_renderer_begin; renderer_class->end = gdk_pango_renderer_end;