]> Pileus Git - ~andy/gtk/commitdiff
Add GdkPangoRenderer, a subclass of PangoRenderer targeting GDK drawables.
authorOwen Taylor <otaylor@redhat.com>
Sun, 21 Nov 2004 16:24:01 +0000 (16:24 +0000)
committerOwen Taylor <otaylor@src.gnome.org>
Sun, 21 Nov 2004 16:24:01 +0000 (16:24 +0000)
Sat Nov 20 15:13:51 2004  Owen Taylor  <otaylor@redhat.com>

* gdk/gdkpango.[ch]: Add GdkPangoRenderer, a subclass of
PangoRenderer targeting GDK drawables. Use to implement the old
gdk_draw_layout() and friends.

* gdk/gdkdraw.c gdk/gdkdrawable.h gdk/gdkwindow.c gdk/gdkpixmap.c:
Add gdk_draw_glyphs_transformed() gdk_draw_trapezoids() and
the corresponding members of GdkDrawableClass. Add a fallback
implementation of gdk_draw_trapezoids() in terms of pixbufs.

* gdk/gdkwindowing.h gdk/x11/gdkg-x11.h: Add
_gdk_windowing_gc_get_foreground() to enable the fallback
trapezoid implementation.

* gdk/x11/gdkdrawable-x11.c gdk/x11/gdkdisplay-x11.h: Implement
draw_glyph_transformed, draw_trapezoids.

* gdk/x11/gdkdrawable-x11.[ch]: Add
_gdk_x11_drawable_draw_xtrapezoids, _gdk_x11_drawable_draw_xft_glyphs
for use of GdkX11Renderer.

* gdk/x11/gdkgc-x11.c gdk/x11/gdkprivate-x11.h: Implement
GDK_TILED, GDK_STIPPLED, GDK_OPAQUE_STIPPLED in the RENDER codepath.

* gdk/gdkpango-x11.c: Add GdkX11Renderer... a subclass of
PangoXftRenderer that does tiles/stipples and fallback rendering
of trapezoids without the RENDER extension.

* gdk/gdkpango-x11.c gdk/x11/gdkscreen-x11.[ch] _gdk_x11_renderer_get:
Add _gdk_x11_renderer_get() to get a singleton GdkX11Renderer
for the screen.

* gdk/x11/gdkdrawable-x11.c (get_impl_drawable): Fix a None/NULL
confusion.

* gtk/gtklabel.[ch] gtk/gtk.symbols: Add gtk_label_set/get_angle(),
and an ::angle property.

* gtk/gtklabel.c: Remove #if 0'd dead code gtk_label_paint_word().

* gtk/gtktextdisplay.c: Switch to using a GtkTextRenderer subclass
of GdkPangoRenderer for drawing.

* gtk/gtktextlayout.[ch] gtk/gtktextdisplay.c: Switch to using
gtk_attr_shape_new_with_data() to store backreferences to
embedded pixmaps and widgets. Leave line_display->shaped_objects
around for backwords compatibility.

* gdk/gdkpango.[ch] (gdk_pango_context_set_colormap): Describe
as deprecated, remove implementation.

* gtk/gtkwidget.c (gtk_widget_create_pango_context): Remove
call to gdk_pango_context_set_colormap.

* demos/gtk-demo/Makefile.am demos/gtk-demo/rotated_text.c: Add
a demo showing drawing rotated text.

* tests/testgtk.c: Add a rotated-label test, and also a rotated
drawing test (differs from demos/gtk-demo/rotated_text by also
using a tile)

25 files changed:
demos/gtk-demo/Makefile.am
demos/gtk-demo/rotated_text.c [new file with mode: 0644]
gdk/gdk.symbols
gdk/gdkdraw.c
gdk/gdkdrawable.h
gdk/gdkinternals.h
gdk/gdkpango.c
gdk/gdkpango.h
gdk/gdkpixmap.c
gdk/gdkwindow.c
gdk/x11/gdkdisplay-x11.h
gdk/x11/gdkdrawable-x11.c
gdk/x11/gdkdrawable-x11.h
gdk/x11/gdkgc-x11.c
gdk/x11/gdkpango-x11.c
gdk/x11/gdkprivate-x11.h
gdk/x11/gdkscreen-x11.c
gdk/x11/gdkscreen-x11.h
gtk/gtk.symbols
gtk/gtklabel.c
gtk/gtklabel.h
gtk/gtktextdisplay.c
gtk/gtktextlayout.c
gtk/gtktextlayout.h
tests/testgtk.c

index 65a52ca9b866db30163dff98946f2285d229fee3..dd904195bea4366ba9d2b2c41aec652037250436 100644 (file)
@@ -22,6 +22,7 @@ demos =                                               \
        menus.c                                 \
        panes.c                                 \
        pixbufs.c                               \
+       rotated_text.c                          \
        sizegroup.c                             \
        stock_browser.c                         \
        textview.c                              \
diff --git a/demos/gtk-demo/rotated_text.c b/demos/gtk-demo/rotated_text.c
new file mode 100644 (file)
index 0000000..1c37548
--- /dev/null
@@ -0,0 +1,142 @@
+/* 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.
+ *
+ * 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.
+ */
+#include <math.h>
+#include <gtk/gtk.h>
+
+static GtkWidget *window = NULL;
+
+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;
+  PangoContext *context;
+  PangoLayout *layout;
+  PangoFontDescription *desc;
+
+  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 */
+  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);
+
+  /* Create a PangoLayout, set the font and text */
+  context = gtk_widget_create_pango_context (widget);
+  layout = pango_layout_new (context);
+  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);
+
+  /* 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) * M_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_layout_get_size (layout, &width, &height);
+      pango_renderer_draw_layout (renderer, layout,
+                                 - width / 2, - RADIUS * PANGO_SCALE);
+    }
+
+  /* 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 */
+  g_object_unref (layout);
+  g_object_unref (context);
+  
+  return FALSE;
+}
+
+GtkWidget *
+do_rotated_text (GtkWidget *do_widget)
+{
+  GtkWidget *drawing_area;
+  
+  if (!window)
+    {
+      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");
+
+      g_signal_connect (window, "destroy", G_CALLBACK (gtk_widget_destroyed), &window);
+
+      drawing_area = gtk_drawing_area_new ();
+      gtk_container_add (GTK_CONTAINER (window), drawing_area);
+
+      /* This overrides the background color from the theme */
+      gtk_widget_modify_bg (drawing_area, GTK_STATE_NORMAL, &white);
+
+      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);
+    }
+
+  if (!GTK_WIDGET_VISIBLE (window))
+    {
+      gtk_widget_show_all (window);
+    }
+  else
+    {
+      gtk_widget_destroy (window);
+      window = NULL;
+    }
+
+  return window;
+}
index af498c1e41b8e11b33554eb59bb396c3f9689cab..f7fc0135835d9d9d0e3576ea78c661324cc82b1b 100644 (file)
@@ -134,6 +134,7 @@ gdk_drawable_unref
 gdk_draw_arc
 gdk_draw_drawable
 gdk_draw_glyphs
+gdk_draw_glyphs_transformed
 gdk_draw_gray_image
 gdk_draw_image
 gdk_draw_indexed_image
@@ -157,6 +158,7 @@ gdk_draw_segments
 gdk_draw_string
 gdk_draw_text
 gdk_draw_text_wc
+gdk_draw_trapezoids
 gdk_drop_finish
 gdk_drop_reply
 gdk_error_trap_pop
@@ -305,6 +307,13 @@ gdk_pango_context_get_for_screen
 gdk_pango_context_set_colormap
 gdk_pango_layout_get_clip_region
 gdk_pango_layout_line_get_clip_region
+gdk_pango_renderer_get_default
+gdk_pango_renderer_get_type
+gdk_pango_renderer_new
+gdk_pango_renderer_set_drawable
+gdk_pango_renderer_set_gc
+gdk_pango_renderer_set_override_color
+gdk_pango_renderer_set_stipple
 gdk_parse_args
 gdk_pixbuf_get_from_drawable
 gdk_pixbuf_get_from_image
index 2145a723029f2bf6e9f003c3a21143af0f18006e..f547fad9042e970dd1a2f1ca66a988cd047e9792 100644 (file)
@@ -25,6 +25,7 @@
  */
 
 #include <config.h>
+#include <math.h>
 #include "gdkalias.h"
 #include "gdkdrawable.h"
 #include "gdkinternals.h"
@@ -58,7 +59,11 @@ static void         gdk_drawable_real_draw_pixbuf            (GdkDrawable  *draw
                                                              GdkRgbDither  dither,
                                                              gint          x_dither,
                                                              gint          y_dither);
-
+static void         gdk_drawable_real_draw_trapezoids        (GdkDrawable   *drawable,
+                                                             GdkGC         *gc,
+                                                             GdkTrapezoid  *trapezoids,
+                                                             gint           n_trapezoids);
+     
 static void gdk_drawable_class_init (GdkDrawableClass *klass);
 
 GType
@@ -99,6 +104,7 @@ gdk_drawable_class_init (GdkDrawableClass *klass)
   klass->get_clip_region = gdk_drawable_real_get_visible_region;
   klass->get_visible_region = gdk_drawable_real_get_visible_region;
   klass->draw_pixbuf = gdk_drawable_real_draw_pixbuf;
+  klass->draw_trapezoids = gdk_drawable_real_draw_trapezoids;
 }
 
 /* Manipulation of drawables
@@ -873,12 +879,12 @@ gdk_draw_lines (GdkDrawable *drawable,
  * @font: font to be used
  * @x: X coordinate of baseline origin
  * @y: Y coordinate of baseline origin
- * @glyphs: glyphs to render
+ * @glyphs: the glyph string to draw
  *
  * This is a low-level function; 99% of text rendering should be done
  * using gdk_draw_layout() instead.
  *
- * A glyph is a character in a font. This function draws a sequence of
+ * A glyph is a single image in a font. This function draws a sequence of
  * glyphs.  To obtain a sequence of glyphs you have to understand a
  * lot about internationalized text handling, which you don't want to
  * understand; thus, use gdk_draw_layout() instead of this function,
@@ -900,6 +906,74 @@ gdk_draw_glyphs (GdkDrawable      *drawable,
   GDK_DRAWABLE_GET_CLASS (drawable)->draw_glyphs (drawable, gc, font, x, y, glyphs);
 }
 
+/**
+ * gdk_draw_glyphs_transformed:
+ * @drawable: a #GdkDrawable
+ * @gc: a #GdkGC
+ * @matrix: a #PangoMatrix, or %NULL to use an identity transformation
+ * @font: the font in which to draw the string
+ * @x:       the x position of the start of the string (in Pango
+ *           units in user space coordinates)
+ * @y:       the y position of the baseline (in Pango units
+ *           in user space coordinates)
+ * @glyphs:  the glyph string to draw
+ * 
+ * Renders a #PangoGlyphString onto a drawable, possibly
+ * transforming the layed-out coordinates through a transformation
+ * matrix. Note that the transformation matrix for @font is not
+ * changed, so to produce correct rendering results, the @font
+ * must have been loaded using a #PangoContext with an identical
+ * transformation matrix to that passed in to this function.
+ *
+ * See also gdk_draw_glyphs(), gdk_draw_layout().
+ *
+ * Since: 2.6
+ **/
+void
+gdk_draw_glyphs_transformed (GdkDrawable      *drawable,
+                            GdkGC            *gc,
+                            PangoMatrix      *matrix,
+                            PangoFont        *font,
+                            gint              x,
+                            gint              y,
+                            PangoGlyphString *glyphs)
+{
+  g_return_if_fail (GDK_IS_DRAWABLE (drawable));
+  g_return_if_fail (GDK_IS_GC (gc));
+
+  if (GDK_DRAWABLE_GET_CLASS (drawable)->draw_glyphs_transformed)
+    GDK_DRAWABLE_GET_CLASS (drawable)->draw_glyphs_transformed (drawable, gc, matrix,
+                                                               font, x, y, glyphs);
+}
+
+/**
+ * gdk_draw_trapezoids:
+ * @drawable: a #GdkDrawable
+ * @gc: a #GdkGC
+ * @trapezoids: an array of #GdkTrapezoid structures
+ * @n_trapezoids: the number of trapezoids to draw
+ * 
+ * Draws a set of anti-aliased trapezoids. The trapezoids are
+ * combined using saturation addition, then drawn over the background
+ * as a set. This is low level functionality used internally to implement
+ * rotated underlines and backgrouds when rendering a PangoLayout and is
+ * likely not useful for applications.
+ *
+ * Since: 2.6
+ **/
+void
+gdk_draw_trapezoids (GdkDrawable    *drawable,
+                    GdkGC          *gc,
+                    GdkTrapezoid   *trapezoids,
+                    gint            n_trapezoids)
+{
+  g_return_if_fail (GDK_IS_DRAWABLE (drawable));
+  g_return_if_fail (GDK_IS_GC (gc));
+  g_return_if_fail (n_trapezoids == 0 || trapezoids != NULL);
+
+  GDK_DRAWABLE_GET_CLASS (drawable)->draw_trapezoids (drawable, gc,
+                                                     trapezoids, n_trapezoids);
+}
 
 /**
  * gdk_drawable_copy_to_image:
@@ -1534,6 +1608,282 @@ gdk_drawable_real_draw_pixbuf (GdkDrawable  *drawable,
     g_object_unref (composited);
 }
 
+/************************************************************************/
+
+/* Fallback rendering code for anti-aliased trapezoids. Note that this code
+ * is cut-and-pasted (with the substitution of GdkPixbuf for FT_Bitmap) between
+ * here and pangoft2-render.c.
+ */
+typedef struct {
+  double y;
+  double x1;
+  double x2;
+} Position;
+
+static void
+draw_simple_trap (GdkPixbuf     *pixbuf,
+                 int            pixbuf_x,
+                 int            pixbuf_y,
+                 Position      *t,
+                 Position      *b)
+{
+  guchar *pixels = gdk_pixbuf_get_pixels (pixbuf);
+  int rowstride = gdk_pixbuf_get_rowstride (pixbuf);
+  int pixbuf_width = gdk_pixbuf_get_width (pixbuf);
+  int pixbuf_height = gdk_pixbuf_get_height (pixbuf);
+  int iy = floor (t->y);
+  int x1, x2, x;
+  double dy = b->y - t->y;
+  guchar *dest;
+
+  if (iy < pixbuf_y || iy >= pixbuf_y + pixbuf_height)
+    return;
+
+  if (t->x1 < b->x1)
+    x1 = floor (t->x1);
+  else
+    x1 = floor (b->x1);
+
+  if (t->x2 > b->x2)
+    x2 = ceil (t->x2);
+  else
+    x2 = ceil (b->x2);
+
+  x1 = CLAMP (x1, pixbuf_x, pixbuf_x + pixbuf_width);
+  x2 = CLAMP (x2, pixbuf_x, pixbuf_x + pixbuf_width);
+
+  dest = pixels + (iy - pixbuf_y) * rowstride + (x1 - pixbuf_x) * 4;
+  
+  for (x = x1; x < x2; x++, dest += 4)
+    {
+      double top_left = MAX (t->x1, x);
+      double top_right = MIN (t->x2, x + 1);
+      double bottom_left = MAX (b->x1, x);
+      double bottom_right = MIN (b->x2, x + 1);
+      double c = 0.5 * dy * ((top_right - top_left) + (bottom_right - bottom_left));
+
+      /* When converting to [0,255], we round up. This is intended
+       * to prevent the problem of pixels that get divided into
+       * multiple slices not being fully black.
+       */
+      int ic = c * 256;
+
+      /* We already set the entire buffer to the destination color */
+      dest[3] = MIN (dest[3] + ic, 255);
+    }
+}
+
+static void
+interpolate_position (Position *result,
+                     Position *top,
+                     Position *bottom,
+                     double    val,
+                     double    val1,
+                     double    val2)
+{
+  result->y  = (top->y *  (val2 - val) + bottom->y *  (val - val1)) / (val2 - val1);
+  result->x1 = (top->x1 * (val2 - val) + bottom->x1 * (val - val1)) / (val2 - val1);
+  result->x2 = (top->x2 * (val2 - val) + bottom->x2 * (val - val1)) / (val2 - val1);
+}
+
+/* This draws a trapezoid with the parallel sides aligned with
+ * the X axis. We do this by subdividing the trapezoid vertically
+ * into thin slices (themselves trapezoids) where two edge sides are each
+ * contained within a single pixel and then rasterizing each
+ * slice. There are frequently multiple slices within a single
+ * line so we have to accumulate to get the final result.
+ */
+static void
+draw_trapezoid (GdkPixbuf       *pixbuf,
+               int              pixbuf_x,
+               int              pixbuf_y,
+               GdkTrapezoid    *trapezoid)
+{
+  Position pos;
+  Position t;
+  Position b;
+  gboolean done = FALSE;
+
+  if (trapezoid->y1 == trapezoid->y2)
+    return;
+
+  pos.y = t.y = trapezoid->y1;
+  pos.x1 = t.x1 = trapezoid->x11;
+  pos.x2 = t.x2 = trapezoid->x21;
+  b.y = trapezoid->y2;
+  b.x1 = trapezoid->x12;
+  b.x2 = trapezoid->x22;
+
+  while (!done)
+    {
+      Position pos_next;
+      double y_next, x1_next, x2_next;
+      double ix1, ix2;
+
+      /* The algorithm here is written to emphasize simplicity and
+       * numerical stability as opposed to speed.
+       *
+       * While the end result is slicing up the polygon vertically,
+       * conceptually we aren't walking in the X direction, rather we
+       * are walking along the edges. When we compute crossing of
+       * horizontal pixel boundaries, we use the X coordinate as the
+       * interpolating variable, when we compute crossing for vertical
+       * pixel boundaries, we use the Y coordinate.
+       *
+       * This allows us to handle almost exactly horizontal edges without
+       * running into difficulties. (Almost exactly horizontal edges
+       * come up frequently due to inexactness in computing, say,
+       * a 90 degree rotation transformation)
+       */
+
+      pos_next = b;
+      done = TRUE;
+
+      /* Check for crossing vertical pixel boundaries */
+      y_next = floor (pos.y) + 1;
+      if (y_next < pos_next.y)
+       {
+         interpolate_position (&pos_next, &t, &b,
+                               y_next, t.y, b.y);
+         pos_next.y = y_next;
+         done = FALSE;
+       }
+
+      /* Check left side for crossing horizontal pixel boundaries */
+      ix1 = floor (pos.x1);
+
+      if (b.x1 < t.x1)
+       {
+         if (ix1 == pos.x1)
+           x1_next = ix1 - 1;
+         else
+           x1_next = ix1;
+
+         if (x1_next > pos_next.x1)
+           {
+             interpolate_position (&pos_next, &t, &b,
+                                   x1_next, t.x1, b.x1);
+             pos_next.x1 = x1_next;
+             done = FALSE;
+           }
+       }
+      else if (b.x1 > t.x1)
+       {
+         x1_next = ix1 + 1;
+
+         if (x1_next < pos_next.x1)
+           {
+             interpolate_position (&pos_next, &t, &b,
+                                   x1_next, t.x1, b.x1);
+             pos_next.x1 = x1_next;
+             done = FALSE;
+           }
+       }
+
+      /* Check right side for crossing horizontal pixel boundaries */
+      ix2 = floor (pos.x2);
+
+      if (b.x2 < t.x2)
+       {
+         if (ix2 == pos.x2)
+           x2_next = ix2 - 1;
+         else
+           x2_next = ix2;
+
+         if (x2_next > pos_next.x2)
+           {
+             interpolate_position (&pos_next, &t, &b,
+                                   x2_next, t.x2, b.x2);
+             pos_next.x2 = x2_next;
+             done = FALSE;
+           }
+       }
+      else if (trapezoid->x22 > trapezoid->x21)
+       {
+         x2_next = ix2 + 1;
+
+         if (x2_next < pos_next.x2)
+           {
+             interpolate_position (&pos_next, &t, &b,
+                                   x2_next, t.x2, b.x2);
+             pos_next.x2 = x2_next;
+             done = FALSE;
+           }
+       }
+
+      draw_simple_trap (pixbuf, pixbuf_x, pixbuf_y, &pos, &pos_next);
+      pos = pos_next;
+    }
+}
+
+static void
+gdk_drawable_real_draw_trapezoids (GdkDrawable  *drawable,
+                                  GdkGC        *gc,
+                                  GdkTrapezoid *trapezoids,
+                                  gint          n_trapezoids)
+{
+  GdkPixbuf *pixbuf;
+  double min_x, max_x, min_y, max_y;
+  int x, y, width, height;
+  GdkColor color;
+  int i;
+  
+  if (n_trapezoids == 0)
+    return;
+
+  /* compute bounding box */
+  
+  min_x = max_x = trapezoids[0].x11;
+  min_y = max_y = trapezoids[0].y1;
+
+  for (i = 0; i < n_trapezoids; i++)
+    {
+      if (trapezoids[i].x11 < min_x) min_x = trapezoids[i].x11;
+      if (trapezoids[i].x21 > max_x) max_x = trapezoids[i].x21;
+      if (trapezoids[i].x12 < min_x) min_x = trapezoids[i].x12;
+      if (trapezoids[i].x22 > max_x) max_x = trapezoids[i].x22;
+      if (trapezoids[i].y1 < min_y) min_y = trapezoids[i].y1;
+      if (trapezoids[i].y2 > max_y) max_y = trapezoids[i].y2;
+    }
+
+  /* allocate temporary pixbuf */
+
+  x = floor (min_x);
+  width = ceil (max_x) - x;
+  y = floor (min_y);
+  height = ceil (max_y) - y;
+
+  if (width == 0 || height == 0)
+    return;
+
+  pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, width, height);
+  if (!pixbuf)
+    return;
+
+  /* Fill the pixbuf with the foreground color and alpha 0 */
+  
+  _gdk_windowing_gc_get_foreground (gc, &color);
+  gdk_pixbuf_fill (pixbuf,
+                  (((color.red   & 0xff00) << 16) |
+                   ((color.green & 0xff00) <<  8) |
+                   ((color.blue  & 0xff00))));
+
+  /* draw the trapezoids into the alpha channel */
+
+  for (i = 0; i < n_trapezoids; i++)
+    draw_trapezoid (pixbuf, x, y, &trapezoids[i]);
+
+  /* composite that onto the drawable */
+
+  gdk_draw_pixbuf (drawable, gc, pixbuf,
+                  0, 0, x, y, width, height,
+                  GDK_RGB_DITHER_NORMAL, 0, 0);
+
+  g_object_unref (pixbuf);
+}
+
+/************************************************************************/
+
 /**
  * _gdk_drawable_get_scratch_gc:
  * @drawable: A #GdkDrawable
index 7f8058493f390e72f901659c528837b972c9774d..cc2cf6a4bf4f09393a47dde4795455a521d48a70 100644 (file)
@@ -11,6 +11,7 @@ extern "C" {
 #endif /* __cplusplus */
 
 typedef struct _GdkDrawableClass GdkDrawableClass;
+typedef struct _GdkTrapezoid     GdkTrapezoid;
 
 #define GDK_TYPE_DRAWABLE              (gdk_drawable_get_type ())
 #define GDK_DRAWABLE(object)           (G_TYPE_CHECK_INSTANCE_CAST ((object), GDK_TYPE_DRAWABLE, GdkDrawable))
@@ -154,10 +155,20 @@ struct _GdkDrawableClass
                                  gint            dest_y,
                                  gint            width,
                                  gint            height);
+  
+  void (*draw_glyphs_transformed) (GdkDrawable      *drawable,
+                                  GdkGC            *gc,
+                                  PangoMatrix      *matrix,
+                                  PangoFont        *font,
+                                  gint              x,
+                                  gint              y,
+                                  PangoGlyphString *glyphs);
+  void (*draw_trapezoids)         (GdkDrawable      *drawable,
+                                  GdkGC            *gc,
+                                  GdkTrapezoid     *trapezoids,
+                                  gint              n_trapezoids);
 
   /* Padding for future expansion */
-  void         (*_gdk_reserved1)  (void);
-  void         (*_gdk_reserved2)  (void);
   void         (*_gdk_reserved3)  (void);
   void         (*_gdk_reserved4)  (void);
   void         (*_gdk_reserved5)  (void);
@@ -173,6 +184,11 @@ struct _GdkDrawableClass
   void         (*_gdk_reserved16) (void);
 };
 
