summaryrefslogtreecommitdiff
path: root/gdk/gdkdraw.c
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 /gdk/gdkdraw.c
parent4ef2649257f57961b7e017aeb46afe611a0f73dd (diff)
downloadgtk+-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)
Diffstat (limited to 'gdk/gdkdraw.c')
-rw-r--r--gdk/gdkdraw.c356
1 files changed, 353 insertions, 3 deletions
diff --git a/gdk/gdkdraw.c b/gdk/gdkdraw.c
index 2145a72302..f547fad904 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