/* -*- Mode: C; tab-width: 8; indent-tabs-mode: 8; c-basic-offset: 8 -*- */ /* * Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation * All rights reserved. * * This file is part of the Mate Library. * * The Mate 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 Mate 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 Mate Library; see the file COPYING.LIB. If not, * write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301, USA. */ /* @NOTATION@ */ /* Rectangle and ellipse item types for EelCanvas widget * * EelCanvas 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> #include <math.h> #include "eel-canvas-rect-ellipse.h" #include "eel-canvas-util.h" #include <string.h> #ifdef HAVE_RENDER #include <gdk/gdkx.h> #include <X11/extensions/Xrender.h> #endif /* Base class for rectangle and ellipse item types */ #define noVERBOSE enum { PROP_0, PROP_X1, PROP_Y1, PROP_X2, PROP_Y2, PROP_FILL_COLOR, PROP_FILL_COLOR_GDK, PROP_FILL_COLOR_RGBA, PROP_OUTLINE_COLOR, PROP_OUTLINE_COLOR_GDK, PROP_OUTLINE_COLOR_RGBA, PROP_FILL_STIPPLE, PROP_OUTLINE_STIPPLE, PROP_WIDTH_PIXELS, PROP_WIDTH_UNITS }; static void eel_canvas_re_class_init (EelCanvasREClass *klass); static void eel_canvas_re_init (EelCanvasRE *re); static void eel_canvas_re_destroy (GtkObject *object); static void eel_canvas_re_set_property (GObject *object, guint param_id, const GValue *value, GParamSpec *pspec); static void eel_canvas_re_get_property (GObject *object, guint param_id, GValue *value, GParamSpec *pspec); static void eel_canvas_re_update_shared (EelCanvasItem *item, double i2w_dx, double i2w_dy, int flags); static void eel_canvas_re_realize (EelCanvasItem *item); static void eel_canvas_re_unrealize (EelCanvasItem *item); static void eel_canvas_re_bounds (EelCanvasItem *item, double *x1, double *y1, double *x2, double *y2); static void eel_canvas_re_translate (EelCanvasItem *item, double dx, double dy); static void eel_canvas_rect_update (EelCanvasItem *item, double i2w_dx, double i2w_dy, int flags); static void eel_canvas_ellipse_update (EelCanvasItem *item, double i2w_dx, double i2w_dy, int flags); typedef struct { /*< public >*/ int x0, y0, x1, y1; } Rect; static Rect make_rect (int x0, int y0, int x1, int y1); static void diff_rects (Rect r1, Rect r2, int *count, Rect result[4]); static EelCanvasItemClass *re_parent_class; static EelCanvasREClass *rect_parent_class; GType eel_canvas_re_get_type (void) { static GType re_type = 0; if (!re_type) { GTypeInfo re_info = { sizeof (EelCanvasREClass), (GBaseInitFunc) NULL, (GBaseFinalizeFunc) NULL, (GClassInitFunc) eel_canvas_re_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof (EelCanvasRE), 0, /* n_preallocs */ (GInstanceInitFunc) eel_canvas_re_init }; re_type = g_type_register_static (eel_canvas_item_get_type (), "EelCanvasRE", &re_info, 0); } return re_type; } static void eel_canvas_re_class_init (EelCanvasREClass *klass) { GObjectClass *gobject_class; GtkObjectClass *object_class; EelCanvasItemClass *item_class; gobject_class = (GObjectClass *) klass; object_class = (GtkObjectClass *) klass; item_class = (EelCanvasItemClass *) klass; re_parent_class = g_type_class_peek_parent (klass); gobject_class->set_property = eel_canvas_re_set_property; gobject_class->get_property = eel_canvas_re_get_property; g_object_class_install_property (gobject_class, PROP_X1, g_param_spec_double ("x1", NULL, NULL, -G_MAXDOUBLE, G_MAXDOUBLE, 0, G_PARAM_READWRITE)); g_object_class_install_property (gobject_class, PROP_Y1, g_param_spec_double ("y1", NULL, NULL, -G_MAXDOUBLE, G_MAXDOUBLE, 0, G_PARAM_READWRITE)); g_object_class_install_property (gobject_class, PROP_X2, g_param_spec_double ("x2", NULL, NULL, -G_MAXDOUBLE, G_MAXDOUBLE, 0, G_PARAM_READWRITE)); g_object_class_install_property (gobject_class, PROP_Y2, g_param_spec_double ("y2", NULL, NULL, -G_MAXDOUBLE, G_MAXDOUBLE, 0, G_PARAM_READWRITE)); g_object_class_install_property (gobject_class, PROP_FILL_COLOR, g_param_spec_string ("fill-color", NULL, NULL, NULL, G_PARAM_READWRITE)); g_object_class_install_property (gobject_class, PROP_FILL_COLOR_GDK, g_param_spec_boxed ("fill-color-gdk", NULL, NULL, GDK_TYPE_COLOR, G_PARAM_READWRITE)); g_object_class_install_property (gobject_class, PROP_FILL_COLOR_RGBA, g_param_spec_uint ("fill-color-rgba", NULL, NULL, 0, G_MAXUINT, 0, G_PARAM_READWRITE)); g_object_class_install_property (gobject_class, PROP_FILL_STIPPLE, g_param_spec_object ("fill-stipple", NULL, NULL, GDK_TYPE_DRAWABLE, G_PARAM_READWRITE)); g_object_class_install_property (gobject_class, PROP_OUTLINE_COLOR, g_param_spec_string ("outline-color", NULL, NULL, NULL, G_PARAM_READWRITE)); g_object_class_install_property (gobject_class, PROP_OUTLINE_COLOR_GDK, g_param_spec_boxed ("outline-color-gdk", NULL, NULL, GDK_TYPE_COLOR, G_PARAM_READWRITE)); g_object_class_install_property (gobject_class, PROP_OUTLINE_COLOR_RGBA, g_param_spec_uint ("outline-color-rgba", NULL, NULL, 0, G_MAXUINT, 0, G_PARAM_READWRITE)); g_object_class_install_property (gobject_class, PROP_OUTLINE_STIPPLE, g_param_spec_object ("outline-stipple", NULL, NULL, GDK_TYPE_DRAWABLE, G_PARAM_READWRITE)); g_object_class_install_property (gobject_class, PROP_WIDTH_PIXELS, g_param_spec_uint ("width-pixels", NULL, NULL, 0, G_MAXUINT, 0, G_PARAM_READWRITE)); g_object_class_install_property (gobject_class, PROP_WIDTH_UNITS, g_param_spec_double ("width-units", NULL, NULL, 0.0, G_MAXDOUBLE, 0.0, G_PARAM_READWRITE)); object_class->destroy = eel_canvas_re_destroy; item_class->realize = eel_canvas_re_realize; item_class->unrealize = eel_canvas_re_unrealize; item_class->translate = eel_canvas_re_translate; item_class->bounds = eel_canvas_re_bounds; } static void eel_canvas_re_init (EelCanvasRE *re) { re->x1 = 0.0; re->y1 = 0.0; re->x2 = 0.0; re->y2 = 0.0; re->width = 0.0; } static void eel_canvas_re_destroy (GtkObject *object) { EelCanvasRE *re; g_return_if_fail (object != NULL); g_return_if_fail (EEL_IS_CANVAS_RE (object)); re = EEL_CANVAS_RE (object); /* remember, destroy can be run multiple times! */ if (re->fill_stipple) g_object_unref (re->fill_stipple); re->fill_stipple = NULL; if (re->outline_stipple) g_object_unref (re->outline_stipple); re->outline_stipple = NULL; if (GTK_OBJECT_CLASS (re_parent_class)->destroy) (* GTK_OBJECT_CLASS (re_parent_class)->destroy) (object); } static void get_bounds (EelCanvasRE *re, double *px1, double *py1, double *px2, double *py2) { EelCanvasItem *item; double x1, y1, x2, y2; int cx1, cy1, cx2, cy2; double hwidth; #ifdef VERBOSE g_print ("re get_bounds\n"); #endif item = EEL_CANVAS_ITEM (re); if (re->width_pixels) hwidth = (re->width / item->canvas->pixels_per_unit) / 2.0; else hwidth = re->width / 2.0; x1 = re->x1; y1 = re->y1; x2 = re->x2; y2 = re->y2; eel_canvas_item_i2w (item, &x1, &y1); eel_canvas_item_i2w (item, &x2, &y2); eel_canvas_w2c (item->canvas, x1 - hwidth, y1 - hwidth, &cx1, &cy1); eel_canvas_w2c (item->canvas, x2 + hwidth, y2 + hwidth, &cx2, &cy2); *px1 = cx1; *py1 = cy1; *px2 = cx2; *py2 = cy2; /* Some safety fudging */ *px1 -= 2; *py1 -= 2; *px2 += 2; *py2 += 2; } /* Convenience function to set a GC's foreground color to the specified pixel value */ static void set_gc_foreground (GdkGC *gc, gulong pixel) { GdkColor c; if (!gc) return; c.pixel = pixel; gdk_gc_set_foreground (gc, &c); } /* Sets the stipple pattern for the specified gc */ static void set_stipple (GdkGC *gc, GdkBitmap **internal_stipple, GdkBitmap *stipple, int reconfigure) { if (*internal_stipple && !reconfigure) g_object_unref (*internal_stipple); *internal_stipple = stipple; if (stipple && !reconfigure) g_object_ref (stipple); if (gc) { if (stipple) { gdk_gc_set_stipple (gc, stipple); gdk_gc_set_fill (gc, GDK_STIPPLED); } else gdk_gc_set_fill (gc, GDK_SOLID); } } /* Recalculate the outline width of the rectangle/ellipse and set it in its GC */ static void set_outline_gc_width (EelCanvasRE *re) { int width; if (!re->outline_gc) return; if (re->width_pixels) width = (int) re->width; else width = (int) (re->width * re->item.canvas->pixels_per_unit + 0.5); gdk_gc_set_line_attributes (re->outline_gc, width, GDK_LINE_SOLID, GDK_CAP_PROJECTING, GDK_JOIN_MITER); } static void eel_canvas_re_set_fill (EelCanvasRE *re, gboolean fill_set) { if (re->fill_set != fill_set) { re->fill_set = fill_set; eel_canvas_item_request_update (EEL_CANVAS_ITEM (re)); } } static void eel_canvas_re_set_outline (EelCanvasRE *re, gboolean outline_set) { if (re->outline_set != outline_set) { re->outline_set = outline_set; eel_canvas_item_request_update (EEL_CANVAS_ITEM (re)); } } static void eel_canvas_re_set_property (GObject *object, guint param_id, const GValue *value, GParamSpec *pspec) { EelCanvasItem *item; EelCanvasRE *re; GdkColor color = { 0, 0, 0, 0, }; GdkColor *pcolor; int have_pixel; g_return_if_fail (object != NULL); g_return_if_fail (EEL_IS_CANVAS_RE (object)); item = EEL_CANVAS_ITEM (object); re = EEL_CANVAS_RE (object); have_pixel = FALSE; switch (param_id) { case PROP_X1: re->x1 = g_value_get_double (value); eel_canvas_item_request_update (item); break; case PROP_Y1: re->y1 = g_value_get_double (value); eel_canvas_item_request_update (item); break; case PROP_X2: re->x2 = g_value_get_double (value); eel_canvas_item_request_update (item); break; case PROP_Y2: re->y2 = g_value_get_double (value); eel_canvas_item_request_update (item); break; case PROP_FILL_COLOR: case PROP_FILL_COLOR_GDK: case PROP_FILL_COLOR_RGBA: switch (param_id) { case PROP_FILL_COLOR: if (g_value_get_string (value) && gdk_color_parse (g_value_get_string (value), &color)) eel_canvas_re_set_fill (re, TRUE); else eel_canvas_re_set_fill (re, FALSE); re->fill_color = ((color.red & 0xff00) << 16 | (color.green & 0xff00) << 8 | (color.blue & 0xff00) | 0xff); break; case PROP_FILL_COLOR_GDK: pcolor = g_value_get_boxed (value); eel_canvas_re_set_fill (re, pcolor != NULL); if (pcolor) { GdkColormap *colormap; color = *pcolor; colormap = gtk_widget_get_colormap (GTK_WIDGET (item->canvas)); gdk_rgb_find_color (colormap, &color); have_pixel = TRUE; } re->fill_color = ((color.red & 0xff00) << 16 | (color.green & 0xff00) << 8 | (color.blue & 0xff00) | 0xff); break; case PROP_FILL_COLOR_RGBA: eel_canvas_re_set_fill (re, TRUE); re->fill_color = g_value_get_uint (value); break; } #ifdef VERBOSE g_print ("re fill color = %08x\n", re->fill_color); #endif if (have_pixel) re->fill_pixel = color.pixel; else re->fill_pixel = eel_canvas_get_color_pixel (item->canvas, re->fill_color); set_gc_foreground (re->fill_gc, re->fill_pixel); eel_canvas_item_request_redraw (item); break; case PROP_OUTLINE_COLOR: case PROP_OUTLINE_COLOR_GDK: case PROP_OUTLINE_COLOR_RGBA: switch (param_id) { case PROP_OUTLINE_COLOR: if (g_value_get_string (value) && gdk_color_parse (g_value_get_string (value), &color)) eel_canvas_re_set_outline (re, TRUE); else eel_canvas_re_set_outline (re, FALSE); re->outline_color = ((color.red & 0xff00) << 16 | (color.green & 0xff00) << 8 | (color.blue & 0xff00) | 0xff); break; case PROP_OUTLINE_COLOR_GDK: pcolor = g_value_get_boxed (value); eel_canvas_re_set_outline (re, pcolor != NULL); if (pcolor) { GdkColormap *colormap; color = *pcolor; colormap = gtk_widget_get_colormap (GTK_WIDGET (item->canvas)); gdk_rgb_find_color (colormap, &color); have_pixel = TRUE; } re->outline_color = ((color.red & 0xff00) << 16 | (color.green & 0xff00) << 8 | (color.blue & 0xff00) | 0xff); break; case PROP_OUTLINE_COLOR_RGBA: eel_canvas_re_set_outline (re, TRUE); re->outline_color = g_value_get_uint (value); break; } #ifdef VERBOSE g_print ("re outline color %x %x %x\n", color.red, color.green, color.blue); #endif if (have_pixel) re->outline_pixel = color.pixel; else re->outline_pixel = eel_canvas_get_color_pixel (item->canvas, re->outline_color); set_gc_foreground (re->outline_gc, re->outline_pixel); eel_canvas_item_request_redraw (item); break; case PROP_FILL_STIPPLE: set_stipple (re->fill_gc, &re->fill_stipple, (GdkBitmap *) g_value_get_object (value), FALSE); break; case PROP_OUTLINE_STIPPLE: set_stipple (re->outline_gc, &re->outline_stipple, (GdkBitmap *) g_value_get_object (value), FALSE); break; case PROP_WIDTH_PIXELS: re->width = g_value_get_uint (value); re->width_pixels = TRUE; set_outline_gc_width (re); eel_canvas_item_request_update (item); break; case PROP_WIDTH_UNITS: re->width = fabs (g_value_get_double (value)); re->width_pixels = FALSE; set_outline_gc_width (re); eel_canvas_item_request_update (item); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); break; } } /* Allocates a GdkColor structure filled with the specified pixel, and puts it into the specified * value for returning it in the get_property method. */ static void get_color_value (EelCanvasRE *re, gulong pixel, GValue *value) { GdkColor color; EelCanvasItem *item = (EelCanvasItem *) re; GdkColormap *colormap = gtk_widget_get_colormap (GTK_WIDGET (item->canvas)); gdk_colormap_query_color (colormap, pixel, &color); g_value_set_boxed (value, &color); } static void eel_canvas_re_get_property (GObject *object, guint param_id, GValue *value, GParamSpec *pspec) { EelCanvasRE *re; g_return_if_fail (object != NULL); g_return_if_fail (EEL_IS_CANVAS_RE (object)); re = EEL_CANVAS_RE (object); switch (param_id) { case PROP_X1: g_value_set_double (value, re->x1); break; case PROP_Y1: g_value_set_double (value, re->y1); break; case PROP_X2: g_value_set_double (value, re->x2); break; case PROP_Y2: g_value_set_double (value, re->y2); break; case PROP_FILL_COLOR_GDK: get_color_value (re, re->fill_pixel, value); break; case PROP_OUTLINE_COLOR_GDK: get_color_value (re, re->outline_pixel, value); break; case PROP_FILL_COLOR_RGBA: g_value_set_uint (value, re->fill_color); break; case PROP_OUTLINE_COLOR_RGBA: g_value_set_uint (value, re->outline_color); break; case PROP_FILL_STIPPLE: g_value_set_object (value, (GObject *) re->fill_stipple); break; case PROP_OUTLINE_STIPPLE: g_value_set_object (value, (GObject *) re->outline_stipple); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); break; } } static void set_colors_and_stipples (EelCanvasRE *re) { set_gc_foreground (re->fill_gc, re->fill_pixel); set_gc_foreground (re->outline_gc, re->outline_pixel); set_stipple (re->fill_gc, &re->fill_stipple, re->fill_stipple, TRUE); set_stipple (re->outline_gc, &re->outline_stipple, re->outline_stipple, TRUE); set_outline_gc_width (re); } static void eel_canvas_re_update_shared (EelCanvasItem *item, double i2w_dx, double i2w_dy, int flags) { EelCanvasRE *re; #ifdef VERBOSE g_print ("eel_canvas_re_update_shared\n"); #endif re = EEL_CANVAS_RE (item); if (re_parent_class->update) (* re_parent_class->update) (item, i2w_dx, i2w_dy, flags); set_colors_and_stipples (re); #ifdef OLD_XFORM recalc_bounds (re); #endif } static void eel_canvas_re_realize (EelCanvasItem *item) { EelCanvasRE *re; #ifdef VERBOSE g_print ("eel_canvas_re_realize\n"); #endif re = EEL_CANVAS_RE (item); if (re_parent_class->realize) (* re_parent_class->realize) (item); re->fill_gc = gdk_gc_new (gtk_layout_get_bin_window (&item->canvas->layout)); re->fill_pixel = eel_canvas_get_color_pixel (item->canvas, re->fill_color); re->outline_gc = gdk_gc_new (gtk_layout_get_bin_window (&item->canvas->layout)); re->outline_pixel = eel_canvas_get_color_pixel (item->canvas, re->outline_color); set_colors_and_stipples (re); #ifdef OLD_XFORM (* EEL_CANVAS_ITEM_CLASS (item->object.klass)->update) (item, NULL, NULL, 0); #endif } static void eel_canvas_re_unrealize (EelCanvasItem *item) { EelCanvasRE *re; re = EEL_CANVAS_RE (item); g_object_unref (re->fill_gc); re->fill_gc = NULL; g_object_unref (re->outline_gc); re->outline_gc = NULL; if (re_parent_class->unrealize) (* re_parent_class->unrealize) (item); } static void eel_canvas_re_translate (EelCanvasItem *item, double dx, double dy) { EelCanvasRE *re; #ifdef VERBOSE g_print ("eel_canvas_re_translate\n"); #endif re = EEL_CANVAS_RE (item); re->x1 += dx; re->y1 += dy; re->x2 += dx; re->y2 += dy; } static void eel_canvas_re_bounds (EelCanvasItem *item, double *x1, double *y1, double *x2, double *y2) { EelCanvasRE *re; double hwidth; #ifdef VERBOSE g_print ("eel_canvas_re_bounds\n"); #endif re = EEL_CANVAS_RE (item); if (re->width_pixels) hwidth = (re->width / item->canvas->pixels_per_unit) / 2.0; else hwidth = re->width / 2.0; *x1 = re->x1 - hwidth; *y1 = re->y1 - hwidth; *x2 = re->x2 + hwidth; *y2 = re->y2 + hwidth; } /* Rectangle item */ static void eel_canvas_rect_class_init (EelCanvasRectClass *klass); static void eel_canvas_rect_init (EelCanvasRect *rect); static void eel_canvas_rect_finalize (GObject *object); static void eel_canvas_rect_realize (EelCanvasItem *item); static void eel_canvas_rect_draw (EelCanvasItem *item, GdkDrawable *drawable, GdkEventExpose *expose); static double eel_canvas_rect_point (EelCanvasItem *item, double x, double y, int cx, int cy, EelCanvasItem **actual_item); struct _EelCanvasRectPrivate { Rect last_update_rect; Rect last_outline_update_rect; int last_outline_update_width; #ifdef HAVE_RENDER gboolean use_render; XRenderPictFormat *format; #endif }; GType eel_canvas_rect_get_type (void) { static GType rect_type = 0; if (!rect_type) { GTypeInfo rect_info = { sizeof (EelCanvasRectClass), (GBaseInitFunc) NULL, (GBaseFinalizeFunc) NULL, (GClassInitFunc) eel_canvas_rect_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof (EelCanvasRect), 0, /* n_preallocs */ (GInstanceInitFunc) eel_canvas_rect_init }; rect_type = g_type_register_static (eel_canvas_re_get_type (), "EelCanvasRect", &rect_info, 0); } return rect_type; } static void eel_canvas_rect_class_init (EelCanvasRectClass *klass) { EelCanvasItemClass *item_class; rect_parent_class = g_type_class_peek_parent (klass); item_class = (EelCanvasItemClass *) klass; item_class->draw = eel_canvas_rect_draw; item_class->point = eel_canvas_rect_point; item_class->update = eel_canvas_rect_update; item_class->realize = eel_canvas_rect_realize; G_OBJECT_CLASS (klass)->finalize = eel_canvas_rect_finalize; } static void eel_canvas_rect_init (EelCanvasRect *rect) { rect->priv = g_new0 (EelCanvasRectPrivate, 1); } static void eel_canvas_rect_finalize (GObject *object) { EelCanvasRect *rect = EEL_CANVAS_RECT (object); if (rect->priv) { g_free (rect->priv); } G_OBJECT_CLASS (rect_parent_class)->finalize (object); } static void eel_canvas_rect_realize (EelCanvasItem *item) { #ifdef HAVE_RENDER EelCanvasRectPrivate *priv; int event_base, error_base; Display *dpy; priv = EEL_CANVAS_RECT (item)->priv; dpy = gdk_x11_drawable_get_xdisplay (gtk_widget_get_window (GTK_WIDGET (item->canvas))); priv->use_render = XRenderQueryExtension (dpy, &event_base, &error_base); if (priv->use_render) { GdkVisual *gdk_visual; Visual *visual; gdk_visual = gtk_widget_get_visual (GTK_WIDGET (item->canvas)); visual = gdk_x11_visual_get_xvisual (gdk_visual); priv->format = XRenderFindVisualFormat (dpy, visual); } #endif if (EEL_CANVAS_ITEM_CLASS (rect_parent_class)->realize) { (* EEL_CANVAS_ITEM_CLASS (rect_parent_class)->realize) (item); } } static void render_rect_alpha (EelCanvasRect *rect, GdkDrawable *drawable, int x, int y, int width, int height, guint32 rgba) { GdkPixbuf *pixbuf; guchar *data; int rowstride, i; guchar r, g, b, a; EelCanvasRectPrivate *priv; if (width <= 0 || height <= 0 ) { return; } priv = rect->priv; r = (rgba >> 24) & 0xff; g = (rgba >> 16) & 0xff; b = (rgba >> 8) & 0xff; a = (rgba >> 0) & 0xff; #ifdef HAVE_RENDER /* Every visual is not guaranteed to have a matching * XRenderPictFormat. So make sure that format is not null before * trying to render using Xrender calls. */ if (priv->use_render && (priv->format != NULL)) { GdkDrawable *real_drawable; int x_offset, y_offset; Display *dpy; Picture pict; XRenderPictureAttributes attributes; XRenderColor color; gdk_window_get_internal_paint_info (drawable, &real_drawable, &x_offset, &y_offset); dpy = gdk_x11_drawable_get_xdisplay (real_drawable); pict = XRenderCreatePicture (dpy, gdk_x11_drawable_get_xid (real_drawable), priv->format, 0, &attributes); /* Convert to premultiplied alpha: */ r = r * a / 255; g = g * a / 255; b = b * a / 255; color.red = (r << 8) + r; color.green = (g << 8) + g; color.blue = (b << 8) + b; color.alpha = (a << 8) + a; XRenderFillRectangle (dpy, PictOpOver, pict, &color, x - x_offset, y - y_offset, width, height); XRenderFreePicture (dpy, pict); return; } #endif pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, width, height); data = gdk_pixbuf_get_pixels (pixbuf); rowstride = gdk_pixbuf_get_rowstride (pixbuf); r = (rgba >> 24) & 0xff; g = (rgba >> 16) & 0xff; b = (rgba >> 8) & 0xff; a = (rgba >> 0) & 0xff; for (i = 0; i < width*4; ) { data[i++] = r; data[i++] = g; data[i++] = b; data[i++] = a; } for (i = 1; i < height; i++) { memcpy (data + i*rowstride, data, width*4); } gdk_draw_pixbuf (drawable, NULL, pixbuf, 0, 0, x, y, width, height, GDK_RGB_DITHER_NONE, 0, 0); g_object_unref (pixbuf); } static void eel_canvas_rect_draw (EelCanvasItem *item, GdkDrawable *drawable, GdkEventExpose *expose) { EelCanvasRE *re; double x1, y1, x2, y2; int cx1, cy1, cx2, cy2; double i2w_dx, i2w_dy; re = EEL_CANVAS_RE (item); /* Get canvas pixel coordinates */ i2w_dx = 0.0; i2w_dy = 0.0; eel_canvas_item_i2w (item, &i2w_dx, &i2w_dy); x1 = re->x1 + i2w_dx; y1 = re->y1 + i2w_dy; x2 = re->x2 + i2w_dx; y2 = re->y2 + i2w_dy; eel_canvas_w2c (item->canvas, x1, y1, &cx1, &cy1); eel_canvas_w2c (item->canvas, x2, y2, &cx2, &cy2); if (re->fill_set) { if ((re->fill_color & 0xff) != 255) { GdkRectangle *rectangles; gint i, n_rectangles; GdkRectangle draw_rect; GdkRectangle part; draw_rect.x = cx1; draw_rect.y = cy1; draw_rect.width = cx2 - cx1 + 1; draw_rect.height = cy2 - cy1 + 1; /* For alpha mode, only render the parts of the region that are actually exposed */ gdk_region_get_rectangles (expose->region, &rectangles, &n_rectangles); for (i = 0; i < n_rectangles; i++) { if (gdk_rectangle_intersect (&rectangles[i], &draw_rect, &part)) { render_rect_alpha (EEL_CANVAS_RECT (item), drawable, part.x, part.y, part.width, part.height, re->fill_color); } } g_free (rectangles); } else { if (re->fill_stipple) eel_canvas_set_stipple_origin (item->canvas, re->fill_gc); gdk_draw_rectangle (drawable, re->fill_gc, TRUE, cx1, cy1, cx2 - cx1 + 1, cy2 - cy1 + 1); } } if (re->outline_set) { if (re->outline_stipple) eel_canvas_set_stipple_origin (item->canvas, re->outline_gc); gdk_draw_rectangle (drawable, re->outline_gc, FALSE, cx1, cy1, cx2 - cx1, cy2 - cy1); } } static double eel_canvas_rect_point (EelCanvasItem *item, double x, double y, int cx, int cy, EelCanvasItem **actual_item) { EelCanvasRE *re; double x1, y1, x2, y2; double hwidth; double dx, dy; double tmp; #ifdef VERBOSE g_print ("eel_canvas_rect_point\n"); #endif re = EEL_CANVAS_RE (item); *actual_item = item; /* Find the bounds for the rectangle plus its outline width */ x1 = re->x1; y1 = re->y1; x2 = re->x2; y2 = re->y2; if (re->outline_set) { if (re->width_pixels) hwidth = (re->width / item->canvas->pixels_per_unit) / 2.0; else hwidth = re->width / 2.0; x1 -= hwidth; y1 -= hwidth; x2 += hwidth; y2 += hwidth; } else hwidth = 0.0; /* Is point inside rectangle (which can be hollow if it has no fill set)? */ if ((x >= x1) && (y >= y1) && (x <= x2) && (y <= y2)) { if (re->fill_set || !re->outline_set) return 0.0; dx = x - x1; tmp = x2 - x; if (tmp < dx) dx = tmp; dy = y - y1; tmp = y2 - y; if (tmp < dy) dy = tmp; if (dy < dx) dx = dy; dx -= 2.0 * hwidth; if (dx < 0.0) return 0.0; else return dx; } /* Point is outside rectangle */ if (x < x1) dx = x1 - x; else if (x > x2) dx = x - x2; else dx = 0.0; if (y < y1) dy = y1 - y; else if (y > y2) dy = y - y2; else dy = 0.0; return sqrt (dx * dx + dy * dy); } static void request_redraw_borders (EelCanvas *canvas, Rect *update_rect, int width) { eel_canvas_request_redraw (canvas, update_rect->x0, update_rect->y0, update_rect->x1, update_rect->y0 + width); eel_canvas_request_redraw (canvas, update_rect->x0, update_rect->y1-width, update_rect->x1, update_rect->y1); eel_canvas_request_redraw (canvas, update_rect->x0, update_rect->y0, update_rect->x0+width, update_rect->y1); eel_canvas_request_redraw (canvas, update_rect->x1-width, update_rect->y0, update_rect->x1, update_rect->y1); } static void eel_canvas_rect_update (EelCanvasItem *item, double i2w_dx, double i2w_dy, gint flags) { EelCanvasRE *re; double x1, y1, x2, y2; int cx1, cy1, cx2, cy2; int repaint_rects_count, i; int width_pixels; int width_lt, width_rb; Rect update_rect, repaint_rects[4]; EelCanvasRectPrivate *priv; eel_canvas_re_update_shared (item, i2w_dx, i2w_dy, flags); re = EEL_CANVAS_RE (item); priv = EEL_CANVAS_RECT (item)->priv; x1 = re->x1 + i2w_dx; y1 = re->y1 + i2w_dy; x2 = re->x2 + i2w_dx; y2 = re->y2 + i2w_dy; eel_canvas_w2c (item->canvas, x1, y1, &cx1, &cy1); eel_canvas_w2c (item->canvas, x2, y2, &cx2, &cy2); update_rect = make_rect (cx1, cy1, cx2+1, cy2+1); #if 0 eel_canvas_request_redraw (item->canvas, update_rect.x0, update_rect.y0, update_rect.x1, update_rect.y1); eel_canvas_request_redraw (item->canvas, priv->last_update_rect.x0, priv->last_update_rect.y0, priv->last_update_rect.x1, priv->last_update_rect.y1); #else diff_rects (update_rect, priv->last_update_rect, &repaint_rects_count, repaint_rects); for (i = 0; i < repaint_rects_count; i++) { eel_canvas_request_redraw (item->canvas, repaint_rects[i].x0, repaint_rects[i].y0, repaint_rects[i].x1, repaint_rects[i].y1); } #endif priv->last_update_rect = update_rect; if (re->outline_set) { /* Outline and bounding box */ if (re->width_pixels) width_pixels = (int) re->width; else width_pixels = (int) floor (re->width * re->item.canvas->pixels_per_unit + 0.5); width_lt = width_pixels / 2; width_rb = (width_pixels + 1) / 2; cx1 -= width_lt; cy1 -= width_lt; cx2 += width_rb; cy2 += width_rb; update_rect = make_rect (cx1, cy1, cx2, cy2); request_redraw_borders (item->canvas, &update_rect, (width_lt + width_rb)); request_redraw_borders (item->canvas, &priv->last_outline_update_rect, priv->last_outline_update_width); priv->last_outline_update_rect = update_rect; priv->last_outline_update_width = width_lt + width_rb; item->x1 = cx1; item->y1 = cy1; item->x2 = cx2+1; item->y2 = cy2+1; } else { item->x1 = cx1; item->y1 = cy1; item->x2 = cx2+1; item->y2 = cy2+1; } } /* Ellipse item */ static void eel_canvas_ellipse_class_init (EelCanvasEllipseClass *klass); static void eel_canvas_ellipse_draw (EelCanvasItem *item, GdkDrawable *drawable, GdkEventExpose *expose); static double eel_canvas_ellipse_point (EelCanvasItem *item, double x, double y, int cx, int cy, EelCanvasItem **actual_item); GType eel_canvas_ellipse_get_type (void) { static GType ellipse_type = 0; if (!ellipse_type) { GTypeInfo ellipse_info = { sizeof (EelCanvasEllipseClass), (GBaseInitFunc) NULL, (GBaseFinalizeFunc) NULL, (GClassInitFunc) eel_canvas_ellipse_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof (EelCanvasEllipse), 0, /* n_preallocs */ (GInstanceInitFunc) NULL }; ellipse_type = g_type_register_static (eel_canvas_re_get_type (), "EelCanvasEllipse", &ellipse_info, 0); } return ellipse_type; } static void eel_canvas_ellipse_class_init (EelCanvasEllipseClass *klass) { EelCanvasItemClass *item_class; item_class = (EelCanvasItemClass *) klass; item_class->draw = eel_canvas_ellipse_draw; item_class->point = eel_canvas_ellipse_point; item_class->update = eel_canvas_ellipse_update; } static void eel_canvas_ellipse_draw (EelCanvasItem *item, GdkDrawable *drawable, GdkEventExpose *expose) { EelCanvasRE *re; int x1, y1, x2, y2; double i2w_dx, i2w_dy; re = EEL_CANVAS_RE (item); /* Get canvas pixel coordinates */ i2w_dx = 0.0; i2w_dy = 0.0; eel_canvas_item_i2w (item, &i2w_dx, &i2w_dy); eel_canvas_w2c (item->canvas, re->x1 + i2w_dx, re->y1 + i2w_dy, &x1, &y1); eel_canvas_w2c (item->canvas, re->x2 + i2w_dx, re->y2 + i2w_dy, &x2, &y2); if (re->fill_set) { if (re->fill_stipple) eel_canvas_set_stipple_origin (item->canvas, re->fill_gc); gdk_draw_arc (drawable, re->fill_gc, TRUE, x1, y1, x2 - x1, y2 - y1, 0 * 64, 360 * 64); } if (re->outline_set) { if (re->outline_stipple) eel_canvas_set_stipple_origin (item->canvas, re->outline_gc); gdk_draw_arc (drawable, re->outline_gc, FALSE, x1, y1, x2 - x1, y2 - y1, 0 * 64, 360 * 64); } } static double eel_canvas_ellipse_point (EelCanvasItem *item, double x, double y, int cx, int cy, EelCanvasItem **actual_item) { EelCanvasRE *re; double dx, dy; double scaled_dist; double outline_dist; double center_dist; double width; double a, b; double diamx, diamy; re = EEL_CANVAS_RE (item); *actual_item = item; if (re->outline_set) { if (re->width_pixels) width = re->width / item->canvas->pixels_per_unit; else width = re->width; } else width = 0.0; /* Compute the distance between the center of the ellipse and the point, with the ellipse * considered as being scaled to a circle. */ dx = x - (re->x1 + re->x2) / 2.0; dy = y - (re->y1 + re->y2) / 2.0; center_dist = sqrt (dx * dx + dy * dy); a = dx / ((re->x2 + width - re->x1) / 2.0); b = dy / ((re->y2 + width - re->y1) / 2.0); scaled_dist = sqrt (a * a + b * b); /* If the scaled distance is greater than 1, then we are outside. Compute the distance from * the point to the edge of the circle, then scale back to the original un-scaled coordinate * system. */ if (scaled_dist > 1.0) return (center_dist / scaled_dist) * (scaled_dist - 1.0); /* We are inside the outer edge of the ellipse. If it is filled, then we are "inside". * Otherwise, do the same computation as above, but also check whether we are inside the * outline. */ if (re->fill_set) return 0.0; if (scaled_dist > EEL_CANVAS_EPSILON) outline_dist = (center_dist / scaled_dist) * (1.0 - scaled_dist) - width; else { /* Handle very small distance */ diamx = re->x2 - re->x1; diamy = re->y2 - re->y1; if (diamx < diamy) outline_dist = (diamx - width) / 2.0; else outline_dist = (diamy - width) / 2.0; } if (outline_dist < 0.0) return 0.0; return outline_dist; } static void eel_canvas_ellipse_update (EelCanvasItem *item, double i2w_dx, double i2w_dy, gint flags) { EelCanvasRE *re; double x0, y0, x1, y1; #ifdef VERBOSE g_print ("eel_canvas_sllipse_update item %x\n", item); #endif eel_canvas_re_update_shared (item, i2w_dx, i2w_dy, flags); re = EEL_CANVAS_RE (item); get_bounds (re, &x0, &y0, &x1, &y1); eel_canvas_update_bbox (item, x0, y0, x1, y1); } static int rect_empty (const Rect *src) { return (src->x1 <= src->x0 || src->y1 <= src->y0); } static Rect make_rect (int x0, int y0, int x1, int y1) { Rect r; r.x0 = x0; r.y0 = y0; r.x1 = x1; r.y1 = y1; return r; } static gboolean rects_intersect (Rect r1, Rect r2) { if (r1.x0 >= r2.x1) { return FALSE; } if (r2.x0 >= r1.x1) { return FALSE; } if (r1.y0 >= r2.y1) { return FALSE; } if (r2.y0 >= r1.y1) { return FALSE; } return TRUE; } static void diff_rects_guts (Rect ra, Rect rb, int *count, Rect result[4]) { if (ra.x0 < rb.x0) { result[(*count)++] = make_rect (ra.x0, ra.y0, rb.x0, ra.y1); } if (ra.y0 < rb.y0) { result[(*count)++] = make_rect (ra.x0, ra.y0, ra.x1, rb.y0); } if (ra.x1 < rb.x1) { result[(*count)++] = make_rect (ra.x1, rb.y0, rb.x1, rb.y1); } if (ra.y1 < rb.y1) { result[(*count)++] = make_rect (rb.x0, ra.y1, rb.x1, rb.y1); } } static void diff_rects (Rect r1, Rect r2, int *count, Rect result[4]) { g_assert (count != NULL); g_assert (result != NULL); *count = 0; if (rects_intersect (r1, r2)) { diff_rects_guts (r1, r2, count, result); diff_rects_guts (r2, r1, count, result); } else { if (!rect_empty (&r1)) { result[(*count)++] = r1; } if (!rect_empty (&r2)) { result[(*count)++] = r2; } } }