diff options
author | Owen Taylor <otaylor@redhat.com> | 2004-09-24 17:40:46 +0000 |
---|---|---|
committer | Owen Taylor <otaylor@src.gnome.org> | 2004-09-24 17:40:46 +0000 |
commit | 0b140650f239bd8fec38c9e5a2d09e95dd8440c0 (patch) | |
tree | f5e9cda843855b2f5fbd2be472269660ead9aac3 /pango/pango-renderer.c | |
parent | 763ab93cb59c0f3ff1b540afbbe1b5a689d2a4fb (diff) | |
download | pango-0b140650f239bd8fec38c9e5a2d09e95dd8440c0.tar.gz |
Up version to 1.7.0.
Fri Sep 24 12:59:22 2004 Owen Taylor <otaylor@redhat.com>
* configure.in: Up version to 1.7.0.
* pango/pango-renderer.[ch] pango/pango.h pango/Makefile.am:
Add PangoRenderer, a base class that is subclassed to produce
rendering drivers for different backends and purposes.
* pango/pangoft2-private.h pango/pangoft2-render.c
pango/pangoft2.c: Move rendering into pangoft2-render.c,
use PangoRenderer.
* pango/pangoft2-fontmap.c pango/pangoft2-private.h: Add
_pango_ft2_font_map_get_renderer() to retrieve a singleton
fontmap for the fontmap.
* pango/pangoxft-render.[ch] pango/pangoxft.c pango/Makefile.am:
Make Xft rendering use PangoRenderer, add publically
visible, subclassable PangoXftRenderer.
* pango/pangoxft-fontmap.c pango/pangoxft-private.h: Add
_pango_xft_font_map_get_renderer() to retrieve a singleton
fontmap for the fontmap.
* examples/xftview.c examples/Makefile.am: Add a test program
using the Xft backend.
* docs/*: Update minimally for PangoRenderer.
Diffstat (limited to 'pango/pango-renderer.c')
-rw-r--r-- | pango/pango-renderer.c | 1207 |
1 files changed, 1207 insertions, 0 deletions
diff --git a/pango/pango-renderer.c b/pango/pango-renderer.c new file mode 100644 index 00000000..620685d1 --- /dev/null +++ b/pango/pango-renderer.c @@ -0,0 +1,1207 @@ +/* Pango + * pango-renderer.h: Base class for rendering + * + * Copyright (C) 2004 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <stdlib.h> + +#include "pango-renderer.h" + +#define N_RENDER_PARTS 4 + +#define IS_VALID_PART(part) ((guint)part < N_RENDER_PARTS) + +typedef struct _LineState LineState; +typedef struct _Point Point; + +struct _Point +{ + double x, y; +}; + +struct _LineState +{ + PangoUnderline underline; + PangoRectangle underline_rect; + + gboolean strikethrough; + PangoRectangle strikethrough_rect; + + int logical_rect_end; +}; + +struct _PangoRendererPrivate +{ + PangoColor color[N_RENDER_PARTS]; + gboolean color_set[N_RENDER_PARTS]; + + LineState *line_state; +}; + +static void pango_renderer_finalize (GObject *gobject); +static void pango_renderer_default_draw_glyphs (PangoRenderer *renderer, + PangoFont *font, + PangoGlyphString *glyphs, + int x, + int y); +static void pango_renderer_default_draw_rectangle (PangoRenderer *renderer, + PangoRenderPart part, + int x, + int y, + int width, + int height); +static void pango_renderer_default_draw_error_underline (PangoRenderer *renderer, + int x, + int y, + int width, + int height); +static void pango_renderer_default_prepare_run (PangoRenderer *renderer, + PangoLayoutRun *run); + +static void pango_renderer_prepare_run (PangoRenderer *renderer, + PangoLayoutRun *run); + +static void +to_device (PangoMatrix *matrix, + double x, + double y, + Point *result) +{ + if (matrix) + { + result->x = (x * matrix->xx + y * matrix->xy) / PANGO_SCALE + matrix->x0; + result->y = (x * matrix->yx + y * matrix->yy) / PANGO_SCALE + matrix->y0; + } + else + { + result->x = x / PANGO_SCALE; + result->y = y / PANGO_SCALE; + } +} + +G_DEFINE_ABSTRACT_TYPE (PangoRenderer, pango_renderer, G_TYPE_OBJECT) + +static void +pango_renderer_class_init (PangoRendererClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + klass->draw_glyphs = pango_renderer_default_draw_glyphs; + klass->draw_rectangle = pango_renderer_default_draw_rectangle; + klass->draw_error_underline = pango_renderer_default_draw_error_underline; + klass->prepare_run = pango_renderer_default_prepare_run; + + gobject_class->finalize = pango_renderer_finalize; + + g_type_class_add_private (gobject_class, sizeof (PangoRendererPrivate)); +} + +static void +pango_renderer_init (PangoRenderer *renderer) +{ + renderer->priv = G_TYPE_INSTANCE_GET_PRIVATE (renderer, + PANGO_TYPE_RENDERER, + PangoRendererPrivate); + renderer->matrix = NULL; +} + +static void +pango_renderer_finalize (GObject *gobject) +{ + PangoRenderer *renderer = PANGO_RENDERER (gobject); + + if (renderer->matrix) + pango_matrix_free (renderer->matrix); +} + +/** + * pango_renderer_draw_layout: + * @renderer: a #PangoRenderer + * @layout: a #PangoLayout + * @x: x position of left edge of baseline, in user space coordinates + * in Pango units. + * @y: x position of left edge of baseline, in user space coordinates + * in Pango units. + * + * Draws @layout with the specified #PangoRenderer. + * + * Since: 1.8 + **/ +void +pango_renderer_draw_layout (PangoRenderer *renderer, + PangoLayout *layout, + int x, + int y) +{ + PangoLayoutIter *iter; + + g_return_if_fail (PANGO_IS_RENDERER (renderer)); + g_return_if_fail (PANGO_IS_LAYOUT (layout)); + + /* We only change the matrix if the renderer isn't already + * active. + */ + if (!renderer->active_count) + { + PangoContext *context = pango_layout_get_context (layout); + pango_renderer_set_matrix (renderer, + pango_context_get_matrix (context)); + } + + pango_renderer_activate (renderer); + + iter = pango_layout_get_iter (layout); + + do + { + PangoRectangle logical_rect; + PangoLayoutLine *line; + int baseline; + + line = pango_layout_iter_get_line (iter); + + pango_layout_iter_get_line_extents (iter, NULL, &logical_rect); + baseline = pango_layout_iter_get_baseline (iter); + + pango_renderer_draw_layout_line (renderer, + line, + x + logical_rect.x, + y + baseline); + } + while (pango_layout_iter_next_line (iter)); + + pango_layout_iter_free (iter); + + pango_renderer_deactivate (renderer); +} + +static void +draw_underline (PangoRenderer *renderer, + LineState *state) +{ + PangoRectangle *rect = &state->underline_rect; + + switch (state->underline) + { + case PANGO_UNDERLINE_NONE: + break; + case PANGO_UNDERLINE_DOUBLE: + pango_renderer_draw_rectangle (renderer, + PANGO_RENDER_PART_UNDERLINE, + rect->x, + rect->y + 2 * rect->height, + rect->width, + rect->height); + /* Fall through */ + case PANGO_UNDERLINE_SINGLE: + case PANGO_UNDERLINE_LOW: + pango_renderer_draw_rectangle (renderer, + PANGO_RENDER_PART_UNDERLINE, + rect->x, + rect->y, + rect->width, + rect->height); + break; + case PANGO_UNDERLINE_ERROR: + pango_renderer_draw_error_underline (renderer, + rect->x, + rect->y, + rect->width, + 3 * rect->height); + break; + } + + state->underline = PANGO_UNDERLINE_NONE; +} + +static void +draw_strikethrough (PangoRenderer *renderer, + LineState *state) +{ + PangoRectangle *rect = &state->strikethrough_rect; + + if (state->strikethrough) + pango_renderer_draw_rectangle (renderer, + PANGO_RENDER_PART_STRIKETHROUGH, + rect->x, + rect->y, + rect->width, + rect->height); + + state->strikethrough = FALSE; +} + +static void +handle_line_state_change (PangoRenderer *renderer, + PangoRenderPart part) +{ + LineState *state = renderer->priv->line_state; + if (!state) + return; + + if (part == PANGO_RENDER_PART_UNDERLINE && + state->underline != PANGO_UNDERLINE_NONE) + { + PangoRectangle *rect = &state->underline_rect; + + rect->width = state->logical_rect_end - rect->x; + draw_underline (renderer, state); + state->underline = renderer->underline; + rect->x = state->logical_rect_end; + } + + if (part == PANGO_RENDER_PART_STRIKETHROUGH && + state->strikethrough) + { + PangoRectangle *rect = &state->strikethrough_rect; + + rect->width = state->logical_rect_end - rect->x; + draw_underline (renderer, state); + state->underline = renderer->underline; + rect->x = state->logical_rect_end; + } +} + +static void +add_underline (PangoRenderer *renderer, + LineState *state, + PangoFontMetrics *metrics, /* NULL if no underline */ + int base_x, + int base_y, + PangoRectangle *ink_rect, + PangoRectangle *logical_rect) +{ + if (renderer->underline == PANGO_UNDERLINE_NONE) + { + draw_underline (renderer, state); + } + else + { + PangoRectangle *current_rect = &state->underline_rect; + PangoRectangle new_rect; + + int underline_thickness = pango_font_metrics_get_underline_thickness (metrics); + int underline_position = pango_font_metrics_get_underline_position (metrics); + + new_rect.x = base_x + ink_rect->x; + new_rect.width = ink_rect->width; + new_rect.height = underline_thickness; + + switch (renderer->underline) + { + case PANGO_UNDERLINE_NONE: + g_assert_not_reached (); + break; + case PANGO_UNDERLINE_SINGLE: + case PANGO_UNDERLINE_DOUBLE: + case PANGO_UNDERLINE_ERROR: + new_rect.y = base_y - underline_position; + break; + case PANGO_UNDERLINE_LOW: + new_rect.y = base_y + ink_rect->y + ink_rect->height + underline_thickness; + break; + } + + if (renderer->underline == state->underline && + new_rect.y == current_rect->y && + new_rect.height == current_rect->height) + { + current_rect->y = new_rect.y; + current_rect->width = new_rect.x + new_rect.width - current_rect->x; + current_rect->height = new_rect.height; + } + else + { + draw_underline (renderer, state); + + *current_rect = new_rect; + state->underline = renderer->underline; + } + } +} + +static void +add_strikethrough (PangoRenderer *renderer, + LineState *state, + PangoFontMetrics *metrics, /* NULL if no strikethrough */ + int base_x, + int base_y, + PangoRectangle *ink_rect, + PangoRectangle *logical_rect) +{ + if (!renderer->strikethrough) + { + draw_strikethrough (renderer, state); + } + else + { + PangoRectangle *current_rect = &state->strikethrough_rect; + PangoRectangle new_rect; + + int strikethrough_thickness = pango_font_metrics_get_strikethrough_thickness (metrics); + int strikethrough_position = pango_font_metrics_get_strikethrough_position (metrics); + + new_rect.x = base_x + ink_rect->x; + new_rect.width = ink_rect->width; + new_rect.y = base_y - strikethrough_position; + new_rect.height = strikethrough_thickness; + + if (state->strikethrough && + new_rect.y == current_rect->y && + new_rect.height == current_rect->height) + { + current_rect->y = new_rect.y; + current_rect->width = new_rect.x + new_rect.width - current_rect->x; + current_rect->height = new_rect.height; + } + else + { + draw_strikethrough (renderer, state); + + *current_rect = new_rect; + state->strikethrough = TRUE; + } + } +} + +static void +get_item_properties (PangoItem *item, + gint *rise, + gboolean *shape_set, + PangoRectangle *ink_rect, + PangoRectangle *logical_rect) +{ + GSList *l; + + if (rise) + *rise = 0; + + if (shape_set) + *shape_set = FALSE; + + for (l = item->analysis.extra_attrs; l; l = l->next) + { + PangoAttribute *attr = l->data; + + switch (attr->klass->type) + { + 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: + break; + } + } +} + +/** + * pango_renderer_draw_layout_line: + * @renderer: a #PangoRenderer + * @line: a #PangoLayoutLine + * @x: x position of left edge of baseline, in user space coordinates + * in Pango units. + * @y: x position of left edge of baseline, in user space coordinates + * in Pango units. + * + * Draws @line with the specified #PangoRenderer. + * + * Since: 1.8 + **/ +void +pango_renderer_draw_layout_line (PangoRenderer *renderer, + PangoLayoutLine *line, + int x, + int y) +{ + int x_off = 0; + LineState state; + GSList *l; + gboolean got_overall = FALSE; + PangoRectangle overall_rect; + + g_return_if_fail (PANGO_IS_RENDERER (renderer)); + + /* We only change the matrix if the renderer isn't already + * active. + */ + if (!renderer->active_count) + { + PangoContext *context = pango_layout_get_context (line->layout); + pango_renderer_set_matrix (renderer, + pango_context_get_matrix (context)); + } + + pango_renderer_activate (renderer); + + renderer->priv->line_state = &state; + + state.underline = PANGO_UNDERLINE_NONE; + state.strikethrough = FALSE; + + for (l = line->runs; l; l = l->next) + { + PangoFontMetrics *metrics; + gint rise; + PangoLayoutRun *run = l->data; + PangoRectangle logical_rect; + PangoRectangle ink_rect; + gboolean shape_set; + + pango_renderer_prepare_run (renderer, run); + + get_item_properties (run->item, + &rise, + &shape_set, &ink_rect, &logical_rect); + + if (!shape_set) + { + if (renderer->underline != PANGO_UNDERLINE_NONE || + renderer->strikethrough) + 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); + } + + state.logical_rect_end = x_off + logical_rect.x + logical_rect.width; + + if (renderer->priv->color_set[PANGO_RENDER_PART_BACKGROUND]) + { + if (!got_overall) + { + pango_layout_line_get_extents (line, NULL, &overall_rect); + got_overall = TRUE; + } + + pango_renderer_draw_rectangle (renderer, + PANGO_RENDER_PART_BACKGROUND, + x + x_off + logical_rect.x, + y - rise + overall_rect.y, + logical_rect.width, + overall_rect.height); + } + + if (!shape_set) + { + pango_renderer_draw_glyphs (renderer, + run->item->analysis.font, run->glyphs, + x + x_off, y - rise); + } +#if 0 + else + { + pango_renderer_draw_shaped (renderer, + x + x_off, y - rise); + } +#endif + + if (renderer->underline != PANGO_UNDERLINE_NONE || + renderer->strikethrough) + { + metrics = pango_font_get_metrics (run->item->analysis.font, + run->item->analysis.language); + + if (renderer->underline != PANGO_UNDERLINE_NONE) + add_underline (renderer, &state,metrics, + x + x_off, y - rise, + &ink_rect, &logical_rect); + + if (renderer->strikethrough) + add_strikethrough (renderer, &state, metrics, + x + x_off, y - rise, + &ink_rect, &logical_rect); + + pango_font_metrics_unref (metrics); + } + + x_off += logical_rect.width; + } + + /* Finish off any remaining underlines + */ + draw_underline (renderer, &state); + draw_strikethrough (renderer, &state); + + pango_renderer_deactivate (renderer); + +} + +/** + * pango_renderer_draw_glyphs: + * @renderer: a #PangoRenderer + * @font: a #PangoFont + * @glyphs: a #PangoGlyphString + * @x: x position of left edge of baseline, in user space coordinates + * in Pango units. + * @y: x position of left edge of baseline, in user space coordinates + * in Pango units. + * + * Draws the glyphs in @glyphs with the specified #PangoRenderer. + * + * Since: 1.8 + **/ +void +pango_renderer_draw_glyphs (PangoRenderer *renderer, + PangoFont *font, + PangoGlyphString *glyphs, + int x, + int y) +{ + g_return_if_fail (PANGO_IS_RENDERER (renderer)); + + PANGO_RENDERER_GET_CLASS (renderer)->draw_glyphs (renderer, font, glyphs, x, y); +} + +static void +pango_renderer_default_draw_glyphs (PangoRenderer *renderer, + PangoFont *font, + PangoGlyphString *glyphs, + int x, + int y) +{ + int i; + int x_position = 0; + + for (i = 0; i < glyphs->num_glyphs; i++) + { + PangoGlyphInfo *gi = &glyphs->glyphs[i]; + Point p; + + to_device (renderer->matrix, + x + x_position + gi->geometry.x_offset, + y + gi->geometry.y_offset, + &p); + + pango_renderer_draw_glyph (renderer, font, gi->glyph, p.x, p.y); + + x_position += gi->geometry.width; + } +} + +/** + * pango_renderer_draw_rectangle: + * @renderer: a #PangoRenderer + * @part: type of object this rectangle is part of + * @x: x position at which to draw rectangle, in user space coordinates in Pango units + * @y: y position at which to draw rectangle, in user space coordinates in Pango units + * @width: width of rectangle in PangoUnits in user space coordinates + * @height: height of rectangle in PangoUnits in user space coordinates + * + * Draws an axis-aligned rectangle in user space coordinates with the + * specified #PangoRenderer. + * + * Since: 1.8 + **/ +void +pango_renderer_draw_rectangle (PangoRenderer *renderer, + PangoRenderPart part, + int x, + int y, + int width, + int height) +{ + g_return_if_fail (PANGO_IS_RENDERER (renderer)); + g_return_if_fail (IS_VALID_PART (part)); + + PANGO_RENDERER_GET_CLASS (renderer)->draw_rectangle (renderer, part, x, y, width, height); +} + +static int +compare_points (const void *a, + const void *b) +{ + const Point *pa = a; + const Point *pb = b; + + if (pa->y < pb->y) + return -1; + else if (pa->y > pb->y) + return 1; + else if (pa->x < pb->x) + return -1; + else if (pa->x > pb->x) + return 1; + else + return 0; +} + +static void +draw_rectangle (PangoRenderer *renderer, + PangoMatrix *matrix, + PangoRenderPart part, + int x, + int y, + int width, + int height) +{ + Point points[4]; + + /* Convert the points to device coordinates, and sort + * in ascending Y order. (Ordering by X for ties) + */ + to_device (matrix, x, y, &points[0]); + to_device (matrix, x + width, y, &points[1]); + to_device (matrix, x, y + height, &points[2]); + to_device (matrix, x + width, y + height, &points[3]); + + qsort (points, 4, sizeof (Point), compare_points); + + /* There are essentially three cases. (There is a fourth + * case where trapezoid B is degenerate and we just have + * two triangles, but we don't need to handle it separately.) + * + * 1 2 3 + * + * ______ /\ /\ + * / / /A \ /A \ + * / B / /____\ /____\ + * /_____/ / B / \ B \ + * /_____/ \_____\ + * \ C / \ C / + * \ / \ / + * \/ \/ + */ + if (points[0].y == points[1].y) + { + /* Case 1 (pure shear) */ + pango_renderer_draw_trapezoid (renderer, part, /* B */ + points[0].y, points[0].x, points[1].x, + points[2].y, points[2].x, points[3].x); + } + else if (points[1].x < points[2].x) + { + /* Case 2 */ + double tmp_width = ((points[2].x - points[0].x) * (points[1].y - points[0].y)) / (points[2].y - points[0].y); + double base_width = tmp_width + points[0].x - points[1].x; + + pango_renderer_draw_trapezoid (renderer, part, /* A */ + points[0].y, points[0].x, points[0].x, + points[1].y, points[1].x, points[1].x + base_width); + pango_renderer_draw_trapezoid (renderer, part, /* B */ + points[1].y, points[1].x, points[1].x + base_width, + points[2].y, points[2].x - base_width, points[2].x); + pango_renderer_draw_trapezoid (renderer, part, /* C */ + points[2].y, points[2].x - base_width, points[2].x, + points[3].y, points[3].x, points[3].x); + } + else + { + /* case 3 */ + double tmp_width = ((points[0].x - points[2].x) * (points[1].y - points[0].y)) / (points[2].y - points[0].y); + double base_width = tmp_width + points[1].x - points[0].x; + + pango_renderer_draw_trapezoid (renderer, part, /* A */ + points[0].y, points[0].x, points[0].x, + points[1].y, points[1].x - base_width, points[1].x); + pango_renderer_draw_trapezoid (renderer, part, /* B */ + points[1].y, points[1].x - base_width, points[1].x, + points[2].y, points[2].x, points[2].x + base_width); + pango_renderer_draw_trapezoid (renderer, part, /* C */ + points[2].y, points[2].x, points[2].x + base_width, + points[3].y, points[3].x, points[3].x); + } +} + +static void +pango_renderer_default_draw_rectangle (PangoRenderer *renderer, + PangoRenderPart part, + int x, + int y, + int width, + int height) +{ + draw_rectangle (renderer, renderer->matrix, part, x, y, width, height); +} + +/** + * pango_renderer_draw_error_underline: + * @renderer: a #PangoRenderer + * @x: X coordinate of underline, in Pango units in user coordinate system + * @y: Y coordinate of underline, in Pango units in user coordinate system + * @width: width of underline, in Pango units in user coordinate system + * @height: height of underline, in Pango units in user coordinate system + * + * Draw a squiggly line that approximately covers the given rectangle + * in the style of an underline used to indicate a spelling error. + * (The width of the underline is rounded to an integer number + * of up/down segments and the resulting rectangle is centered + * in the original rectangle) + * + * Since: 1.8 + **/ +void +pango_renderer_draw_error_underline (PangoRenderer *renderer, + int x, + int y, + int width, + int height) +{ + g_return_if_fail (PANGO_IS_RENDERER (renderer)); + + PANGO_RENDERER_GET_CLASS (renderer)->draw_error_underline (renderer, x, y, width, height); +} + +/* We are drawing an error underline that looks like one of: + * + * /\ /\ /\ /\ /\ - + * / \ / \ / \ / \ / \ | + * \ \ /\ \ / / \ \ /\ \ | + * \ \/B \ \/ C / \ \/B \ \ | height = HEIGHT_SQUARES * square + * \ A \ /\ A \ / \ A \ /\ A \ | + * \ \/ \ \/ \ \/ \ \ | + * \ / \ / \ / \ / | + * \/ \/ \/ \/ - + * + * |----| + * unit_width = (HEIGHT_SQUARES - 1) * square + * + * To do this conveniently, we work in a coordinate system where A,B,C + * are axis aligned rectangles. (If fonts were square, the diagrams + * would be clearer) + * + * (0,0) + * /\ /\ + * / \ / \ + * /\ /\ /\ / + * / \/ \/ \/ + * / \ /\ / + * Y axis \/ \/ + * \ /\ + * \/ \ + * \ X axis + * + * Note that the long side in this coordinate system is HEIGHT_SQUARES + 1 + * units long + * + * The diagrams above are shown with HEIGHT_SQUARES an integer, but + * that is actually incidental; the value 2.5 below seems better than + * either HEIGHT_SQUARES=3 (a little long and skinny) or + * HEIGHT_SQUARES=2 (a bit short and stubby) + */ + +#define HEIGHT_SQUARES 2.5 + +static void +get_total_matrix (PangoMatrix *total, + PangoMatrix *global, + int x, + int y, + int square) +{ + PangoMatrix local; + gdouble scale = 0.5 * square; + + /* The local matrix translates from the axis aligned coordinate system + * to the original user space coordinate system. + */ + local.xx = scale; + local.xy = - scale; + local.yx = scale; + local.yy = scale; + local.x0 = 0; + local.y0 = 0; + + *total = *global; + pango_matrix_concat (total, &local); + + total->x0 = (global->xx * x + global->xy * y) / PANGO_SCALE + global->x0; + total->y0 = (global->yx * x + global->yy * y) / PANGO_SCALE + global->y0; +} + +static void +pango_renderer_default_draw_error_underline (PangoRenderer *renderer, + int x, + int y, + int width, + int height) +{ + int square = height / HEIGHT_SQUARES; + int unit_width = (HEIGHT_SQUARES - 1) * square; + int width_units = (width + unit_width / 2) / unit_width; + static const PangoMatrix identity = PANGO_MATRIX_INIT; + const PangoMatrix *matrix; + + x += (width - width_units * unit_width) / 2; + width = width_units * unit_width; + + if (renderer->matrix) + matrix = renderer->matrix; + else + matrix = &identity; + + while (TRUE) + { + PangoMatrix total; + get_total_matrix (&total, renderer->matrix, x, y, square); + + draw_rectangle (renderer, &total, PANGO_RENDER_PART_UNDERLINE, /* A */ + 0, 0, + HEIGHT_SQUARES * 2 - 1, 1); + + if (width_units > 2) + { + draw_rectangle (renderer, &total, PANGO_RENDER_PART_UNDERLINE, /* B */ + HEIGHT_SQUARES * 2 - 2, - (HEIGHT_SQUARES * 2 - 3), + 1, HEIGHT_SQUARES * 2 - 3); + width_units -= 2; + x += unit_width * 2; + } + else if (width_units == 2) + { + draw_rectangle (renderer, &total, PANGO_RENDER_PART_UNDERLINE, /* C */ + HEIGHT_SQUARES * 2 - 2, - (HEIGHT_SQUARES * 2 - 2), + 1, HEIGHT_SQUARES * 2 - 2); + break; + } + else + break; + } +} + +/** + * pango_renderer_draw_trapezoid: + * @renderer: a #PangoRenderer + * @part: type of object this trapezoid is part of + * @y1: Y coordinate of top of trapezoid + * @x11: X coordinate of left end of top of trapezoid + * @x21: X coordinate of right end of top of trapezoid + * @y2: X coordinate of top of trapezoid + * @x12: X coordinate of left end of top of trapezoid + * @x22: Y coordinate of left end of top of trapezoid + * + * Draws a trapezoid with the parallel sides aligned with the X axis + * using the given #PangoRenderer; coordinates are in device space. + * + * Since: 1.8 + **/ +void +pango_renderer_draw_trapezoid (PangoRenderer *renderer, + PangoRenderPart part, + double y1, + double x11, + double x21, + double y2, + double x12, + double x22) +{ + g_return_if_fail (PANGO_IS_RENDERER (renderer)); + + PANGO_RENDERER_GET_CLASS (renderer)->draw_trapezoid (renderer, part, + y1, x11, x21, + y2, x12, x22); +} + +/** + * pango_renderer_draw_glyph: + * @renderer: a #PangoRenderer + * @font: a #PangoFont + * @glyph: the glyph index of a single glyph + * @x: X coordinate of left edge of baseline of glyph + * @y: Y coordinate of left edge of baseline of glyph + * + * Draws a single glyph with coordinates in device space. + * + * Since: 1.8 + **/ +void +pango_renderer_draw_glyph (PangoRenderer *renderer, + PangoFont *font, + PangoGlyph glyph, + double x, + double y) +{ + g_return_if_fail (PANGO_IS_RENDERER (renderer)); + + if (glyph == 0) /* glyph 0 never renders */ + return; + + PANGO_RENDERER_GET_CLASS (renderer)->draw_glyph (renderer, font, glyph, x, y); +} + +/** + * pango_renderer_activate: + * @renderer: a #PangoRenderer + * + * Does initial setup before rendering operations on @renderer. + * pango_renderer_deactivate() should be called when done drawing. + * Calls such as pango_renderer_draw_layout() automatically + * activate the layout before drawing on it. Calls to + * pango_renderer_activate() and pango_renderer_deactivate() can + * be nested and the renderer will only be initialized and + * deinitialized once. + * + * Since: 1.8 + **/ +void +pango_renderer_activate (PangoRenderer *renderer) +{ + g_return_if_fail (PANGO_IS_RENDERER (renderer)); + + renderer->active_count++; + if (renderer->active_count == 1) + { + if (PANGO_RENDERER_GET_CLASS (renderer)->begin) + PANGO_RENDERER_GET_CLASS (renderer)->begin (renderer); + } +} + +/** + * pango_renderer_deactivate: + * @renderer: a #PangoRenderer + * + * Cleans up after rendering operations on @renderer. See + * docs for pango_renderer_activate(). + * + * Since: 1.8 + **/ +void +pango_renderer_deactivate (PangoRenderer *renderer) +{ + g_return_if_fail (PANGO_IS_RENDERER (renderer)); + g_return_if_fail (renderer->active_count > 0); + + if (renderer->active_count == 1) + { + if (PANGO_RENDERER_GET_CLASS (renderer)->end) + PANGO_RENDERER_GET_CLASS (renderer)->end (renderer); + } + renderer->active_count--; +} + +/** + * pango_renderer_set_color: + * @renderer: a #PangoRenderer + * @part: the part to change the color of + * @color: the new color or %NULL to unset the current color + * + * Sets the color for part of the rendering. + * + * Since: 1.8 + **/ +void +pango_renderer_set_color (PangoRenderer *renderer, + PangoRenderPart part, + PangoColor *color) +{ + g_return_if_fail (PANGO_IS_RENDERER (renderer)); + g_return_if_fail (IS_VALID_PART (part)); + + pango_renderer_part_changed (renderer, part); + + if ((!color && !renderer->priv->color_set[part]) || + (color && renderer->priv->color_set[part] && + renderer->priv->color[part].red == color->red && + renderer->priv->color[part].green == color->green && + renderer->priv->color[part].blue == color->blue)) + return; + + if (color) + { + renderer->priv->color_set[part] = TRUE; + renderer->priv->color[part] = *color; + } + else + { + renderer->priv->color_set[part] = FALSE; + } + + if (PANGO_RENDERER_GET_CLASS (renderer)->color_set) + PANGO_RENDERER_GET_CLASS (renderer)->color_set (renderer, part, color); +} + +/** + * pango_renderer_get_color: + * @renderer: a #PangoRenderer + * @part: the part to get the color for + * + * Gets the current rendering color for the specified part. + * + * Return value: the color for the specified part, or %NULL + * if it hasn't been set and should be inherited from the + * environment. + * + * Since: 1.8 + **/ +PangoColor * +pango_renderer_get_color (PangoRenderer *renderer, + PangoRenderPart part) +{ + g_return_val_if_fail (PANGO_IS_RENDERER (renderer), NULL); + g_return_val_if_fail (IS_VALID_PART (part), NULL); + + if (renderer->priv->color_set[part]) + return &renderer->priv->color[part]; + else + return NULL; +} + +/** + * pango_renderer_part_changed: + * @renderer: a #PangoRenderer + * @part: the part for which rendering has changed. + * + * Informs Pango that the way that the renderering is done + * for @part has changed in a way that would prevent multiple + * pieces being joined together into one drawing call. For + * instance, if a subclass of #PangoRenderer was to add a stipple + * option for drawing underlines, it needs to call + * + * <informalexample><programlisting> + * pango_renderer_part_changed (render, PANGO_RENDER_PART_UNDERLINE); + * </programlisting></informalexample> + * + * When the stipple changes or underlines with different stipples + * might be joined together. Pango automatically calls this for + * changes to colors. (See pango_renderer_set_color()) + * + * Since: 1.8 + **/ +void +pango_renderer_part_changed (PangoRenderer *renderer, + PangoRenderPart part) +{ + g_return_if_fail (PANGO_IS_RENDERER (renderer)); + g_return_if_fail (IS_VALID_PART (part)); + + handle_line_state_change (renderer, part); + + if (PANGO_RENDERER_GET_CLASS (renderer)->part_changed) + PANGO_RENDERER_GET_CLASS (renderer)->part_changed (renderer, part); +} + +/** + * pango_renderer_prepare_run: + * @renderer: a #PangoRenderer + * @run: a #PangoLayoutRun + * + * Set up the state of the PangoRenderer for rendering @run. + * + * Since: 1.8 + **/ +static void +pango_renderer_prepare_run (PangoRenderer *renderer, + PangoLayoutRun *run) +{ + g_return_if_fail (PANGO_IS_RENDERER (renderer)); + + PANGO_RENDERER_GET_CLASS (renderer)->prepare_run (renderer, run); +} + +static void +pango_renderer_default_prepare_run (PangoRenderer *renderer, + PangoLayoutRun *run) +{ + PangoColor *fg_color = NULL; + PangoColor *bg_color = NULL; + GSList *l; + + renderer->underline = PANGO_UNDERLINE_NONE; + renderer->strikethrough = FALSE; + + for (l = run->item->analysis.extra_attrs; l; l = l->next) + { + PangoAttribute *attr = l->data; + + switch (attr->klass->type) + { + case PANGO_ATTR_UNDERLINE: + renderer->underline = ((PangoAttrInt *)attr)->value; + break; + + case PANGO_ATTR_STRIKETHROUGH: + renderer->strikethrough = ((PangoAttrInt *)attr)->value; + break; + + case PANGO_ATTR_FOREGROUND: + fg_color = &((PangoAttrColor *)attr)->color; + break; + + case PANGO_ATTR_BACKGROUND: + bg_color = &((PangoAttrColor *)attr)->color; + break; + + default: + break; + } + } + + pango_renderer_set_color (renderer, PANGO_RENDER_PART_FOREGROUND, fg_color); + pango_renderer_set_color (renderer, PANGO_RENDER_PART_UNDERLINE, fg_color); + pango_renderer_set_color (renderer, PANGO_RENDER_PART_STRIKETHROUGH, fg_color); + pango_renderer_set_color (renderer, PANGO_RENDER_PART_BACKGROUND, bg_color); +} + +/** + * pango_renderer_set_matrix: + * @renderer: a #PangoRenderer + * @matrix: a #PangoMatrix, or %NULL to unset any existing matrix. + * (No matrix set is the same as setting the identity matrix.) + * + * Sets the transformation matrix that will be applied when rendering. + * + * Since: 1.8 + **/ +void +pango_renderer_set_matrix (PangoRenderer *renderer, + const PangoMatrix *matrix) +{ + g_return_if_fail (PANGO_IS_RENDERER (renderer)); + + if (renderer->matrix) + pango_matrix_free (renderer->matrix); + if (matrix) + renderer->matrix = pango_matrix_copy (matrix); + else + renderer->matrix = NULL; + +} + +/** + * pango_renderer_get_matrix: + * @renderer: a #PangoRenderer + * + * Gets the transformation matrix that will be applied when + * rendering. See pango_renderer_set_matrix(). + * + * Returns: the matrix, or %NULL if no matrix has been set + * (which is the same as the identity matrix). The returned + * matrix is owned by Pango and must not be modified or + * freed. + * + * Since: 1.8 + **/ +G_CONST_RETURN PangoMatrix * +pango_renderer_get_matrix (PangoRenderer *renderer) +{ + g_return_val_if_fail (PANGO_IS_RENDERER (renderer), NULL); + + return renderer->matrix; +} |