/* dia-shape-art.c * Copyright (C) 2001 Arjan Molenaar * * 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 "dia-shape-art.h" #include "dia-shape-view-info.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static inline ArtPathStrokeJoinType dia_join_style_to_art (DiaJoinStyle js) { switch (js) { case DIA_JOIN_MITER: return ART_PATH_STROKE_JOIN_MITER; case DIA_JOIN_ROUND: return ART_PATH_STROKE_JOIN_ROUND; case DIA_JOIN_BEVEL: return ART_PATH_STROKE_JOIN_BEVEL; default: g_warning ("Invalid DiaJoinStyle type: %d.", js); return ART_PATH_STROKE_JOIN_MITER; } } static inline ArtPathStrokeCapType dia_cap_style_to_art (DiaCapStyle cs) { switch (cs) { case DIA_CAP_BUTT: return ART_PATH_STROKE_CAP_BUTT; case DIA_CAP_ROUND: return ART_PATH_STROKE_CAP_ROUND; case DIA_CAP_SQUARE: return ART_PATH_STROKE_CAP_SQUARE; default: g_warning ("Invalid DiaCapStyle type: %d.", cs); return ART_PATH_STROKE_CAP_BUTT; } } /** * dia_shape_art_get_pango_layout: * * Return a new PangoLayout belonging to the AA renderer. * * Return value: A newly created PangoLayout object. **/ PangoLayout* dia_shape_art_get_pango_layout (void) { /* Use the default (canvas) PangoLayout setup. */ return dia_canvas_get_pango_layout (); } static ArtSVP* svp_dup (ArtSVP* svp) { register int sz = sizeof(ArtSVP) + (svp->n_segs - 1) * sizeof (ArtSVPSeg); int i; ArtSVP *new_svp; new_svp = art_alloc (sz); memcpy (new_svp, svp, sz); /* copy ArtSVPSeg.points */ for (i = 0; i < new_svp->n_segs; i++) { sz = new_svp->segs[i].n_points * sizeof (ArtPoint); new_svp->segs[i].points = art_alloc (sz); memcpy (new_svp->segs[i].points, svp->segs[i].points, sz); } return new_svp; } /* * Path */ static void path_free (DiaCanvasViewItem *item, DiaShapeViewInfo *view_info); #define PERTURBATION 2e-3 #define ELLIPSE 1e-3 #define RANDOM ((PERTURBATION * rand ()) / RAND_MAX - PERTURBATION * 0.5); /* Do not render if the size is smaller than a quarter point. */ #define MIN_LINE_WIDTH 0.1 #define MIN_SIZE 0.25 static ArtUta* create_svps_from_vpath (ArtVpath *vpath, gdouble line_width, DiaJoinStyle join, DiaCapStyle cap, gboolean cyclic, ArtVpathDash *dash, gdouble *affine, ArtSVP *clip, ArtSVP **stroke_svp, ArtSVP **fill_svp) { ArtVpath *path = NULL; guint i, seg = 1; //gdouble min = 10.0; if (line_width > 1000 || !(stroke_svp || fill_svp)) return NULL; if (!vpath) { if (stroke_svp) *stroke_svp = NULL; if (fill_svp) *fill_svp = NULL; return NULL; } /* Determine how much memory should be allocated for a * copy of the shape's vpath. */ for (path = vpath; path->code != ART_END; path++) seg++; if (cyclic) seg++; path = g_new (ArtVpath, seg); if (affine && (fabs (affine[0] - 1.0) < ELLIPSE) && (fabs (affine[1]) < ELLIPSE) && (fabs (affine[2]) < ELLIPSE) && (fabs (affine[3] -1.0) < ELLIPSE)) { for (i = 0; vpath[i].code != ART_END; i++) { path[i].code = vpath[i].code; path[i].x = vpath[i].x + affine[4] + RANDOM; //min = MIN(min, path[i].x); path[i].y = vpath[i].y + affine[5] + RANDOM; //min = MIN(min, path[i].y); } } else if (affine) { line_width = line_width * art_affine_expansion (affine); for (i = 0; vpath[i].code != ART_END; i++) { path[i].code = vpath[i].code; path[i].x = affine[0] * vpath[i].x + affine[2] * vpath[i].y + affine[4] + RANDOM; //min = MIN(min, path[i].x); path[i].y = affine[1] * vpath[i].x + affine[3] * vpath[i].y + affine[5] + RANDOM; //min = MIN(min, path[i].y); } } else { for (i = 0; vpath[i].code != ART_END; i++) { path[i].code = vpath[i].code; path[i].x = vpath[i].x + RANDOM; //min = MIN(min, path[i].x); path[i].y = vpath[i].y + RANDOM; //min = MIN(min, path[i].y); } } //if (min < MIN_SIZE) //return NULL; /* Add an extra segment to close the line */ if (cyclic) { path[i].code = ART_LINETO; path[i].x = path[0].x; path[i].y = path[0].y; i++; } path[i].code = ART_END; path[i].x = path[i].y = 0.0; g_assert (i == seg - 1); if (stroke_svp) { ArtSVP *svp, *tmp_svp; ArtVpath *tmp_path; if (dash) tmp_path = art_vpath_dash (path, dash); else tmp_path = path; svp = art_svp_vpath_stroke (tmp_path, dia_join_style_to_art (join), dia_cap_style_to_art (cap), line_width, 4, 0.25); if (tmp_path != path) art_free (tmp_path); tmp_svp = art_svp_uncross (svp); art_svp_free (svp); svp = art_svp_rewind_uncrossed (tmp_svp, ART_WIND_RULE_NONZERO); art_svp_free (tmp_svp); if (clip) { tmp_svp = svp; svp = art_svp_intersect (tmp_svp, clip); art_svp_free (tmp_svp); } *stroke_svp = svp; } /* Create the fill SVP */ if (fill_svp) { ArtVpath *tmp_path; ArtSVP *svp, *tmp_svp; tmp_path = art_vpath_perturb (path); svp = art_svp_from_vpath (tmp_path); art_free (tmp_path); tmp_svp = art_svp_uncross (svp); art_svp_free (svp); svp = art_svp_rewind_uncrossed (tmp_svp, ART_WIND_RULE_ODDEVEN); art_svp_free (tmp_svp); if (clip) { tmp_svp = svp; svp = art_svp_intersect (tmp_svp, clip); art_svp_free (tmp_svp); } *fill_svp = svp; } g_free (path); /* The stroke has a line width, so it is probably the */ if (stroke_svp && fill_svp) { ArtUta *uta1, *uta2, *uta3; uta1 = art_uta_from_svp (*stroke_svp); uta2 = art_uta_from_svp (*fill_svp); uta3 = art_uta_union (uta1, uta2); art_uta_free (uta1); art_uta_free (uta2); return uta3; } else if (stroke_svp) return art_uta_from_svp (*stroke_svp); else if (fill_svp) return art_uta_from_svp (*fill_svp); g_assert_not_reached(); return NULL; } typedef struct { ArtSVP *stroke_svp; ArtSVP *fill_svp; } PathHelper; static void reset_path_helper (PathHelper *helper, DiaCanvasViewItem *item) { ArtUta *repaint_uta; GnomeCanvasItem *gitem = (GnomeCanvasItem*) item; if (!helper) return; if (item && gitem->canvas && GTK_WIDGET_DRAWABLE (gitem->canvas)) { if (helper->stroke_svp) { repaint_uta = art_uta_from_svp (helper->stroke_svp); gnome_canvas_request_redraw_uta (gitem->canvas, repaint_uta); } if (helper->fill_svp) { repaint_uta = art_uta_from_svp (helper->fill_svp); gnome_canvas_request_redraw_uta (gitem->canvas, repaint_uta); } } if (helper->stroke_svp) { art_svp_free (helper->stroke_svp); helper->stroke_svp = NULL; } if (helper->fill_svp) { art_svp_free (helper->fill_svp); helper->fill_svp = NULL; } } /** * path_update: * * Update a VPath structure to create a SVP. * Code stolen from GnomeCanvasPolygon. **/ static inline ArtSVP* path_update (DiaShapePath *shape, DiaCanvasViewItem *item, double *affine, ArtSVP *clip, int flags, DiaShapeViewInfo *view_info) { ArtUta *repaint_uta; PathHelper *helper; ArtSVP *new_clip = NULL; g_assert (shape != NULL); g_assert (shape->shape.type == DIA_SHAPE_PATH); if (!view_info->data) helper = view_info->data = g_new0 (PathHelper, 1); else { //GnomeCanvasItem *item = view_info->key; helper = view_info->data; reset_path_helper (helper, item); } view_info->free_func = path_free; repaint_uta = create_svps_from_vpath (shape->vpath, shape->line_width, shape->join, shape->cap, shape->cyclic, shape->dash.dash ? &shape->dash : NULL, affine, clip, DIA_COLOR_ALPHA (shape->shape.color) > 0 ? &helper->stroke_svp : NULL, ((shape->fill != DIA_FILL_NONE) && (DIA_COLOR_ALPHA (shape->fill_color) > 0)) || shape->clipping ? &helper->fill_svp : NULL); if (shape->clipping) { if (clip) new_clip = art_svp_union (helper->fill_svp, clip); else if (shape->fill != DIA_FILL_NONE && DIA_COLOR_ALPHA (shape->fill_color) > 0) { new_clip = svp_dup (helper->fill_svp); } else { new_clip = helper->fill_svp; helper->fill_svp = NULL; } } if (repaint_uta) dia_canvas_view_item_request_redraw_uta (item, repaint_uta); return new_clip; } static inline void path_render (DiaShape *shape, DiaCanvasViewItem *item, GnomeCanvasBuf *buf, DiaShapeViewInfo *view_info) { PathHelper *helper = view_info->data; if (!helper) return; if (helper->fill_svp) { DiaColor fill_color = 0; switch (shape->type) { case DIA_SHAPE_PATH: fill_color = ((DiaShapePath*) shape)->fill_color; break; case DIA_SHAPE_BEZIER: fill_color = ((DiaShapeBezier*) shape)->fill_color; break; case DIA_SHAPE_ELLIPSE: fill_color = ((DiaShapeEllipse*) shape)->fill_color; break; default: g_assert_not_reached(); } gnome_canvas_render_svp (buf, helper->fill_svp, fill_color); } if (helper->stroke_svp) { gnome_canvas_render_svp (buf, helper->stroke_svp, shape->color); } } /* * Free the content of a path's and also request an update for the area * covered by the path. * Also redraw requests are emited here. * This function is used by paths, beziers and ellipses. */ static void path_free (DiaCanvasViewItem *item, DiaShapeViewInfo *view_info) { PathHelper *helper; g_assert (view_info != NULL); helper = view_info->data; view_info->data = NULL; if (!helper) return; reset_path_helper (helper, item); g_free (helper); } /* * Bezier * * path_render() and path_free() are also used. */ static inline ArtSVP* bezier_update (DiaShapeBezier *shape, DiaCanvasViewItem *item, double *affine, ArtSVP *clip, int flags, DiaShapeViewInfo *view_info) { ArtVpath *vpath = NULL; ArtBpath *bpath = NULL; gdouble width = 0.0; ArtUta *repaint_uta = NULL; PathHelper *helper; ArtSVP *new_clip = NULL; g_assert (shape != NULL); g_assert (shape->shape.type == DIA_SHAPE_BEZIER); if (!view_info->data) helper = view_info->data = g_new0 (PathHelper, 1); else { //GnomeCanvasItem *item = view_info->key; helper = view_info->data; reset_path_helper (helper, item); } view_info->free_func = path_free; if (affine) { bpath = art_bpath_affine_transform (shape->bpath, affine); width = shape->line_width * art_affine_expansion (affine); } else { bpath = shape->bpath; width = shape->line_width; } vpath = art_bez_path_to_vec (bpath, 0.25); if (bpath != shape->bpath) art_free (bpath); repaint_uta = create_svps_from_vpath (vpath, width, shape->join, shape->cap, shape->cyclic, NULL, NULL, clip, DIA_COLOR_ALPHA (shape->shape.color) > 0 ? &helper->stroke_svp : NULL, shape->fill != DIA_FILL_NONE && DIA_COLOR_ALPHA (shape->fill_color) > 0 ? &helper->fill_svp : NULL); /* Cleanup */ art_free (vpath); if (shape->clipping) { if (clip) new_clip = art_svp_union (helper->fill_svp, clip); else if (shape->fill != DIA_FILL_NONE && DIA_COLOR_ALPHA (shape->fill_color) > 0) { new_clip = svp_dup (helper->fill_svp); } else { new_clip = helper->fill_svp; helper->fill_svp = NULL; } } if (repaint_uta) dia_canvas_view_item_request_redraw_uta (item, repaint_uta); return new_clip; } /* * Ellipse * * Uses path_render() and path_free(). */ static inline ArtSVP* ellipse_update (DiaShapeEllipse *shape, DiaCanvasViewItem *item, double *affine, ArtSVP *clip, int flags, DiaShapeViewInfo *view_info) { static ArtVpath *ellipse = NULL; ArtUta *repaint_uta = NULL; gdouble a[6], b[6]; gdouble line_width; PathHelper *helper; ArtSVP *new_clip = NULL; g_assert (shape != NULL); g_assert (shape->shape.type == DIA_SHAPE_ELLIPSE); if (!view_info->data) helper = view_info->data = g_new0 (PathHelper, 1); else { //GnomeCanvasItem *item = view_info->key; helper = view_info->data; reset_path_helper (helper, item); } view_info->free_func = path_free; art_affine_scale (a, shape->width / 2.0, shape->height / 2.0); art_affine_translate (b, shape->center.x, shape->center.y); art_affine_multiply (a, a, b); if (!ellipse) ellipse = art_vpath_new_circle (0.0, 0.0, 1.0); if (affine) { line_width = shape->line_width / art_affine_expansion (a); art_affine_multiply (a, a, affine); } else { line_width = shape->line_width; } repaint_uta = create_svps_from_vpath (ellipse, line_width, ART_PATH_STROKE_JOIN_BEVEL, ART_PATH_STROKE_CAP_BUTT, FALSE, NULL, a, clip, DIA_COLOR_ALPHA (shape->shape.color) > 0 ? &helper->stroke_svp : NULL, shape->fill != DIA_FILL_NONE && DIA_COLOR_ALPHA (shape->fill_color) > 0 ? &helper->fill_svp : NULL); /* Do not free ellipse, it's static! */ if (shape->clipping) { if (clip) new_clip = art_svp_union (helper->fill_svp, clip); else if (shape->fill != DIA_FILL_NONE && DIA_COLOR_ALPHA (shape->fill_color) > 0) { new_clip = svp_dup (helper->fill_svp); } else { new_clip = helper->fill_svp; helper->fill_svp = NULL; } } if (repaint_uta) dia_canvas_view_item_request_redraw_uta (item, repaint_uta); return new_clip; } /* * Text */ typedef struct { ArtIRect bb; gdouble affine[6]; //FT_Bitmap bitmap; //GSList *selection_box; /* List of ArtSVP's */ PangoLayout *layout; gdouble width; gdouble height; gdouble offset_x; } TextHelper; static void text_free (DiaCanvasViewItem *item, DiaShapeViewInfo *view_info); static inline ArtSVP* text_update (DiaShapeText *shape, DiaCanvasViewItem *item, double *affine, ArtSVP *clip, int flags, DiaShapeViewInfo *view_info) { TextHelper *helper; PangoLayout *layout; gint width, height, max_width, max_height, offset_x = 0; gint dwidth; ArtDRect dr; gint r, c, a; guchar alpha; gdouble scale; if (!shape->text) return NULL; /* Cleanup and initialization: */ if (!view_info->data) helper = g_new0 (TextHelper, 1); else { helper = view_info->data; gnome_canvas_request_redraw (GNOME_CANVAS_ITEM (item)->canvas, helper->bb.x0, helper->bb.y0, helper->bb.x1, helper->bb.y1); } art_affine_multiply (helper->affine, shape->affine, affine); /* Find amount of scaling for the font size. If scale <= 1.0 the font is not resized, but the produced bitmap is scaled. */ scale = art_affine_expansion (helper->affine); if (scale < 1e-4) return NULL; layout = helper->layout; if (!layout) { layout = dia_shape_art_get_pango_layout (); helper->layout = layout; } max_width = (gint) ceil (shape->max_width); max_height = (gint) ceil (shape->max_height); dia_shape_text_fill_pango_layout ((DiaShape*) shape, layout); /* Create a bitmap to render the text on: */ pango_layout_get_pixel_size (layout, &width, &height); dwidth = max_width - width; width = MIN (width, max_width); height = MIN (height, max_height); helper->width = width; helper->height = height; // if ((height != helper->bitmap.rows) // || (width != helper->bitmap.width)) { // helper->bitmap.rows = height; // helper->bitmap.width = width; // /* pitch does the same as rowstride for LibArt functions. */ // helper->bitmap.pitch = (helper->bitmap.width+3)&~3; // if (helper->bitmap.buffer) { // helper->bitmap.buffer = g_realloc (helper->bitmap.buffer, // helper->bitmap.rows * helper->bitmap.pitch); // memset (helper->bitmap.buffer, 0, // helper->bitmap.rows * helper->bitmap.pitch); // } else { // helper->bitmap.buffer = g_malloc0 (helper->bitmap.rows * helper->bitmap.pitch); // } // helper->bitmap.num_grays = 256; // helper->bitmap.pixel_mode = ft_pixel_mode_grays; // } else // memset (helper->bitmap.buffer, 0, // helper->bitmap.rows * helper->bitmap.pitch); /* Set the width to the buffers width. */ if (shape->alignment != PANGO_ALIGN_LEFT) { gint w; pango_layout_get_size (layout, &w, NULL); pango_layout_set_width (layout, w); } /* Change offset depending on shape->alignment */ if (shape->alignment == PANGO_ALIGN_LEFT) { helper->offset_x = 0; } else if (shape->alignment == PANGO_ALIGN_CENTER) { gdouble a[6]; art_affine_translate (a, (double) MAX(dwidth, 0) / 2.0, 0.0); art_affine_multiply (helper->affine, a, helper->affine); helper->offset_x = MIN(dwidth, 0) / 2; } else if (shape->alignment == PANGO_ALIGN_RIGHT) { gdouble a[6]; art_affine_translate (a, (double) MAX(dwidth, 0), 0.0); art_affine_multiply (helper->affine, a, helper->affine); helper->offset_x = MIN(dwidth, 0); } else g_assert_not_reached (); /* Render the text: The result is not nice for scale factors > 1.0, * since we resized the font. */ // pango_ft2_render_layout (&helper->bitmap, layout, offset_x, 0); /* And we also want text to be transparent, do we adjust the bitmap * produced by pango_ft2_render_layout(). * * FixMe: is this a nice fuction to apply alpha to text? */ // alpha = shape->shape.color & 0xFF; // if (alpha != 0xFF) { // for (r = 0; r < helper->bitmap.rows; r++) { // for (c = 0; c < helper->bitmap.width; c++) { // a = helper->bitmap.buffer[r*helper->bitmap.pitch+c]; // if (a != 0) { // helper->bitmap.buffer[r * helper->bitmap.pitch + c] = a - MIN (255 - alpha, a); // } // } // } // } /* Mark the region as dirty. */ dr.x0 = 0.0; dr.y0 = 0.0; dr.x1 = (gdouble) width; dr.y1 = (gdouble) height; art_drect_affine_transform (&dr, &dr, helper->affine); art_drect_to_irect (&helper->bb, &dr); #ifdef ENABLE_DEBUG g_message ("Cleaning box (%d, %d, %d, %d)", helper->bb.x0, helper->bb.y0, helper->bb.x1, helper->bb.y1); #endif gnome_canvas_request_redraw (GNOME_CANVAS_ITEM (item)->canvas, helper->bb.x0, helper->bb.y0, helper->bb.x1, helper->bb.y1); view_info->data = helper; view_info->free_func = text_free; //g_object_unref (layout); return NULL; } static inline void text_render (DiaShape *shape, DiaCanvasViewItem *item, GnomeCanvasBuf *buf, DiaShapeViewInfo *view_info) { TextHelper *helper = view_info->data; FT_Bitmap bitmap; PangoContext *context; PangoMatrix matrix; gdouble affine[6]; gdouble x0 = buf->rect.x0, y0 = buf->rect.y0; if (!view_info->data) return; bitmap.rows = buf->rect.y1 - buf->rect.y0; bitmap.width = buf->rect.x1 - buf->rect.x0; /* Set pitch boundary on a 4 byte limit. */ bitmap.pitch = (bitmap.width + 3) & ~3; bitmap.num_grays = 256; bitmap.pixel_mode = ft_pixel_mode_grays; bitmap.palette_mode = 0; bitmap.palette = NULL; bitmap.buffer = g_malloc0 (bitmap.rows * bitmap.pitch); context = pango_layout_get_context (helper->layout); matrix.xx = helper->affine[0]; matrix.yx = helper->affine[1]; matrix.xy = helper->affine[2]; matrix.yy = helper->affine[3]; matrix.x0 = helper->affine[4] - buf->rect.x0; matrix.y0 = helper->affine[5] - buf->rect.y0; art_affine_identity (affine); pango_context_set_matrix (context, &matrix); /*g_message ("Rendering text on (%d, %d)", buf->rect.x0, buf->rect.y0); g_message (" affine = [%f, %f, %f, %f, %f, %f]", helper->affine[0], helper->affine[1], helper->affine[2], helper->affine[3], helper->affine[4], helper->affine[5]); */ pango_ft2_render_layout (&bitmap, helper->layout, 0, 0); // TODO: clip buffer on bounding box boundries art_rgb_a_affine (buf->buf, 0, 0, buf->rect.x1 - buf->rect.x0, buf->rect.y1 -buf->rect.y0, buf->buf_rowstride, bitmap.buffer, bitmap.width, bitmap.rows, bitmap.pitch, shape->color >> 8, affine, ART_FILTER_NEAREST, NULL); g_free(bitmap.buffer); } static void text_free (DiaCanvasViewItem *item, DiaShapeViewInfo *view_info) { TextHelper *helper = (TextHelper*) view_info->data; //g_free (helper->bitmap.buffer); g_object_unref (helper->layout); g_free (view_info->data); view_info->data = NULL; } /* * Image */ typedef struct { ArtIRect bb; gdouble affine[6]; GdkPixbuf *pixbuf; } ImageHelper; static void image_free (DiaCanvasViewItem *item, DiaShapeViewInfo *view_info); static inline ArtSVP* image_update (DiaShapeImage *shape, DiaCanvasViewItem *item, double *affine, ArtSVP *clip, int flags, DiaShapeViewInfo *view_info) { ImageHelper *helper; ArtDRect dr; GnomeCanvasItem *gitem = GNOME_CANVAS_ITEM (item); if (!shape->pixbuf) return NULL; if (!view_info->data) helper = g_new (ImageHelper, 1); else { helper = view_info->data; gnome_canvas_request_redraw (GNOME_CANVAS_ITEM (item)->canvas, helper->bb.x0, helper->bb.y0, helper->bb.x1, helper->bb.y1); if (helper->pixbuf) { gdk_pixbuf_unref (helper->pixbuf); helper->pixbuf = NULL; } } if (clip) { /* FixMe: We can't handle clipped images right now */ #if 0 ArtDRect pix_bb; ArtDRect intersect; gdouble *a = shape->affine; gint width = gdk_pixbuf_get_width (shape->pixbuf); gint height = gdk_pixbuf_get_height (shape->pixbuf); pix_bb.x0 = a[4]; pix_bb.y0 = a[5]; pix_bb.x1 = width * a[0] + height * a[2] + a[4]; pix_bb.y1 = width * a[1] + height * a[3] + a[5]; art_drect_intersect (&intersect, &pix_bb, clip); if (art_drect_empty (&intersect)) { helper->pixbuf = NULL; } else { gdouble inv[6]; art_affine_invert (inv, shape->affine); art_drect_affine_transform (&dr, &intersect, inv); helper->pixbuf = gdk_pixbuf_new_subpixbuf (shape->pixbuf, MAX ((gint) (dr.x0 + 1), 0), MAX ((gint) (dr.y0 + 1), 0), (gint) (dr.x1 - dr.x0 - 1), (gint) (dr.y1 - dr.y0 - 1)); } #endif dr.x0 = 0.0; dr.y0 = 0.0; dr.x1 = (gdouble) gdk_pixbuf_get_width (shape->pixbuf); dr.y1 = (gdouble) gdk_pixbuf_get_height (shape->pixbuf); helper->pixbuf = gdk_pixbuf_ref (shape->pixbuf); } else { dr.x0 = 0.0; dr.y0 = 0.0; dr.x1 = (gdouble) gdk_pixbuf_get_width (shape->pixbuf); dr.y1 = (gdouble) gdk_pixbuf_get_height (shape->pixbuf); helper->pixbuf = gdk_pixbuf_ref (shape->pixbuf); } art_affine_multiply (helper->affine, shape->affine, affine); art_drect_affine_transform (&dr, &dr, helper->affine); art_drect_to_irect (&helper->bb, &dr); gnome_canvas_request_redraw (gitem->canvas, helper->bb.x0, helper->bb.y0, helper->bb.x1, helper->bb.y1); view_info->free_func = image_free; view_info->data = helper; return NULL; } static inline void image_render (DiaShape *shape, DiaCanvasViewItem *item, GnomeCanvasBuf *buf, DiaShapeViewInfo *view_info) { ImageHelper *helper = view_info->data; if (!helper->pixbuf || (((DiaShapeImage*) shape)->affine[0] == 0.0) || (((DiaShapeImage*) shape)->affine[3] == 0.0)) return; if (gdk_pixbuf_get_has_alpha(helper->pixbuf)) art_rgb_rgba_affine (buf->buf, buf->rect.x0, buf->rect.y0, buf->rect.x1, buf->rect.y1, buf->buf_rowstride, gdk_pixbuf_get_pixels (helper->pixbuf), gdk_pixbuf_get_width (helper->pixbuf), gdk_pixbuf_get_height (helper->pixbuf), gdk_pixbuf_get_rowstride (helper->pixbuf), helper->affine, ART_FILTER_NEAREST, NULL); else art_rgb_affine (buf->buf, buf->rect.x0, buf->rect.y0, buf->rect.x1, buf->rect.y1, buf->buf_rowstride, gdk_pixbuf_get_pixels (helper->pixbuf), gdk_pixbuf_get_width (helper->pixbuf), gdk_pixbuf_get_height (helper->pixbuf), gdk_pixbuf_get_rowstride (helper->pixbuf), helper->affine, ART_FILTER_NEAREST, NULL); } static void image_free (DiaCanvasViewItem *item, DiaShapeViewInfo *view_info) { if (view_info->data) { ImageHelper *helper = view_info->data; //GnomeCanvasItem *item = view_info->key; gnome_canvas_request_redraw (GNOME_CANVAS_ITEM (item)->canvas, helper->bb.x0, helper->bb.y0, helper->bb.x1, helper->bb.y1); if (helper->pixbuf) gdk_pixbuf_unref (helper->pixbuf); g_free (view_info->data); view_info->data = NULL; } } /** * dia_shape_art_update: * @shape: * @item: * @affine: affine transformation matrix * @clip: clipping path for the shape (may be %NULL) * @flags: flags... * * This function is called for every CanvasViewItem. This way ViewItems are * notified that the need updating. * * Return value: A new clip path. The new clip path is to be used for all * elements that are rendered on top of @shape. **/ ArtSVP* dia_shape_art_update (DiaShape *shape, DiaCanvasViewItem *item, double *affine, ArtSVP *clip, int flags) { ArtSVP *new_clip = NULL; DiaShapeViewInfo *view_info = NULL; gboolean is_new = FALSE; g_assert (DIA_IS_CANVAS_VIEW_ITEM (item)); /* First, find the old shape-item-info (if none, create a new entry) */ view_info = dia_shape_view_info_get (item, shape); g_assert (view_info != NULL); //if (!dia_shape_need_update (shape) // && !(GTK_OBJECT_FLAGS (item) & DIA_CANVAS_VIEW_ITEM_UPDATE_SHAPES)) // return NULL; //g_assert (DIA_IS_CANVAS_VIEW_ITEM (view_info->key)); switch (shape->type) { case DIA_SHAPE_PATH: new_clip = path_update ((DiaShapePath*) shape, item, affine, clip, flags, view_info); break; case DIA_SHAPE_BEZIER: new_clip = bezier_update ((DiaShapeBezier*) shape, item, affine, clip, flags, view_info); break; case DIA_SHAPE_ELLIPSE: new_clip = ellipse_update ((DiaShapeEllipse*) shape, item, affine, clip, flags, view_info); break; case DIA_SHAPE_TEXT: new_clip = text_update ((DiaShapeText*) shape, item, affine, clip, flags, view_info); break; case DIA_SHAPE_IMAGE: new_clip = image_update ((DiaShapeImage*) shape, item, affine, clip, flags, view_info); break; default: g_warning ("No update method for shape of type %d", shape->type); break; } if (!is_new) dia_shape_is_updated (shape); return new_clip; } void dia_shape_art_render (DiaShape *shape, DiaCanvasViewItem *item, GnomeCanvasBuf *buf) { DiaShapeViewInfo *view_info; g_assert (DIA_IS_CANVAS_VIEW_ITEM (item)); view_info = dia_shape_view_info_get (item, shape); if (!view_info) return; switch (shape->type) { case DIA_SHAPE_PATH: case DIA_SHAPE_BEZIER: case DIA_SHAPE_ELLIPSE: path_render (shape, item, buf, view_info); break; case DIA_SHAPE_TEXT: text_render (shape, item, buf, view_info); break; case DIA_SHAPE_IMAGE: image_render (shape, item, buf, view_info); break; default: break; } }