aboutsummaryrefslogtreecommitdiffstats
path: root/libgnomecanvas/gnome-canvas-util.c
diff options
context:
space:
mode:
Diffstat (limited to 'libgnomecanvas/gnome-canvas-util.c')
-rw-r--r--libgnomecanvas/gnome-canvas-util.c700
1 files changed, 700 insertions, 0 deletions
diff --git a/libgnomecanvas/gnome-canvas-util.c b/libgnomecanvas/gnome-canvas-util.c
new file mode 100644
index 0000000000..d306292144
--- /dev/null
+++ b/libgnomecanvas/gnome-canvas-util.c
@@ -0,0 +1,700 @@
+/*
+ * Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation
+ * All rights reserved.
+ *
+ * This file is part of the Gnome Library.
+ *
+ * The Gnome 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.
+ *
+ * The Gnome 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 the Gnome Library; see the file COPYING.LIB. If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+/*
+ @NOTATION@
+ */
+/* Miscellaneous utility functions for the GnomeCanvas widget
+ *
+ * GnomeCanvas is basically a port of the Tk toolkit's most excellent canvas widget. Tk is
+ * copyrighted by the Regents of the University of California, Sun Microsystems, and other parties.
+ *
+ *
+ * Author: Federico Mena <federico@nuclecu.unam.mx>
+ */
+
+#include <config.h>
+
+/* needed for M_PI_2 under 'gcc -ansi -predantic' on GNU/Linux */
+#ifndef _BSD_SOURCE
+# define _BSD_SOURCE 1
+#endif
+#include <sys/types.h>
+
+#include <glib.h>
+#include <math.h>
+#include "gnome-canvas.h"
+#include "gnome-canvas-util.h"
+#include <libart_lgpl/art_uta.h>
+#include <libart_lgpl/art_svp.h>
+#include <libart_lgpl/art_svp_ops.h>
+#include <libart_lgpl/art_rgb.h>
+#include <libart_lgpl/art_rgb_svp.h>
+#include <libart_lgpl/art_uta_svp.h>
+#include <libart_lgpl/art_rect_svp.h>
+
+/**
+ * gnome_canvas_points_new:
+ * @num_points: The number of points to allocate space for in the array.
+ *
+ * Creates a structure that should be used to pass an array of points to
+ * items.
+ *
+ * Return value: A newly-created array of points. It should be filled in
+ * by the user.
+ **/
+GnomeCanvasPoints *
+gnome_canvas_points_new (int num_points)
+{
+ GnomeCanvasPoints *points;
+
+ g_return_val_if_fail (num_points > 1, NULL);
+
+ points = g_new (GnomeCanvasPoints, 1);
+ points->num_points = num_points;
+ points->coords = g_new (double, 2 * num_points);
+ points->ref_count = 1;
+
+ return points;
+}
+
+/**
+ * gnome_canvas_points_ref:
+ * @points: A canvas points structure.
+ *
+ * Increases the reference count of the specified points structure.
+ *
+ * Return value: The canvas points structure itself.
+ **/
+GnomeCanvasPoints *
+gnome_canvas_points_ref (GnomeCanvasPoints *points)
+{
+ g_return_val_if_fail (points != NULL, NULL);
+
+ points->ref_count += 1;
+ return points;
+}
+
+/**
+ * gnome_canvas_points_free:
+ * @points: A canvas points structure.
+ *
+ * Decreases the reference count of the specified points structure. If it
+ * reaches zero, then the structure is freed.
+ **/
+void
+gnome_canvas_points_free (GnomeCanvasPoints *points)
+{
+ g_return_if_fail (points != NULL);
+
+ points->ref_count -= 1;
+ if (points->ref_count == 0) {
+ g_free (points->coords);
+ g_free (points);
+ }
+}
+
+/**
+ * gnome_canvas_get_miter_points:
+ * @x1: X coordinate of the first point
+ * @y1: Y coordinate of the first point
+ * @x2: X coordinate of the second (angle) point
+ * @y2: Y coordinate of the second (angle) point
+ * @x3: X coordinate of the third point
+ * @y3: Y coordinate of the third point
+ * @width: Width of the line
+ * @mx1: The X coordinate of the first miter point is returned here.
+ * @my1: The Y coordinate of the first miter point is returned here.
+ * @mx2: The X coordinate of the second miter point is returned here.
+ * @my2: The Y coordinate of the second miter point is returned here.
+ *
+ * Given three points forming an angle, computes the coordinates of the inside
+ * and outside points of the mitered corner formed by a line of a given width at
+ * that angle.
+ *
+ * Return value: FALSE if the angle is less than 11 degrees (this is the same
+ * threshold as X uses. If this occurs, the return points are not modified.
+ * Otherwise, returns TRUE.
+ **/
+int
+gnome_canvas_get_miter_points (double x1, double y1, double x2, double y2, double x3, double y3,
+ double width,
+ double *mx1, double *my1, double *mx2, double *my2)
+{
+ double theta1; /* angle of segment p2-p1 */
+ double theta2; /* angle of segment p2-p3 */
+ double theta; /* angle between line segments */
+ double theta3; /* angle that bisects theta1 and theta2 and points to p1 */
+ double dist; /* distance of miter points from p2 */
+ double dx, dy; /* x and y offsets corresponding to dist */
+
+#define ELEVEN_DEGREES (11.0 * G_PI / 180.0)
+
+ if (y2 == y1)
+ theta1 = (x2 < x1) ? 0.0 : G_PI;
+ else if (x2 == x1)
+ theta1 = (y2 < y1) ? G_PI_2 : -G_PI_2;
+ else
+ theta1 = atan2 (y1 - y2, x1 - x2);
+
+ if (y3 == y2)
+ theta2 = (x3 > x2) ? 0 : G_PI;
+ else if (x3 == x2)
+ theta2 = (y3 > y2) ? G_PI_2 : -G_PI_2;
+ else
+ theta2 = atan2 (y3 - y2, x3 - x2);
+
+ theta = theta1 - theta2;
+
+ if (theta > G_PI)
+ theta -= 2.0 * G_PI;
+ else if (theta < -G_PI)
+ theta += 2.0 * G_PI;
+
+ if ((theta < ELEVEN_DEGREES) && (theta > -ELEVEN_DEGREES))
+ return FALSE;
+
+ dist = 0.5 * width / sin (0.5 * theta);
+ if (dist < 0.0)
+ dist = -dist;
+
+ theta3 = (theta1 + theta2) / 2.0;
+ if (sin (theta3 - (theta1 + G_PI)) < 0.0)
+ theta3 += G_PI;
+
+ dx = dist * cos (theta3);
+ dy = dist * sin (theta3);
+
+ *mx1 = x2 + dx;
+ *mx2 = x2 - dx;
+ *my1 = y2 + dy;
+ *my2 = y2 - dy;
+
+ return TRUE;
+}
+
+/**
+ * gnome_canvas_get_butt_points:
+ * @x1: X coordinate of first point in the line
+ * @y1: Y cooordinate of first point in the line
+ * @x2: X coordinate of second point (endpoint) of the line
+ * @y2: Y coordinate of second point (endpoint) of the line
+ * @width: Width of the line
+ * @project: Whether the butt points should project out by width/2 distance
+ * @bx1: X coordinate of first butt point is returned here
+ * @by1: Y coordinate of first butt point is returned here
+ * @bx2: X coordinate of second butt point is returned here
+ * @by2: Y coordinate of second butt point is returned here
+ *
+ * Computes the butt points of a line segment.
+ **/
+void
+gnome_canvas_get_butt_points (double x1, double y1, double x2, double y2,
+ double width, int project,
+ double *bx1, double *by1, double *bx2, double *by2)
+{
+ double length;
+ double dx, dy;
+
+ width *= 0.5;
+ dx = x2 - x1;
+ dy = y2 - y1;
+ length = sqrt (dx * dx + dy * dy);
+
+ if (length < GNOME_CANVAS_EPSILON) {
+ *bx1 = *bx2 = x2;
+ *by1 = *by2 = y2;
+ } else {
+ dx = -width * (y2 - y1) / length;
+ dy = width * (x2 - x1) / length;
+
+ *bx1 = x2 + dx;
+ *bx2 = x2 - dx;
+ *by1 = y2 + dy;
+ *by2 = y2 - dy;
+
+ if (project) {
+ *bx1 += dy;
+ *bx2 += dy;
+ *by1 -= dx;
+ *by2 -= dx;
+ }
+ }
+}
+
+/**
+ * gnome_canvas_polygon_to_point:
+ * @poly: Vertices of the polygon. X coordinates are in the even indices, and Y
+ * coordinates are in the odd indices
+ * @num_points: Number of points in the polygon
+ * @x: X coordinate of the point
+ * @y: Y coordinate of the point
+ *
+ * Computes the distance between a point and a polygon.
+ *
+ * Return value: The distance from the point to the polygon, or zero if the
+ * point is inside the polygon.
+ **/
+double
+gnome_canvas_polygon_to_point (double *poly, int num_points, double x, double y)
+{
+ double best;
+ int intersections;
+ int i;
+ double *p;
+ double dx, dy;
+
+ /* Iterate through all the edges in the polygon, updating best and intersections.
+ *
+ * When computing intersections, include left X coordinate of line within its range, but not
+ * Y coordinate. Otherwise if the point lies exactly below a vertex we'll count it as two
+ * intersections.
+ */
+
+ best = 1.0e36;
+ intersections = 0;
+
+ for (i = num_points, p = poly; i > 1; i--, p += 2) {
+ double px, py, dist;
+
+ /* Compute the point on the current edge closest to the point and update the
+ * intersection count. This must be done separately for vertical edges, horizontal
+ * edges, and others.
+ */
+
+ if (p[2] == p[0]) {
+ /* Vertical edge */
+
+ px = p[0];
+
+ if (p[1] >= p[3]) {
+ py = MIN (p[1], y);
+ py = MAX (py, p[3]);
+ } else {
+ py = MIN (p[3], y);
+ py = MAX (py, p[1]);
+ }
+ } else if (p[3] == p[1]) {
+ /* Horizontal edge */
+
+ py = p[1];
+
+ if (p[0] >= p[2]) {
+ px = MIN (p[0], x);
+ px = MAX (px, p[2]);
+
+ if ((y < py) && (x < p[0]) && (x >= p[2]))
+ intersections++;
+ } else {
+ px = MIN (p[2], x);
+ px = MAX (px, p[0]);
+
+ if ((y < py) && (x < p[2]) && (x >= p[0]))
+ intersections++;
+ }
+ } else {
+ double m1, b1, m2, b2;
+ int lower;
+
+ /* Diagonal edge. Convert the edge to a line equation (y = m1*x + b1), then
+ * compute a line perpendicular to this edge but passing through the point,
+ * (y = m2*x + b2).
+ */
+
+ m1 = (p[3] - p[1]) / (p[2] - p[0]);
+ b1 = p[1] - m1 * p[0];
+
+ m2 = -1.0 / m1;
+ b2 = y - m2 * x;
+
+ px = (b2 - b1) / (m1 - m2);
+ py = m1 * px + b1;
+
+ if (p[0] > p[2]) {
+ if (px > p[0]) {
+ px = p[0];
+ py = p[1];
+ } else if (px < p[2]) {
+ px = p[2];
+ py = p[3];
+ }
+ } else {
+ if (px > p[2]) {
+ px = p[2];
+ py = p[3];
+ } else if (px < p[0]) {
+ px = p[0];
+ py = p[1];
+ }
+ }
+
+ lower = (m1 * x + b1) > y;
+
+ if (lower && (x >= MIN (p[0], p[2])) && (x < MAX (p[0], p[2])))
+ intersections++;
+ }
+
+ /* Compute the distance to the closest point, and see if that is the best so far */
+
+ dx = x - px;
+ dy = y - py;
+ dist = sqrt (dx * dx + dy * dy);
+ if (dist < best)
+ best = dist;
+ }
+
+ /* We've processed all the points. If the number of intersections is odd, the point is
+ * inside the polygon.
+ */
+
+ if (intersections & 0x1)
+ return 0.0;
+ else
+ return best;
+}
+
+/* Here are some helper functions for aa rendering: */
+
+/**
+ * gnome_canvas_render_svp:
+ * @buf: the canvas buffer to render over
+ * @svp: the vector path to render
+ * @rgba: the rgba color to render
+ *
+ * Render the svp over the buf.
+ **/
+void
+gnome_canvas_render_svp (GnomeCanvasBuf *buf, ArtSVP *svp, guint32 rgba)
+{
+ guint32 fg_color, bg_color;
+ int alpha;
+
+ if (buf->is_bg) {
+ bg_color = buf->bg_color;
+ alpha = rgba & 0xff;
+ if (alpha == 0xff)
+ fg_color = rgba >> 8;
+ else {
+ /* composite over background color */
+ int bg_r, bg_g, bg_b;
+ int fg_r, fg_g, fg_b;
+ int tmp;
+
+ bg_r = (bg_color >> 16) & 0xff;
+ fg_r = (rgba >> 24) & 0xff;
+ tmp = (fg_r - bg_r) * alpha;
+ fg_r = bg_r + ((tmp + (tmp >> 8) + 0x80) >> 8);
+
+ bg_g = (bg_color >> 8) & 0xff;
+ fg_g = (rgba >> 16) & 0xff;
+ tmp = (fg_g - bg_g) * alpha;
+ fg_g = bg_g + ((tmp + (tmp >> 8) + 0x80) >> 8);
+
+ bg_b = bg_color & 0xff;
+ fg_b = (rgba >> 8) & 0xff;
+ tmp = (fg_b - bg_b) * alpha;
+ fg_b = bg_b + ((tmp + (tmp >> 8) + 0x80) >> 8);
+
+ fg_color = (fg_r << 16) | (fg_g << 8) | fg_b;
+ }
+ art_rgb_svp_aa (svp,
+ buf->rect.x0, buf->rect.y0, buf->rect.x1, buf->rect.y1,
+ fg_color, bg_color,
+ buf->buf, buf->buf_rowstride,
+ NULL);
+ buf->is_bg = 0;
+ buf->is_buf = 1;
+ } else {
+ art_rgb_svp_alpha (svp,
+ buf->rect.x0, buf->rect.y0, buf->rect.x1, buf->rect.y1,
+ rgba,
+ buf->buf, buf->buf_rowstride,
+ NULL);
+ }
+}
+
+/**
+ * gnome_canvas_update_svp:
+ * @canvas: the canvas containing the svp that needs updating.
+ * @p_svp: a pointer to the existing svp
+ * @new_svp: the new svp
+ *
+ * Sets the svp to the new value, requesting repaint on what's changed. This
+ * function takes responsibility for freeing new_svp.
+ **/
+void
+gnome_canvas_update_svp (GnomeCanvas *canvas, ArtSVP **p_svp, ArtSVP *new_svp)
+{
+ ArtSVP *old_svp;
+ ArtSVP *diff G_GNUC_UNUSED;
+ ArtUta *repaint_uta;
+
+ old_svp = *p_svp;
+
+ if (old_svp != NULL) {
+ ArtDRect bb;
+ art_drect_svp (&bb, old_svp);
+ if ((bb.x1 - bb.x0) * (bb.y1 - bb.y0) > (64 * 64)) {
+ repaint_uta = art_uta_from_svp (old_svp);
+ gnome_canvas_request_redraw_uta (canvas, repaint_uta);
+ } else {
+ ArtIRect ib;
+ art_drect_to_irect (&ib, &bb);
+ gnome_canvas_request_redraw (canvas, ib.x0, ib.y0, ib.x1, ib.y1);
+ }
+ art_svp_free (old_svp);
+ }
+
+ if (new_svp != NULL) {
+ ArtDRect bb;
+ art_drect_svp (&bb, new_svp);
+ if ((bb.x1 - bb.x0) * (bb.y1 - bb.y0) > (64 * 64)) {
+ repaint_uta = art_uta_from_svp (new_svp);
+ gnome_canvas_request_redraw_uta (canvas, repaint_uta);
+ } else {
+ ArtIRect ib;
+ art_drect_to_irect (&ib, &bb);
+ gnome_canvas_request_redraw (canvas, ib.x0, ib.y0, ib.x1, ib.y1);
+ }
+ }
+
+ *p_svp = new_svp;
+}
+
+/**
+ * gnome_canvas_update_svp_clip:
+ * @canvas: the canvas containing the svp that needs updating.
+ * @p_svp: a pointer to the existing svp
+ * @new_svp: the new svp
+ * @clip_svp: a clip path, if non-null
+ *
+ * Sets the svp to the new value, clipping if necessary, and requesting repaint
+ * on what's changed. This function takes responsibility for freeing new_svp.
+ **/
+void
+gnome_canvas_update_svp_clip (GnomeCanvas *canvas, ArtSVP **p_svp, ArtSVP *new_svp, ArtSVP *clip_svp)
+{
+ ArtSVP *clipped_svp;
+
+ if (clip_svp != NULL) {
+ clipped_svp = art_svp_intersect (new_svp, clip_svp);
+ art_svp_free (new_svp);
+ } else {
+ clipped_svp = new_svp;
+ }
+ gnome_canvas_update_svp (canvas, p_svp, clipped_svp);
+}
+
+/**
+ * gnome_canvas_item_reset_bounds:
+ * @item: A canvas item
+ *
+ * Resets the bounding box of a canvas item to an empty rectangle.
+ **/
+void
+gnome_canvas_item_reset_bounds (GnomeCanvasItem *item)
+{
+ item->x1 = 0.0;
+ item->y1 = 0.0;
+ item->x2 = 0.0;
+ item->y2 = 0.0;
+}
+
+/**
+ * gnome_canvas_item_update_svp:
+ * @item: the canvas item containing the svp that needs updating.
+ * @p_svp: a pointer to the existing svp
+ * @new_svp: the new svp
+ *
+ * Sets the svp to the new value, requesting repaint on what's changed. This
+ * function takes responsibility for freeing new_svp. This routine also adds the
+ * svp's bbox to the item's.
+ **/
+void
+gnome_canvas_item_update_svp (GnomeCanvasItem *item, ArtSVP **p_svp, ArtSVP *new_svp)
+{
+ ArtDRect bbox;
+
+ gnome_canvas_update_svp (item->canvas, p_svp, new_svp);
+ if (new_svp) {
+ bbox.x0 = item->x1;
+ bbox.y0 = item->y1;
+ bbox.x1 = item->x2;
+ bbox.y1 = item->y2;
+ art_drect_svp_union (&bbox, new_svp);
+ item->x1 = bbox.x0;
+ item->y1 = bbox.y0;
+ item->x2 = bbox.x1;
+ item->y2 = bbox.y1;
+ }
+}
+
+/**
+ * gnome_canvas_item_update_svp_clip:
+ * @item: the canvas item containing the svp that needs updating.
+ * @p_svp: a pointer to the existing svp
+ * @new_svp: the new svp
+ * @clip_svp: a clip path, if non-null
+ *
+ * Sets the svp to the new value, clipping if necessary, and requesting repaint
+ * on what's changed. This function takes responsibility for freeing new_svp.
+ **/
+void
+gnome_canvas_item_update_svp_clip (GnomeCanvasItem *item, ArtSVP **p_svp, ArtSVP *new_svp,
+ ArtSVP *clip_svp)
+{
+ ArtSVP *clipped_svp;
+
+ if (clip_svp != NULL) {
+ clipped_svp = art_svp_intersect (new_svp, clip_svp);
+ art_svp_free (new_svp);
+ } else {
+ clipped_svp = new_svp;
+ }
+
+ gnome_canvas_item_update_svp (item, p_svp, clipped_svp);
+}
+
+/**
+ * gnome_canvas_item_request_redraw_svp
+ * @item: the item containing the svp
+ * @svp: the svp that needs to be redrawn
+ *
+ * Request redraw of the svp if in aa mode, or the entire item in in xlib mode.
+ **/
+void
+gnome_canvas_item_request_redraw_svp (GnomeCanvasItem *item, const ArtSVP *svp)
+{
+ GnomeCanvas *canvas;
+ ArtUta *uta;
+
+ canvas = item->canvas;
+ if (canvas->aa) {
+ if (svp != NULL) {
+ uta = art_uta_from_svp (svp);
+ gnome_canvas_request_redraw_uta (canvas, uta);
+ }
+ } else {
+ gnome_canvas_request_redraw (canvas, item->x1, item->y1, item->x2, item->y2);
+ }
+}
+
+/**
+ * gnome_canvas_update_bbox:
+ * @item: the canvas item needing update
+ * @x1: Left coordinate of the new bounding box
+ * @y1: Top coordinate of the new bounding box
+ * @x2: Right coordinate of the new bounding box
+ * @y2: Bottom coordinate of the new bounding box
+ *
+ * Sets the bbox to the new value, requesting full repaint.
+ **/
+void
+gnome_canvas_update_bbox (GnomeCanvasItem *item, int x1, int y1, int x2, int y2)
+{
+ gnome_canvas_request_redraw (item->canvas, item->x1, item->y1, item->x2, item->y2);
+ item->x1 = x1;
+ item->y1 = y1;
+ item->x2 = x2;
+ item->y2 = y2;
+ gnome_canvas_request_redraw (item->canvas, item->x1, item->y1, item->x2, item->y2);
+}
+
+/**
+ * gnome_canvas_buf_ensure_buf:
+ * @buf: the buf that needs to be represened in RGB format
+ *
+ * Ensure that the buffer is in RGB format, suitable for compositing.
+ **/
+void
+gnome_canvas_buf_ensure_buf (GnomeCanvasBuf *buf)
+{
+ guchar *bufptr;
+ int y;
+
+ if (!buf->is_buf) {
+ bufptr = buf->buf;
+ for (y = buf->rect.y0; y < buf->rect.y1; y++) {
+ art_rgb_fill_run (bufptr,
+ buf->bg_color >> 16,
+ (buf->bg_color >> 8) & 0xff,
+ buf->bg_color & 0xff,
+ buf->rect.x1 - buf->rect.x0);
+ bufptr += buf->buf_rowstride;
+ }
+ buf->is_buf = 1;
+ }
+}
+
+/**
+ * gnome_canvas_join_gdk_to_art
+ * @gdk_join: a join type, represented in GDK format
+ *
+ * Convert from GDK line join specifier to libart.
+ *
+ * Return value: The line join specifier in libart format.
+ **/
+ArtPathStrokeJoinType
+gnome_canvas_join_gdk_to_art (GdkJoinStyle gdk_join)
+{
+ switch (gdk_join) {
+ case GDK_JOIN_MITER:
+ return ART_PATH_STROKE_JOIN_MITER;
+
+ case GDK_JOIN_ROUND:
+ return ART_PATH_STROKE_JOIN_ROUND;
+
+ case GDK_JOIN_BEVEL:
+ return ART_PATH_STROKE_JOIN_BEVEL;
+
+ default:
+ g_assert_not_reached ();
+ return ART_PATH_STROKE_JOIN_MITER; /* shut up the compiler */
+ }
+}
+
+/**
+ * gnome_canvas_cap_gdk_to_art
+ * @gdk_cap: a cap type, represented in GDK format
+ *
+ * Convert from GDK line cap specifier to libart.
+ *
+ * Return value: The line cap specifier in libart format.
+ **/
+ArtPathStrokeCapType
+gnome_canvas_cap_gdk_to_art (GdkCapStyle gdk_cap)
+{
+ switch (gdk_cap) {
+ case GDK_CAP_BUTT:
+ case GDK_CAP_NOT_LAST:
+ return ART_PATH_STROKE_CAP_BUTT;
+
+ case GDK_CAP_ROUND:
+ return ART_PATH_STROKE_CAP_ROUND;
+
+ case GDK_CAP_PROJECTING:
+ return ART_PATH_STROKE_CAP_SQUARE;
+
+ default:
+ g_assert_not_reached ();
+ return ART_PATH_STROKE_CAP_BUTT; /* shut up the compiler */
+ }
+}