#define __NR_ARENA_GLYPHS_C__ /* * RGBA display list system for inkscape * * Author: * Lauris Kaplinski * * Copyright (C) 2002 Lauris Kaplinski * * Released under GNU GPL * */ #ifdef HAVE_CONFIG_H # include #endif #include "libnr/nr-convert2geom.h" #include <2geom/matrix.h> #include "style.h" #include "display/nr-arena.h" #include "display/nr-arena-glyphs.h" #include #include "display/cairo-utils.h" #include "helper/geom.h" #ifdef test_glyph_liv #include "../display/canvas-bpath.h" #include "libnrtype/font-instance.h" // defined in nr-arena-shape.cpp void nr_pixblock_render_shape_mask_or(NRPixBlock &m, Shape *theS); #endif #ifdef ENABLE_SVG_FONTS #include "nr-svgfonts.h" #endif //#ifdef ENABLE_SVG_FONTS static void nr_arena_glyphs_class_init(NRArenaGlyphsClass *klass); static void nr_arena_glyphs_init(NRArenaGlyphs *glyphs); static void nr_arena_glyphs_finalize(NRObject *object); static guint nr_arena_glyphs_update(NRArenaItem *item, NRRectL *area, NRGC *gc, guint state, guint reset); static guint nr_arena_glyphs_clip(cairo_t *ct, NRArenaItem *item, NRRectL *area); static NRArenaItem *nr_arena_glyphs_pick(NRArenaItem *item, Geom::Point p, double delta, unsigned int sticky); static NRArenaItemClass *glyphs_parent_class; NRType nr_arena_glyphs_get_type(void) { static NRType type = 0; if (!type) { type = nr_object_register_type(NR_TYPE_ARENA_ITEM, "NRArenaGlyphs", sizeof(NRArenaGlyphsClass), sizeof(NRArenaGlyphs), (void (*)(NRObjectClass *)) nr_arena_glyphs_class_init, (void (*)(NRObject *)) nr_arena_glyphs_init); } return type; } static void nr_arena_glyphs_class_init(NRArenaGlyphsClass *klass) { NRObjectClass *object_class; NRArenaItemClass *item_class; object_class = (NRObjectClass *) klass; item_class = (NRArenaItemClass *) klass; glyphs_parent_class = (NRArenaItemClass *) ((NRObjectClass *) klass)->parent; object_class->finalize = nr_arena_glyphs_finalize; object_class->cpp_ctor = NRObject::invoke_ctor; item_class->update = nr_arena_glyphs_update; item_class->clip = nr_arena_glyphs_clip; item_class->pick = nr_arena_glyphs_pick; } static void nr_arena_glyphs_init(NRArenaGlyphs *glyphs) { glyphs->g_transform.setIdentity(); glyphs->font = NULL; glyphs->glyph = 0; glyphs->x = glyphs->y = 0.0; } static void nr_arena_glyphs_finalize(NRObject *object) { NRArenaGlyphs *glyphs = static_cast(object); if (glyphs->font) { glyphs->font->Unref(); glyphs->font=NULL; } ((NRObjectClass *) glyphs_parent_class)->finalize(object); } static guint nr_arena_glyphs_update(NRArenaItem *item, NRRectL */*area*/, NRGC *gc, guint /*state*/, guint /*reset*/) { NRArenaGlyphs *glyphs = NR_ARENA_GLYPHS(item); NRArenaGlyphsGroup *ggroup = NR_ARENA_GLYPHS_GROUP(item->parent); if (!glyphs->font || !ggroup->style) return NR_ARENA_ITEM_STATE_ALL; if (ggroup->nrstyle.fill.type == NRStyle::PAINT_NONE && ggroup->nrstyle.stroke.type == NRStyle::PAINT_NONE) return NR_ARENA_ITEM_STATE_ALL; Geom::OptRect b; Geom::Matrix t = glyphs->g_transform * gc->transform; glyphs->x = t[4]; glyphs->y = t[5]; b = bounds_exact_transformed(*glyphs->font->PathVector(glyphs->glyph), t); if (b && ggroup->nrstyle.stroke.type != NRStyle::PAINT_NONE) { float width, scale; scale = gc->transform.descrim(); width = MAX(0.125, ggroup->nrstyle.stroke_width * scale); if ( fabs(ggroup->nrstyle.stroke_width * scale) > 0.01 ) { // FIXME: this is always true b->expandBy(width); } // those pesky miters, now float miterMax = width * ggroup->nrstyle.miter_limit; if ( miterMax > 0.01 ) { // grunt mode. we should compute the various miters instead // (one for each point on the curve) b->expandBy(miterMax); } } if (b) { item->bbox.x0 = static_cast(floor(b->left())); item->bbox.y0 = static_cast(floor(b->top())); item->bbox.x1 = static_cast(ceil (b->right())); item->bbox.y1 = static_cast(ceil (b->bottom())); } else { item->bbox.x0 = 0; item->bbox.y0 = 0; item->bbox.x1 = -1; item->bbox.y1 = -1; } return NR_ARENA_ITEM_STATE_ALL; } static guint nr_arena_glyphs_clip(cairo_t *ct, NRArenaItem *item, NRRectL */*area*/) { NRArenaGlyphs *glyphs; glyphs = NR_ARENA_GLYPHS(item); if (!glyphs->font) return item->state; /* TODO : render to greyscale pixblock provided for clipping */ return item->state; } static NRArenaItem * nr_arena_glyphs_pick(NRArenaItem *item, Geom::Point p, gdouble delta, unsigned int /*sticky*/) { NRArenaGlyphs *glyphs; glyphs = NR_ARENA_GLYPHS(item); if (!glyphs->font ) return NULL; double const x = p[Geom::X]; double const y = p[Geom::Y]; /* With text we take a simple approach: pick if the point is in a characher bbox */ if ((x + delta >= item->bbox.x0) && (y + delta >= item->bbox.y0) && (x - delta <= item->bbox.x1) && (y - delta <= item->bbox.y1)) return item; return NULL; } void nr_arena_glyphs_set_path(NRArenaGlyphs *glyphs, SPCurve */*curve*/, unsigned int /*lieutenant*/, font_instance *font, gint glyph, Geom::Matrix const *transform) { nr_return_if_fail(glyphs != NULL); nr_return_if_fail(NR_IS_ARENA_GLYPHS(glyphs)); nr_arena_item_request_render(NR_ARENA_ITEM(glyphs)); if (transform) { glyphs->g_transform = *transform; } else { glyphs->g_transform.setIdentity(); } if (font) font->Ref(); if (glyphs->font) glyphs->font->Unref(); glyphs->font=font; glyphs->glyph = glyph; nr_arena_item_request_update(NR_ARENA_ITEM(glyphs), NR_ARENA_ITEM_STATE_ALL, FALSE); } static void nr_arena_glyphs_group_class_init(NRArenaGlyphsGroupClass *klass); static void nr_arena_glyphs_group_init(NRArenaGlyphsGroup *group); static void nr_arena_glyphs_group_finalize(NRObject *object); static guint nr_arena_glyphs_group_update(NRArenaItem *item, NRRectL *area, NRGC *gc, guint state, guint reset); static unsigned int nr_arena_glyphs_group_render(cairo_t *ct, NRArenaItem *item, NRRectL *area, NRPixBlock *pb, unsigned int flags); static unsigned int nr_arena_glyphs_group_clip(cairo_t *ct, NRArenaItem *item, NRRectL *area); static NRArenaItem *nr_arena_glyphs_group_pick(NRArenaItem *item, Geom::Point p, gdouble delta, unsigned int sticky); static NRArenaGroupClass *group_parent_class; NRType nr_arena_glyphs_group_get_type(void) { static NRType type = 0; if (!type) { type = nr_object_register_type(NR_TYPE_ARENA_GROUP, "NRArenaGlyphsGroup", sizeof(NRArenaGlyphsGroupClass), sizeof(NRArenaGlyphsGroup), (void (*)(NRObjectClass *)) nr_arena_glyphs_group_class_init, (void (*)(NRObject *)) nr_arena_glyphs_group_init); } return type; } static void nr_arena_glyphs_group_class_init(NRArenaGlyphsGroupClass *klass) { NRObjectClass *object_class; NRArenaItemClass *item_class; object_class = (NRObjectClass *) klass; item_class = (NRArenaItemClass *) klass; group_parent_class = (NRArenaGroupClass *) ((NRObjectClass *) klass)->parent; object_class->finalize = nr_arena_glyphs_group_finalize; object_class->cpp_ctor = NRObject::invoke_ctor; item_class->update = nr_arena_glyphs_group_update; item_class->render = nr_arena_glyphs_group_render; item_class->clip = nr_arena_glyphs_group_clip; item_class->pick = nr_arena_glyphs_group_pick; } static void nr_arena_glyphs_group_init(NRArenaGlyphsGroup *group) { group->style = NULL; group->paintbox.x0 = group->paintbox.y0 = 0.0F; group->paintbox.x1 = group->paintbox.y1 = -1.0F; } static void nr_arena_glyphs_group_finalize(NRObject *object) { NRArenaGlyphsGroup *group=static_cast(object); if (group->style) { sp_style_unref(group->style); group->style = NULL; } ((NRObjectClass *) group_parent_class)->finalize(object); } static guint nr_arena_glyphs_group_update(NRArenaItem *item, NRRectL *area, NRGC *gc, guint state, guint reset) { NRArenaGlyphsGroup *group = NR_ARENA_GLYPHS_GROUP(item); group->nrstyle.update(); if (((NRArenaItemClass *) group_parent_class)->update) return ((NRArenaItemClass *) group_parent_class)->update(item, area, gc, state, reset); return NR_ARENA_ITEM_STATE_ALL; } static unsigned int nr_arena_glyphs_group_render(cairo_t *ct, NRArenaItem *item, NRRectL *area, NRPixBlock *pb, unsigned int /*flags*/) { NRArenaItem *child; NRArenaGroup *group = NR_ARENA_GROUP(item); NRArenaGlyphsGroup *ggroup = NR_ARENA_GLYPHS_GROUP(item); if (!ct) return item->state; if (item->arena->rendermode == Inkscape::RENDERMODE_OUTLINE) { guint32 rgba = item->arena->outlinecolor; // FIXME: we use RGBA buffers but cairo writes BGRA (on i386), so we must cheat // by setting color channels in the "wrong" order cairo_set_source_rgba(ct, SP_RGBA32_B_F(rgba), SP_RGBA32_G_F(rgba), SP_RGBA32_R_F(rgba), SP_RGBA32_A_F(rgba)); cairo_set_tolerance(ct, 1.25); // low quality, but good enough for outline mode NRRect temp(area->x0, area->y0, area->x1, area->y1); Geom::OptRect area_2geom = temp.upgrade_2geom(); for (child = group->children; child != NULL; child = child->next) { NRArenaGlyphs *g = NR_ARENA_GLYPHS(child); Geom::PathVector const * pathv = g->font->PathVector(g->glyph); cairo_new_path(ct); Geom::Matrix transform = g->g_transform * group->ctm; feed_pathvector_to_cairo (ct, *pathv, transform, area_2geom, false, 0); cairo_fill(ct); } return item->state; } // NOTE: this is very similar to nr-arena-shape.cpp; the only difference is path feeding bool has_stroke, has_fill; cairo_save(ct); cairo_translate(ct, -area->x0, -area->y0); ink_cairo_transform(ct, ggroup->ctm); has_fill = ggroup->nrstyle.prepareFill(ct, &ggroup->paintbox); has_stroke = ggroup->nrstyle.prepareStroke(ct, &ggroup->paintbox); if (has_fill || has_stroke) { for (NRArenaItem *child = ggroup->children; child != NULL; child = child->next) { NRArenaGlyphs *g = NR_ARENA_GLYPHS(child); Geom::PathVector const &pathv = *g->font->PathVector(g->glyph); cairo_save(ct); ink_cairo_transform(ct, g->g_transform); feed_pathvector_to_cairo(ct, pathv); cairo_restore(ct); } if (has_fill) { ggroup->nrstyle.applyFill(ct); cairo_fill_preserve(ct); } if (has_stroke) { ggroup->nrstyle.applyStroke(ct); cairo_stroke_preserve(ct); } cairo_new_path(ct); // clear path } // has fill or stroke pattern cairo_restore(ct); return item->state; } static unsigned int nr_arena_glyphs_group_clip(cairo_t *ct, NRArenaItem *item, NRRectL *area) { //NRArenaGroup *group = NR_ARENA_GROUP(item); guint ret = item->state; /* Render children fill mask */ /* for (NRArenaItem *child = group->children; child != NULL; child = child->next) { ret = nr_arena_glyphs_fill_mask(NR_ARENA_GLYPHS(child), area, pb); if (!(ret & NR_ARENA_ITEM_STATE_RENDER)) return ret; }*/ return ret; } static NRArenaItem * nr_arena_glyphs_group_pick(NRArenaItem *item, Geom::Point p, gdouble delta, unsigned int sticky) { NRArenaItem *picked = NULL; if (((NRArenaItemClass *) group_parent_class)->pick) picked = ((NRArenaItemClass *) group_parent_class)->pick(item, p, delta, sticky); if (picked) picked = item; return picked; } void nr_arena_glyphs_group_clear(NRArenaGlyphsGroup *sg) { NRArenaGroup *group = NR_ARENA_GROUP(sg); nr_arena_item_request_render(NR_ARENA_ITEM(group)); while (group->children) { nr_arena_item_remove_child(NR_ARENA_ITEM(group), group->children); } } void nr_arena_glyphs_group_add_component(NRArenaGlyphsGroup *sg, font_instance *font, int glyph, Geom::Matrix const &transform) { NRArenaGroup *group; group = NR_ARENA_GROUP(sg); Geom::PathVector const * pathv = ( font ? font->PathVector(glyph) : NULL ); if ( pathv ) { nr_arena_item_request_render(NR_ARENA_ITEM(group)); NRArenaItem *new_arena = NRArenaGlyphs::create(group->arena); nr_arena_item_append_child(NR_ARENA_ITEM(group), new_arena); nr_arena_item_unref(new_arena); nr_arena_glyphs_set_path(NR_ARENA_GLYPHS(new_arena), NULL, FALSE, font, glyph, &transform); } } void nr_arena_glyphs_group_set_style(NRArenaGlyphsGroup *sg, SPStyle *style) { nr_return_if_fail(sg != NULL); nr_return_if_fail(NR_IS_ARENA_GLYPHS_GROUP(sg)); if (style) sp_style_ref(style); if (sg->style) sp_style_unref(sg->style); sg->style = style; sg->nrstyle.set(style); nr_arena_item_request_update(NR_ARENA_ITEM(sg), NR_ARENA_ITEM_STATE_ALL, FALSE); } void nr_arena_glyphs_group_set_paintbox(NRArenaGlyphsGroup *gg, NRRect const *pbox) { nr_return_if_fail(gg != NULL); nr_return_if_fail(NR_IS_ARENA_GLYPHS_GROUP(gg)); nr_return_if_fail(pbox != NULL); if ((pbox->x0 < pbox->x1) && (pbox->y0 < pbox->y1)) { gg->paintbox.x0 = pbox->x0; gg->paintbox.y0 = pbox->y0; gg->paintbox.x1 = pbox->x1; gg->paintbox.y1 = pbox->y1; } else { gg->paintbox.x0 = gg->paintbox.y0 = 0.0F; gg->paintbox.x1 = gg->paintbox.y1 = -1.0F; } nr_arena_item_request_update(NR_ARENA_ITEM(gg), NR_ARENA_ITEM_STATE_ALL, FALSE); } /* Local Variables: mode:c++ c-file-style:"stroustrup" c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) indent-tabs-mode:nil fill-column:99 End: */ // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :