2 * Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation
5 * This file is part of the Gnome Library.
7 * The Gnome Library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public License as
9 * published by the Free Software Foundation; either version 2 of the
10 * License, or (at your option) any later version.
12 * The Gnome Library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
17 * You should have received a copy of the GNU Library General Public
18 * License along with the Gnome Library; see the file COPYING.LIB. If not,
19 * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 * Boston, MA 02111-1307, USA.
25 /* Miscellaneous utility functions for the GnomeCanvas widget
27 * GnomeCanvas is basically a port of the Tk toolkit's most excellent canvas widget. Tk is
28 * copyrighted by the Regents of the University of California, Sun Microsystems, and other parties.
31 * Author: Federico Mena <federico@nuclecu.unam.mx>
36 /* needed for M_PI_2 under 'gcc -ansi -predantic' on GNU/Linux */
38 # define _BSD_SOURCE 1
40 #include <sys/types.h>
44 #include "gnome-canvas.h"
45 #include "gnome-canvas-util.h"
46 #include <libart_lgpl/art_uta.h>
47 #include <libart_lgpl/art_svp.h>
48 #include <libart_lgpl/art_svp_ops.h>
49 #include <libart_lgpl/art_rgb.h>
50 #include <libart_lgpl/art_rgb_svp.h>
51 #include <libart_lgpl/art_uta_svp.h>
52 #include <libart_lgpl/art_rect_svp.h>
55 * gnome_canvas_points_new:
56 * @num_points: The number of points to allocate space for in the array.
58 * Creates a structure that should be used to pass an array of points to
61 * Return value: A newly-created array of points. It should be filled in
65 gnome_canvas_points_new (int num_points)
67 GnomeCanvasPoints *points;
69 g_return_val_if_fail (num_points > 1, NULL);
71 points = g_new (GnomeCanvasPoints, 1);
72 points->num_points = num_points;
73 points->coords = g_new (double, 2 * num_points);
74 points->ref_count = 1;
80 * gnome_canvas_points_ref:
81 * @points: A canvas points structure.
83 * Increases the reference count of the specified points structure.
85 * Return value: The canvas points structure itself.
88 gnome_canvas_points_ref (GnomeCanvasPoints *points)
90 g_return_val_if_fail (points != NULL, NULL);
92 points->ref_count += 1;
97 * gnome_canvas_points_free:
98 * @points: A canvas points structure.
100 * Decreases the reference count of the specified points structure. If it
101 * reaches zero, then the structure is freed.
104 gnome_canvas_points_free (GnomeCanvasPoints *points)
106 g_return_if_fail (points != NULL);
108 points->ref_count -= 1;
109 if (points->ref_count == 0) {
110 g_free (points->coords);
116 * gnome_canvas_get_miter_points:
117 * @x1: X coordinate of the first point
118 * @y1: Y coordinate of the first point
119 * @x2: X coordinate of the second (angle) point
120 * @y2: Y coordinate of the second (angle) point
121 * @x3: X coordinate of the third point
122 * @y3: Y coordinate of the third point
123 * @width: Width of the line
124 * @mx1: The X coordinate of the first miter point is returned here.
125 * @my1: The Y coordinate of the first miter point is returned here.
126 * @mx2: The X coordinate of the second miter point is returned here.
127 * @my2: The Y coordinate of the second miter point is returned here.
129 * Given three points forming an angle, computes the coordinates of the inside
130 * and outside points of the mitered corner formed by a line of a given width at
133 * Return value: FALSE if the angle is less than 11 degrees (this is the same
134 * threshold as X uses. If this occurs, the return points are not modified.
135 * Otherwise, returns TRUE.
138 gnome_canvas_get_miter_points (double x1, double y1, double x2, double y2, double x3, double y3,
140 double *mx1, double *my1, double *mx2, double *my2)
142 double theta1; /* angle of segment p2-p1 */
143 double theta2; /* angle of segment p2-p3 */
144 double theta; /* angle between line segments */
145 double theta3; /* angle that bisects theta1 and theta2 and points to p1 */
146 double dist; /* distance of miter points from p2 */
147 double dx, dy; /* x and y offsets corresponding to dist */
149 #define ELEVEN_DEGREES (11.0 * G_PI / 180.0)
152 theta1 = (x2 < x1) ? 0.0 : G_PI;
154 theta1 = (y2 < y1) ? G_PI_2 : -G_PI_2;
156 theta1 = atan2 (y1 - y2, x1 - x2);
159 theta2 = (x3 > x2) ? 0 : G_PI;
161 theta2 = (y3 > y2) ? G_PI_2 : -G_PI_2;
163 theta2 = atan2 (y3 - y2, x3 - x2);
165 theta = theta1 - theta2;
169 else if (theta < -G_PI)
172 if ((theta < ELEVEN_DEGREES) && (theta > -ELEVEN_DEGREES))
175 dist = 0.5 * width / sin (0.5 * theta);
179 theta3 = (theta1 + theta2) / 2.0;
180 if (sin (theta3 - (theta1 + G_PI)) < 0.0)
183 dx = dist * cos (theta3);
184 dy = dist * sin (theta3);
195 * gnome_canvas_get_butt_points:
196 * @x1: X coordinate of first point in the line
197 * @y1: Y cooordinate of first point in the line
198 * @x2: X coordinate of second point (endpoint) of the line
199 * @y2: Y coordinate of second point (endpoint) of the line
200 * @width: Width of the line
201 * @project: Whether the butt points should project out by width/2 distance
202 * @bx1: X coordinate of first butt point is returned here
203 * @by1: Y coordinate of first butt point is returned here
204 * @bx2: X coordinate of second butt point is returned here
205 * @by2: Y coordinate of second butt point is returned here
207 * Computes the butt points of a line segment.
210 gnome_canvas_get_butt_points (double x1, double y1, double x2, double y2,
211 double width, int project,
212 double *bx1, double *by1, double *bx2, double *by2)
220 length = sqrt (dx * dx + dy * dy);
222 if (length < GNOME_CANVAS_EPSILON) {
226 dx = -width * (y2 - y1) / length;
227 dy = width * (x2 - x1) / length;
244 * gnome_canvas_polygon_to_point:
245 * @poly: Vertices of the polygon. X coordinates are in the even indices, and Y
246 * coordinates are in the odd indices
247 * @num_points: Number of points in the polygon
248 * @x: X coordinate of the point
249 * @y: Y coordinate of the point
251 * Computes the distance between a point and a polygon.
253 * Return value: The distance from the point to the polygon, or zero if the
254 * point is inside the polygon.
257 gnome_canvas_polygon_to_point (double *poly, int num_points, double x, double y)
265 /* Iterate through all the edges in the polygon, updating best and intersections.
267 * When computing intersections, include left X coordinate of line within its range, but not
268 * Y coordinate. Otherwise if the point lies exactly below a vertex we'll count it as two
275 for (i = num_points, p = poly; i > 1; i--, p += 2) {
278 /* Compute the point on the current edge closest to the point and update the
279 * intersection count. This must be done separately for vertical edges, horizontal
295 } else if (p[3] == p[1]) {
296 /* Horizontal edge */
304 if ((y < py) && (x < p[0]) && (x >= p[2]))
310 if ((y < py) && (x < p[2]) && (x >= p[0]))
314 double m1, b1, m2, b2;
317 /* Diagonal edge. Convert the edge to a line equation (y = m1*x + b1), then
318 * compute a line perpendicular to this edge but passing through the point,
322 m1 = (p[3] - p[1]) / (p[2] - p[0]);
323 b1 = p[1] - m1 * p[0];
328 px = (b2 - b1) / (m1 - m2);
335 } else if (px < p[2]) {
343 } else if (px < p[0]) {
349 lower = (m1 * x + b1) > y;
351 if (lower && (x >= MIN (p[0], p[2])) && (x < MAX (p[0], p[2])))
355 /* Compute the distance to the closest point, and see if that is the best so far */
359 dist = sqrt (dx * dx + dy * dy);
364 /* We've processed all the points. If the number of intersections is odd, the point is
365 * inside the polygon.
368 if (intersections & 0x1)
374 /* Here are some helper functions for aa rendering: */
377 * gnome_canvas_render_svp:
378 * @buf: the canvas buffer to render over
379 * @svp: the vector path to render
380 * @rgba: the rgba color to render
382 * Render the svp over the buf.
385 gnome_canvas_render_svp (GnomeCanvasBuf *buf, ArtSVP *svp, guint32 rgba)
387 guint32 fg_color, bg_color;
391 bg_color = buf->bg_color;
394 fg_color = rgba >> 8;
396 /* composite over background color */
397 int bg_r, bg_g, bg_b;
398 int fg_r, fg_g, fg_b;
401 bg_r = (bg_color >> 16) & 0xff;
402 fg_r = (rgba >> 24) & 0xff;
403 tmp = (fg_r - bg_r) * alpha;
404 fg_r = bg_r + ((tmp + (tmp >> 8) + 0x80) >> 8);
406 bg_g = (bg_color >> 8) & 0xff;
407 fg_g = (rgba >> 16) & 0xff;
408 tmp = (fg_g - bg_g) * alpha;
409 fg_g = bg_g + ((tmp + (tmp >> 8) + 0x80) >> 8);
411 bg_b = bg_color & 0xff;
412 fg_b = (rgba >> 8) & 0xff;
413 tmp = (fg_b - bg_b) * alpha;
414 fg_b = bg_b + ((tmp + (tmp >> 8) + 0x80) >> 8);
416 fg_color = (fg_r << 16) | (fg_g << 8) | fg_b;
419 buf->rect.x0, buf->rect.y0, buf->rect.x1, buf->rect.y1,
421 buf->buf, buf->buf_rowstride,
426 art_rgb_svp_alpha (svp,
427 buf->rect.x0, buf->rect.y0, buf->rect.x1, buf->rect.y1,
429 buf->buf, buf->buf_rowstride,
435 * gnome_canvas_update_svp:
436 * @canvas: the canvas containing the svp that needs updating.
437 * @p_svp: a pointer to the existing svp
438 * @new_svp: the new svp
440 * Sets the svp to the new value, requesting repaint on what's changed. This
441 * function takes responsibility for freeing new_svp.
444 gnome_canvas_update_svp (GnomeCanvas *canvas, ArtSVP **p_svp, ArtSVP *new_svp)
447 ArtSVP *diff G_GNUC_UNUSED;
452 if (old_svp != NULL) {
454 art_drect_svp (&bb, old_svp);
455 if ((bb.x1 - bb.x0) * (bb.y1 - bb.y0) > (64 * 64)) {
456 repaint_uta = art_uta_from_svp (old_svp);
457 gnome_canvas_request_redraw_uta (canvas, repaint_uta);
460 art_drect_to_irect (&ib, &bb);
461 gnome_canvas_request_redraw (canvas, ib.x0, ib.y0, ib.x1, ib.y1);
463 art_svp_free (old_svp);
466 if (new_svp != NULL) {
468 art_drect_svp (&bb, new_svp);
469 if ((bb.x1 - bb.x0) * (bb.y1 - bb.y0) > (64 * 64)) {
470 repaint_uta = art_uta_from_svp (new_svp);
471 gnome_canvas_request_redraw_uta (canvas, repaint_uta);
474 art_drect_to_irect (&ib, &bb);
475 gnome_canvas_request_redraw (canvas, ib.x0, ib.y0, ib.x1, ib.y1);
483 * gnome_canvas_update_svp_clip:
484 * @canvas: the canvas containing the svp that needs updating.
485 * @p_svp: a pointer to the existing svp
486 * @new_svp: the new svp
487 * @clip_svp: a clip path, if non-null
489 * Sets the svp to the new value, clipping if necessary, and requesting repaint
490 * on what's changed. This function takes responsibility for freeing new_svp.
493 gnome_canvas_update_svp_clip (GnomeCanvas *canvas, ArtSVP **p_svp, ArtSVP *new_svp, ArtSVP *clip_svp)
497 if (clip_svp != NULL) {
498 clipped_svp = art_svp_intersect (new_svp, clip_svp);
499 art_svp_free (new_svp);
501 clipped_svp = new_svp;
503 gnome_canvas_update_svp (canvas, p_svp, clipped_svp);
507 * gnome_canvas_item_reset_bounds:
508 * @item: A canvas item
510 * Resets the bounding box of a canvas item to an empty rectangle.
513 gnome_canvas_item_reset_bounds (GnomeCanvasItem *item)
522 * gnome_canvas_item_update_svp:
523 * @item: the canvas item containing the svp that needs updating.
524 * @p_svp: a pointer to the existing svp
525 * @new_svp: the new svp
527 * Sets the svp to the new value, requesting repaint on what's changed. This
528 * function takes responsibility for freeing new_svp. This routine also adds the
529 * svp's bbox to the item's.
532 gnome_canvas_item_update_svp (GnomeCanvasItem *item, ArtSVP **p_svp, ArtSVP *new_svp)
536 gnome_canvas_update_svp (item->canvas, p_svp, new_svp);
542 art_drect_svp_union (&bbox, new_svp);
551 * gnome_canvas_item_update_svp_clip:
552 * @item: the canvas item containing the svp that needs updating.
553 * @p_svp: a pointer to the existing svp
554 * @new_svp: the new svp
555 * @clip_svp: a clip path, if non-null
557 * Sets the svp to the new value, clipping if necessary, and requesting repaint
558 * on what's changed. This function takes responsibility for freeing new_svp.
561 gnome_canvas_item_update_svp_clip (GnomeCanvasItem *item, ArtSVP **p_svp, ArtSVP *new_svp,
566 if (clip_svp != NULL) {
567 clipped_svp = art_svp_intersect (new_svp, clip_svp);
568 art_svp_free (new_svp);
570 clipped_svp = new_svp;
573 gnome_canvas_item_update_svp (item, p_svp, clipped_svp);
577 * gnome_canvas_item_request_redraw_svp
578 * @item: the item containing the svp
579 * @svp: the svp that needs to be redrawn
581 * Request redraw of the svp if in aa mode, or the entire item in in xlib mode.
584 gnome_canvas_item_request_redraw_svp (GnomeCanvasItem *item, const ArtSVP *svp)
589 canvas = item->canvas;
592 uta = art_uta_from_svp (svp);
593 gnome_canvas_request_redraw_uta (canvas, uta);
596 gnome_canvas_request_redraw (canvas, item->x1, item->y1, item->x2, item->y2);
601 * gnome_canvas_update_bbox:
602 * @item: the canvas item needing update
603 * @x1: Left coordinate of the new bounding box
604 * @y1: Top coordinate of the new bounding box
605 * @x2: Right coordinate of the new bounding box
606 * @y2: Bottom coordinate of the new bounding box
608 * Sets the bbox to the new value, requesting full repaint.
611 gnome_canvas_update_bbox (GnomeCanvasItem *item, int x1, int y1, int x2, int y2)
613 gnome_canvas_request_redraw (item->canvas, item->x1, item->y1, item->x2, item->y2);
618 gnome_canvas_request_redraw (item->canvas, item->x1, item->y1, item->x2, item->y2);
622 * gnome_canvas_buf_ensure_buf:
623 * @buf: the buf that needs to be represened in RGB format
625 * Ensure that the buffer is in RGB format, suitable for compositing.
628 gnome_canvas_buf_ensure_buf (GnomeCanvasBuf *buf)
635 for (y = buf->rect.y0; y < buf->rect.y1; y++) {
636 art_rgb_fill_run (bufptr,
638 (buf->bg_color >> 8) & 0xff,
639 buf->bg_color & 0xff,
640 buf->rect.x1 - buf->rect.x0);
641 bufptr += buf->buf_rowstride;
648 * gnome_canvas_join_gdk_to_art
649 * @gdk_join: a join type, represented in GDK format
651 * Convert from GDK line join specifier to libart.
653 * Return value: The line join specifier in libart format.
655 ArtPathStrokeJoinType
656 gnome_canvas_join_gdk_to_art (GdkJoinStyle gdk_join)
660 return ART_PATH_STROKE_JOIN_MITER;
663 return ART_PATH_STROKE_JOIN_ROUND;
666 return ART_PATH_STROKE_JOIN_BEVEL;
669 g_assert_not_reached ();
670 return ART_PATH_STROKE_JOIN_MITER; /* shut up the compiler */
675 * gnome_canvas_cap_gdk_to_art
676 * @gdk_cap: a cap type, represented in GDK format
678 * Convert from GDK line cap specifier to libart.
680 * Return value: The line cap specifier in libart format.
683 gnome_canvas_cap_gdk_to_art (GdkCapStyle gdk_cap)
687 case GDK_CAP_NOT_LAST:
688 return ART_PATH_STROKE_CAP_BUTT;
691 return ART_PATH_STROKE_CAP_ROUND;
693 case GDK_CAP_PROJECTING:
694 return ART_PATH_STROKE_CAP_SQUARE;
697 g_assert_not_reached ();
698 return ART_PATH_STROKE_CAP_BUTT; /* shut up the compiler */