Bug 437533 – Implement draw_shape PangoRenderer method

2009-03-27  Behdad Esfahbod  <behdad@gnome.org>

        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
This commit is contained in:
Behdad Esfahbod 2009-03-27 16:54:10 +00:00 committed by Behdad Esfahbod
parent 4d520f241d
commit d21f55fe9a
3 changed files with 218 additions and 61 deletions

View File

@ -1,3 +1,18 @@
2009-03-27 Behdad Esfahbod <behdad@gnome.org>
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 <stef@memberwebs.com>
* gtk/gtkfilechooserentry.c

View File

@ -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 <math.h>
#include <gtk/gtk.h>
#include <string.h>
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))

View File

@ -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;