+struct _GdkTrapezoid
+{
+  double y1, x11, x21, y2, x12, x22;
+};
+
 GType           gdk_drawable_get_type     (void);
 
 /* Manipulation of drawables
@@ -338,6 +354,18 @@ void gdk_draw_layout_with_colors      (GdkDrawable     *drawable,
                                        const GdkColor  *foreground,
                                        const GdkColor  *background);
 
+void gdk_draw_glyphs_transformed (GdkDrawable      *drawable,
+                                 GdkGC            *gc,
+                                 PangoMatrix      *matrix,
+                                 PangoFont        *font,
+                                 gint              x,
+                                 gint              y,
+                                 PangoGlyphString *glyphs);
+void gdk_draw_trapezoids         (GdkDrawable      *drawable,
+                                 GdkGC            *gc,
+                                 GdkTrapezoid     *trapezoids,
+                                 gint              n_trapezoids);
+
 #ifndef GDK_DISABLE_DEPRECATED
 #define gdk_draw_pixmap                gdk_draw_drawable
 #define gdk_draw_bitmap                gdk_draw_drawable
@@ -360,7 +388,6 @@ GdkImage *gdk_drawable_copy_to_image (GdkDrawable  *drawable,
 GdkRegion *gdk_drawable_get_clip_region    (GdkDrawable *drawable);
 GdkRegion *gdk_drawable_get_visible_region (GdkDrawable *drawable);
 
-
 gboolean gdk_draw_rectangle_alpha_libgtk_only (GdkDrawable *drawable,
                                               gint         x,
                                               gint         y,
index b9e14eba46650e1dcd1f0c4b36a469ea30a8ee56..19667da3b52921ea4e55b9695530baa6f8df58a2 100644 (file)
@@ -320,6 +320,11 @@ void _gdk_windowing_display_set_sm_client_id (GdkDisplay  *display,
 GType _gdk_window_impl_get_type (void) G_GNUC_CONST;
 GType _gdk_pixmap_impl_get_type (void) G_GNUC_CONST;
 
+
+/* Queries the current foreground color of a GdkGC */
+void _gdk_windowing_gc_get_foreground (GdkGC    *gc,
+                                      GdkColor *color);
+
 /************************************
  * Initialization and exit routines *
  ************************************/
index 93e5ab2883f496870f4085c12d85902412822745..374d90d6d1d9ff5273d02811935be06adf17b102 100644 (file)
@@ -18,6 +18,7 @@
  */
 
 #include <config.h>
+#include <math.h>
 #include "gdkalias.h"
 #include "gdkcolor.h"
 #include "gdkgc.h"
 #include "gdkprivate.h"
 #include "gdkscreen.h"
 
+/* This is for P_() ... a bit non-kosher, but works fine */
+#include "gtk/gtkintl.h"
+
 #define GDK_INFO_KEY "gdk-info"
 
-typedef struct _GdkPangoContextInfo GdkPangoContextInfo;
+/* We have various arrays indexed by render part; if PangoRenderPart
+ * is extended, we want to make sure not to overwrite the end of
+ * those arrays.
+ */
+#define MAX_RENDER_PART  PANGO_RENDER_PART_STRIKETHROUGH
 
-struct _GdkPangoContextInfo
+struct _GdkPangoRendererPrivate
 {
-  GdkColormap *colormap;
+  GdkScreen *screen;
+
+  /* GdkPangoRenderer specific state */
+  PangoColor override_color[MAX_RENDER_PART + 1];
+  gboolean override_color_set[MAX_RENDER_PART + 1];
+  
+  GdkBitmap *stipple[MAX_RENDER_PART + 1];
+  gboolean embossed;
+
+  /* When switching between the normal and shadow copies when
+   * drawing shadows we can get unexpected recursion into the
+   * drawing functions; the 'in_emboss' flag guards against that.
+   */
+  gboolean in_emboss;
+
+  /* Current target */
+  GdkDrawable *drawable;
+  GdkGC *base_gc;
+
+  /* Cached GC, derived from base_gc */
+  GdkGC *gc;
+  PangoColor gc_color;
+  gboolean gc_color_set;
+  GdkBitmap *gc_stipple;
+  
+  /* we accumulate trapezoids for the same PangoRenderPart */
+  GArray *trapezoids;
+  PangoRenderPart trapezoid_part;
 };
 
 static PangoAttrType gdk_pango_attr_stipple_type;
 static PangoAttrType gdk_pango_attr_embossed_type;
 
-static void gdk_pango_get_item_properties (PangoItem      *item,
-                                          PangoUnderline *uline,
-                                          gboolean       *strikethrough,
-                                           gint           *rise,
-                                          PangoColor     *fg_color,
-                                          gboolean       *fg_set,
-                                          PangoColor     *bg_color,
-                                          gboolean       *bg_set,
-                                           gboolean       *embossed,
-                                           GdkBitmap     **stipple,
-                                          gboolean       *shape_set,
-                                          PangoRectangle *ink_rect,
-                                          PangoRectangle *logical_rect);
+static void flush_trapezoids (GdkPangoRenderer *gdk_renderer);
+
+enum {
+  PROP_0,
+  PROP_SCREEN
+};
+
+G_DEFINE_TYPE (GdkPangoRenderer, gdk_pango_renderer, PANGO_TYPE_RENDERER)
 
 static void
-gdk_pango_context_destroy (GdkPangoContextInfo *info)
+gdk_pango_renderer_finalize (GObject *object)
 {
-  if (info->colormap)
-    g_object_unref (info->colormap);
-  g_free (info);
+  GdkPangoRenderer *gdk_renderer = GDK_PANGO_RENDERER (object);
+  GdkPangoRendererPrivate *priv = gdk_renderer->priv;
+  int i;
+
+  if (priv->gc)
+    g_object_unref (priv->gc);
+  if (priv->gc_stipple)
+    g_object_unref (priv->gc_stipple);
+  if (priv->base_gc)
+    g_object_unref (priv->base_gc);
+  if (priv->drawable)
+    g_object_unref (priv->drawable);
+
+  for (i = 0; i <= MAX_RENDER_PART; i++)
+    if (priv->stipple[i])
+      g_object_unref (priv->stipple[i]);
+
+  g_array_free (priv->trapezoids, TRUE);
+
+  G_OBJECT_CLASS (gdk_pango_renderer_parent_class)->finalize (object);
 }
 
-static GdkPangoContextInfo *
-gdk_pango_context_get_info (PangoContext *context, gboolean create)
+static GObject*
+gdk_pango_renderer_constructor (GType                  type,
+                               guint                  n_construct_properties,
+                               GObjectConstructParam *construct_params)
 {
-  GdkPangoContextInfo *info =
-    g_object_get_qdata (G_OBJECT (context),
-                        g_quark_try_string (GDK_INFO_KEY));
-  if (!info && create)
+  GObject *object;
+  GdkPangoRenderer *gdk_renderer;
+
+  object = (* G_OBJECT_CLASS (gdk_pango_renderer_parent_class)->constructor) (type,
+                                                                             n_construct_properties,
+                                                                             construct_params);
+
+  gdk_renderer = GDK_PANGO_RENDERER (object);
+  
+  if (!gdk_renderer->priv->screen)
     {
-      info = g_new (GdkPangoContextInfo, 1);
-      info->colormap = NULL;
+      g_warning ("Screen must be specified at construct time for GdkPangoRenderer");
+      gdk_renderer->priv->screen = gdk_screen_get_default ();
+    }
 
-      g_object_set_qdata_full (G_OBJECT (context),
-                               g_quark_from_static_string (GDK_INFO_KEY),
-                               info, (GDestroyNotify)gdk_pango_context_destroy);
+  return object;
+}
+
+/* Adjusts matrix and color for the renderer to draw the secondar
+ * "shadow" copy for embossed text */
+static void
+emboss_renderer (PangoRenderer   *renderer,
+                PangoRenderPart  part,
+                PangoMatrix    **save_matrix,
+                PangoColor     **save_color)
+{
+  GdkPangoRendererPrivate *priv = GDK_PANGO_RENDERER(renderer)->priv;
+  static const PangoColor white = { 0xffff, 0xffff, 0xffff };
+  PangoMatrix tmp_matrix = PANGO_MATRIX_INIT;
+
+  priv->in_emboss = TRUE;
+  
+  *save_color = pango_renderer_get_color (renderer, part);
+  if (*save_color)
+    *save_color = pango_color_copy (*save_color);
+  
+  *save_matrix = renderer->matrix;
+  if (*save_matrix)
+    {
+      *save_matrix = pango_matrix_copy (*save_matrix);
+      tmp_matrix = **save_matrix;
     }
+  
+  /* The gymnastics here to adjust the matrix are because we want
+   * to offset by +1,+1 in device-space, not in user-space,
+   * so we can't just draw the layout at x + 1, y + 1
+   */
+  tmp_matrix.x0 += 1;
+  tmp_matrix.y0 += 1;
+  
+  pango_renderer_set_matrix (renderer, &tmp_matrix);
+  pango_renderer_set_color (renderer, part, &white);
+}
 
-  return info;
+/* Restores from emboss_renderer() */
+static void
+unemboss_renderer (PangoRenderer   *renderer,
+                  PangoRenderPart  part,
+                  PangoMatrix    **save_matrix,
+                  PangoColor     **save_color)
+{
+  GdkPangoRendererPrivate *priv = GDK_PANGO_RENDERER(renderer)->priv;
+  pango_renderer_set_matrix (renderer, *save_matrix);
+  pango_renderer_set_color (renderer, part, *save_color);
+
+  if (*save_matrix)
+    pango_matrix_free (*save_matrix);
+  if (*save_color)
+    pango_color_free (*save_color);
+
+  priv->in_emboss = FALSE;
 }
 
+/* Gets the GC for drawing @part. This make involve copying the base GC
+ * for the renderer, in which case we keep a one-GC cache. */
 static GdkGC *
-gdk_pango_get_gc (GdkDrawable    *drawable,
-                 PangoContext   *context,
-                 PangoColor     *fg_color,
-                  GdkBitmap      *stipple,
-                 GdkGC          *base_gc)
+get_gc (GdkPangoRenderer *gdk_renderer,
+       PangoRenderPart   part)
+{
+  PangoRenderer *renderer = PANGO_RENDERER (gdk_renderer);
+  PangoColor *color;
+  GdkBitmap *stipple;
+  GdkPangoRendererPrivate *priv = gdk_renderer->priv;
+
+  color = pango_renderer_get_color (renderer, part);
+
+  if (part <= MAX_RENDER_PART)
+    stipple = priv->stipple[part];
+  else
+    stipple = NULL;
+
+  if (!color && !stipple)      /* nothing override, use base_gc */
+    return priv->base_gc;
+  else
+    {
+      gboolean new_stipple = FALSE;
+      gboolean new_color = FALSE;
+      
+      if (stipple != priv->gc_stipple)
+       new_stipple = TRUE;
+
+      if ((priv->gc_color_set && !color) ||
+         (!priv->gc_color_set && color) ||
+         priv->gc_color.red != color->red ||
+         priv->gc_color.green != color->green ||
+         priv->gc_color.blue != color->blue)
+       new_color = TRUE;
+      
+      if (!priv->gc)
+       {
+         priv->gc = gdk_gc_new (priv->drawable);
+         gdk_gc_copy (priv->gc, priv->base_gc);
+       }
+      else if (new_color && priv->gc_color_set && !color)
+       {
+         /* We have to recopy the original GC onto the cached GC
+          * to get the default color */
+         new_stipple = TRUE;
+         gdk_gc_copy (priv->gc, priv->base_gc);
+       }
+      else if (new_stipple && priv->gc_stipple && !stipple)
+       {
+         /* Similarly, we need to make a new copy to restore to the
+          * default stipple state (the caller may have set a stipple
+          * on the GC, and even if not, gdk_gc_set_stipple (gc, NULL)
+          * doesn't work currently to restore to the default X stipple) */
+         new_color = TRUE;
+         gdk_gc_copy (priv->gc, priv->base_gc);
+       }
+
+      if (new_color)
+       {
+         if (color)
+           {
+             GdkColor gdk_color;
+
+             gdk_color.red = color->red;
+             gdk_color.green = color->green;
+             gdk_color.blue = color->blue;
+             
+             gdk_gc_set_rgb_fg_color (priv->gc, &gdk_color);
+
+             priv->gc_color = *color;
+             priv->gc_color_set = TRUE;
+           }
+         else
+           priv->gc_color_set = FALSE;
+       }
+
+      if (new_stipple)
+       {
+         if (priv->gc_stipple)
+           g_object_unref (priv->gc_stipple);
+
+         if (stipple)
+           {
+             gdk_gc_set_stipple (priv->gc, stipple);
+             gdk_gc_set_fill (priv->gc, GDK_STIPPLED);
+             priv->gc_stipple = g_object_ref (stipple);
+           }
+         else
+           priv->gc_stipple = NULL;
+       }
+
+      return priv->gc;
+    }
+}
+
+static void
+gdk_pango_renderer_draw_glyphs (PangoRenderer    *renderer,
+                               PangoFont        *font,
+                               PangoGlyphString *glyphs,
+                               int               x,
+                               int               y)
+{
+  GdkPangoRenderer *gdk_renderer = GDK_PANGO_RENDERER (renderer);
+  GdkPangoRendererPrivate *priv = gdk_renderer->priv;
+
+  flush_trapezoids (gdk_renderer);
+
+  if (!priv->in_emboss && priv->embossed)
+    {
+      PangoMatrix *save_matrix;
+      PangoColor *save_color;
+
+      emboss_renderer (renderer, PANGO_RENDER_PART_FOREGROUND, &save_matrix, &save_color);
+      gdk_draw_glyphs_transformed (priv->drawable,
+                                  get_gc (gdk_renderer, PANGO_RENDER_PART_FOREGROUND),
+                                  renderer->matrix, font, x, y, glyphs);
+      unemboss_renderer (renderer, PANGO_RENDER_PART_FOREGROUND, &save_matrix, &save_color);
+    }
+
+  gdk_draw_glyphs_transformed (priv->drawable,
+                              get_gc (gdk_renderer, PANGO_RENDER_PART_FOREGROUND),
+                              renderer->matrix, font, x, y, glyphs);
+}
+
+/* Outputs any pending trapezoids, we do this when the part or
+ * part color changes, when we are about to draw text, etc. */
+static void
+flush_trapezoids (GdkPangoRenderer *gdk_renderer)
 {
-  GdkGC *result;
-  GdkPangoContextInfo *info;
+  GdkPangoRendererPrivate *priv = gdk_renderer->priv;
+
+  if (!priv->trapezoids || priv->trapezoids->len == 0)
+    return;
+
+  gdk_draw_trapezoids (priv->drawable,
+                      get_gc (gdk_renderer, priv->trapezoid_part),
+                      (GdkTrapezoid *)priv->trapezoids->data,
+                      priv->trapezoids->len);
+
+  g_array_set_size (priv->trapezoids, 0);
+}
+
+/* Draws a single trapezoid ... we don't draw it immediately, but rather
+ * cache it to join together with other trapezoids that form part of the
+ * same logical shape */
+static void
+gdk_pango_renderer_draw_trapezoid (PangoRenderer   *renderer,
+                                  PangoRenderPart  part,
+                                  double           y1,
+                                  double           x11,
+                                  double           x21,
+                                  double           y2,
+                                  double           x12,
+                                  double           x22)
+{
+  GdkPangoRenderer *gdk_renderer = GDK_PANGO_RENDERER (renderer);
+  GdkTrapezoid trap;
+
+  if (!gdk_renderer->priv->trapezoids)
+    gdk_renderer->priv->trapezoids = g_array_new (FALSE, FALSE,
+                                                 sizeof (GdkTrapezoid));
   
-  g_return_val_if_fail (context != NULL, NULL);
+  if (gdk_renderer->priv->trapezoids->len > 0 &&
+      gdk_renderer->priv->trapezoid_part != part)
+    flush_trapezoids (gdk_renderer);
+  
+  gdk_renderer->priv->trapezoid_part = part;
+
+  trap.y1 = y1;
+  trap.x11 = x11;
+  trap.x21 = x21;
+  trap.y2 = y2;
+  trap.x12 = x12;
+  trap.x22 = x22;
 
-  info = gdk_pango_context_get_info (context, FALSE);
+  g_array_append_val (gdk_renderer->priv->trapezoids, trap);
+}
+
+/* We can't handle embossing at the level of trapezoids, because when an
+ * underline is split into multiple trapezoids, the normal and shadow
+ * trapezoids will be drawn mixed together. Instead, we have to emboss
+ * and entire rectangle or error underline
+ */
+
+static void
+gdk_pango_renderer_draw_rectangle (PangoRenderer    *renderer,
+                                  PangoRenderPart   part,
+                                  int               x,
+                                  int               y,
+                                  int               width,
+                                  int               height)
+{
+  GdkPangoRenderer *gdk_renderer = GDK_PANGO_RENDERER (renderer);
+  GdkPangoRendererPrivate *priv = gdk_renderer->priv;
 
-  if (info == NULL || info->colormap == NULL)
+  if (!priv->in_emboss && priv->embossed && part != PANGO_RENDER_PART_BACKGROUND)
     {
-      g_warning ("you must set the colormap on a PangoContext before using it to draw a layout");
-      return NULL;
+      PangoMatrix *save_matrix;
+      PangoColor *save_color;
+
+      emboss_renderer (renderer, part, &save_matrix, &save_color);
+      PANGO_RENDERER_CLASS (gdk_pango_renderer_parent_class)->draw_rectangle (renderer, part,
+                                                                             x, y, width, height);
+      unemboss_renderer (renderer, part, &save_matrix, &save_color);
+    }
+
+  PANGO_RENDERER_CLASS (gdk_pango_renderer_parent_class)->draw_rectangle (renderer, part,
+                                                                         x, y, width, height);
+}
+
+static void
+gdk_pango_renderer_draw_error_underline (PangoRenderer    *renderer,
+                                        int               x,
+                                        int               y,
+                                        int               width,
+                                        int               height)
+{
+  GdkPangoRenderer *gdk_renderer = GDK_PANGO_RENDERER (renderer);
+  GdkPangoRendererPrivate *priv = gdk_renderer->priv;
+
+  if (!priv->in_emboss && priv->embossed)
+    {
+      PangoMatrix *save_matrix;
+      PangoColor *save_color;
+
+      emboss_renderer (renderer, PANGO_RENDER_PART_UNDERLINE, &save_matrix, &save_color);
+      PANGO_RENDERER_CLASS (gdk_pango_renderer_parent_class)->draw_error_underline (renderer,
+                                                                                   x, y, width, height);
+      unemboss_renderer (renderer, PANGO_RENDER_PART_UNDERLINE, &save_matrix, &save_color);
+    }
+
+  PANGO_RENDERER_CLASS (gdk_pango_renderer_parent_class)->draw_error_underline (renderer,
+                                                                               x, y, width, height);
+}
+
+static void
+gdk_pango_renderer_part_changed (PangoRenderer   *renderer,
+                                PangoRenderPart  part)
+{
+  GdkPangoRenderer *gdk_renderer = GDK_PANGO_RENDERER (renderer);
+
+  if (part == gdk_renderer->priv->trapezoid_part)
+    flush_trapezoids (gdk_renderer);
+}
+
+static void
+gdk_pango_renderer_begin (PangoRenderer *renderer)
+{
+  GdkPangoRenderer *gdk_renderer = GDK_PANGO_RENDERER (renderer);
+
+  if (!gdk_renderer->priv->drawable || !gdk_renderer->priv->base_gc)
+    {
+      g_warning ("gdk_pango_renderer_set_drawable() and gdk_pango_renderer_set_drawable()"
+                "must be used to set the target drawable and GC before using the renderer\n");
     }
+}
+
+static void
+gdk_pango_renderer_end (PangoRenderer *renderer)
+{
+  GdkPangoRenderer *gdk_renderer = GDK_PANGO_RENDERER (renderer);
+
+  flush_trapezoids (gdk_renderer);
+}
 
