/* GIMP - The GNU Image Manipulation Program * Copyright (C) 1995 Spencer Kimball and Peter Mattis * * gimpcanvasboundary.c * Copyright (C) 2010 Michael Natterer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "config.h" #include #include #include "libgimpbase/gimpbase.h" #include "libgimpmath/gimpmath.h" #include "display-types.h" #include "base/boundary.h" #include "core/gimpparamspecs.h" #include "widgets/gimpcairo.h" #include "gimpcanvasboundary.h" #include "gimpdisplayshell.h" #include "gimpdisplayshell-transform.h" enum { PROP_0, PROP_SEGS, PROP_TRANSFORM, PROP_OFFSET_X, PROP_OFFSET_Y }; typedef struct _GimpCanvasBoundaryPrivate GimpCanvasBoundaryPrivate; struct _GimpCanvasBoundaryPrivate { BoundSeg *segs; gint n_segs; GimpMatrix3 *transform; gdouble offset_x; gdouble offset_y; }; #define GET_PRIVATE(boundary) \ G_TYPE_INSTANCE_GET_PRIVATE (boundary, \ GIMP_TYPE_CANVAS_BOUNDARY, \ GimpCanvasBoundaryPrivate) /* local function prototypes */ static void gimp_canvas_boundary_finalize (GObject *object); static void gimp_canvas_boundary_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); static void gimp_canvas_boundary_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec); static void gimp_canvas_boundary_draw (GimpCanvasItem *item, GimpDisplayShell *shell, cairo_t *cr); static cairo_region_t * gimp_canvas_boundary_get_extents (GimpCanvasItem *item, GimpDisplayShell *shell); G_DEFINE_TYPE (GimpCanvasBoundary, gimp_canvas_boundary, GIMP_TYPE_CANVAS_ITEM) #define parent_class gimp_canvas_boundary_parent_class static void gimp_canvas_boundary_class_init (GimpCanvasBoundaryClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GimpCanvasItemClass *item_class = GIMP_CANVAS_ITEM_CLASS (klass); object_class->finalize = gimp_canvas_boundary_finalize; object_class->set_property = gimp_canvas_boundary_set_property; object_class->get_property = gimp_canvas_boundary_get_property; item_class->draw = gimp_canvas_boundary_draw; item_class->get_extents = gimp_canvas_boundary_get_extents; g_object_class_install_property (object_class, PROP_SEGS, gimp_param_spec_array ("segs", NULL, NULL, GIMP_PARAM_READWRITE)); g_object_class_install_property (object_class, PROP_TRANSFORM, g_param_spec_pointer ("transform", NULL, NULL, GIMP_PARAM_READWRITE)); g_object_class_install_property (object_class, PROP_OFFSET_X, g_param_spec_double ("offset-x", NULL, NULL, -GIMP_MAX_IMAGE_SIZE, GIMP_MAX_IMAGE_SIZE, 0, GIMP_PARAM_READWRITE)); g_object_class_install_property (object_class, PROP_OFFSET_Y, g_param_spec_double ("offset-y", NULL, NULL, -GIMP_MAX_IMAGE_SIZE, GIMP_MAX_IMAGE_SIZE, 0, GIMP_PARAM_READWRITE)); g_type_class_add_private (klass, sizeof (GimpCanvasBoundaryPrivate)); } static void gimp_canvas_boundary_init (GimpCanvasBoundary *boundary) { gimp_canvas_item_set_line_cap (GIMP_CANVAS_ITEM (boundary), CAIRO_LINE_CAP_SQUARE); } static void gimp_canvas_boundary_finalize (GObject *object) { GimpCanvasBoundaryPrivate *private = GET_PRIVATE (object); if (private->segs) { g_free (private->segs); private->segs = NULL; private->n_segs = 0; } if (private->transform) { g_free (private->transform); private->transform = NULL; } G_OBJECT_CLASS (parent_class)->finalize (object); } static void gimp_canvas_boundary_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { GimpCanvasBoundaryPrivate *private = GET_PRIVATE (object); switch (property_id) { case PROP_SEGS: break; case PROP_TRANSFORM: { GimpMatrix3 *transform = g_value_get_pointer (value); if (private->transform) g_free (private->transform); if (transform) private->transform = g_memdup (transform, sizeof (GimpMatrix3)); else private->transform = NULL; } break; case PROP_OFFSET_X: private->offset_x = g_value_get_double (value); break; case PROP_OFFSET_Y: private->offset_y = g_value_get_double (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void gimp_canvas_boundary_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { GimpCanvasBoundaryPrivate *private = GET_PRIVATE (object); switch (property_id) { case PROP_SEGS: break; case PROP_TRANSFORM: g_value_set_pointer (value, private->transform); break; case PROP_OFFSET_X: g_value_set_double (value, private->offset_x); break; case PROP_OFFSET_Y: g_value_set_double (value, private->offset_y); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void gimp_canvas_boundary_transform (GimpCanvasItem *item, GimpDisplayShell *shell, GimpSegment *segs) { GimpCanvasBoundaryPrivate *private = GET_PRIVATE (item); gint i; if (private->transform) { for (i = 0; i < private->n_segs; i++) { gdouble tx, ty; gimp_matrix3_transform_point (private->transform, private->segs[i].x1, private->segs[i].y1, &tx, &ty); gimp_display_shell_transform_xy (shell, tx + private->offset_x, ty + private->offset_y, &segs[i].x1, &segs[i].y1); gimp_matrix3_transform_point (private->transform, private->segs[i].x2, private->segs[i].y2, &tx, &ty); gimp_display_shell_transform_xy (shell, tx + private->offset_x, ty + private->offset_y, &segs[i].x2, &segs[i].y2); } } else { gimp_display_shell_transform_segments (shell, private->segs, segs, private->n_segs, private->offset_x, private->offset_y); for (i = 0; i < private->n_segs; i++) { /* If this segment is a closing segment && the segments lie inside * the region, OR if this is an opening segment and the segments * lie outside the region... * we need to transform it by one display pixel */ if (! private->segs[i].open) { /* If it is vertical */ if (segs[i].x1 == segs[i].x2) { segs[i].x1 -= 1; segs[i].x2 -= 1; } else { segs[i].y1 -= 1; segs[i].y2 -= 1; } } } } } static void gimp_canvas_boundary_draw (GimpCanvasItem *item, GimpDisplayShell *shell, cairo_t *cr) { GimpCanvasBoundaryPrivate *private = GET_PRIVATE (item); GimpSegment *segs; segs = g_new0 (GimpSegment, private->n_segs); gimp_canvas_boundary_transform (item, shell, segs); gimp_cairo_add_segments (cr, segs, private->n_segs); _gimp_canvas_item_stroke (item, cr); g_free (segs); } static cairo_region_t * gimp_canvas_boundary_get_extents (GimpCanvasItem *item, GimpDisplayShell *shell) { GimpCanvasBoundaryPrivate *private = GET_PRIVATE (item); cairo_rectangle_int_t rectangle; GimpSegment *segs; gint x1, y1, x2, y2; gint i; segs = g_new0 (GimpSegment, private->n_segs); gimp_canvas_boundary_transform (item, shell, segs); x1 = MIN (segs[0].x1, segs[0].x2); y1 = MIN (segs[0].y1, segs[0].y2); x2 = MAX (segs[0].x1, segs[0].x2); y2 = MAX (segs[0].y1, segs[0].y2); for (i = 1; i < private->n_segs; i++) { gint x3 = MIN (segs[i].x1, segs[i].x2); gint y3 = MIN (segs[i].y1, segs[i].y2); gint x4 = MAX (segs[i].x1, segs[i].x2); gint y4 = MAX (segs[i].y1, segs[i].y2); x1 = MIN (x1, x3); y1 = MIN (y1, y3); x2 = MAX (x2, x4); y2 = MAX (y2, y4); } g_free (segs); rectangle.x = x1 - 2; rectangle.y = y1 - 2; rectangle.width = x2 - x1 + 4; rectangle.height = y2 - y1 + 4; return cairo_region_create_rectangle (&rectangle); } GimpCanvasItem * gimp_canvas_boundary_new (GimpDisplayShell *shell, const BoundSeg *segs, gint n_segs, GimpMatrix3 *transform, gdouble offset_x, gdouble offset_y) { GimpCanvasItem *item; GimpCanvasBoundaryPrivate *private; g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), NULL); item = g_object_new (GIMP_TYPE_CANVAS_BOUNDARY, "shell", shell, "transform", transform, "offset-x", offset_x, "offset-y", offset_y, NULL); private = GET_PRIVATE (item); /* puke */ private->segs = g_memdup (segs, n_segs * sizeof (BoundSeg)); private->n_segs = n_segs; return item; }