summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOwen Taylor <otaylor@redhat.com>2004-11-21 16:24:01 +0000
committerOwen Taylor <otaylor@src.gnome.org>2004-11-21 16:24:01 +0000
commit3d737ee8bae66d3395ff7975fafea99f87f1ed40 (patch)
treeb3cf7a30ab9a1ac9c67a3cdc99c74e0c9456a467
parent4ef2649257f57961b7e017aeb46afe611a0f73dd (diff)
downloadgdk-pixbuf-3d737ee8bae66d3395ff7975fafea99f87f1ed40.tar.gz
Add GdkPangoRenderer, a subclass of PangoRenderer targeting GDK drawables.
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)
-rw-r--r--demos/gtk-demo/Makefile.am1
-rw-r--r--demos/gtk-demo/rotated_text.c142
-rw-r--r--gdk/gdk.symbols9
-rw-r--r--gdk/gdkdraw.c356
-rw-r--r--gdk/gdkdrawable.h33
-rw-r--r--gdk/gdkinternals.h5
-rw-r--r--gdk/gdkpango.c1354
-rw-r--r--gdk/gdkpango.h76
-rw-r--r--gdk/gdkpixmap.c53
-rw-r--r--gdk/gdkwindow.c118
-rw-r--r--gdk/x11/gdkdisplay-x11.h3
-rw-r--r--gdk/x11/gdkdrawable-x11.c193
-rw-r--r--gdk/x11/gdkdrawable-x11.h11
-rw-r--r--gdk/x11/gdkgc-x11.c310
-rw-r--r--gdk/x11/gdkpango-x11.c111
-rw-r--r--gdk/x11/gdkprivate-x11.h8
-rw-r--r--gdk/x11/gdkscreen-x11.c3
-rw-r--r--gdk/x11/gdkscreen-x11.h3
-rw-r--r--gtk/gtk.symbols2
-rw-r--r--gtk/gtklabel.c214
-rw-r--r--gtk/gtklabel.h3
-rw-r--r--gtk/gtktextdisplay.c817
-rw-r--r--gtk/gtktextlayout.c25
-rw-r--r--gtk/gtktextlayout.h2
-rw-r--r--tests/testgtk.c215
25 files changed, 3090 insertions, 977 deletions
diff --git a/demos/gtk-demo/Makefile.am b/demos/gtk-demo/Makefile.am
index 65a52ca9b..dd904195b 100644
--- a/demos/gtk-demo/Makefile.am
+++ b/demos/gtk-demo/Makefile.am
@@ -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
index 000000000..1c37548dd
--- /dev/null
+++ b/demos/gtk-demo/rotated_text.c
@@ -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;
+}
diff --git a/gdk/gdk.symbols b/gdk/gdk.symbols
index af498c1e4..f7fc01358 100644
--- a/gdk/gdk.symbols
+++ b/gdk/gdk.symbols
@@ -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
diff --git a/gdk/gdkdraw.c b/gdk/gdkdraw.c
index 2145a7230..f547fad90 100644
--- a/gdk/gdkdraw.c
+++ b/gdk/gdkdraw.c
@@ -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
diff --git a/gdk/gdkdrawable.h b/gdk/gdkdrawable.h
index 7f8058493..cc2cf6a4b 100644
--- a/gdk/gdkdrawable.h
+++ b/gdk/gdkdrawable.h
@@ -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,
diff --git a/gdk/gdkinternals.h b/gdk/gdkinternals.h
index b9e14eba4..19667da3b 100644
--- a/gdk/gdkinternals.h
+++ b/gdk/gdkinternals.h
@@ -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 *
************************************/
diff --git a/gdk/gdkpango.c b/gdk/gdkpango.c
index 93e5ab288..374d90d6d 100644
--- a/gdk/gdkpango.c
+++ b/gdk/gdkpango.c
@@ -18,6 +18,7 @@
*/
#include <config.h>
+#include <math.h>
#include "gdkalias.h"
#include "gdkcolor.h"
#include "gdkgc.h"
@@ -26,196 +27,871 @@
#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
* @gc: base graphics to use
@@ -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)
{
diff --git a/gdk/gdkpango.h b/gdk/gdkpango.h
index 5a35c45fb..492f144ad 100644
--- a/gdk/gdkpango.h
+++ b/gdk/gdkpango.h
@@ -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
diff --git a/gdk/gdkpixmap.c b/gdk/gdkpixmap.c
index c4b9c55fd..978712a9e 100644
--- a/gdk/gdkpixmap.c
+++ b/gdk/gdkpixmap.c
@@ -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;
@@ -372,6 +386,20 @@ gdk_pixmap_draw_glyphs (GdkDrawable *drawable,
}
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,
GdkImage *image,
@@ -410,6 +438,17 @@ gdk_pixmap_draw_pixbuf (GdkDrawable *drawable,
}
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,
gint *height)
diff --git a/gdk/gdkwindow.c b/gdk/gdkwindow.c
index 556d1c9da..06212913b 100644
--- a/gdk/gdkwindow.c
+++ b/gdk/gdkwindow.c
@@ -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)
@@ -1896,6 +1956,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,
gint *height)
diff --git a/gdk/x11/gdkdisplay-x11.h b/gdk/x11/gdkdisplay-x11.h
index a523da940..198bf6a72 100644
--- a/gdk/x11/gdkdisplay-x11.h
+++ b/gdk/x11/gdkdisplay-x11.h
@@ -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
diff --git a/gdk/x11/gdkdrawable-x11.c b/gdk/x11/gdkdrawable-x11.c
index 2308a3acd..6517f36a5 100644
--- a/gdk/x11/gdkdrawable-x11.c
+++ b/gdk/x11/gdkdrawable-x11.c
@@ -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);
@@ -790,20 +806,42 @@ gdk_x11_draw_glyphs (GdkDrawable *drawable,
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);
+ }
+}
diff --git a/gdk/x11/gdkdrawable-x11.h b/gdk/x11/gdkdrawable-x11.h
index bc3f75dbc..45c8e38b8 100644
--- a/gdk/x11/gdkdrawable-x11.h
+++ b/gdk/x11/gdkdrawable-x11.h
@@ -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 */
diff --git a/gdk/x11/gdkgc-x11.c b/gdk/x11/gdkgc-x11.c
index fec9784df..391c3deec 100644
--- a/gdk/x11/gdkgc-x11.c
+++ b/gdk/x11/gdkgc-x11.c
@@ -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");
+}
diff --git a/gdk/x11/gdkpango-x11.c b/gdk/x11/gdkpango-x11.c
index eb9923fc3..de7577bee 100644
--- a/gdk/x11/gdkpango-x11.c
+++ b/gdk/x11/gdkpango-x11.c
@@ -25,6 +25,116 @@
#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;
}
+
diff --git a/gdk/x11/gdkprivate-x11.h b/gdk/x11/gdkprivate-x11.h
index 5ded4338a..d48940fb5 100644
--- a/gdk/x11/gdkprivate-x11.h
+++ b/gdk/x11/gdkprivate-x11.h
@@ -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;
diff --git a/gdk/x11/gdkscreen-x11.c b/gdk/x11/gdkscreen-x11.c
index 0856a010f..1d181eb5c 100644
--- a/gdk/x11/gdkscreen-x11.c
+++ b/gdk/x11/gdkscreen-x11.c
@@ -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) */
diff --git a/gdk/x11/gdkscreen-x11.h b/gdk/x11/gdkscreen-x11.h
index 89aa4390d..fddc28dd0 100644
--- a/gdk/x11/gdkscreen-x11.h
+++ b/gdk/x11/gdkscreen-x11.h
@@ -88,6 +88,9 @@ struct _GdkScreenX11
/* Xinerama */
gint num_monitors;
GdkRectangle *monitors;
+
+ /* Pango renderer object singleton */
+ PangoRenderer *renderer;
};
struct _GdkScreenX11Class
diff --git a/gtk/gtk.symbols b/gtk/gtk.symbols
index 5a21e9425..7e58a14f5 100644
--- a/gtk/gtk.symbols
+++ b/gtk/gtk.symbols
@@ -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
diff --git a/gtk/gtklabel.c b/gtk/gtklabel.c
index ac148ebe4..9c26a23b6 100644
--- a/gtk/gtklabel.c
+++ b/gtk/gtklabel.c
@@ -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)
diff --git a/gtk/gtklabel.h b/gtk/gtklabel.h
index 5da9c47ef..c6e9407f8 100644
--- a/gtk/gtklabel.h
+++ b/gtk/gtklabel.h
@@ -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);
diff --git a/gtk/gtktextdisplay.c b/gtk/gtktextdisplay.c
index bb7867f37..4b621b431 100644
--- a/gtk/gtktextdisplay.c
+++ b/gtk/gtktextdisplay.c
@@ -82,452 +82,379 @@
* 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);
}
diff --git a/gtk/gtktextlayout.c b/gtk/gtktextlayout.c
index 982032835..7c5dff55f 100644
--- a/gtk/gtktextlayout.c
+++ b/gtk/gtktextlayout.c
@@ -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);
diff --git a/gtk/gtktextlayout.h b/gtk/gtktextlayout.h
index fa6802ebb..f42a7c3c0 100644
--- a/gtk/gtktextlayout.h
+++ b/gtk/gtktextlayout.h
@@ -235,7 +235,7 @@ struct _GtkTextLineDisplay
{
PangoLayout *layout;
GSList *cursors;
- GSList *shaped_objects;
+ GSList *shaped_objects; /* Only for backwards compatibility */
GtkTextDirection direction;
diff --git a/tests/testgtk.c b/tests/testgtk.c
index b52af0828..3a1f6791a 100644
--- a/tests/testgtk.c
+++ b/tests/testgtk.c
@@ -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 },