diff options
author | Owen Taylor <otaylor@redhat.com> | 2004-11-21 16:24:01 +0000 |
---|---|---|
committer | Owen Taylor <otaylor@src.gnome.org> | 2004-11-21 16:24:01 +0000 |
commit | 3d737ee8bae66d3395ff7975fafea99f87f1ed40 (patch) | |
tree | b3cf7a30ab9a1ac9c67a3cdc99c74e0c9456a467 /gdk/gdkdraw.c | |
parent | 4ef2649257f57961b7e017aeb46afe611a0f73dd (diff) | |
download | gtk+-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.c | 356 |
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 |