-  result = gdk_gc_new (drawable);
-  gdk_gc_copy (result, base_gc);
+static void
+gdk_pango_renderer_prepare_run (PangoRenderer  *renderer,
+                               PangoLayoutRun *run)
+{
+  GdkPangoRenderer *gdk_renderer = GDK_PANGO_RENDERER (renderer);
+  gboolean embossed = FALSE;
+  GdkBitmap *stipple = NULL;
+  GSList *l;
+  int i;
+  
+  embossed = FALSE;
+  stipple = NULL;
   
-  if (fg_color)
+  for (l = run->item->analysis.extra_attrs; l; l = l->next)
     {
-      GdkColor color;
-      
-      color.red = fg_color->red;
-      color.green = fg_color->green;
-      color.blue = fg_color->blue;
+      PangoAttribute *attr = l->data;
 
-      gdk_rgb_find_color (info->colormap, &color);
-      gdk_gc_set_foreground (result, &color);
+      /* stipple_type and embossed_type aren't necessarily
+       * initialized, but they are 0, which is an
+       * invalid type so won't occur. 
+       */
+      if (attr->klass->type == gdk_pango_attr_stipple_type)
+       {
+         stipple = ((GdkPangoAttrStipple*)attr)->stipple;
+       }
+      else if (attr->klass->type == gdk_pango_attr_embossed_type)
+       {
+         embossed = ((GdkPangoAttrEmbossed*)attr)->embossed;
+       }
     }
 
-  if (stipple)
+  gdk_pango_renderer_set_stipple (gdk_renderer, PANGO_RENDER_PART_FOREGROUND, stipple);
+  gdk_pango_renderer_set_stipple (gdk_renderer, PANGO_RENDER_PART_BACKGROUND, stipple);
+  gdk_pango_renderer_set_stipple (gdk_renderer, PANGO_RENDER_PART_UNDERLINE, stipple);
+  gdk_pango_renderer_set_stipple (gdk_renderer, PANGO_RENDER_PART_STRIKETHROUGH, stipple);
+
+  if (embossed != gdk_renderer->priv->embossed)
     {
-      gdk_gc_set_fill (result, GDK_STIPPLED);
-      gdk_gc_set_stipple (result, stipple);
+      gdk_renderer->priv->embossed = embossed;
+      pango_renderer_part_changed (renderer, PANGO_RENDER_PART_FOREGROUND);
     }
+
+  PANGO_RENDERER_CLASS (gdk_pango_renderer_parent_class)->prepare_run (renderer, run);
+
+  for (i = 0; i <= MAX_RENDER_PART; i++)
+    {
+      if (gdk_renderer->priv->override_color_set[i])
+       pango_renderer_set_color (renderer, i, &gdk_renderer->priv->override_color[i]);
+    }
+}
+
+static void
+gdk_pango_renderer_set_property (GObject         *object,
+                                guint            prop_id,
+                                const GValue    *value,
+                                GParamSpec      *pspec)
+{
+  GdkPangoRenderer *gdk_renderer = GDK_PANGO_RENDERER (object);
+
+  switch (prop_id)
+    {
+    case PROP_SCREEN:
+      gdk_renderer->priv->screen = g_value_get_object (value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+gdk_pango_renderer_get_property (GObject    *object,
+                                guint       prop_id,
+                                GValue     *value,
+                                GParamSpec *pspec)
+{
+  GdkPangoRenderer *gdk_renderer = GDK_PANGO_RENDERER (object);
+
+  switch (prop_id)
+    {
+    case PROP_SCREEN:
+      g_value_set_object (value, gdk_renderer->priv->screen);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+gdk_pango_renderer_init (GdkPangoRenderer *renderer)
+{
+  renderer->priv = G_TYPE_INSTANCE_GET_PRIVATE (renderer,
+                                               GDK_TYPE_PANGO_RENDERER,
+                                               GdkPangoRendererPrivate);
+}
+
+static void
+gdk_pango_renderer_class_init (GdkPangoRendererClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  
+  PangoRendererClass *renderer_class = PANGO_RENDERER_CLASS (klass);
+  
+  renderer_class->draw_glyphs = gdk_pango_renderer_draw_glyphs;
+  renderer_class->draw_trapezoid = gdk_pango_renderer_draw_trapezoid;
+  renderer_class->draw_rectangle = gdk_pango_renderer_draw_rectangle;
+  renderer_class->draw_error_underline = gdk_pango_renderer_draw_error_underline;
+  renderer_class->part_changed = gdk_pango_renderer_part_changed;
+  renderer_class->begin = gdk_pango_renderer_begin;
+  renderer_class->end = gdk_pango_renderer_end;
+  renderer_class->prepare_run = gdk_pango_renderer_prepare_run;
+
+  object_class->finalize = gdk_pango_renderer_finalize;
+  object_class->constructor = gdk_pango_renderer_constructor;
+  object_class->set_property = gdk_pango_renderer_set_property;
+  object_class->get_property = gdk_pango_renderer_get_property;
+  
+  g_object_class_install_property (object_class,
+                                   PROP_SCREEN,
+                                   g_param_spec_object ("screen",
+                                                        P_("Screen"),
+                                                        P_("the GdkScreen for the renderer"),
+                                                        GDK_TYPE_SCREEN,
+                                                        G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+  g_type_class_add_private (object_class, sizeof (GdkPangoRendererPrivate));  
+}
+
+/**
+ * gdk_pango_renderer_new:
+ * @screen: a #GdkScreen
+ * 
+ * Creates a new #PangoRenderer for @screen. Normally you can use the
+ * results of gdk_pango_renderer_get_default() rather than creating a new
+ * renderer.
+ * 
+ * Return value: a newly created #PangoRenderer. Free with g_object_unref().
+ *
+ * Since: 2.6
+ **/
+PangoRenderer *
+gdk_pango_renderer_new (GdkScreen *screen)
+{
+  g_return_val_if_fail (screen != NULL, NULL);
   
-  return result;
+  return g_object_new (GDK_TYPE_PANGO_RENDERER,
+                      "screen", screen,
+                      NULL);
 }
 
 static void
-gdk_pango_free_gc (PangoContext *context,
-                  GdkGC        *gc)
+on_renderer_display_closed (GdkDisplay       *display,
+                           GdkPangoRenderer *renderer)
 {
-  g_object_unref (gc);
+  g_signal_handlers_disconnect_by_func (renderer->priv->screen,
+                                       (gpointer)on_renderer_display_closed,
+                                       renderer);
+  g_object_set_data (G_OBJECT (renderer->priv->screen), "gdk-pango-renderer", NULL);
 }
 
 /**
- * gdk_pango_context_set_colormap:
- * @context: a #PangoContext
- * @colormap: a #GdkColormap
+ * gdk_pango_renderer_get_default:
+ * @screen: a #GdkScreen
+ * 
+ * Gets the default #PangoRenderer for a screen. This default renderer
+ * is shared by all users of the display, so properties such as the color
+ * or transformation matrix set for the renderer may be overwritten
+ * by functions such as gdk_draw_layout().
  *
- * Sets the colormap to be used for drawing with @context.
+ * Before using the renderer, you need to call gdk_pango_renderer_set_drawable()
+ * and gdk_pango_renderer_set_drawable() to set the drawable and graphics context
+ * to use for drawing.
+ * 
+ * Return value: the default #PangoRenderer for @screen. The
+ *  renderer is owned by GTK+ and will be kept around until the
+ *  screen is closed.
  *
- * If you obtained your context from gtk_widget_get_pango_context() or
- * gtk_widget_create_pango_context(), the colormap will already be set
- * to the colormap for the widget, so you shouldn't need this
- * function.
+ * Since: 2.6
+ **/
+PangoRenderer *
+gdk_pango_renderer_get_default (GdkScreen *screen)
+{
+  PangoRenderer *renderer;
+
+  g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL);
+  
+  renderer = g_object_get_data (G_OBJECT (screen), "gdk-pango-renderer");
+  if (!renderer)
+    {
+      renderer = gdk_pango_renderer_new (screen);
+      g_object_set_data_full (G_OBJECT (screen), "gdk-pango-renderer", renderer,
+                             (GDestroyNotify)g_object_unref);
+
+      g_signal_connect (gdk_screen_get_display (screen), "closed",
+                       G_CALLBACK (on_renderer_display_closed), renderer);
+    }
+
+  return renderer;
+}
+
+/**
+ * gdk_pango_renderer_set_drawable:
+ * @gdk_renderer: a #GdkPangoRenderer
+ * @drawable: the new target drawable, or %NULL
+ * 
+ * Sets the drawable the renderer draws to.
+ *
+ * Since: 2.6
  **/
 void
-gdk_pango_context_set_colormap (PangoContext *context,
-                               GdkColormap  *colormap)
+gdk_pango_renderer_set_drawable (GdkPangoRenderer *gdk_renderer,
+                                GdkDrawable      *drawable)
+{
+  GdkPangoRendererPrivate *priv;
+  
+  g_return_if_fail (GDK_IS_PANGO_RENDERER (gdk_renderer));
+  g_return_if_fail (drawable == NULL || GDK_IS_DRAWABLE (drawable));
+
+  priv = gdk_renderer->priv;
+  
+  flush_trapezoids (gdk_renderer);
+
+  if (priv->drawable != drawable)
+    {
+      if (priv->drawable)
+       g_object_unref (priv->drawable);
+      priv->drawable = drawable;
+      if (priv->drawable)
+       g_object_ref (priv->drawable);
+    }
+}
+
+/**
+ * gdk_pango_renderer_set_gc:
+ * @gdk_renderer: a #GdkPangoRenderer
+ * @gc: the new GC to use for drawing, or %NULL
+ * 
+ * Sets the GC the renderer draws with. Note that the GC must not be
+ * modified until it is unset by calling the function again with
+ * %NULL for the @gc parameter, since GDK may make internal copies
+ * of the GC which won't be updated to follow changes to the
+ * original GC.
+ *
+ * Since: 2.6
+ **/
+void
+gdk_pango_renderer_set_gc (GdkPangoRenderer *gdk_renderer,
+                          GdkGC            *gc)
 {
-  GdkPangoContextInfo *info;
+  GdkPangoRendererPrivate *priv;
   
-  g_return_if_fail (context != NULL);
+  g_return_if_fail (GDK_IS_PANGO_RENDERER (gdk_renderer));
+  g_return_if_fail (gc == NULL || GDK_IS_GC (gc));
 
-  info = gdk_pango_context_get_info (context, TRUE);
-  g_return_if_fail (info != NULL);
+  priv = gdk_renderer->priv;
   
-  if (info->colormap != colormap)
+  flush_trapezoids (gdk_renderer);
+
+  if (priv->base_gc != gc)
     {
-      if (info->colormap)
-       g_object_unref (info->colormap);
+      if (priv->base_gc)
+       g_object_unref (priv->base_gc);
+      priv->base_gc = gc;
+      if (priv->base_gc)
+       g_object_ref (priv->base_gc);
 
-      info->colormap = colormap;
+      if (priv->gc)
+       {
+         g_object_unref (priv->gc);
+         priv->gc = NULL;
+       }
       
-      if (info->colormap)
-       g_object_ref (info->colormap);
+      priv->gc_color_set = FALSE;
+
+      if (priv->gc_stipple)
+       {
+         g_object_unref (priv->gc_stipple);
+         priv->gc_stipple = NULL;
+       }
     }
 }
 
-static void
-draw_underline (GdkDrawable    *drawable,
-               GdkGC          *gc,
-               PangoUnderline  uline,
-               int             baseline_y,
-               int             low_y,
-               int             start_x,
-               int             end_x)
-{
-  switch (uline)
+
+/**
+ * gdk_pango_renderer_set_stipple:
+ * @gdk_renderer: a #GdkPangoRenderer
+ * @part: the part to render with the stipple
+ * @stipple: the new stipple value.
+ * 
+ * Sets the stipple for one render part (foreground, background, underline,
+ * etc.) Note that this is overwritten when iterating through the individual
+ * styled runs of a #PangoLayout or #PangoLayoutLine. This function is thus
+ * only useful when you call low level functions like pango_renderer_draw_glyphs()
+ * directly, or in the 'prepare_run' virtual function of a subclass of
+ * #GdkPangoRenderer.
+ *
+ * Since: 2.6
+ **/
+void
+gdk_pango_renderer_set_stipple (GdkPangoRenderer *gdk_renderer,
+                               PangoRenderPart   part,
+                               GdkBitmap        *stipple)
+{
+  g_return_if_fail (GDK_IS_PANGO_RENDERER (gdk_renderer));
+
+  if (part > MAX_RENDER_PART)  /* Silently ignore unknown parts */
+    return;
+
+  if (stipple != gdk_renderer->priv->stipple[part])
     {
-    case PANGO_UNDERLINE_NONE:
-      break;
-    case PANGO_UNDERLINE_DOUBLE:
-      gdk_draw_line (drawable, gc,
-                    start_x, baseline_y + 3,
-                    end_x,   baseline_y + 3);
-      /* Fall through */
-    case PANGO_UNDERLINE_SINGLE:
-      gdk_draw_line (drawable, gc,
-                    start_x, baseline_y + 1,
-                    end_x,   baseline_y + 1);
-      break;
-    case PANGO_UNDERLINE_ERROR:
-      {
-        int point_x, point_y;
-        int counter = 0;
-
-        for (point_x = start_x;
-             point_x <= end_x;
-             point_x += 2)
-         {
-           point_y = counter ? baseline_y + 1 : baseline_y + 2;
-           
-           gdk_draw_line (drawable, gc,
-                          point_x, point_y,
-                          MIN (point_x + 1, end_x), point_y);
-           
-           counter = (counter + 1) % 2;
-         }
-      }
-      break;
-    case PANGO_UNDERLINE_LOW:
-      gdk_draw_line (drawable, gc,
-                    start_x, low_y + 1,
-                    end_x,   low_y + 1);
-      break;
+      if (gdk_renderer->priv->stipple[part])
+       g_object_unref (gdk_renderer->priv->stipple[part]);
+
+      gdk_renderer->priv->stipple[part] = stipple;
+      
+      if (gdk_renderer->priv->stipple[part])
+       g_object_ref (gdk_renderer->priv->stipple[part]);
+
+      pango_renderer_part_changed (PANGO_RENDERER (gdk_renderer), part);
     }
 }
 
+/**
+ * gdk_pango_renderer_set_override_color:
+ * @gdk_renderer: a #GdkPangoRenderer
+ * @part: the part to render to set the color of
+ * @color: the color to use, or %NULL to unset a previously
+ *         set override color.
+ * 
+ * Sets the color for a particular render part (foreground,
+ * background, underline, etc.), overriding any attributes on the layouts
+ * renderered with this renderer.
+ * 
+ * Since: 2.6
+ **/
+void
+gdk_pango_renderer_set_override_color (GdkPangoRenderer *gdk_renderer,
+                                      PangoRenderPart   part,
+                                      const GdkColor   *color)
+{
+  GdkPangoRendererPrivate *priv;
+  
+  g_return_if_fail (GDK_IS_PANGO_RENDERER (gdk_renderer));
+
+  priv = gdk_renderer->priv;
+  
+  if (part > MAX_RENDER_PART)  /* Silently ignore unknown parts */
+    return;
+
+  if (color)
+    {
+      priv->override_color[part].red = color->red;
+      priv->override_color[part].green = color->green;
+      priv->override_color[part].blue = color->blue;
+      priv->override_color_set[part] = TRUE;
+    }
+  else
+    priv->override_color_set[part] = FALSE;
+}
+
+/**
+ * gdk_pango_context_set_colormap:
+ * @context: a #PangoContext
+ * @colormap: a #GdkColormap
+ *
+ * This function used to set the colormap to be used for drawing with
+ * @context. The colormap is now always derived from the graphics
+ * context used for drawing, so calling this function is no longer
+ * necessary.
+ **/
+void
+gdk_pango_context_set_colormap (PangoContext *context,
+                               GdkColormap  *colormap)
+{
+  g_return_if_fail (PANGO_IS_CONTEXT (context));
+  g_return_if_fail (colormap == NULL || GDK_IS_COLORMAP (colormap));
+}
+
+/* Gets a renderer to draw with, setting the properties of the
+ * renderer and activating it. Note that since we activate the
+ * renderer here, the implicit setting of the matrix that
+ * pango_renderer_draw_layout_[line] normally do when they
+ * activate the renderer is suppressed. */
+static PangoRenderer *
+get_renderer (GdkDrawable     *drawable,
+             GdkGC           *gc,
+             const GdkColor  *foreground,
+             const GdkColor  *background)
+{
+  GdkScreen *screen = gdk_drawable_get_screen (drawable);
+  PangoRenderer *renderer = gdk_pango_renderer_get_default (screen);
+  GdkPangoRenderer *gdk_renderer = GDK_PANGO_RENDERER (renderer);
+
+  gdk_pango_renderer_set_drawable (gdk_renderer, drawable);
+  gdk_pango_renderer_set_gc (gdk_renderer, gc);  
+
+  gdk_pango_renderer_set_override_color (gdk_renderer,
+                                        PANGO_RENDER_PART_FOREGROUND,
+                                        foreground);
+  gdk_pango_renderer_set_override_color (gdk_renderer,
+                                        PANGO_RENDER_PART_UNDERLINE,
+                                        foreground);
+  gdk_pango_renderer_set_override_color (gdk_renderer,
+                                        PANGO_RENDER_PART_STRIKETHROUGH,
+                                        foreground);
+
+  gdk_pango_renderer_set_override_color (gdk_renderer,
+                                        PANGO_RENDER_PART_BACKGROUND,
+                                        background);
+
+  pango_renderer_activate (renderer);
+
+  return renderer;
+}
+
+/* Cleans up the renderer obtained with get_renderer() */
+static void
+release_renderer (PangoRenderer *renderer)
+{
+  GdkPangoRenderer *gdk_renderer = GDK_PANGO_RENDERER (renderer);
+  
+  pango_renderer_deactivate (renderer);
+  
+  gdk_pango_renderer_set_override_color (gdk_renderer,
+                                        PANGO_RENDER_PART_FOREGROUND,
+                                        NULL);
+  gdk_pango_renderer_set_override_color (gdk_renderer,
+                                        PANGO_RENDER_PART_UNDERLINE,
+                                        NULL);
+  gdk_pango_renderer_set_override_color (gdk_renderer,
+                                        PANGO_RENDER_PART_STRIKETHROUGH,
+                                        NULL);
+  gdk_pango_renderer_set_override_color (gdk_renderer,
+                                        PANGO_RENDER_PART_BACKGROUND,
+                                        NULL);
+  
+  gdk_pango_renderer_set_drawable (gdk_renderer, NULL);
+  gdk_pango_renderer_set_gc (gdk_renderer, NULL);
+}
+
 /**
  * gdk_draw_layout_line_with_colors:
  * @drawable:  the drawable on which to draw the line
@@ -228,6 +904,11 @@ draw_underline (GdkDrawable    *drawable,
  * Render a #PangoLayoutLine onto a #GdkDrawable, overriding the
  * layout's normal colors with @foreground and/or @background.
  * @foreground and @background need not be allocated.
+ *
+ * If the layout's #PangoContext has a transformation matrix set, then
+ * @x and @y specify the position of the left edge of the baseline
+ * (left is in before-tranform user coordinates) in after-transform
+ * device coordinates.
  */
 void 
 gdk_draw_layout_line_with_colors (GdkDrawable      *drawable,
@@ -238,215 +919,88 @@ gdk_draw_layout_line_with_colors (GdkDrawable      *drawable,
                                   const GdkColor   *foreground,
                                   const GdkColor   *background)
 {
-  GSList *tmp_list = line->runs;
-  PangoRectangle overall_rect;
-  PangoRectangle logical_rect;
-  PangoRectangle ink_rect;
-  PangoContext *context;
-  gint x_off = 0;
-  gint rise = 0;
-  gboolean embossed;
-  GdkBitmap *stipple;
-  PangoUnderline last_uline = PANGO_UNDERLINE_NONE;
-  gint uline_start_x = 0;
-  gint uline_end_x = 0;
-  gint uline_end_x_extended = 0;
-  gint last_risen_y = 0;
-  gint low_y = G_MININT;
-  GdkGC *last_fg_gc = NULL;
-  gboolean last_fg_set = FALSE;
-  PangoColor last_fg_color;
-
+  PangoRenderer *renderer;
+  const PangoMatrix *matrix;
+  
   g_return_if_fail (GDK_IS_DRAWABLE (drawable));
   g_return_if_fail (GDK_IS_GC (gc));
   g_return_if_fail (line != NULL);
 
-  context = pango_layout_get_context (line->layout);
-  
-  pango_layout_line_get_extents (line,NULL, &overall_rect);
-  
-  while (tmp_list)
+  renderer = get_renderer (drawable, gc, foreground, background);
+
+  /* When we have a matrix, we do positioning by adjusting the matrix, and
+   * clamp just pass x=0, y=0 to the lower levels. We don't want to introduce
+   * a matrix when the caller didn't provide one, however, since that adds
+   * lots of floating point arithmetic for each glyph.
+   */
+  matrix = pango_context_get_matrix (pango_layout_get_context (line->layout));
+  if (matrix)
     {
-      PangoUnderline this_uline = PANGO_UNDERLINE_NONE;
-      PangoLayoutRun *run = tmp_list->data;
-      PangoColor fg_color, bg_color;
-      gboolean strike, fg_set, bg_set, shape_set;
-      GdkGC *fg_gc;
-      gint risen_y;
-      
-      tmp_list = tmp_list->next;
+      PangoMatrix tmp_matrix;
       
-      gdk_pango_get_item_properties (run->item, &this_uline,
-                                    &strike,
-                                     &rise,
-                                     &fg_color, &fg_set,
-                                     &bg_color, &bg_set,
-                                     &embossed,
-                                     &stipple,
-                                     &shape_set,
-                                     &ink_rect,
-                                    &logical_rect);
-
-      /* we subtract the rise because X coordinates are upside down */
-      risen_y = y - rise / PANGO_SCALE;
-      
-      if (!shape_set)
-       {
-         if (this_uline == PANGO_UNDERLINE_NONE)
-           pango_glyph_string_extents (run->glyphs, run->item->analysis.font,
-                                       NULL, &logical_rect);
-         else
-           pango_glyph_string_extents (run->glyphs, run->item->analysis.font,
-                                       &ink_rect, &logical_rect);
-       }
+      tmp_matrix = *matrix;
+      tmp_matrix.x0 = x;
+      tmp_matrix.y0 = y;
+      pango_renderer_set_matrix (renderer, &tmp_matrix);
 
-      if (bg_set || background)
-       {
-         GdkGC *bg_gc;
-          PangoColor tmp = bg_color;
-          
-          if (background)
-            {
-              tmp.red = background->red;
-              tmp.blue = background->blue;
-              tmp.green = background->green;
-            }
-          
-          bg_gc = gdk_pango_get_gc (drawable, context, &tmp, stipple, gc);
-          
-         gdk_draw_rectangle (drawable, bg_gc, TRUE,
-                             x + (x_off + logical_rect.x) / PANGO_SCALE,
-                             risen_y + overall_rect.y / PANGO_SCALE,
-                             logical_rect.width / PANGO_SCALE,
-                             overall_rect.height / PANGO_SCALE);
-
-          if (stipple)
-            gdk_gc_set_fill (bg_gc, GDK_SOLID);
-          
-         gdk_pango_free_gc (context, bg_gc);
-       }
+      x = 0;
+      y = 0;
+    }
+  else
+    pango_renderer_set_matrix (renderer, NULL);
 
-      if (fg_set || stipple || foreground)
-        {
-          PangoColor tmp = fg_color;
-          
-          if (foreground)
-            {
-              tmp.red = foreground->red;
-              tmp.blue = foreground->blue;
-              tmp.green = foreground->green;
-            }
-          
-          fg_gc = gdk_pango_get_gc (drawable, context, (fg_set || foreground) ? &tmp : NULL,
-                                    stipple, gc);
-        }
-      else
-       fg_gc = gc;
+  pango_renderer_draw_layout_line (renderer, line, x * PANGO_SCALE, y * PANGO_SCALE);
 
-      if (!shape_set)
-        {
-          gint gx, gy;
+  release_renderer (renderer);
+}
 
-          gx = x + x_off / PANGO_SCALE;
-          gy = risen_y;
-          
-          if (embossed)
-            {
-              PangoColor color = { 65535, 65535, 65535 };
-              GdkGC *white_gc = gdk_pango_get_gc (drawable, context, &color, stipple, fg_gc);
-              gdk_draw_glyphs (drawable, white_gc, run->item->analysis.font,
-                               gx + 1,
-                               gy + 1,
-                               run->glyphs);
-              gdk_pango_free_gc (context, white_gc);
-            }
-          
-          gdk_draw_glyphs (drawable, fg_gc, run->item->analysis.font,
-                           gx, gy,
-                           run->glyphs);
-        }
+/* Gets the bounds of a layout in device coordinates. Note cut-and-paste
+ * between here and gtklabel.c */
+static void
+get_rotated_layout_bounds (PangoLayout  *layout,
+                          GdkRectangle *rect)
+{
+  PangoContext *context = pango_layout_get_context (layout);
+  const PangoMatrix *matrix = pango_context_get_matrix (context);
+  gdouble x_min = 0, x_max = 0, y_min = 0, y_max = 0; /* quiet gcc */
+  PangoRectangle logical_rect;
+  gint i, j;
 
-      if (this_uline != last_uline ||
-         risen_y != last_risen_y ||
-         fg_set != last_fg_set ||
-         (fg_set && (last_fg_color.red != fg_color.red ||
-                     last_fg_color.green != fg_color.green ||
-                     last_fg_color.blue != fg_color.blue)))
+  pango_layout_get_extents (layout, NULL, &logical_rect);
+  
+  for (i = 0; i < 2; i++)
+    {
+      gdouble x = (i == 0) ? logical_rect.x : logical_rect.x + logical_rect.width;
+      for (j = 0; j < 2; j++)
        {
-         /* If only color changes, the underlines extend to the edge
-          * of the logical rectangle so they join up; otherwise they
-          * go 1 pixel beyond the ink rectangle. This doesn't work
-          * for low underlines (they will be at a different y anyways),
-          * so they follow the normal path.
-          */
-         gboolean extend_uline = (this_uline == last_uline &&
-                                  this_uline != PANGO_UNDERLINE_LOW &&
-                                  risen_y == last_risen_y);
+         gdouble y = (j == 0) ? logical_rect.y : logical_rect.y + logical_rect.height;
+         
+         gdouble xt = (x * matrix->xx + y * matrix->xy) / PANGO_SCALE + matrix->x0;
+         gdouble yt = (x * matrix->yx + y * matrix->yy) / PANGO_SCALE + matrix->y0;
          
-         /* Starting a new underline run
-          */
-         if (last_uline != PANGO_UNDERLINE_NONE)
+         if (i == 0 && j == 0)
            {
-             draw_underline (drawable, last_fg_gc, last_uline,
-                             last_risen_y, low_y,
-                             uline_start_x,
-                             extend_uline ? uline_end_x_extended : uline_end_x);
+             x_min = x_max = xt;
+             y_min = y_max = yt;
            }
-
-         if (this_uline != PANGO_UNDERLINE_NONE)
+         else
            {
-             if (extend_uline)
-               uline_start_x = x + x_off / PANGO_SCALE;
-             else
-               uline_start_x = x + (x_off + ink_rect.x) / PANGO_SCALE - 1;
-
-             low_y = G_MININT;
+             if (xt < x_min)
+               x_min = xt;
+             if (yt < y_min)
+               y_min = yt;
+             if (xt > x_max)
+               x_max = xt;
+             if (yt > y_max)
+               y_max = yt;
            }
        }
-
-      /* Update current underline segment information
-       */
-      if (this_uline != PANGO_UNDERLINE_NONE)
-       {
-         uline_end_x = x + (x_off + ink_rect.x + ink_rect.width) / PANGO_SCALE;
-         uline_end_x_extended = x + (x_off + logical_rect.x + logical_rect.width) / PANGO_SCALE - 1;
-       }
-
-      if (this_uline == PANGO_UNDERLINE_LOW)
-       low_y = MAX (low_y, risen_y + (ink_rect.y + ink_rect.height) / PANGO_SCALE);
-
-      if (strike)
-       {
-         int centerline = logical_rect.y + logical_rect.height / 2;
-         
-         gdk_draw_line (drawable, fg_gc,
-                        x + (x_off + logical_rect.x) / PANGO_SCALE - 1,
-                        risen_y + centerline / PANGO_SCALE,
-                        x + (x_off + logical_rect.x + logical_rect.width) / PANGO_SCALE + 1,
-                        risen_y + centerline / PANGO_SCALE);
-       }
-
-      if (last_fg_gc != gc && last_fg_gc)
-       gdk_pango_free_gc (context, last_fg_gc);
-
-      last_risen_y = risen_y;
-      last_uline = this_uline;
-      last_fg_gc = fg_gc;
-      last_fg_set = fg_set;
-      if (fg_set)
-       last_fg_color = fg_color;
-      
-      x_off += logical_rect.width;
     }
-
-  /* Finish off any remaining underlines
-   */
-  if (last_uline != PANGO_UNDERLINE_NONE)
-    draw_underline (drawable, last_fg_gc, last_uline, last_risen_y, low_y,
-                   uline_start_x, uline_end_x);
-
-  if (last_fg_gc != gc && last_fg_gc)
-    gdk_pango_free_gc (context, last_fg_gc);
+  
+  rect->x = floor (x_min);
+  rect->width = ceil (x_max) - rect->x;
+  rect->y = floor (y_min);
+  rect->height = floor (y_max) - rect->y;
 }
 
 /**
@@ -463,6 +1017,10 @@ gdk_draw_layout_line_with_colors (GdkDrawable      *drawable,
  * layout's normal colors with @foreground and/or @background.
  * @foreground and @background need not be allocated.
  *
+ * If the layout's #PangoContext has a transformation matrix set, then
+ * @x and @y specify the position of the top left corner of the
+ * bounding box (in device space) of the transformed layout.
+ *
  * If you're using GTK+, the ususal way to obtain a #PangoLayout
  * is gtk_widget_create_pango_layout().
  */
@@ -475,35 +1033,42 @@ gdk_draw_layout_with_colors (GdkDrawable     *drawable,
                              const GdkColor  *foreground,
                              const GdkColor  *background)
 {
-  PangoLayoutIter *iter;
+  PangoRenderer *renderer;
+  const PangoMatrix *matrix;
   
   g_return_if_fail (GDK_IS_DRAWABLE (drawable));
   g_return_if_fail (GDK_IS_GC (gc));
   g_return_if_fail (PANGO_IS_LAYOUT (layout));
 
-  iter = pango_layout_get_iter (layout);
-  
-  do
+  renderer = get_renderer (drawable, gc, foreground, background);
+
+  /* When we have a matrix, we do positioning by adjusting the matrix, and
+   * clamp just pass x=0, y=0 to the lower levels. We don't want to introduce
+   * a matrix when the caller didn't provide one, however, since that adds
+   * lots of floating point arithmetic for each glyph.
+   */
+  matrix = pango_context_get_matrix (pango_layout_get_context (layout));
+  if (matrix)
     {
-      PangoRectangle logical_rect;
-      PangoLayoutLine *line;
-      int baseline;
-      
-      line = pango_layout_iter_get_line (iter);
+      PangoMatrix tmp_matrix;
+      GdkRectangle rect;
+
+      get_rotated_layout_bounds (layout, &rect);
       
-      pango_layout_iter_get_line_extents (iter, NULL, &logical_rect);
-      baseline = pango_layout_iter_get_baseline (iter);
+      tmp_matrix = *matrix;
+      tmp_matrix.x0 += x - rect.x;
+      tmp_matrix.y0 += y - rect.y;
+      pango_renderer_set_matrix (renderer, &tmp_matrix);
       
-      gdk_draw_layout_line_with_colors (drawable, gc,
-                                        x + logical_rect.x / PANGO_SCALE,
-                                        y + baseline / PANGO_SCALE,
-                                        line,
-                                        foreground,
-                                        background);
+      x = 0;
+      y = 0;
     }
-  while (pango_layout_iter_next_line (iter));
-
-  pango_layout_iter_free (iter);
+  else
+    pango_renderer_set_matrix (renderer, NULL);
+  
+  pango_renderer_draw_layout (renderer, layout, x * PANGO_SCALE, y * PANGO_SCALE);
+  
+  release_renderer (renderer);
 }
 
 /**
@@ -515,6 +1080,11 @@ gdk_draw_layout_with_colors (GdkDrawable     *drawable,
  * @line:      a #PangoLayoutLine
  *
  * Render a #PangoLayoutLine onto an GDK drawable
+ *
+ * If the layout's #PangoContext has a transformation matrix set, then
+ * @x and @y specify the position of the left edge of the baseline
+ * (left is in before-tranform user coordinates) in after-transform
+ * device coordinates.
  */
 void 
 gdk_draw_layout_line (GdkDrawable      *drawable,
@@ -540,7 +1110,11 @@ gdk_draw_layout_line (GdkDrawable      *drawable,
  *
  * Render a #PangoLayout onto a GDK drawable
  *
- * If you're using GTK+, the ususal way to obtain a #PangoLayout
+ * If the layout's #PangoContext has a transformation matrix set, then
+ * @x and @y specify the position of the top left corner of the
+ * bounding box (in device space) of the transformed layout.
+ *
+ * If you're using GTK+, the usual way to obtain a #PangoLayout
  * is gtk_widget_create_pango_layout().
  */
 void 
@@ -557,110 +1131,6 @@ gdk_draw_layout (GdkDrawable     *drawable,
   gdk_draw_layout_with_colors (drawable, gc, x, y, layout, NULL, NULL);
 }
 
-static void
-gdk_pango_get_item_properties (PangoItem      *item,
-                              PangoUnderline *uline,
-                              gboolean       *strikethrough,
-                               gint           *rise,
-                              PangoColor     *fg_color,
-                              gboolean       *fg_set,
-                              PangoColor     *bg_color,
-                              gboolean       *bg_set,
-                               gboolean       *embossed,
-                               GdkBitmap     **stipple,
-                              gboolean       *shape_set,
-                              PangoRectangle *ink_rect,
-                              PangoRectangle *logical_rect)
-{
-  GSList *tmp_list = item->analysis.extra_attrs;
-
-  if (strikethrough)
-      *strikethrough = FALSE;
-  
-  if (fg_set)
-    *fg_set = FALSE;
-  
-  if (bg_set)
-    *bg_set = FALSE;
-
-  if (shape_set)
-    *shape_set = FALSE;
-
-  if (rise)
-    *rise = 0;
-
-  if (embossed)
-    *embossed = FALSE;
-
-  if (stipple)
-    *stipple = NULL;
-  
-  while (tmp_list)
-    {
-      PangoAttribute *attr = tmp_list->data;
-
-      switch (attr->klass->type)
-       {
-       case PANGO_ATTR_UNDERLINE:
-         if (uline)
-           *uline = ((PangoAttrInt *)attr)->value;
-         break;
-
-       case PANGO_ATTR_STRIKETHROUGH:
-           if (strikethrough)
-               *strikethrough = ((PangoAttrInt *)attr)->value;
-           break;
-           
-       case PANGO_ATTR_FOREGROUND:
-         if (fg_color)
-           *fg_color = ((PangoAttrColor *)attr)->color;
-         if (fg_set)
-           *fg_set = TRUE;
-         
-         break;
-         
-       case PANGO_ATTR_BACKGROUND:
-         if (bg_color)
-           *bg_color = ((PangoAttrColor *)attr)->color;
-         if (bg_set)
-           *bg_set = TRUE;
-         
-         break;
-
-       case PANGO_ATTR_SHAPE:
-         if (shape_set)
-           *shape_set = TRUE;
-         if (logical_rect)
-           *logical_rect = ((PangoAttrShape *)attr)->logical_rect;
-         if (ink_rect)
-           *ink_rect = ((PangoAttrShape *)attr)->ink_rect;
-         break;
-
-        case PANGO_ATTR_RISE:
-          if (rise)
-            *rise = ((PangoAttrInt *)attr)->value;
-          break;
-          
-       default:
-          /* stipple_type and embossed_type aren't necessarily
-           * initialized, but they are 0, which is an
-           * invalid type so won't occur. 
-           */
-          if (stipple && attr->klass->type == gdk_pango_attr_stipple_type)
-            {
-              *stipple = ((GdkPangoAttrStipple*)attr)->stipple;
-            }
-          else if (embossed && attr->klass->type == gdk_pango_attr_embossed_type)
-            {
-              *embossed = ((GdkPangoAttrEmbossed*)attr)->embossed;
-            }
-         break;
-       }
-      tmp_list = tmp_list->next;
-    }
-}
-
-
 static PangoAttribute *
 gdk_pango_attr_stipple_copy (const PangoAttribute *attr)
 {
index 5a35c45fb908dab719284aa3c13982b1ddf39b5f..492f144ad10d8ba1fa743a17b9481d81e3f3ced6 100644 (file)
@@ -28,12 +28,88 @@ extern "C" {
 
 /* Pango interaction */
 
+typedef struct _GdkPangoRenderer        GdkPangoRenderer;
+typedef struct _GdkPangoRendererClass   GdkPangoRendererClass;
+typedef struct _GdkPangoRendererPrivate GdkPangoRendererPrivate;
+
+#define GDK_TYPE_PANGO_RENDERER            (gdk_pango_renderer_get_type())
+#define GDK_PANGO_RENDERER(object)         (G_TYPE_CHECK_INSTANCE_CAST ((object), GDK_TYPE_PANGO_RENDERER, GdkPangoRenderer))
+#define GDK_IS_PANGO_RENDERER(object)      (G_TYPE_CHECK_INSTANCE_TYPE ((object), GDK_TYPE_PANGO_RENDERER))
+#define GDK_PANGO_RENDERER_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_PANGO_RENDERER, GdkPangoRendererClass))
+#define GDK_IS_PANGO_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_PANGO_RENDERER))
+#define GDK_PANGO_RENDERER_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_PANGO_RENDERER, GdkPangoRendererClass))
+
+/**
+ * GdkPangoRenderer:
+ *
+ * #GdkPangoRenderer is a subclass of #PangoRenderer used for rendering
+ * Pango objects into GDK drawables. The default renderer for a particular
+ * screen is obtained with gdk_pango_renderer_get_default(); Pango
+ * functions like pango_renderer_draw_layout() and
+ * pango_renderer_draw_layout_line() are then used to draw objects with
+ * the renderer.
+ *
+ * In most simple cases, applications can just use gdk_draw_layout(), and
+ * don't need to directly use #GdkPangoRenderer at all. Using the
+ * #GdkPangoRenderer directly is most useful when working with a
+ * transformation such as a rotation, because the Pango drawing functions
+ * take user space coordinates (coordinates before the transformation)
+ * instead of device coordinates.
+ *
+ * In certain cases it can be useful to subclass #GdkPangoRenderer. Examples
+ * of reasons to do this are to add handling of custom attributes by
+ * overriding 'prepare_run' or to do custom drawing of embedded objects
+ * by overriding 'draw_shape'.
+ *
+ * Since: 2.6
+ **/
+struct _GdkPangoRenderer
+{
+  /*< private >*/
+  PangoRenderer parent_instance;
+
+  GdkPangoRendererPrivate *priv;
+};
+
+/**
+ * GdkPangoRendererClass:
+ *
+ * #GdkPangoRenderer is the class structure for #GdkPangoRenderer.
+ *
+ * Since: 2.6
+ **/
+struct _GdkPangoRendererClass
+{
+  /*< private >*/
+  PangoRendererClass parent_class;
+};
+
+GType gdk_pango_renderer_get_type (void) G_GNUC_CONST;
+
+PangoRenderer *gdk_pango_renderer_new         (GdkScreen *screen);
+PangoRenderer *gdk_pango_renderer_get_default (GdkScreen *screen);
+
+void gdk_pango_renderer_set_drawable       (GdkPangoRenderer *gdk_renderer,
+                                           GdkDrawable      *drawable);
+void gdk_pango_renderer_set_gc             (GdkPangoRenderer *gdk_renderer,
+                                           GdkGC            *gc);
+void gdk_pango_renderer_set_stipple        (GdkPangoRenderer *gdk_renderer,
+                                           PangoRenderPart   part,
+                                           GdkBitmap        *stipple);
+void gdk_pango_renderer_set_override_color (GdkPangoRenderer *gdk_renderer,
+                                           PangoRenderPart   part,
+                                           const GdkColor   *color);
+
+/************************************************************************/
+
 PangoContext *gdk_pango_context_get_for_screen (GdkScreen    *screen);
 #ifndef GDK_MULTIHEAD_SAFE
 PangoContext *gdk_pango_context_get            (void);
 #endif
+#ifndef GDK_DISABLE_DEPRECATED
 void          gdk_pango_context_set_colormap   (PangoContext *context,
                                                 GdkColormap  *colormap);
+#endif 
 
 
 /* Get a clip region to draw only part of a layout or
index c4b9c55fd1eee4deb058eca33e93fbcbc30adc42..978712a9e07c45e65955602e823ed4f7b0749172 100644 (file)
@@ -90,12 +90,21 @@ static void   gdk_pixmap_draw_lines     (GdkDrawable     *drawable,
                                         GdkGC           *gc,
                                         GdkPoint        *points,
                                         gint             npoints);
-static void   gdk_pixmap_draw_glyphs    (GdkDrawable      *drawable,
-                                         GdkGC            *gc,
-                                         PangoFont        *font,
-                                         gint              x,
-                                         gint              y,
-                                         PangoGlyphString *glyphs);
+
+static void gdk_pixmap_draw_glyphs             (GdkDrawable      *drawable,
+                                               GdkGC            *gc,
+                                               PangoFont        *font,
+                                               gint              x,
+                                               gint              y,
+                                               PangoGlyphString *glyphs);
+static void gdk_pixmap_draw_glyphs_transformed (GdkDrawable      *drawable,
+                                               GdkGC            *gc,
+                                               PangoMatrix      *matrix,
+                                               PangoFont        *font,
+                                               gint              x,
+                                               gint              y,
+                                               PangoGlyphString *glyphs);
+
 static void   gdk_pixmap_draw_image     (GdkDrawable     *drawable,
                                          GdkGC           *gc,
                                          GdkImage        *image,
@@ -117,7 +126,10 @@ static void   gdk_pixmap_draw_pixbuf    (GdkDrawable     *drawable,
                                         GdkRgbDither     dither,
                                         gint             x_dither,
                                         gint             y_dither);
-
+static void  gdk_pixmap_draw_trapezoids (GdkDrawable     *drawable,
+                                        GdkGC           *gc,
+                                        GdkTrapezoid    *trapezoids,
+                                        gint             n_trapezoids);
 
 static void   gdk_pixmap_real_get_size  (GdkDrawable     *drawable,
                                          gint            *width,
@@ -201,8 +213,10 @@ gdk_pixmap_class_init (GdkPixmapObjectClass *klass)
   drawable_class->draw_segments = gdk_pixmap_draw_segments;
   drawable_class->draw_lines = gdk_pixmap_draw_lines;
   drawable_class->draw_glyphs = gdk_pixmap_draw_glyphs;
+  drawable_class->draw_glyphs_transformed = gdk_pixmap_draw_glyphs_transformed;
   drawable_class->draw_image = gdk_pixmap_draw_image;
   drawable_class->draw_pixbuf = gdk_pixmap_draw_pixbuf;
+  drawable_class->draw_trapezoids = gdk_pixmap_draw_trapezoids;
   drawable_class->get_depth = gdk_pixmap_real_get_depth;
   drawable_class->get_screen = gdk_pixmap_real_get_screen;
   drawable_class->get_size = gdk_pixmap_real_get_size;
@@ -371,6 +385,20 @@ gdk_pixmap_draw_glyphs (GdkDrawable      *drawable,
   gdk_draw_glyphs (private->impl, gc, font, x, y, glyphs);
 }
 
+static void
+gdk_pixmap_draw_glyphs_transformed (GdkDrawable      *drawable,
+                                   GdkGC            *gc,
+                                   PangoMatrix      *matrix,
+                                   PangoFont        *font,
+                                   gint              x,
+                                   gint              y,
+                                   PangoGlyphString *glyphs)
+{
+  GdkPixmapObject *private = (GdkPixmapObject *)drawable;
+
+  gdk_draw_glyphs_transformed (private->impl, gc, matrix, font, x, y, glyphs);
+}
+
 static void
 gdk_pixmap_draw_image (GdkDrawable     *drawable,
                        GdkGC           *gc,
@@ -409,6 +437,17 @@ gdk_pixmap_draw_pixbuf (GdkDrawable     *drawable,
                   dither, x_dither, y_dither);
 }
 
+static void
+gdk_pixmap_draw_trapezoids (GdkDrawable     *drawable,
+                           GdkGC           *gc,
+                           GdkTrapezoid    *trapezoids,
+                           gint             n_trapezoids)
+{
+  GdkPixmapObject *private = (GdkPixmapObject *)drawable;
+
+  gdk_draw_trapezoids (private->impl, gc, trapezoids, n_trapezoids);
+}
+
 static void
 gdk_pixmap_real_get_size (GdkDrawable *drawable,
                           gint *width,
index 556d1c9dabd35bb717735be4e341546cda7939ae..06212913bd7ec31fdf94c8c1229a092e2e83c9d5 100644 (file)
@@ -105,12 +105,20 @@ static void   gdk_window_draw_lines     (GdkDrawable     *drawable,
                                         GdkGC           *gc,
                                         GdkPoint        *points,
                                         gint             npoints);
-static void   gdk_window_draw_glyphs    (GdkDrawable      *drawable,
-                                        GdkGC            *gc,
-                                        PangoFont        *font,
-                                        gint              x,
-                                        gint              y,
-                                        PangoGlyphString *glyphs);
+
+static void gdk_window_draw_glyphs             (GdkDrawable      *drawable,
+                                               GdkGC            *gc,
+                                               PangoFont        *font,
+                                               gint              x,
+                                               gint              y,
+                                               PangoGlyphString *glyphs);
+static void gdk_window_draw_glyphs_transformed (GdkDrawable      *drawable,
+                                               GdkGC            *gc,
+                                               PangoMatrix      *matrix,
+                                               PangoFont        *font,
+                                               gint              x,
+                                               gint              y,
+                                               PangoGlyphString *glyphs);
 
 static void   gdk_window_draw_image     (GdkDrawable     *drawable,
                                          GdkGC           *gc,
@@ -135,6 +143,11 @@ static void gdk_window_draw_pixbuf (GdkDrawable     *drawable,
                                    gint             x_dither,
                                    gint             y_dither);
 
+static void gdk_window_draw_trapezoids (GdkDrawable   *drawable,
+                                       GdkGC         *gc,
+                                       GdkTrapezoid  *trapezoids,
+                                       gint           n_trapezoids);
+
 static GdkImage* gdk_window_copy_to_image (GdkDrawable *drawable,
                                           GdkImage    *image,
                                           gint         src_x,
@@ -239,8 +252,10 @@ gdk_window_class_init (GdkWindowObjectClass *klass)
   drawable_class->draw_segments = gdk_window_draw_segments;
   drawable_class->draw_lines = gdk_window_draw_lines;
   drawable_class->draw_glyphs = gdk_window_draw_glyphs;
+  drawable_class->draw_glyphs_transformed = gdk_window_draw_glyphs_transformed;
   drawable_class->draw_image = gdk_window_draw_image;
   drawable_class->draw_pixbuf = gdk_window_draw_pixbuf;
+  drawable_class->draw_trapezoids = gdk_window_draw_trapezoids;
   drawable_class->get_depth = gdk_window_real_get_depth;
   drawable_class->get_screen = gdk_window_real_get_screen;
   drawable_class->get_size = gdk_window_real_get_size;
@@ -1656,6 +1671,51 @@ gdk_window_draw_glyphs (GdkDrawable      *drawable,
   RESTORE_GC (gc);
 }
 
+static void
+gdk_window_draw_glyphs_transformed (GdkDrawable      *drawable,
+                                   GdkGC            *gc,
+                                   PangoMatrix      *matrix,
+                                   PangoFont        *font,
+                                   gint              x,
+                                   gint              y,
+                                   PangoGlyphString *glyphs)
+{
+  GdkWindowObject *private = (GdkWindowObject *)drawable;
+  PangoMatrix tmp_matrix;
+
+  OFFSET_GC (gc);
+
+  if (GDK_WINDOW_DESTROYED (drawable))
+    return;
+
+  if (x_offset != 0 || y_offset != 0)
+    {
+      if (matrix)
+       {
+         tmp_matrix = *matrix;
+         tmp_matrix.x0 -= x_offset;
+         tmp_matrix.y0 -= y_offset;
+         matrix = &tmp_matrix;
+       }
+      else
+       {
+         x -= x_offset * PANGO_SCALE;
+         y -= y_offset * PANGO_SCALE;
+       }
+    }
+  
+  if (private->paint_stack)
+    {
+      GdkWindowPaint *paint = private->paint_stack->data;
+
+      gdk_draw_glyphs_transformed (paint->pixmap, gc, matrix, font, x, y, glyphs);
+    }
+  else
+    gdk_draw_glyphs_transformed (private->impl, gc, matrix, font, x, y, glyphs);
+
+  RESTORE_GC (gc);
+}
+
 static GdkGC *
 gdk_window_get_bg_gc (GdkWindow      *window,
                      GdkWindowPaint *paint)
@@ -1895,6 +1955,52 @@ gdk_window_draw_pixbuf (GdkDrawable     *drawable,
     }
 }
 
+static void
+gdk_window_draw_trapezoids (GdkDrawable   *drawable,
+                           GdkGC         *gc,
+                           GdkTrapezoid  *trapezoids,
+                           gint           n_trapezoids)
+{
+  GdkWindowObject *private = (GdkWindowObject *)drawable;
+  GdkTrapezoid *new_trapezoids = NULL;
+
+  OFFSET_GC (gc);
+
+  if (GDK_WINDOW_DESTROYED (drawable))
+    return;
+  
+  if (x_offset != 0 || y_offset != 0)
+    {
+      gint i;
+
+      new_trapezoids = g_new (GdkTrapezoid, n_trapezoids);
+      for (i=0; i < n_trapezoids; i++)
+       {
+         new_trapezoids[i].y1 = trapezoids[i].y1 - y_offset;
+         new_trapezoids[i].x11 = trapezoids[i].x11 - x_offset;
+         new_trapezoids[i].x21 = trapezoids[i].x21 - x_offset;
+         new_trapezoids[i].y2 = trapezoids[i].y2 - y_offset;
+         new_trapezoids[i].x12 = trapezoids[i].x12 - x_offset;
+         new_trapezoids[i].x22 = trapezoids[i].x22 - x_offset;
+       }
+
+      trapezoids = new_trapezoids;
+    }
+
+  if (private->paint_stack)
+    {
+      GdkWindowPaint *paint = private->paint_stack->data;
+      gdk_draw_trapezoids (paint->pixmap, gc, trapezoids, n_trapezoids);
+    }
+  else
+    gdk_draw_trapezoids (private->impl, gc, trapezoids, n_trapezoids);
+  
+  if (new_trapezoids)
+    g_free (new_trapezoids);
+
+  RESTORE_GC (gc);
+}
+
 static void
 gdk_window_real_get_size (GdkDrawable *drawable,
                           gint *width,
index a523da94091af3405e5d46b35ac9be487a444977..198bf6a72721f7ad729ee541e19b3bdbaba286b6 100644 (file)
@@ -148,6 +148,9 @@ struct _GdkDisplayX11
   guint xdnd_atoms_precached : 1;
   guint motif_atoms_precached : 1;
   guint use_sync : 1;
+
+  /* Alpha mask picture format */
+  XRenderPictFormat *mask_format;
 };
 
 struct _GdkDisplayX11Class
index 2308a3acd8fc4c19e63b10ca66a018c9138b6e01..6517f36a56b24250696ec370cf1358277c398de9 100644 (file)
@@ -105,12 +105,21 @@ static void gdk_x11_draw_lines     (GdkDrawable    *drawable,
                                    GdkGC          *gc,
                                    GdkPoint       *points,
                                    gint            npoints);
-static void gdk_x11_draw_glyphs    (GdkDrawable      *drawable,
-                                    GdkGC            *gc,
-                                    PangoFont        *font,
-                                    gint              x,
-                                    gint              y,
-                                    PangoGlyphString *glyphs);
+
+static void gdk_x11_draw_glyphs             (GdkDrawable      *drawable,
+                                            GdkGC            *gc,
+                                            PangoFont        *font,
+                                            gint              x,
+                                            gint              y,
+                                            PangoGlyphString *glyphs);
+static void gdk_x11_draw_glyphs_transformed (GdkDrawable      *drawable,
+                                            GdkGC            *gc,
+                                            PangoMatrix      *matrix,
+                                            PangoFont        *font,
+                                            gint              x,
+                                            gint              y,
+                                            PangoGlyphString *glyphs);
+
 static void gdk_x11_draw_image     (GdkDrawable     *drawable,
                                     GdkGC           *gc,
                                     GdkImage        *image,
@@ -133,6 +142,11 @@ static void gdk_x11_draw_pixbuf    (GdkDrawable     *drawable,
                                    gint             x_dither,
                                    gint             y_dither);
 
+static void gdk_x11_draw_trapezoids (GdkDrawable     *drawable,
+                                    GdkGC           *gc,
+                                    GdkTrapezoid    *trapezoids,
+                                    gint             n_trapezoids);
+
 static void gdk_x11_set_colormap   (GdkDrawable    *drawable,
                                     GdkColormap    *colormap);
 
@@ -196,8 +210,10 @@ gdk_drawable_impl_x11_class_init (GdkDrawableImplX11Class *klass)
   drawable_class->draw_segments = gdk_x11_draw_segments;
   drawable_class->draw_lines = gdk_x11_draw_lines;
   drawable_class->draw_glyphs = gdk_x11_draw_glyphs;
+  drawable_class->draw_glyphs_transformed = gdk_x11_draw_glyphs_transformed;
   drawable_class->draw_image = gdk_x11_draw_image;
   drawable_class->draw_pixbuf = gdk_x11_draw_pixbuf;
+  drawable_class->draw_trapezoids = gdk_x11_draw_trapezoids;
   
   drawable_class->set_colormap = gdk_x11_set_colormap;
   drawable_class->get_colormap = gdk_x11_get_colormap;
@@ -351,7 +367,7 @@ gdk_x11_drawable_get_picture (GdkDrawable *drawable)
 
 static void
 gdk_x11_drawable_update_xft_clip (GdkDrawable *drawable,
-                                 GdkGC       *gc)
+                                  GdkGC       *gc)
 {
   GdkGCX11 *gc_private = gc ? GDK_GC_X11 (gc) : NULL;
   XftDraw *xft_draw = gdk_x11_drawable_get_xft_draw (drawable);
@@ -789,21 +805,43 @@ gdk_x11_draw_glyphs (GdkDrawable      *drawable,
                     gint              x,
                     gint              y,
                     PangoGlyphString *glyphs)
+{
+  gdk_x11_draw_glyphs_transformed (drawable, gc, NULL,
+                                  font,
+                                  x * PANGO_SCALE,
+                                  y * PANGO_SCALE,
+                                  glyphs);
+}
+
+static void
+gdk_x11_draw_glyphs_transformed (GdkDrawable      *drawable,
+                                GdkGC            *gc,
+                                PangoMatrix      *matrix,
+                                PangoFont        *font,
+                                gint              x,
+                                gint              y,
+                                PangoGlyphString *glyphs)
 {
   GdkDrawableImplX11 *impl;
+  PangoRenderer *renderer;
   XftColor color;
   XftDraw *draw;
 
   impl = GDK_DRAWABLE_IMPL_X11 (drawable);
 
   g_return_if_fail (PANGO_XFT_IS_FONT (font));
+
   _gdk_gc_x11_get_fg_xft_color (gc, &color);
       
   gdk_x11_drawable_update_xft_clip (drawable, gc);
   draw = gdk_x11_drawable_get_xft_draw (drawable);
       
-  pango_xft_render (draw, &color, font, glyphs, x, y);
+  renderer = _gdk_x11_renderer_get (drawable, gc);
+  if (matrix)
+    pango_renderer_set_matrix (renderer, matrix);
+  pango_renderer_draw_glyphs (renderer, font, glyphs, x, y);
+  if (matrix)
+    pango_renderer_set_matrix (renderer, NULL);
 }
 
 static void
@@ -841,24 +879,20 @@ gdk_x11_get_depth (GdkDrawable *drawable)
   return gdk_drawable_get_depth (GDK_DRAWABLE_IMPL_X11 (drawable)->wrapper);
 }
 
-
-static GdkDrawable * get_impl_drawable (GdkDrawable *drawable)
+static GdkDrawable *
+get_impl_drawable (GdkDrawable *drawable)
 {
-  GdkDrawable *impl;
-  
   if (GDK_IS_WINDOW (drawable))
-    impl = ((GdkWindowObject *)drawable)->impl;
+    return ((GdkWindowObject *)drawable)->impl;
   else if (GDK_IS_PIXMAP (drawable))
-    impl = ((GdkPixmapObject *)drawable)->impl;
+    return ((GdkPixmapObject *)drawable)->impl;
   else
     {
       g_warning (G_STRLOC " drawable is not a pixmap or window");
-      return (GdkDrawable *)None;
+      return NULL;
     }
-  return impl;
 }
 
-
 static GdkScreen*
 gdk_x11_get_screen (GdkDrawable *drawable)
 {
@@ -1458,6 +1492,47 @@ gdk_x11_draw_pixbuf (GdkDrawable     *drawable,
                      dest_x, dest_y, width, height);
 }
 
+static void
+gdk_x11_draw_trapezoids (GdkDrawable  *drawable,
+                        GdkGC        *gc,
+                        GdkTrapezoid *trapezoids,
+                        gint          n_trapezoids)
+{
+  GdkScreen *screen = GDK_DRAWABLE_IMPL_X11 (drawable)->screen;
+  GdkDisplay *display = gdk_screen_get_display (screen);
+  XTrapezoid *xtrapezoids;
+  gint i;
+
+  if (!_gdk_x11_have_render (display))
+    {
+      GdkDrawable *wrapper = GDK_DRAWABLE_IMPL_X11 (drawable)->wrapper;
+      GDK_DRAWABLE_CLASS (parent_class)->draw_trapezoids (wrapper, gc,
+                                                         trapezoids, n_trapezoids);
+      return;
+    }
+
+  xtrapezoids = g_new (XTrapezoid, n_trapezoids);
+
+  for (i = 0; i < n_trapezoids; i++)
+    {
+      xtrapezoids[i].top = XDoubleToFixed (trapezoids[i].y1);
+      xtrapezoids[i].bottom = XDoubleToFixed (trapezoids[i].y2);
+      xtrapezoids[i].left.p1.x = XDoubleToFixed (trapezoids[i].x11);
+      xtrapezoids[i].left.p1.y = XDoubleToFixed (trapezoids[i].y1);
+      xtrapezoids[i].left.p2.x = XDoubleToFixed (trapezoids[i].x12);
+      xtrapezoids[i].left.p2.y = XDoubleToFixed (trapezoids[i].y2);
+      xtrapezoids[i].right.p1.x = XDoubleToFixed (trapezoids[i].x21);
+      xtrapezoids[i].right.p1.y = XDoubleToFixed (trapezoids[i].y1);
+      xtrapezoids[i].right.p2.x = XDoubleToFixed (trapezoids[i].x22);
+      xtrapezoids[i].right.p2.y = XDoubleToFixed (trapezoids[i].y2);
+    }
+
+  _gdk_x11_drawable_draw_xtrapezoids (drawable, gc,
+                                     xtrapezoids, n_trapezoids);
+  
+  g_free (xtrapezoids);
+}
+
 /**
  * gdk_draw_rectangle_alpha_libgtk_only:
  * @drawable: The #GdkDrawable to draw on
@@ -1522,3 +1597,85 @@ gdk_draw_rectangle_alpha_libgtk_only (GdkDrawable  *drawable,
                        width, height);
   return TRUE;
 }
+
+void
+_gdk_x11_drawable_draw_xtrapezoids (GdkDrawable      *drawable,
+                                   GdkGC            *gc,
+                                   XTrapezoid       *xtrapezoids,
+                                   int               n_trapezoids)
+{
+  GdkScreen *screen = GDK_DRAWABLE_IMPL_X11 (drawable)->screen;
+  GdkDisplay *display = gdk_screen_get_display (screen);
+  GdkDisplayX11 *x11display = GDK_DISPLAY_X11 (display);
+   
+  XftDraw *draw;
+
+  if (!_gdk_x11_have_render (display))
+    {
+      /* This is the case of drawing the borders of the unknown glyph box
+       * without render on the display, we need to feed it back to
+       * fallback code. Not efficient, but doesn't matter.
+       */
+      GdkTrapezoid *trapezoids = g_new (GdkTrapezoid, n_trapezoids);
+      int i;
+
+      for (i = 0; i < n_trapezoids; i++)
+       {
+         trapezoids[i].y1 = XFixedToDouble (xtrapezoids[i].top);
+         trapezoids[i].y2 = XFixedToDouble (xtrapezoids[i].bottom);
+         trapezoids[i].x11 = XFixedToDouble (xtrapezoids[i].left.p1.x);
+         trapezoids[i].x12 = XFixedToDouble (xtrapezoids[i].left.p2.x);
+         trapezoids[i].x21 = XFixedToDouble (xtrapezoids[i].right.p1.x);
+         trapezoids[i].x22 = XFixedToDouble (xtrapezoids[i].right.p2.x);
+       }
+
+      gdk_x11_draw_trapezoids (drawable, gc, trapezoids, n_trapezoids);
+      g_free (trapezoids);
+
+      return;
+    }
+
+  draw = gdk_x11_drawable_get_xft_draw (drawable);
+
+  if (!x11display->mask_format)
+    x11display->mask_format = XRenderFindStandardFormat (x11display->xdisplay,
+                                                        PictStandardA8);
+
+  XRenderCompositeTrapezoids (x11display->xdisplay, PictOpOver,
+                             _gdk_x11_gc_get_fg_picture (gc),
+                             XftDrawPicture (draw),
+                             x11display->mask_format,
+                             - gc->ts_x_origin, - gc->ts_y_origin,
+                             xtrapezoids, n_trapezoids);
+}
+
+void
+_gdk_x11_drawable_draw_xft_glyphs (GdkDrawable      *drawable,
+                                  GdkGC            *gc,
+                                  XftFont          *xft_font,
+                                  XftGlyphSpec     *glyphs,
+                                  gint              n_glyphs)
+{
+  GdkScreen *screen = GDK_DRAWABLE_IMPL_X11 (drawable)->screen;
+  GdkDisplay *display = gdk_screen_get_display (screen);
+  GdkDisplayX11 *x11display = GDK_DISPLAY_X11 (display);
+   
+  XftDraw *draw = gdk_x11_drawable_get_xft_draw (drawable);
+
+  if (_gdk_x11_have_render (display))
+    {
+      XftGlyphSpecRender (x11display->xdisplay, PictOpOver,
+                         _gdk_x11_gc_get_fg_picture (gc),
+                         xft_font,
+                         XftDrawPicture (draw),
+                         - gc->ts_x_origin, - gc->ts_y_origin,
+                         glyphs, n_glyphs);
+    }
+  else
+    {
+      XftColor color;
+      
+      _gdk_gc_x11_get_fg_xft_color (gc, &color);
+      XftDrawGlyphSpec (draw, &color, xft_font, glyphs, n_glyphs);
+    }
+}
index bc3f75dbc06bd760fa3208f0a9aa58a322cf333a..45c8e38b8dd0167d887e861583cb8fff03d25359 100644 (file)
@@ -90,6 +90,17 @@ void  _gdk_x11_convert_to_format      (guchar           *src_buf,
                                        gint              width,
                                        gint              height);
 
+/* Note that the following take GdkDrawableImplX11, not the wrapper drawable */
+void _gdk_x11_drawable_draw_xtrapezoids (GdkDrawable  *drawable,
+                                        GdkGC        *gc,
+                                        XTrapezoid   *xtrapezoids,
+                                        int           n_trapezoids);
+void _gdk_x11_drawable_draw_xft_glyphs  (GdkDrawable  *drawable,
+                                        GdkGC        *gc,
+                                        XftFont      *xft_font,
+                                        XftGlyphSpec *glyphs,
+                                        gint          n_glyphs);
+
 #ifdef __cplusplus
 }
 #endif /* __cplusplus */
index fec9784dfd2063afa12b00cc6eb396475134c99d..391c3deec1ee953152afad131a1fdc1c002e2297 100644 (file)
@@ -112,6 +112,11 @@ gdk_gc_x11_finalize (GObject *object)
   
   if (x11_gc->fg_picture != None)
     XRenderFreePicture (GDK_GC_XDISPLAY (x11_gc), x11_gc->fg_picture);
+
+  if (x11_gc->stipple)
+    g_object_unref (x11_gc->stipple);
+  if (x11_gc->tile)
+    g_object_unref (x11_gc->tile);
   
   XFreeGC (GDK_GC_XDISPLAY (x11_gc), GDK_GC_XGC (x11_gc));
 
@@ -161,6 +166,26 @@ _gdk_x11_gc_new (GdkDrawable      *drawable,
   if (values_mask & GDK_GC_FOREGROUND)
     private->fg_pixel = values->foreground.pixel;
 
+  if (values_mask & GDK_GC_BACKGROUND)
+    private->bg_pixel = values->background.pixel;
+
+  if (values_mask & GDK_GC_FILL)
+    private->fill = values->fill;
+  
+  if (values_mask & GDK_GC_STIPPLE)
+    {
+      private->stipple = values->stipple;
+      if (private->stipple)
+       g_object_ref (private->stipple);
+    }
+  
+  if (values_mask & GDK_GC_TILE)
+    {
+      private->tile = values->tile;
+      if (private->tile)
+       g_object_ref (private->tile);
+    }
+  
   if ((values_mask & GDK_GC_CLIP_MASK) && values->clip_mask)
     private->have_clip_mask = TRUE;
 
@@ -368,6 +393,17 @@ gdk_x11_gc_get_values (GdkGC       *gc,
     }
 }
 
+static void
+clear_fg_picture (GdkGC *gc)
+{
+  GdkGCX11 *x11_gc = GDK_GC_X11 (gc);
+
+  if (x11_gc->fg_picture != None)
+    {
+      XRenderFreePicture (GDK_GC_XDISPLAY (x11_gc), x11_gc->fg_picture);
+      x11_gc->fg_picture = None;
+    }
+}
 
 static void
 gdk_x11_gc_set_values (GdkGC           *gc,
@@ -405,6 +441,53 @@ gdk_x11_gc_set_values (GdkGC           *gc,
 
   if (values_mask & GDK_GC_FOREGROUND)
     x11_gc->fg_pixel = values->foreground.pixel;
+
+  if (values_mask & GDK_GC_BACKGROUND)
+    {
+      if (x11_gc->bg_pixel != values->background.pixel)
+       {
+         x11_gc->bg_pixel = values->background.pixel;
+         if (x11_gc->fill == GDK_OPAQUE_STIPPLED)
+           clear_fg_picture (gc);
+       }
+    }
+
+  if (values_mask & GDK_GC_FILL)
+    {
+      if (x11_gc->fill != values->fill)
+       {
+         clear_fg_picture (gc);
+         x11_gc->fill = values->fill;
+       }
+    }
+  
+  if (values_mask & GDK_GC_STIPPLE)
+    {
+      if (x11_gc->stipple != values->stipple)
+       {
+         if (x11_gc->fill == GDK_STIPPLED || x11_gc->fill == GDK_OPAQUE_STIPPLED)
+           clear_fg_picture (gc);
+         if (x11_gc->stipple)
+           g_object_unref (x11_gc->stipple);
+         x11_gc->stipple = values->stipple;
+         if (x11_gc->stipple)
+           g_object_ref (x11_gc->stipple);
+       }
+    }
+  
+  if (values_mask & GDK_GC_TILE)
+    {
+      if (x11_gc->tile != values->tile)
+       {
+         if (x11_gc->fill == GDK_TILED)
+           clear_fg_picture (gc);
+         if (x11_gc->tile)
+           g_object_unref (x11_gc->tile);
+         x11_gc->tile = values->tile;
+         if (x11_gc->tile)
+           g_object_ref (x11_gc->tile);
+       }
+    }
   
   gdk_x11_gc_values_to_xvalues (values, values_mask, &xvalues, &xvalues_mask);
 
@@ -784,6 +867,21 @@ gdk_gc_copy (GdkGC *dst_gc, GdkGC *src_gc)
 
   x11_dst_gc->dirty_mask = x11_src_gc->dirty_mask;
   x11_dst_gc->fg_pixel = x11_src_gc->fg_pixel;
+  x11_dst_gc->fill = x11_src_gc->fill;
+  
+  if (x11_dst_gc->stipple)
+    g_object_unref (x11_dst_gc->stipple);
+  x11_dst_gc->stipple = x11_src_gc->stipple;
+  if (x11_dst_gc->stipple)
+    g_object_ref (x11_dst_gc->stipple);
+  
+  if (x11_dst_gc->tile)
+    g_object_unref (x11_dst_gc->tile);
+  x11_dst_gc->tile = x11_src_gc->tile;
+  if (x11_dst_gc->tile)
+    g_object_ref (x11_dst_gc->tile);
+
+  clear_fg_picture (dst_gc);
 }
 
 /**
@@ -870,14 +968,118 @@ foreground_format (GdkGC *gc)
                            0);
 }
 
+static Picture
+make_fg_tile_picture (GdkGC *gc)
+{
+  GdkGCX11 *x11_gc = GDK_GC_X11 (gc);
+  GdkVisual *visual = gdk_drawable_get_visual (x11_gc->tile);
+  XRenderPictFormat *format = NULL;
+
+  if (visual)
+    {
+      format = XRenderFindVisualFormat (GDK_GC_XDISPLAY (gc),
+                                       GDK_VISUAL_XVISUAL (visual));
+    }
+  else if (x11_gc->depth == 1)
+    {
+      format = XRenderFindStandardFormat (GDK_GC_XDISPLAY (gc),
+                                         PictStandardA1);
+    }
+
+  if (format)
+    {
+      XRenderPictureAttributes pa;
+      pa.repeat = True;
+
+      return XRenderCreatePicture (GDK_GC_XDISPLAY (gc), 
+                                  GDK_PIXMAP_XID (x11_gc->tile),
+                                  format,
+                                  CPRepeat, &pa);
+    }
+}
+
+static Picture
+make_stipple_picture (GdkGC *gc)
+{
+  GdkGCX11 *x11_gc = GDK_GC_X11 (gc);
+  XRenderPictFormat *format = NULL;
+  XRenderPictureAttributes pa;
+  
+  format = XRenderFindStandardFormat (GDK_GC_XDISPLAY (gc),
+                                     PictStandardA1);
+
+  pa.repeat = True;
+  return XRenderCreatePicture (GDK_GC_XDISPLAY (gc), 
+                              GDK_PIXMAP_XID (x11_gc->stipple),
+                              format,
+                              CPRepeat, &pa);
+}
+
+static Picture
+make_color_picture (GdkGC        *gc,
+                   XRenderColor *color)
+{
+  GdkGCX11 *x11_gc = GDK_GC_X11 (gc);
+  XRenderPictureAttributes pa;
+  XRenderPictFormat *pix_format = foreground_format (gc);
+  Pixmap pix;
+  Picture picture;
+  
+  if (!pix_format)
+    return None;
+  
+  pix = XCreatePixmap (GDK_GC_XDISPLAY (gc),
+                      GDK_SCREEN_XROOTWIN (x11_gc->screen),
+                      1, 1, pix_format->depth);
+  pa.repeat = True;
+  picture = XRenderCreatePicture (GDK_GC_XDISPLAY (gc),
+                                 pix,
+                                 pix_format,
+                                 CPRepeat, &pa);
+  XFreePixmap (GDK_GC_XDISPLAY (gc), pix);
+  
+  XRenderFillRectangle (GDK_GC_XDISPLAY (gc), PictOpSrc, 
+                       picture, color,
+                       0, 0, 1, 1);
+
+  return picture;
+}
+
+static void
+get_bg_color (GdkGC        *gc,
+             XRenderColor *render_color)
+{
+  GdkGCX11 *x11_gc = GDK_GC_X11 (gc);
+  GdkColormap *cmap;
+  
+  cmap = gdk_gc_get_colormap (gc);
+
+  if (cmap)
+    {
+      GdkColor color;
+      
+      gdk_colormap_query_color (cmap, x11_gc->bg_pixel, &color);
+      
+      render_color->alpha = 0xffff;
+      render_color->red = color.red;
+      render_color->green = color.green;
+      render_color->blue = color.blue;
+    }
+  else                         /* Not worth warning, just use black */
+    {
+      render_color->alpha = 0xffff;
+      render_color->red = 0;
+      render_color->green = 0;
+      render_color->blue = 0;
+    }
+}
+
 /**
  * _gdk_x11_gc_get_fg_picture:
  * @gc: a #GdkGC
  * 
  * Gets a Xrender Picture object suitable for being the source
  * drawable for drawing with the foreground the graphics context.
- * (Currently, only foreground color is handled, but in the
- * future we should handle tiles/stipples as well.)
  * 
  * Return value: a Picture, owned by the GC; this cannot be
  *   used over subsequent modification of the GC.
@@ -888,6 +1090,8 @@ _gdk_x11_gc_get_fg_picture (GdkGC *gc)
   GdkGCX11 *x11_gc;
   gboolean new = FALSE;
   XftColor xftcolor;
+  GdkFill fill;
+  int width, height;
   
   g_return_val_if_fail (GDK_IS_GC_X11 (gc), None);
 
@@ -896,6 +1100,34 @@ _gdk_x11_gc_get_fg_picture (GdkGC *gc)
 
   x11_gc = GDK_GC_X11 (gc);
 
+  fill = GDK_SOLID;
+  width = 1;
+  height = 1;
+  
+  switch (x11_gc->fill)
+    {
+    case GDK_SOLID:
+      break;
+    case GDK_TILED:
+      if (x11_gc->tile)
+       {
+         if (!x11_gc->fg_picture)
+           x11_gc->fg_picture = make_fg_tile_picture (gc);
+
+         if (x11_gc->fg_picture != None)
+           return x11_gc->fg_picture;
+       }
+      break;
+    case GDK_STIPPLED:
+    case GDK_OPAQUE_STIPPLED:
+      if (x11_gc->stipple)
+       {
+         gdk_drawable_get_size (x11_gc->stipple, &width, &height);
+         fill = x11_gc->fill;
+       }
+      break;
+    }
+  
   if (x11_gc->fg_picture == None)
     {
       XRenderPictureAttributes pa;
@@ -907,7 +1139,7 @@ _gdk_x11_gc_get_fg_picture (GdkGC *gc)
 
       pix = XCreatePixmap (GDK_GC_XDISPLAY (gc), 
                           GDK_SCREEN_XROOTWIN (x11_gc->screen),
-                          1, 1, pix_format->depth);
+                          width, height, pix_format->depth);
       pa.repeat = True;
       x11_gc->fg_picture = XRenderCreatePicture (GDK_GC_XDISPLAY (gc), 
                                                 pix,
@@ -920,18 +1152,65 @@ _gdk_x11_gc_get_fg_picture (GdkGC *gc)
 
   _gdk_gc_x11_get_fg_xft_color (gc, &xftcolor);
   
-  if (new ||
+  if (x11_gc->fg_picture_color.alpha != 0xffff ||
       x11_gc->fg_picture_color.red != xftcolor.color.red ||
       x11_gc->fg_picture_color.green != xftcolor.color.green ||
       x11_gc->fg_picture_color.blue != xftcolor.color.blue)
     {
+      x11_gc->fg_picture_color.alpha = 0xffff;
       x11_gc->fg_picture_color.red = xftcolor.color.red;
       x11_gc->fg_picture_color.green = xftcolor.color.green;
       x11_gc->fg_picture_color.blue = xftcolor.color.blue;
 
+      new = TRUE;
+    }
+
+  switch (fill)
+    {
+    case GDK_SOLID:
       XRenderFillRectangle (GDK_GC_XDISPLAY (gc), PictOpSrc, 
                            x11_gc->fg_picture, &x11_gc->fg_picture_color,
-                           0, 0, 1, 1); 
+                           0, 0, width, height);
+      break;
+    case GDK_STIPPLED:
+      {
+       Picture stipple_picture = make_stipple_picture (gc);
+       
+       XRenderFillRectangle (GDK_GC_XDISPLAY (gc), PictOpSrc, 
+                             x11_gc->fg_picture, &x11_gc->fg_picture_color,
+                             0, 0, width, height);
+       XRenderComposite (GDK_GC_XDISPLAY (gc),
+                         PictOpInReverse,
+                         stipple_picture, None, x11_gc->fg_picture,
+                         0, 0, 0, 0, 0, 0, width, height);
+       
+       XRenderFreePicture (GDK_GC_XDISPLAY (x11_gc), stipple_picture);
+      }
+      break;
+    case GDK_OPAQUE_STIPPLED:
+      {
+       XRenderColor bg_color;
+
+       Picture stipple_picture = make_stipple_picture (gc);
+       Picture fg_picture = make_color_picture (gc, &x11_gc->fg_picture_color);
+
+       get_bg_color (gc, &bg_color);
+       
+       XRenderFillRectangle (GDK_GC_XDISPLAY (gc), PictOpSrc, 
+                             x11_gc->fg_picture, &bg_color,
+                             0, 0, width, height);
+       XRenderComposite (GDK_GC_XDISPLAY (gc),
+                         PictOpOver,
+                         fg_picture, stipple_picture, x11_gc->fg_picture,
+                         0, 0, 0, 0, 0, 0, width, height);
+
+       XRenderFreePicture (GDK_GC_XDISPLAY (x11_gc), stipple_picture);
+       XRenderFreePicture (GDK_GC_XDISPLAY (x11_gc), fg_picture);
+      }
+      break;
+    case GDK_TILED:
+      g_assert_not_reached (); /* handled above */
+      break;
     }
 
   return x11_gc->fg_picture;
@@ -998,3 +1277,24 @@ _gdk_gc_x11_get_fg_xft_color (GdkGC    *gc,
                 "gdk_gc_set_colormap");
     }
 }
+
+void
+_gdk_windowing_gc_get_foreground (GdkGC    *gc,
+                                 GdkColor *color)
+{
+  GdkGCX11 *x11_gc;
+  GdkColormap *cmap;
+  
+  g_return_if_fail (GDK_IS_GC_X11 (gc));
+
+  x11_gc = GDK_GC_X11 (gc);
+
+  color->pixel = x11_gc->fg_pixel;
+
+  cmap = gdk_gc_get_colormap (gc);
+
+  if (cmap)
+    gdk_colormap_query_color (cmap, x11_gc->fg_pixel, color);
+  else
+    g_warning ("No colormap in _gdk_windowing_gc_get_foreground");
+}
index eb9923fc39d5266fb4ba24d5b8da716528ec8480..de7577bee0e9ec0552d3a40ae301feedf56ff254 100644 (file)
 #include "gdkdisplay-x11.h"
 #include "gdkpango.h"
 #include <pango/pangoxft.h>
+#include <pango/pangoxft-render.h>
+
+#include <math.h>
+
+typedef struct _GdkX11Renderer      GdkX11Renderer;
+typedef struct _GdkX11RendererClass GdkX11RendererClass;
+
+#define GDK_TYPE_X11_RENDERER            (gdk_x11_renderer_get_type())
+#define GDK_X11_RENDERER(object)         (G_TYPE_CHECK_INSTANCE_CAST ((object), GDK_TYPE_X11_RENDERER, GdkX11Renderer))
+#define GDK_IS_X11_RENDERER(object)      (G_TYPE_CHECK_INSTANCE_TYPE ((object), GDK_TYPE_X11_RENDERER))
+#define GDK_X11_RENDERER_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_X11_RENDERER, GdkX11RendererClass))
+#define GDK_IS_X11_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_X11_RENDERER))
+#define GDK_X11_RENDERER_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_X11_RENDERER, GdkX11RendererClass))
+
+#define MAX_RENDER_PART  PANGO_RENDER_PART_STRIKETHROUGH
+
+struct _GdkX11Renderer
+{
+  PangoXftRenderer parent_instance;
+
+  XRenderPictFormat *mask_format;
+  
+  GdkDrawable *drawable;
+  GdkGC *gc;
+};
+
+struct _GdkX11RendererClass
+{
+  PangoXftRendererClass parent_class;
+};
+
+G_DEFINE_TYPE (GdkX11Renderer, gdk_x11_renderer, PANGO_TYPE_XFT_RENDERER)
+
+static void
+gdk_x11_renderer_finalize (GObject *object)
+{
+  G_OBJECT_CLASS (gdk_x11_renderer_parent_class)->finalize (object);
+}
+     
+static void
+gdk_x11_renderer_composite_trapezoids (PangoXftRenderer *xftrenderer,
+                                      PangoRenderPart   part,
+                                      XTrapezoid       *trapezoids,
+                                      int               n_trapezoids)
+{
+  /* Because we only use this renderer for "draw_glyphs()" calls, we
+   * won't hit this code path much. However, it is hit for drawing
+   * the "unknown glyph" hex squares. We can safely ignore the part,
+   */
+  GdkX11Renderer *x11_renderer = GDK_X11_RENDERER (xftrenderer);
+
+  _gdk_x11_drawable_draw_xtrapezoids (x11_renderer->drawable,
+                                     x11_renderer->gc,
+                                     trapezoids, n_trapezoids);
+
+}
+
+static void
+gdk_x11_renderer_composite_glyphs (PangoXftRenderer *xftrenderer,
+                                  XftFont          *xft_font,
+                                  XftGlyphSpec     *glyphs,
+                                  gint              n_glyphs)
+{
+  GdkX11Renderer *x11_renderer = GDK_X11_RENDERER (xftrenderer);
+
+  _gdk_x11_drawable_draw_xft_glyphs (x11_renderer->drawable,
+                                    x11_renderer->gc,
+                                    xft_font, glyphs, n_glyphs);
+}
+
+static void
+gdk_x11_renderer_init (GdkX11Renderer *renderer)
+{
+}
+
+static void
+gdk_x11_renderer_class_init (GdkX11RendererClass *klass)
+{
+  PangoXftRendererClass *xftrenderer_class = PANGO_XFT_RENDERER_CLASS (klass);
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  
+  xftrenderer_class->composite_glyphs = gdk_x11_renderer_composite_glyphs;
+  xftrenderer_class->composite_trapezoids = gdk_x11_renderer_composite_trapezoids;
+
+  object_class->finalize = gdk_x11_renderer_finalize;
+}
+
+PangoRenderer *
+_gdk_x11_renderer_get (GdkDrawable *drawable,
+                      GdkGC       *gc)
+{
+  GdkScreen *screen = GDK_DRAWABLE_IMPL_X11 (drawable)->screen;
+  GdkScreenX11 *screen_x11 = GDK_SCREEN_X11 (screen);
+  GdkX11Renderer *x11_renderer;
+
+  if (!screen_x11->renderer)
+    {
+      screen_x11->renderer = g_object_new (GDK_TYPE_X11_RENDERER,
+                                          "display", GDK_SCREEN_XDISPLAY (screen),
+                                          "screen",  GDK_SCREEN_XNUMBER (screen),
+                                          NULL);
+    }
+
+  x11_renderer = GDK_X11_RENDERER (screen_x11->renderer);
+
+  x11_renderer->drawable = drawable;
+  x11_renderer->gc = gc;
+
+  return screen_x11->renderer;
+}
 
 /**
  * gdk_pango_context_get_for_screen:
@@ -59,3 +169,4 @@ gdk_pango_context_get_for_screen (GdkScreen *screen)
   
   return context;
 }
+
index 5ded4338aac902f965a3824617f24dc513d831fb..d48940fb5e448941caead7adbfaed9f5aadf0dea 100644 (file)
@@ -66,9 +66,14 @@ struct _GdkGCX11
   guint have_clip_mask : 1;
   guint depth : 8;
 
+  GdkFill fill;
+  GdkBitmap *stipple;
+  GdkPixmap *tile;
+
   Picture fg_picture;
   XRenderColor fg_picture_color; 
   gulong fg_pixel;
+  gulong bg_pixel;
 };
 
 struct _GdkGCX11Class
@@ -176,6 +181,9 @@ void _gdk_dnd_init          (GdkDisplay *display);
 void _gdk_windowing_image_init  (GdkDisplay *display);
 void _gdk_input_init            (GdkDisplay *display);
 
+PangoRenderer *_gdk_x11_renderer_get (GdkDrawable *drawable,
+                                     GdkGC       *gc);
+
 extern GdkDrawableClass  _gdk_x11_drawable_class;
 extern gboolean                 _gdk_use_xshm;
 extern const int         _gdk_nenvent_masks;
index 0856a010f692ce733e9b40b9c549d3d0e1751040..1d181eb5c1d50f9d99110b0820a646849ee3b9bb 100644 (file)
@@ -306,6 +306,9 @@ gdk_screen_x11_finalize (GObject *object)
   GdkScreenX11 *screen_x11 = GDK_SCREEN_X11 (object);
   /* int i; */
   g_object_unref (screen_x11->root_window);
+
+  if (screen_x11->renderer)
+    g_object_unref (screen_x11->renderer);
   
   /* Visual Part (Need to implement finalize for Visuals for a clean
    * finalize) */
index 89aa4390de1577c22bd5a69fa7fb6f0af0ecfd2f..fddc28dd04e812e60b209eda8ea1782cea203f73 100644 (file)
@@ -88,6 +88,9 @@ struct _GdkScreenX11
   /* Xinerama */
   gint     num_monitors;
   GdkRectangle *monitors;
+
+  /* Pango renderer object singleton */
+  PangoRenderer *renderer;
 };
   
 struct _GdkScreenX11Class
index 5a21e9425eaeb5cb203b604cdc79262056a71ac1..7e58a14f547577a68b8b7056bb181212ab6a7006 100644 (file)
@@ -1226,6 +1226,7 @@ gtk_justification_get_type
 gtk_key_snooper_install
 gtk_key_snooper_remove
 gtk_label_get
+gtk_label_get_angle
 gtk_label_get_attributes
 gtk_label_get_ellipsize
 gtk_label_get_justify
@@ -1247,6 +1248,7 @@ gtk_label_new
 gtk_label_new_with_mnemonic
 gtk_label_parse_uline
 gtk_label_select_region
+gtk_label_set_angle
 gtk_label_set_attributes
 gtk_label_set_ellipsize
 gtk_label_set_justify
index ac148ebe4ad372713faf49f15b61f38151c11ced..9c26a23b642926342de7a376d097da9005d966e1 100644 (file)
@@ -49,6 +49,8 @@ typedef struct
 {
   gint width_chars;
   guint single_line_mode : 1;
+  guint have_transform : 1;
+  gdouble angle;
 }
 GtkLabelPrivate;
 
@@ -88,7 +90,8 @@ enum {
   PROP_SELECTION_BOUND,
   PROP_ELLIPSIZE,
   PROP_WIDTH_CHARS,
-  PROP_SINGLE_LINE_MODE
+  PROP_SINGLE_LINE_MODE,
+  PROP_ANGLE
 };
 
 static guint signals[LAST_SIGNAL] = { 0 };
@@ -468,6 +471,27 @@ gtk_label_class_init (GtkLabelClass *class)
                                                         P_("Whether the label is in single line mode"),
                                                         FALSE,
                                                         G_PARAM_READWRITE));
+
+  /**
+   * GtkLabel:angle:
+   * 
+   * The angle that the baseline of the label makes with the horizontal,
+   * in degrees, measured counterclockwise. An angle of 90 reads from
+   * from bottom to top, an angle of 270, from top to bottom. Ignored
+   * if the label is selectable, wrapped, or ellipsized.
+   *
+   * Since: 2.6
+   **/
+  g_object_class_install_property (gobject_class,
+                                   PROP_ANGLE,
+                                   g_param_spec_double ("angle",
+                                                       P_("Angle"),
+                                                       P_("Angle at which the label is rotated"),
+                                                       0.0,
+                                                       360.0,
+                                                       0.0, 
+                                                       G_PARAM_READWRITE));
+  
   /*
    * Key bindings
    */
@@ -596,6 +620,9 @@ gtk_label_set_property (GObject      *object,
     case PROP_SINGLE_LINE_MODE:
       gtk_label_set_single_line_mode (label, g_value_get_boolean (value));
       break;     
+    case PROP_ANGLE:
+      gtk_label_set_angle (label, g_value_get_double (value));
+      break;     
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -670,6 +697,9 @@ gtk_label_get_property (GObject     *object,
     case PROP_SINGLE_LINE_MODE:
       g_value_set_boolean (value, gtk_label_get_single_line_mode (label));
       break;
+    case PROP_ANGLE:
+      g_value_set_double (value, gtk_label_get_angle (label));
+      break;
 
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -686,7 +716,7 @@ gtk_label_init (GtkLabel *label)
 
   priv = GTK_LABEL_GET_PRIVATE (label);
   priv->width_chars = -1;
-
+  priv->angle = 0.0;
   label->label = NULL;
 
   label->jtype = GTK_JUSTIFY_LEFT;
@@ -1646,6 +1676,28 @@ gtk_label_ensure_layout (GtkLabel *label)
       PangoAlignment align = PANGO_ALIGN_LEFT; /* Quiet gcc */
       GtkLabelPrivate *priv = GTK_LABEL_GET_PRIVATE (label);
 
+      if (priv->angle != 0.0 && !label->wrap && !label->ellipsize && !label->select_info)
+       {
+         /* We rotate the standard singleton PangoContext for the widget,
+          * depending on the fact that it's meant pretty much exclusively
+          * for our use.
+          */
+         PangoMatrix matrix = PANGO_MATRIX_INIT;
+         
+         pango_matrix_rotate (&matrix, priv->angle);
+
+         pango_context_set_matrix (gtk_widget_get_pango_context (widget), &matrix);
+         
+         priv->have_transform = TRUE;
+       }
+      else 
+       {
+         if (priv->have_transform)
+           pango_context_set_matrix (gtk_widget_get_pango_context (widget), NULL);
+
+         priv->have_transform = FALSE;
+       }
+
       label->layout = gtk_widget_create_pango_layout (widget, label->text);
 
       if (label->effective_attrs)
@@ -1750,6 +1802,55 @@ gtk_label_ensure_layout (GtkLabel *label)
     }
 }
 
+/* Gets the bounds of a layout in device coordinates. Note cut-and-paste
+ * between here and gdkpango.c */
+static void
+get_rotated_layout_bounds (PangoLayout  *layout,
+                          GdkRectangle *rect)
+{
+  PangoContext *context = pango_layout_get_context (layout);
+  const PangoMatrix *matrix = pango_context_get_matrix (context);
+  gdouble x_min = 0, x_max = 0, y_min = 0, y_max = 0; /* quiet gcc */
+  PangoRectangle logical_rect;
+  gint i, j;
+
+  pango_layout_get_extents (layout, NULL, &logical_rect);
+  
+  for (i = 0; i < 2; i++)
+    {
+      gdouble x = (i == 0) ? logical_rect.x : logical_rect.x + logical_rect.width;
+      for (j = 0; j < 2; j++)
+       {
+         gdouble y = (j == 0) ? logical_rect.y : logical_rect.y + logical_rect.height;
+         
+         gdouble xt = (x * matrix->xx + y * matrix->xy) / PANGO_SCALE + matrix->x0;
+         gdouble yt = (x * matrix->yx + y * matrix->yy) / PANGO_SCALE + matrix->y0;
+         
+         if (i == 0 && j == 0)
+           {
+             x_min = x_max = xt;
+             y_min = y_max = yt;
+           }
+         else
+           {
+             if (xt < x_min)
+               x_min = xt;
+             if (yt < y_min)
+               y_min = yt;
+             if (xt > x_max)
+               x_max = xt;
+             if (yt > y_max)
+               y_max = yt;
+           }
+       }
+    }
+  
+  rect->x = floor (x_min);
+  rect->width = ceil (x_max) - rect->x;
+  rect->y = floor (y_min);
+  rect->height = floor (y_max) - rect->y;
+}
+
 static void
 gtk_label_size_request (GtkWidget      *widget,
                        GtkRequisition *requisition)
@@ -1787,9 +1888,21 @@ gtk_label_size_request (GtkWidget      *widget,
   width = label->misc.xpad * 2;
   height = label->misc.ypad * 2;
 
-  pango_layout_get_extents (label->layout, NULL, &logical_rect);
   aux_info = _gtk_widget_get_aux_info (widget, FALSE);
 
+  if (priv->have_transform)
+    {
+      GdkRectangle rect;
+
+      get_rotated_layout_bounds (label->layout, &rect);
+      
+      requisition->width = width + rect.width;
+      requisition->height = height + rect.height;
+      return;
+    }
+  else
+    pango_layout_get_extents (label->layout, NULL, &logical_rect);
+  
   if (label->ellipsize || priv->width_chars > 0)
     {
       PangoContext *context;
@@ -1903,37 +2016,6 @@ gtk_label_direction_changed (GtkWidget        *widget,
   GTK_WIDGET_CLASS (parent_class)->direction_changed (widget, previous_dir);
 }
 
-#if 0
-static void
-gtk_label_paint_word (GtkLabel     *label,
-                     gint          x,
-                     gint          y,
-                     GtkLabelWord *word,
-                     GdkRectangle *area)
-{
-  GtkWidget *widget = GTK_WIDGET (label);
-  GtkLabelULine *uline;
-  gchar *tmp_str;
-  
-  tmp_str = gdk_wcstombs (word->beginning);
-  if (tmp_str)
-    {
-      gtk_paint_string (widget->style, widget->window, widget->state,
-                       area, widget, "label", 
-                       x + word->x,
-                       y + word->y,
-                       tmp_str);
-      g_free (tmp_str);
-    }
-  
-  for (uline = word->uline; uline; uline = uline->next)
-    gtk_paint_hline (widget->style, widget->window, 
-                    widget->state, area,
-                    widget, "label", 
-                    x + uline->x1, x + uline->x2, y + uline->y);
-}
-#endif
-
 static void
 get_layout_location (GtkLabel  *label,
                      gint      *xp,
@@ -2807,6 +2889,70 @@ gtk_label_get_selectable (GtkLabel *label)
   return label->select_info != NULL;
 }
 
+/**
+ * gtk_label_set_angle:
+ * @label: a #GtkLabel
+ * @angle: the angle that the baseline of the label makes with
+ *   the horizontal, in degrees, measured counterclockwise
+ * 
+ * Sets the angle of rotation for the label. An angle of 90 reads from
+ * from bottom to top, an angle of 270, from top to bottom. The angle
+ * setting for the label is ignored if the label is selectable,
+ * wrapped, or ellipsized.
+ *
+ * Since: 2.6
+ **/
+void
+gtk_label_set_angle (GtkLabel *label,
+                    gdouble   angle)
+{
+  GtkLabelPrivate *priv;
+
+  g_return_if_fail (GTK_IS_LABEL (label));
+
+  priv = GTK_LABEL_GET_PRIVATE (label);
+
+  /* Canonicalize to [0,360]. We don't canonicalize 360 to 0, because
+   * double property ranges are inclusive, and changing 360 to 0 would
+   * make a property editor behave strangely.
+   */
+  if (angle < 0 || angle > 360.0)
+    angle = angle - 360. * floor (angle / 360.);
+
+  if (angle != priv->angle)
+    {
+      priv->angle = angle;
+      
+      gtk_label_clear_layout (label);
+      gtk_widget_queue_resize (GTK_WIDGET (label));
+
+      g_object_notify (G_OBJECT (label), "angle");
+    }
+}
+
+/**
+ * gtk_label_get_angle:
+ * @label: a #GtkLabel
+ * 
+ * Gets the angle of rotation for the label. See
+ * gtk_label_set_angle.
+ * 
+ * Return value: the angle of rotation for the label
+ *
+ * Since: 2.6
+ **/
+gdouble
+gtk_label_get_angle  (GtkLabel *label)
+{
+  GtkLabelPrivate *priv;
+
+  g_return_val_if_fail (GTK_IS_LABEL (label), 0.0);
+
+  priv = GTK_LABEL_GET_PRIVATE (label);
+
+  return priv->angle;
+}
+
 static void
 gtk_label_set_selection_text (GtkLabel         *label,
                              GtkSelectionData *selection_data)
index 5da9c47ef5ad15482e56bd8ac135bc796d5c0f59..c6e9407f8428ee9864d56ac336c33d9f15a961cb 100644 (file)
@@ -143,6 +143,9 @@ gboolean gtk_label_get_line_wrap                  (GtkLabel         *label);
 void     gtk_label_set_selectable                 (GtkLabel         *label,
                                                   gboolean          setting);
 gboolean gtk_label_get_selectable                 (GtkLabel         *label);
+void     gtk_label_set_angle                      (GtkLabel         *label,
+                                                  gdouble           angle);
+gdouble  gtk_label_get_angle                      (GtkLabel         *label);
 void     gtk_label_select_region                  (GtkLabel         *label,
                                                   gint              start_offset,
                                                   gint              end_offset);
index bb7867f371e1dec99ee0b31f273460fff918cfa5..4b621b4315d9e1ee79d1f384a71a692117596218 100644 (file)
  * use the semi-public headers, as with gtktextview.c.
  */
 
-#include <pango/pango.h>
+#define GTK_TYPE_TEXT_RENDERER            (gtk_text_renderer_get_type())
+#define GTK_TEXT_RENDERER(object)         (G_TYPE_CHECK_INSTANCE_CAST ((object), GTK_TYPE_TEXT_RENDERER, GtkTextRenderer))
+#define GTK_IS_TEXT_RENDERER(object)      (G_TYPE_CHECK_INSTANCE_TYPE ((object), GTK_TYPE_TEXT_RENDERER))
+#define GTK_TEXT_RENDERER_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_TEXT_RENDERER, GtkTextRendererClass))
+#define GTK_IS_TEXT_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_TEXT_RENDERER))
+#define GTK_TEXT_RENDERER_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_TEXT_RENDERER, GtkTextRendererClass))
 
-typedef struct _GtkTextRenderState GtkTextRenderState;
+typedef struct _GtkTextRenderer      GtkTextRenderer;
+typedef struct _GtkTextRendererClass GtkTextRendererClass;
 
-struct _GtkTextRenderState
+struct _GtkTextRenderer
 {
-  GtkWidget *widget;
+  GdkPangoRenderer parent_instance;
 
-  GtkTextAppearance *last_appearance;
-  GtkTextAppearance *last_bg_appearance;
-  GdkGC *fg_gc;
-  GdkGC *bg_gc;
-  GdkGC *error_gc;
+  GdkScreen *screen;
+
+  GtkWidget *widget;
+  GdkDrawable *drawable;
   GdkRectangle clip_rect;
+  
+  GdkColor *error_color;       /* Error underline color for this widget */
+  GList *widgets;              /* widgets encountered when drawing */
+  
+  gboolean selected;
 };
 
-static void       get_item_properties (PangoItem           *item,
-                                       GtkTextAppearance  **appearance);
-static GdkRegion *get_selected_clip   (GtkTextRenderState  *render_state,
-                                       PangoLayout         *layout,
-                                       PangoLayoutLine     *line,
-                                       int                  x,
-                                       int                  y,
-                                       int                  height,
-                                       int                  start_index,
-                                       int                  end_index);
-
-static GtkTextRenderState *
-gtk_text_render_state_new (GtkWidget    *widget,
-                           GdkDrawable  *drawable,
-                           GdkRectangle *clip_rect)
+struct _GtkTextRendererClass
 {
-  GtkTextRenderState *state = g_new0 (GtkTextRenderState, 1);
+  GdkPangoRendererClass parent_class;
+};
 
-  state->widget = widget;
-  state->fg_gc = gdk_gc_new (drawable);
-  state->bg_gc = gdk_gc_new (drawable);
+G_DEFINE_TYPE (GtkTextRenderer, gtk_text_renderer, GDK_TYPE_PANGO_RENDERER)
 
-  state->clip_rect = *clip_rect;
+static GdkColor *
+text_renderer_get_error_color (GtkTextRenderer *text_renderer)
+{
+  static const GdkColor red = { 0, 0xffff, 0, 0 };
 
-  return state;
+  if (!text_renderer->error_color)
+    gtk_widget_style_get (text_renderer->widget,
+                         "error-underline_color", &text_renderer->error_color,
+                         NULL);
+  
+  if (!text_renderer->error_color)
+    text_renderer->error_color = gdk_color_copy (&red);
+
+  return text_renderer->error_color;
 }
 
 static void
-gtk_text_render_state_destroy (GtkTextRenderState *state)
+text_renderer_set_gdk_color (GtkTextRenderer *text_renderer,
+                            PangoRenderPart  part,
+                            GdkColor        *gdk_color)
 {
-  g_object_unref (state->fg_gc);
-  g_object_unref (state->bg_gc);
-  if (state->error_gc)
-    g_object_unref (state->error_gc);
+  PangoRenderer *renderer = PANGO_RENDERER (text_renderer);
+
+  if (gdk_color)
+    {
+      PangoColor color;
 
-  g_free (state);
+      color.red = gdk_color->red;
+      color.green = gdk_color->green;
+      color.blue = gdk_color->blue;
+      
+      pango_renderer_set_color (renderer, part, &color);
+    }
+  else
+    pango_renderer_set_color (renderer, part, NULL);
+          
 }
 
-static void
-gtk_text_render_state_set_color (GtkTextRenderState *state,
-                                 GdkGC              *gc,
-                                 GdkColor           *color)
+static GtkTextAppearance *
+get_item_appearance (PangoItem *item)
 {
-  gdk_colormap_alloc_color (gtk_widget_get_colormap (state->widget), color, FALSE, TRUE);
-  gdk_gc_set_foreground (gc, color);
+  GSList *tmp_list = item->analysis.extra_attrs;
+
+  while (tmp_list)
+    {
+      PangoAttribute *attr = tmp_list->data;
+
+      if (attr->klass->type == gtk_text_attr_appearance_type)
+       return &((GtkTextAttrAppearance *)attr)->appearance;
+
+      tmp_list = tmp_list->next;
+    }
+
+  return NULL;
 }
 
 static void
-gtk_text_render_state_update (GtkTextRenderState *state,
-                              GtkTextAppearance  *new_appearance)
+gtk_text_renderer_prepare_run (PangoRenderer  *renderer,
+                              PangoLayoutRun *run)
 {
-  GdkScreen *screen = gtk_widget_get_screen (state->widget);
+  GtkTextRenderer *text_renderer = GTK_TEXT_RENDERER (renderer);
+  GdkPangoRenderer *gdk_renderer = GDK_PANGO_RENDERER (renderer);
+  GdkColor *bg_color, *fg_color, *underline_color;
+  GdkPixmap *fg_stipple, *bg_stipple;
+  GtkTextAppearance *appearance;
+
+  PANGO_RENDERER_CLASS (gtk_text_renderer_parent_class)->prepare_run (renderer, run);
+
+  appearance = get_item_appearance (run->item);
+  g_assert (appearance != NULL);
   
-  if (!state->last_appearance ||
-      !gdk_color_equal (&new_appearance->fg_color, &state->last_appearance->fg_color))
-    gtk_text_render_state_set_color (state, state->fg_gc, &new_appearance->fg_color);
+  if (appearance->draw_bg && !text_renderer->selected)
+    bg_color = &appearance->bg_color;
+  else
+    bg_color = NULL;
+  
+  text_renderer_set_gdk_color (text_renderer, PANGO_RENDER_PART_BACKGROUND, bg_color);
 
-  if (!state->last_appearance ||
-      new_appearance->fg_stipple != state->last_appearance->fg_stipple)
+  if (text_renderer->selected)
     {
-      if (new_appearance->fg_stipple)
-        {
-         if (screen == gdk_drawable_get_screen (new_appearance->fg_stipple))
-           {
-             gdk_gc_set_fill (state->fg_gc, GDK_STIPPLED);
-             gdk_gc_set_stipple (state->fg_gc, new_appearance->fg_stipple);
-           }
-         else
-           g_warning ("gtk_text_render_state_update:\n"
-                      "The foreground stipple bitmap has been created on the wrong screen.\n"
-                      "Ignoring the stipple bitmap information.");
-        }
+      if (GTK_WIDGET_HAS_FOCUS (text_renderer->widget))
+       fg_color = &text_renderer->widget->style->text[GTK_STATE_SELECTED];
       else
-        {
-          gdk_gc_set_fill (state->fg_gc, GDK_SOLID);
-        }
+       fg_color = &text_renderer->widget->style->text[GTK_STATE_ACTIVE];
     }
+  else
+    fg_color = &appearance->fg_color;
 
-  if (new_appearance->draw_bg)
-    {
-      if (!state->last_bg_appearance ||
-          !gdk_color_equal (&new_appearance->bg_color, &state->last_bg_appearance->bg_color))
-        gtk_text_render_state_set_color (state, state->bg_gc, &new_appearance->bg_color);
+  text_renderer_set_gdk_color (text_renderer, PANGO_RENDER_PART_FOREGROUND, fg_color);
+  text_renderer_set_gdk_color (text_renderer, PANGO_RENDER_PART_STRIKETHROUGH, fg_color);
 
-      if (!state->last_bg_appearance ||
-          new_appearance->bg_stipple != state->last_bg_appearance->bg_stipple)
-        {
-          if (new_appearance->bg_stipple)
-            {
-             if (screen == gdk_drawable_get_screen (new_appearance->bg_stipple))
-               {
-                 gdk_gc_set_fill (state->bg_gc, GDK_STIPPLED);
-                 gdk_gc_set_stipple (state->bg_gc, new_appearance->bg_stipple);
-               }
-             else
-               g_warning ("gtk_text_render_state_update:\n"
-                          "The background stipple bitmap has been created on the wrong screen.\n"
-                          "Ignoring the stipple bitmap information.");
+  if (appearance->underline == PANGO_UNDERLINE_ERROR)
+    underline_color = text_renderer_get_error_color (text_renderer);
+  else
+    underline_color = fg_color;
 
-            }
-          else
-            {
-              gdk_gc_set_fill (state->bg_gc, GDK_SOLID);
-            }
-        }
+  text_renderer_set_gdk_color (text_renderer, PANGO_RENDER_PART_UNDERLINE, underline_color);
 
-      state->last_bg_appearance = new_appearance;
+  fg_stipple = appearance->fg_stipple;
+  if (fg_stipple && text_renderer->screen != gdk_drawable_get_screen (fg_stipple))
+    {
+      g_warning ("gtk_text_renderer_prepare_run:\n"
+                "The foreground stipple bitmap has been created on the wrong screen.\n"
+                "Ignoring the stipple bitmap information.");
+      fg_stipple = NULL;
     }
+      
+  gdk_pango_renderer_set_stipple (gdk_renderer, PANGO_RENDER_PART_FOREGROUND, fg_stipple);
+  gdk_pango_renderer_set_stipple (gdk_renderer, PANGO_RENDER_PART_STRIKETHROUGH, fg_stipple);
+  gdk_pango_renderer_set_stipple (gdk_renderer, PANGO_RENDER_PART_UNDERLINE, fg_stipple);
 
-  state->last_appearance = new_appearance;
+  bg_stipple = appearance->draw_bg ? appearance->bg_stipple : NULL;
+  
+  if (bg_stipple && text_renderer->screen != gdk_drawable_get_screen (bg_stipple))
+    {
+      g_warning ("gtk_text_renderer_prepare_run:\n"
+                "The background stipple bitmap has been created on the wrong screen.\n"
+                "Ignoring the stipple bitmap information.");
+      bg_stipple = NULL;
+    }
+  
+  gdk_pango_renderer_set_stipple (gdk_renderer, PANGO_RENDER_PART_BACKGROUND, bg_stipple);
 }
 
-static GdkGC *
-gtk_text_render_state_get_error_gc (GtkTextRenderState *state)
+static void
+gtk_text_renderer_draw_shape (PangoRenderer   *renderer,
+                             PangoAttrShape  *attr,
+                             int              x,
+                             int              y)
 {
-  if (!state->error_gc)
+  GtkTextRenderer *text_renderer = GTK_TEXT_RENDERER (renderer);
+  GdkGC *fg_gc;
+
+  if (text_renderer->selected)
+    {
+      if (GTK_WIDGET_HAS_FOCUS (text_renderer->widget))
+       fg_gc = text_renderer->widget->style->text_gc[GTK_STATE_SELECTED];
+      else
+       fg_gc = text_renderer->widget->style->text_gc[GTK_STATE_SELECTED];
+    }
+  else
+    fg_gc = text_renderer->widget->style->text_gc[GTK_STATE_NORMAL];
+  
+  if (attr->data == NULL)
     {
-      static const GdkColor red = { 0, 0xffff, 0, 0 };
+      /* This happens if we have an empty widget anchor. Draw
+       * something empty-looking.
+       */
+      GdkRectangle shape_rect, draw_rect;
       
-      GdkGCValues gc_values;
-      GdkGCValuesMask gc_values_mask;
-      GdkColor *underline_color;
-      GtkWidget *widget = state->widget;
-
-      gtk_widget_style_get (widget, "error-underline_color", &underline_color, NULL);
+      shape_rect.x = PANGO_PIXELS (x);
+      shape_rect.y = PANGO_PIXELS (y + attr->logical_rect.y);
+      shape_rect.width = PANGO_PIXELS (x + attr->logical_rect.width) - shape_rect.x;
+      shape_rect.height = PANGO_PIXELS (y + attr->logical_rect.y + attr->logical_rect.height) - shape_rect.y;
       
-      gc_values_mask = GDK_GC_FOREGROUND;
-      if (underline_color)
+      if (gdk_rectangle_intersect (&shape_rect, &text_renderer->clip_rect,
+                                  &draw_rect))
        {
-         gc_values.foreground = *underline_color;
-         gdk_color_free (underline_color);
+         gdk_draw_rectangle (text_renderer->drawable, fg_gc,
+                             FALSE, shape_rect.x, shape_rect.y,
+                             shape_rect.width, shape_rect.height);
+         
+         gdk_draw_line (text_renderer->drawable, fg_gc,
+                        shape_rect.x, shape_rect.y,
+                        shape_rect.x + shape_rect.width,
+                        shape_rect.y + shape_rect.height);
+         
+         gdk_draw_line (text_renderer->drawable, fg_gc,
+                        shape_rect.x + shape_rect.width, shape_rect.y,
+                        shape_rect.x,
+                        shape_rect.y + shape_rect.height);
        }
-      else
-       gc_values.foreground = red;
+    }
+  else if (GDK_IS_PIXBUF (attr->data))
+    {
+      gint width, height;
+      GdkRectangle pixbuf_rect, draw_rect;
+      GdkPixbuf *pixbuf;
       
-      gdk_rgb_find_color (widget->style->colormap, &gc_values.foreground);
-      state->error_gc = gdk_gc_new_with_values (widget->window, &gc_values, gc_values_mask);
+      pixbuf = GDK_PIXBUF (attr->data);
+      
+      width = gdk_pixbuf_get_width (pixbuf);
+      height = gdk_pixbuf_get_height (pixbuf);
+      
+      pixbuf_rect.x = PANGO_PIXELS (x);
+      pixbuf_rect.y = PANGO_PIXELS (y) - height;
+      pixbuf_rect.width = width;
+      pixbuf_rect.height = height;
+      
+      if (gdk_rectangle_intersect (&pixbuf_rect, &text_renderer->clip_rect,
+                                  &draw_rect))
+       {
+         gdk_draw_pixbuf (text_renderer->drawable,
+                          fg_gc,
+                          pixbuf,
+                          draw_rect.x - pixbuf_rect.x,
+                          draw_rect.y - pixbuf_rect.y,
+                          draw_rect.x, draw_rect.y,
+                          draw_rect.width,
+                          draw_rect.height,
+                          GDK_RGB_DITHER_NORMAL,
+                          0, 0);
+       }
     }
+  else if (GTK_IS_WIDGET (attr->data))
+    {
+      GtkWidget *widget;
+      
+      widget = GTK_WIDGET (attr->data);
 
-  return state->error_gc;
+      text_renderer->widgets = g_list_prepend (text_renderer->widgets,
+                                              g_object_ref (widget));
+    }
+  else
+    g_assert_not_reached (); /* not a pixbuf or widget */
 }
 
 static void
-get_shape_extents (PangoLayoutRun *run,
-                   PangoRectangle *ink_rect,
-                   PangoRectangle *logical_rect)
+gtk_text_renderer_finalize (GObject *object)
 {
-  GSList *tmp_list = run->item->analysis.extra_attrs;
-    
-  while (tmp_list)
-    {
-      PangoAttribute *attr = tmp_list->data;
-
-      if (attr->klass->type == PANGO_ATTR_SHAPE)
-       {          
-         if (logical_rect)
-           *logical_rect = ((PangoAttrShape *)attr)->logical_rect;
-
-         if (ink_rect)
-           *ink_rect = ((PangoAttrShape *)attr)->ink_rect;
-
-          return;
-        }
-
-      tmp_list = tmp_list->next;
-    }
+  G_OBJECT_CLASS (gtk_text_renderer_parent_class)->finalize (object);
+}
 
-  g_assert_not_reached ();
+static void
+gtk_text_renderer_init (GtkTextRenderer *renderer)
+{
 }
 
 static void
-render_layout_line (GdkDrawable        *drawable,
-                    GtkTextRenderState *render_state,
-                    PangoLayoutLine    *line,
-                    GSList            **shaped_pointer,
-                    int                 x,
-                    int                 y,
-                    gboolean            selected,
-                    GList             **widgets)
+gtk_text_renderer_class_init (GtkTextRendererClass *klass)
 {
-  GSList *tmp_list = line->runs;
-  PangoRectangle overall_rect;
-  PangoRectangle logical_rect;
-  PangoRectangle ink_rect;
-  gint x_off = 0;
-  GdkGC *fg_gc;
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
   
-  pango_layout_line_get_extents (line, NULL, &overall_rect);
+  PangoRendererClass *renderer_class = PANGO_RENDERER_CLASS (klass);
+  
+  renderer_class->prepare_run = gtk_text_renderer_prepare_run;
+  renderer_class->draw_shape = gtk_text_renderer_draw_shape;
 
-  while (tmp_list)
-    {
-      PangoLayoutRun *run = tmp_list->data;
-      GtkTextAppearance *appearance;
-      gint risen_y;
-      gint shaped_width_pixels = 0;
-      gboolean need_ink = FALSE;
-      
-      tmp_list = tmp_list->next;
+  object_class->finalize = gtk_text_renderer_finalize;
+}
 
-      get_item_properties (run->item, &appearance);
+static void
+text_renderer_set_selected (GtkTextRenderer *text_renderer,
+                           gboolean         selected)
+{
+  text_renderer->selected = selected;
+}
 
-      g_assert (appearance != NULL);
-      
-      risen_y = y - PANGO_PIXELS (appearance->rise);
-      
-      if (selected)
-        {
-         if (GTK_WIDGET_HAS_FOCUS (render_state->widget))
-           fg_gc = render_state->widget->style->text_gc[GTK_STATE_SELECTED];
-         else
-           fg_gc = render_state->widget->style->text_gc [GTK_STATE_ACTIVE];
-        }
-      else
-        {
-          gtk_text_render_state_update (render_state, appearance);
-          
-          fg_gc = render_state->fg_gc;
-        }
-      
-      if (appearance->underline != PANGO_UNDERLINE_NONE ||
-          appearance->strikethrough)
-        need_ink = TRUE;
-      
-      if (appearance->is_text)
-        {
-          if (need_ink)
-            pango_glyph_string_extents (run->glyphs, run->item->analysis.font,
-                                        &ink_rect, &logical_rect);
-          else
-            pango_glyph_string_extents (run->glyphs, run->item->analysis.font,
-                                        NULL, &logical_rect);
-        }
-      else
-        {
-          if (need_ink)
-            get_shape_extents (run, &ink_rect, &logical_rect);
-          else
-            get_shape_extents (run, NULL, &logical_rect);
-        }
-      
-      if (appearance->draw_bg && !selected)
-        gdk_draw_rectangle (drawable, render_state->bg_gc, TRUE,
-                            x + PANGO_PIXELS (x_off) + PANGO_PIXELS (logical_rect.x),
-                            risen_y + PANGO_PIXELS (logical_rect.y),
-                            PANGO_PIXELS (logical_rect.width),
-                            PANGO_PIXELS (logical_rect.height));
-
-      if (appearance->is_text)
-        gdk_draw_glyphs (drawable, fg_gc,
-                         run->item->analysis.font,
-                         x + PANGO_PIXELS (x_off),
-                         risen_y, run->glyphs);
-      else
-        {
-          GObject *shaped = (*shaped_pointer)->data;
+static void
+text_renderer_begin (GtkTextRenderer *text_renderer,
+                    GtkWidget       *widget,
+                    GdkDrawable     *drawable,
+                    GdkRectangle    *clip_rect)
+{
+  text_renderer->widget = widget;
+  text_renderer->drawable = drawable;
+  text_renderer->clip_rect = *clip_rect;
 
-          *shaped_pointer = (*shaped_pointer)->next;
+  gdk_pango_renderer_set_drawable (GDK_PANGO_RENDERER (text_renderer), drawable);
+  gdk_pango_renderer_set_gc (GDK_PANGO_RENDERER (text_renderer),
+                            widget->style->text_gc[widget->state]);
+}
 
-          if (shaped == NULL)
-            {
-              /* This happens if we have an empty widget anchor. Draw
-               * something empty-looking.
-               */
-              GdkRectangle shape_rect, draw_rect;
-
-              shape_rect.x = x + x_off / PANGO_SCALE;
-              shape_rect.y = risen_y - PANGO_PIXELS (logical_rect.height);
-              shape_rect.width = PANGO_PIXELS (logical_rect.width);
-              shape_rect.height = PANGO_PIXELS (logical_rect.height);
-
-              if (gdk_rectangle_intersect (&shape_rect, &render_state->clip_rect,
-                                           &draw_rect))
-                {
-                  gdk_draw_rectangle (drawable, render_state->fg_gc,
-                                      FALSE, shape_rect.x, shape_rect.y,
-                                      shape_rect.width, shape_rect.height);
-
-                  gdk_draw_line (drawable, render_state->fg_gc,
-                                 shape_rect.x, shape_rect.y,
-                                 shape_rect.x + shape_rect.width,
-                                 shape_rect.y + shape_rect.height);
-
-                  gdk_draw_line (drawable, render_state->fg_gc,
-                                 shape_rect.x + shape_rect.width, shape_rect.y,
-                                 shape_rect.x,
-                                 shape_rect.y + shape_rect.height);
-                }
+/* Returns a GSList of (referenced) widgets encountered while drawing.
+ */
+static GList *
+text_renderer_end (GtkTextRenderer *text_renderer)
+{
+  GList *widgets = text_renderer->widgets;
 
-              shaped_width_pixels = shape_rect.width;
-            }
-          else if (GDK_IS_PIXBUF (shaped))
-            {
-              gint width, height;
-              GdkRectangle pixbuf_rect, draw_rect;
-              GdkPixbuf *pixbuf;
+  text_renderer->widget = NULL;
+  text_renderer->drawable = NULL;
 
-              pixbuf = GDK_PIXBUF (shaped);
-              
-              width = gdk_pixbuf_get_width (pixbuf);
-              height = gdk_pixbuf_get_height (pixbuf);
+  text_renderer->widgets = NULL;
 
-              pixbuf_rect.x = x + x_off / PANGO_SCALE;
-              pixbuf_rect.y = risen_y - height;
-              pixbuf_rect.width = width;
-              pixbuf_rect.height = height;
+  if (text_renderer->error_color)
+    {
+      gdk_color_free (text_renderer->error_color);
+      text_renderer->error_color = NULL;
+    }
 
-              if (gdk_rectangle_intersect (&pixbuf_rect, &render_state->clip_rect,
-                                           &draw_rect))
-                {
-                  gdk_draw_pixbuf (drawable,
-                                  render_state->fg_gc,
-                                  pixbuf,
-                                  draw_rect.x - pixbuf_rect.x,
-                                  draw_rect.y - pixbuf_rect.y,
-                                  draw_rect.x, draw_rect.y,
-                                  draw_rect.width,
-                                  draw_rect.height,
-                                  GDK_RGB_DITHER_NORMAL,
-                                  0, 0);
-                }
+  gdk_pango_renderer_set_drawable (GDK_PANGO_RENDERER (text_renderer), NULL);
+  gdk_pango_renderer_set_gc (GDK_PANGO_RENDERER (text_renderer), NULL);
 
-              shaped_width_pixels = width;
-            }
-          else if (GTK_IS_WIDGET (shaped))
-            {
-              GtkWidget *widget;
-              
-              widget = GTK_WIDGET (shaped);
-              
-              shaped_width_pixels = widget->allocation.width;
+  return widgets;
+}
 
-              if (widgets)
-                {
-                  g_object_ref (widget);
-                  *widgets = g_list_prepend (*widgets, widget);
-                }
-            }
-          else
-            g_assert_not_reached (); /* not a pixbuf or widget */
-        }
 
-      switch (appearance->underline)
-        {
-        case PANGO_UNDERLINE_NONE:
-          break;
-        case PANGO_UNDERLINE_DOUBLE:
-          g_assert (need_ink);
-          gdk_draw_line (drawable, fg_gc,
-                         x + (x_off + ink_rect.x) / PANGO_SCALE - 1,
-                         risen_y + 3,
-                         x + (x_off + ink_rect.x + ink_rect.width) / PANGO_SCALE,
-                         risen_y + 3);
-          /* Fall through */
-        case PANGO_UNDERLINE_SINGLE:
-          g_assert (need_ink);
-          gdk_draw_line (drawable, fg_gc,
-                         x + (x_off + ink_rect.x) / PANGO_SCALE - 1,
-                         risen_y + 1,
-                         x + (x_off + ink_rect.x + ink_rect.width) / PANGO_SCALE,
-                         risen_y + 1);
-          break;
-        case PANGO_UNDERLINE_ERROR:
-          g_assert (need_ink);
-          {
-           GdkGC *error_gc = gtk_text_render_state_get_error_gc (render_state);
-
-            int point_x, point_y;
-            int counter = 0;
-           int end_x = x + (x_off + ink_rect.x + ink_rect.width) / PANGO_SCALE;
-
-            for (point_x = x + (x_off + ink_rect.x) / PANGO_SCALE - 1;
-                 point_x <= end_x;
-                 point_x += 2)
-             {
-               point_y = counter ? risen_y + 1 : risen_y + 2;
-               
-               gdk_draw_line (drawable, error_gc,
-                              point_x, point_y,
-                              MIN (point_x + 1, end_x), point_y);
-               
-               counter = (counter + 1) % 2;
-             }
-          }
-          break;
-        case PANGO_UNDERLINE_LOW:
-          g_assert (need_ink);
-          gdk_draw_line (drawable, fg_gc,
-                         x + (x_off + ink_rect.x) / PANGO_SCALE - 1,
-                         risen_y + (ink_rect.y + ink_rect.height) / PANGO_SCALE + 1,
-                         x + (x_off + ink_rect.x + ink_rect.width) / PANGO_SCALE,
-                         risen_y + (ink_rect.y + ink_rect.height) / PANGO_SCALE + 1);
-          break;
-        }
+static GdkRegion *
+get_selected_clip (GtkTextRenderer    *text_renderer,
+                   PangoLayout        *layout,
+                   PangoLayoutLine    *line,
+                   int                 x,
+                   int                 y,
+                   int                 height,
+                   int                 start_index,
+                   int                 end_index)
+{
+  gint *ranges;
+  gint n_ranges, i;
+  GdkRegion *clip_region = gdk_region_new ();
+  GdkRegion *tmp_region;
 
-      if (appearance->strikethrough)
-        {          
-          gint strikethrough_y = risen_y + (0.3 * logical_rect.y) / PANGO_SCALE;
+  pango_layout_line_get_x_ranges (line, start_index, end_index, &ranges, &n_ranges);
 
-          g_assert (need_ink);
-          
-          gdk_draw_line (drawable, fg_gc,
-                         x + (x_off + ink_rect.x) / PANGO_SCALE - 1, strikethrough_y,
-                         x + (x_off + ink_rect.x + ink_rect.width) / PANGO_SCALE, strikethrough_y);
-        }
+  for (i=0; i < n_ranges; i++)
+    {
+      GdkRectangle rect;
 
-      if (appearance->is_text)
-        x_off += logical_rect.width;
-      else
-        x_off += shaped_width_pixels * PANGO_SCALE;
+      rect.x = x + PANGO_PIXELS (ranges[2*i]);
+      rect.y = y;
+      rect.width = PANGO_PIXELS (ranges[2*i + 1]) - PANGO_PIXELS (ranges[2*i]);
+      rect.height = height;
+      
+      gdk_region_union_with_rect (clip_region, &rect);
     }
+
+  tmp_region = gdk_region_rectangle (&text_renderer->clip_rect);
+  gdk_region_intersect (clip_region, tmp_region);
+  gdk_region_destroy (tmp_region);
+
+  g_free (ranges);
+  return clip_region;
 }
 
 static void
-render_para (GdkDrawable        *drawable,
-             GtkTextRenderState *render_state,
+render_para (GtkTextRenderer    *text_renderer,
              GtkTextLineDisplay *line_display,
              /* Top-left corner of paragraph including all margins */
              int                 x,
              int                 y,
              int                 selection_start_index,
-             int                 selection_end_index,
-             GList             **widgets)
+             int                 selection_end_index)
 {
-  GSList *shaped_pointer = line_display->shaped_objects;
   PangoLayout *layout = line_display->layout;
   int byte_offset = 0;
   PangoLayoutIter *iter;
   PangoRectangle layout_logical;
   int screen_width;
-  GdkGC *fg_gc, *bg_gc;
+  GdkGC *selection_gc, *fg_gc;
   gint state;
   
   gboolean first = TRUE;
@@ -543,13 +470,13 @@ render_para (GdkDrawable        *drawable,
 
   screen_width = line_display->total_width;
   
-  if (GTK_WIDGET_HAS_FOCUS (render_state->widget))
+  if (GTK_WIDGET_HAS_FOCUS (text_renderer->widget))
     state = GTK_STATE_SELECTED;
   else
     state = GTK_STATE_ACTIVE;
 
-  fg_gc = render_state->widget->style->text_gc [state];
-  bg_gc = render_state->widget->style->base_gc [state];
+  selection_gc = text_renderer->widget->style->base_gc [state];
+  fg_gc = text_renderer->widget->style->text_gc[text_renderer->widget->state];
 
   do
     {
@@ -589,59 +516,65 @@ render_para (GdkDrawable        *drawable,
       if (selection_start_index < byte_offset &&
           selection_end_index > line->length + byte_offset) /* All selected */
         {
-          gdk_draw_rectangle (drawable,
-                              bg_gc,
+          gdk_draw_rectangle (text_renderer->drawable,
+                              selection_gc,
                               TRUE,
                               x + line_display->left_margin,
                               selection_y,
                               screen_width,
                               selection_height);
 
-          render_layout_line (drawable, render_state, line, &shaped_pointer,
-                              x + PANGO_PIXELS (line_rect.x),
-                              y + PANGO_PIXELS (baseline),
-                              TRUE,
-                              widgets);
+         text_renderer_set_selected (text_renderer, TRUE);
+         pango_renderer_draw_layout_line (PANGO_RENDERER (text_renderer),
+                                          line, 
+                                          PANGO_SCALE * x + line_rect.x,
+                                          PANGO_SCALE * y + baseline);
         }
       else
         {
-          GSList *shaped_pointer_tmp = shaped_pointer;
-
-          render_layout_line (drawable, render_state,
-                              line, &shaped_pointer,
-                              x + PANGO_PIXELS (line_rect.x),
-                              y + PANGO_PIXELS (baseline),
-                              FALSE,
-                              widgets);
+         text_renderer_set_selected (text_renderer, FALSE);
+         pango_renderer_draw_layout_line (PANGO_RENDERER (text_renderer),
+                                          line, 
+                                          PANGO_SCALE * x + line_rect.x,
+                                          PANGO_SCALE * y + baseline);
 
           if (selection_start_index <= byte_offset + line->length &&
               selection_end_index > byte_offset) /* Some selected */
             {
-              GdkRegion *clip_region = get_selected_clip (render_state, layout, line,
+              GdkRegion *clip_region = get_selected_clip (text_renderer, layout, line,
                                                           x + line_display->x_offset,
                                                           selection_y,
                                                           selection_height,
                                                           selection_start_index, selection_end_index);
+
+             /* When we change the clip on the foreground GC, we have to set
+              * it on the rendererer again, since the rendererer might have
+              * copied the GC to change attributes.
+              */
+             gdk_pango_renderer_set_gc (GDK_PANGO_RENDERER (text_renderer), NULL);
+              gdk_gc_set_clip_region (selection_gc, clip_region);
               gdk_gc_set_clip_region (fg_gc, clip_region);
-              gdk_gc_set_clip_region (bg_gc, clip_region);
+             gdk_pango_renderer_set_gc (GDK_PANGO_RENDERER (text_renderer), fg_gc);
 
-              gdk_draw_rectangle (drawable,
-                                  bg_gc,
+              gdk_draw_rectangle (text_renderer->drawable,
+                                  selection_gc,
                                   TRUE,
                                   x + PANGO_PIXELS (line_rect.x),
                                   selection_y,
                                   PANGO_PIXELS (line_rect.width),
                                   selection_height);
 
-              render_layout_line (drawable, render_state, line, &shaped_pointer_tmp,
-                                  x + PANGO_PIXELS (line_rect.x),
-                                  y + PANGO_PIXELS (baseline),
-                                  TRUE,
-                                  widgets);
+             text_renderer_set_selected (text_renderer, TRUE);
+             pango_renderer_draw_layout_line (PANGO_RENDERER (text_renderer),
+                                              line, 
+                                              PANGO_SCALE * x + line_rect.x,
+                                              PANGO_SCALE * y + baseline);
 
+             gdk_pango_renderer_set_gc (GDK_PANGO_RENDERER (text_renderer), NULL);
+              gdk_gc_set_clip_region (selection_gc, NULL);
               gdk_gc_set_clip_region (fg_gc, NULL);
-              gdk_gc_set_clip_region (bg_gc, NULL);
-
+             gdk_pango_renderer_set_gc (GDK_PANGO_RENDERER (text_renderer), fg_gc);
+             
               gdk_region_destroy (clip_region);
 
               /* Paint in the ends of the line */
@@ -649,8 +582,8 @@ render_para (GdkDrawable        *drawable,
                   ((line_display->direction == GTK_TEXT_DIR_LTR && selection_start_index < byte_offset) ||
                    (line_display->direction == GTK_TEXT_DIR_RTL && selection_end_index > byte_offset + line->length)))
                 {
-                  gdk_draw_rectangle (drawable,
-                                      bg_gc,
+                  gdk_draw_rectangle (text_renderer->drawable,
+                                      selection_gc,
                                       TRUE,
                                       x + line_display->left_margin,
                                       selection_y,
@@ -669,8 +602,8 @@ render_para (GdkDrawable        *drawable,
                     line_display->left_margin + screen_width -
                     PANGO_PIXELS (line_rect.x) - PANGO_PIXELS (line_rect.width);
 
-                  gdk_draw_rectangle (drawable,
-                                      bg_gc,
+                  gdk_draw_rectangle (text_renderer->drawable,
+                                      selection_gc,
                                       TRUE,
                                       x + PANGO_PIXELS (line_rect.x) + PANGO_PIXELS (line_rect.width),
                                       selection_y,
@@ -687,62 +620,37 @@ render_para (GdkDrawable        *drawable,
   pango_layout_iter_free (iter);
 }
 
-static GdkRegion *
-get_selected_clip (GtkTextRenderState *render_state,
-                   PangoLayout        *layout,
-                   PangoLayoutLine    *line,
-                   int                 x,
-                   int                 y,
-                   int                 height,
-                   int                 start_index,
-                   int                 end_index)
+static void
+on_renderer_display_closed (GdkDisplay       *display,
+                           GtkTextRenderer  *text_renderer)
 {
-  gint *ranges;
-  gint n_ranges, i;
-  GdkRegion *clip_region = gdk_region_new ();
-  GdkRegion *tmp_region;
-
-  pango_layout_line_get_x_ranges (line, start_index, end_index, &ranges, &n_ranges);
-
-  for (i=0; i < n_ranges; i++)
-    {
-      GdkRectangle rect;
-
-      rect.x = x + PANGO_PIXELS (ranges[2*i]);
-      rect.y = y;
-      rect.width = PANGO_PIXELS (ranges[2*i + 1]) - PANGO_PIXELS (ranges[2*i]);
-      rect.height = height;
-      
-      gdk_region_union_with_rect (clip_region, &rect);
-    }
-
-  tmp_region = gdk_region_rectangle (&render_state->clip_rect);
-  gdk_region_intersect (clip_region, tmp_region);
-  gdk_region_destroy (tmp_region);
-
-  g_free (ranges);
-  return clip_region;
+  g_signal_handlers_disconnect_by_func (text_renderer->screen,
+                                       (gpointer)on_renderer_display_closed,
+                                       text_renderer);
+  g_object_set_data (G_OBJECT (text_renderer->screen), "gtk-text-renderer", NULL);
 }
 
-static void
-get_item_properties (PangoItem          *item,
-                     GtkTextAppearance **appearance)
+static GtkTextRenderer *
+get_text_renderer (GdkScreen *screen)
 {
-  GSList *tmp_list = item->analysis.extra_attrs;
-
-  *appearance = NULL;
+  GtkTextRenderer *text_renderer;
 
-  while (tmp_list)
+  g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL);
+  
+  text_renderer = g_object_get_data (G_OBJECT (screen), "gtk-text-renderer");
+  if (!text_renderer)
     {
-      PangoAttribute *attr = tmp_list->data;
-
-      if (attr->klass->type == gtk_text_attr_appearance_type)
-        {
-          *appearance = &((GtkTextAttrAppearance *)attr)->appearance;
-        }
+      text_renderer = g_object_new (GTK_TYPE_TEXT_RENDERER, "screen", screen, NULL);
+      text_renderer->screen = screen;
+      
+      g_object_set_data_full (G_OBJECT (screen), "gtk-text-renderer", text_renderer,
+                             (GDestroyNotify)g_object_unref);
 
-      tmp_list = tmp_list->next;
+      g_signal_connect (gdk_screen_get_display (screen), "closed",
+                       G_CALLBACK (on_renderer_display_closed), text_renderer);
     }
+
+  return text_renderer;
 }
 
 void
@@ -766,11 +674,12 @@ gtk_text_layout_draw (GtkTextLayout *layout,
   GdkRectangle clip;
   gint current_y;
   GSList *cursor_list;
-  GtkTextRenderState *render_state;
+  GtkTextRenderer *text_renderer;
   GtkTextIter selection_start, selection_end;
   gboolean have_selection = FALSE;
   GSList *line_list;
   GSList *tmp_list;
+  GList *tmp_widgets;
   
   g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
   g_return_if_fail (layout->default_style != NULL);
@@ -793,10 +702,9 @@ gtk_text_layout_draw (GtkTextLayout *layout,
   clip.width = width;
   clip.height = height;
 
-  render_state = gtk_text_render_state_new (widget, drawable, &clip);
+  text_renderer = get_text_renderer (gdk_drawable_get_screen (drawable));
 
-  gdk_gc_set_clip_rectangle (render_state->fg_gc, &clip);
-  gdk_gc_set_clip_rectangle (render_state->bg_gc, &clip);
+  text_renderer_begin (text_renderer, widget, drawable, &clip);
 
   gtk_text_layout_wrap_loop_start (layout);
 
@@ -850,11 +758,10 @@ gtk_text_layout_draw (GtkTextLayout *layout,
                 }
             }
 
-          render_para (drawable, render_state, line_display,
+          render_para (text_renderer, line_display,
                        - x_offset,
                        current_y,
-                       selection_start_index, selection_end_index,
-                       widgets);
+                       selection_start_index, selection_end_index);
 
           /* We paint the cursors last, because they overlap another chunk
          and need to appear on top. */
@@ -903,14 +810,20 @@ gtk_text_layout_draw (GtkTextLayout *layout,
           
       current_y += line_display->height;
       gtk_text_layout_free_line_display (layout, line_display);
-      render_state->last_appearance = NULL;
-      render_state->last_bg_appearance = NULL;
       
       tmp_list = g_slist_next (tmp_list);
     }
 
   gtk_text_layout_wrap_loop_end (layout);
-  gtk_text_render_state_destroy (render_state);
+
+  tmp_widgets = text_renderer_end (text_renderer);
+  if (widgets)
+    *widgets = tmp_widgets;
+  else
+    {
+      g_list_foreach (tmp_widgets, (GFunc)g_object_unref, NULL);
+      g_list_free (tmp_widgets);
+    }
 
   g_slist_free (line_list);
 }
index 9820328359db665b46bd8ab8b34d668475780cfb..7c5dff55f56967d009e0aa65ab6b4af581d12993 100644 (file)
@@ -1398,6 +1398,16 @@ add_generic_attrs (GtkTextLayout      *layout,
       pango_attr_list_insert (attrs, attr);
     }
 
+  if (appearance->strikethrough)
+    {
+      attr = pango_attr_strikethrough_new (appearance->strikethrough);
+      
+      attr->start_index = start;
+      attr->end_index = start + byte_count;
+      
+      pango_attr_list_insert (attrs, attr);
+    }
+
   if (appearance->rise != 0)
     {
       attr = pango_attr_rise_new (appearance->rise);
@@ -1469,7 +1479,8 @@ add_pixbuf_attrs (GtkTextLayout      *layout,
   logical_rect.width = width * PANGO_SCALE;
   logical_rect.height = height * PANGO_SCALE;
 
-  attr = pango_attr_shape_new (&logical_rect, &logical_rect);
+  attr = pango_attr_shape_new_with_data (&logical_rect, &logical_rect,
+                                        pixbuf->pixbuf, NULL, NULL);
   attr->start_index = start;
   attr->end_index = start + seg->byte_count;
   pango_attr_list_insert (attrs, attr);
@@ -1491,6 +1502,7 @@ add_child_attrs (GtkTextLayout      *layout,
   GtkTextChildAnchor *anchor;
   gint width, height;
   GSList *tmp_list;
+  GtkWidget *widget;
 
   width = 1;
   height = 1;
@@ -1512,8 +1524,7 @@ add_child_attrs (GtkTextLayout      *layout,
           width = req.width;
           height = req.height;
 
-          display->shaped_objects =
-            g_slist_append (display->shaped_objects, child);
+         widget = child;
           
           break;
         }
@@ -1534,16 +1545,18 @@ add_child_attrs (GtkTextLayout      *layout,
       width = 30;
       height = 20;
 
-      display->shaped_objects =
-        g_slist_append (display->shaped_objects, NULL);
+      widget = NULL;
     }
+
+  display->shaped_objects = g_slist_append (display->shaped_objects, widget);
   
   logical_rect.x = 0;
   logical_rect.y = -height * PANGO_SCALE;
   logical_rect.width = width * PANGO_SCALE;
   logical_rect.height = height * PANGO_SCALE;
 
-  attr = pango_attr_shape_new (&logical_rect, &logical_rect);
+  attr = pango_attr_shape_new_with_data (&logical_rect, &logical_rect,
+                                        widget, NULL, NULL);
   attr->start_index = start;
   attr->end_index = start + seg->byte_count;
   pango_attr_list_insert (attrs, attr);
index fa6802ebb07b6fe1910ab43b4e6b5769b1ee402c..f42a7c3c0a6ab68729ca7c241c71849a8de030bd 100644 (file)
@@ -235,7 +235,7 @@ struct _GtkTextLineDisplay
 {
   PangoLayout *layout;
   GSList *cursors;
-  GSList *shaped_objects;
+  GSList *shaped_objects;      /* Only for backwards compatibility */
   
   GtkTextDirection direction;
 
index b52af0828371764250a4f872e4294bec3e10b774..3a1f6791a619c38a05f1ccc98634a20bd0cabfd9 100644 (file)
@@ -2689,6 +2689,219 @@ void create_labels (GtkWidget *widget)
     gtk_widget_destroy (window);
 }
 
+static void
+on_angle_scale_changed (GtkRange *range,
+                       GtkLabel *label)
+{
+  gtk_label_set_angle (GTK_LABEL (label), gtk_range_get_value (range));
+}
+
+static void
+create_rotated_label (GtkWidget *widget)
+{
+  static GtkWidget *window = NULL;
+  GtkWidget *vbox;
+  GtkWidget *hscale;
+  GtkWidget *label;  
+  GtkWidget *scale_label;  
+  GtkWidget *scale_hbox;  
+
+  if (!window)
+    {
+      window = gtk_dialog_new_with_buttons ("Rotated Label",
+                                           GTK_WINDOW (gtk_widget_get_toplevel (widget)), 0,
+                                           GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
+                                           NULL);
+
+      gtk_window_set_resizable (GTK_WINDOW (window), FALSE);
+
+      gtk_window_set_screen (GTK_WINDOW (window),
+                            gtk_widget_get_screen (widget));
+
+      g_signal_connect (window, "response",
+                       G_CALLBACK (gtk_object_destroy), NULL);
+      g_signal_connect (window, "destroy",
+                       G_CALLBACK (gtk_widget_destroyed), &window);
+
+      vbox = gtk_vbox_new (FALSE, 5);
+      gtk_box_pack_start (GTK_BOX (GTK_DIALOG (window)->vbox), vbox, TRUE, TRUE, 0);
+      gtk_container_set_border_width (GTK_CONTAINER (vbox), 10);
+
+      label = gtk_label_new (NULL);
+      gtk_label_set_markup (GTK_LABEL (label), "Hello World\n<i>Rotate</i> <span underline='single' foreground='blue'>me</span>");
+      gtk_box_pack_start (GTK_BOX (vbox), label, TRUE, TRUE, 0);
+
+      scale_hbox = gtk_hbox_new (FALSE, 0);
+      gtk_box_pack_start (GTK_BOX (vbox), scale_hbox, FALSE, FALSE, 0);
+      
+      scale_label = gtk_label_new (NULL);
+      gtk_label_set_markup (GTK_LABEL (scale_label), "<i>Angle: </i>");
+      gtk_box_pack_start (GTK_BOX (scale_hbox), scale_label, FALSE, FALSE, 0);
+
+      hscale = gtk_hscale_new_with_range (0, 360, 5);
+      g_signal_connect (hscale, "value-changed",
+                       G_CALLBACK (on_angle_scale_changed), label);
+      
+      gtk_range_set_value (GTK_RANGE (hscale), 45);
+      gtk_widget_set_usize (hscale, 200, -1);
+      gtk_box_pack_start (GTK_BOX (scale_hbox), hscale, TRUE, TRUE, 0);
+    }
+  
+  if (!GTK_WIDGET_VISIBLE (window))
+    gtk_widget_show_all (window);
+  else
+    gtk_widget_destroy (window);
+}
+
+#define DEFAULT_TEXT_RADIUS 200
+
+static void
+on_rotated_text_unrealize (GtkWidget *widget)
+{
+  g_object_set_data (G_OBJECT (widget), "text-gc", NULL);
+}
+
+static gboolean
+on_rotated_text_expose (GtkWidget      *widget,
+                       GdkEventExpose *event,
+                       GdkPixbuf      *tile_pixbuf)
+{
+  static const gchar *words[] = { "The", "grand", "old", "Duke", "of", "York",
+                                  "had", "10,000", "men" };
+  PangoRenderer *renderer;
+  GdkGC *gc;
+  int n_words;
+  int i;
+  double radius;
+  PangoMatrix matrix = PANGO_MATRIX_INIT;
+  PangoLayout *layout;
+  PangoContext *context;
+  PangoFontDescription *desc;
+
+  gc = g_object_get_data (G_OBJECT (widget), "text-gc");
+  if (!gc)
+    {
+      static GdkColor black = { 0, 0, 0, 0 };
+      
+      gc = gdk_gc_new (widget->window);
+      gdk_gc_set_rgb_fg_color (gc, &black);
+      
+      if (tile_pixbuf)
+       {
+         GdkPixmap *tile;
+         
+         gint width = gdk_pixbuf_get_width (tile_pixbuf);
+         gint height = gdk_pixbuf_get_height (tile_pixbuf);
+         
+         tile = gdk_pixmap_new (widget->window, width, height, -1);
+         gdk_draw_pixbuf (tile, gc, tile_pixbuf,
+                          0, 0, 0, 0, width, height,
+                          GDK_RGB_DITHER_NORMAL, 0, 0);
+
+         gdk_gc_set_tile (gc, tile);
+         gdk_gc_set_fill (gc, GDK_TILED);
+
+         g_object_unref (tile);
+       }
+
+      g_object_set_data_full (G_OBJECT (widget), "text-gc", gc, (GDestroyNotify)g_object_unref);
+    }
+
+  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), gc);
+
+  radius = MIN (widget->allocation.width, widget->allocation.height) / 2.;
+
+  pango_matrix_translate (&matrix,
+                         radius + (widget->allocation.width - 2 * radius) / 2,
+                         radius + (widget->allocation.height - 2 * radius) / 2);
+  pango_matrix_scale (&matrix, radius / DEFAULT_TEXT_RADIUS, radius / DEFAULT_TEXT_RADIUS);
+
+  context = gtk_widget_get_pango_context (widget);
+  layout = pango_layout_new (context);
+  desc = pango_font_description_from_string ("Sans Bold 30");
+  pango_layout_set_font_description (layout, desc);
+  pango_font_description_free (desc);
+    
+  n_words = G_N_ELEMENTS (words);
+  for (i = 0; i < n_words; i++)
+    {
+      PangoMatrix rotated_matrix = matrix;
+      int width, height;
+      
+      pango_matrix_rotate (&rotated_matrix, - (360. * i) / n_words);
+
+      pango_context_set_matrix (context, &rotated_matrix);
+      pango_layout_context_changed (layout);
+      pango_layout_set_text (layout, words[i], -1);
+      
+      pango_layout_get_size (layout, &width, &height);
+
+      pango_renderer_draw_layout (renderer, layout,
+                                 - width / 2, - DEFAULT_TEXT_RADIUS * PANGO_SCALE);
+    }
+
+  gdk_pango_renderer_set_drawable (GDK_PANGO_RENDERER (renderer), NULL);
+  gdk_pango_renderer_set_gc (GDK_PANGO_RENDERER (renderer), NULL);
+  
+  g_object_unref (layout);
+
+  return FALSE;
+}
+
+static void
+create_rotated_text (GtkWidget *widget)
+{
+  static GtkWidget *window = NULL;
+
+  if (!window)
+    {
+      const GdkColor white = { 0, 0xffff, 0xffff, 0xffff };
+      GtkRequisition requisition;
+      GtkWidget *drawing_area;
+      GdkPixbuf *tile_pixbuf;
+
+      window = gtk_dialog_new_with_buttons ("Rotated Text",
+                                           GTK_WINDOW (gtk_widget_get_toplevel (widget)), 0,
+                                           GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
+                                           NULL);
+
+      gtk_window_set_resizable (GTK_WINDOW (window), TRUE);
+
+      gtk_window_set_screen (GTK_WINDOW (window),
+                            gtk_widget_get_screen (widget));
+
+      g_signal_connect (window, "response",
+                       G_CALLBACK (gtk_object_destroy), NULL);
+      g_signal_connect (window, "destroy",
+                       G_CALLBACK (gtk_widget_destroyed), &window);
+
+      drawing_area = gtk_drawing_area_new ();
+      gtk_box_pack_start (GTK_BOX (GTK_DIALOG (window)->vbox), drawing_area, TRUE, TRUE, 0);
+      gtk_widget_modify_bg (drawing_area, GTK_STATE_NORMAL, &white);
+
+      tile_pixbuf = gdk_pixbuf_new_from_file ("marble.xpm", NULL);
+      
+      g_signal_connect (drawing_area, "expose-event",
+                       G_CALLBACK (on_rotated_text_expose), tile_pixbuf);
+      g_signal_connect (drawing_area, "unrealize",
+                       G_CALLBACK (on_rotated_text_unrealize), NULL);
+
+      gtk_widget_show_all (GTK_BIN (window)->child);
+      
+      gtk_widget_set_size_request (drawing_area, DEFAULT_TEXT_RADIUS * 2, DEFAULT_TEXT_RADIUS * 2);
+      gtk_widget_size_request (window, &requisition);
+      gtk_widget_set_size_request (drawing_area, -1, -1);
+      gtk_window_resize (GTK_WINDOW (window), requisition.width, requisition.height);
+    }
+  
+  if (!GTK_WIDGET_VISIBLE (window))
+    gtk_widget_show (window);
+  else
+    gtk_widget_destroy (window);
+}
+
 /*
  * Reparent demo
  */
@@ -12695,6 +12908,8 @@ struct {
   { "rc file", create_rc_file },
   { "reparent", create_reparent },
   { "resize grips", create_resize_grips },
+  { "rotated label", create_rotated_label },
+  { "rotated text", create_rotated_text },
   { "rulers", create_rulers },
   { "saved position", create_saved_position },
   { "scrolled windows", create_scrolled_windows },