From 5c1676dfe6d2f3c837a5e074117b45613fd29a72 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 20:30:19 +0200 Subject: Adding upstream version 2.10.34. Signed-off-by: Daniel Baumann --- app/display/gimpcanvastransformguides.c | 683 ++++++++++++++++++++++++++++++++ 1 file changed, 683 insertions(+) create mode 100644 app/display/gimpcanvastransformguides.c (limited to 'app/display/gimpcanvastransformguides.c') diff --git a/app/display/gimpcanvastransformguides.c b/app/display/gimpcanvastransformguides.c new file mode 100644 index 0000000..ba6c4cb --- /dev/null +++ b/app/display/gimpcanvastransformguides.c @@ -0,0 +1,683 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpcanvastransformguides.c + * Copyright (C) 2011 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 "core/gimp-transform-utils.h" +#include "core/gimp-utils.h" + +#include "gimpcanvastransformguides.h" +#include "gimpdisplayshell.h" + + +#define SQRT5 2.236067977 + + +enum +{ + PROP_0, + PROP_TRANSFORM, + PROP_X1, + PROP_Y1, + PROP_X2, + PROP_Y2, + PROP_TYPE, + PROP_N_GUIDES, + PROP_CLIP +}; + + +typedef struct _GimpCanvasTransformGuidesPrivate GimpCanvasTransformGuidesPrivate; + +struct _GimpCanvasTransformGuidesPrivate +{ + GimpMatrix3 transform; + gdouble x1, y1; + gdouble x2, y2; + GimpGuidesType type; + gint n_guides; + gboolean clip; +}; + +#define GET_PRIVATE(transform) \ + ((GimpCanvasTransformGuidesPrivate *) gimp_canvas_transform_guides_get_instance_private ((GimpCanvasTransformGuides *) (transform))) + + +/* local function prototypes */ + +static void gimp_canvas_transform_guides_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_canvas_transform_guides_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); +static void gimp_canvas_transform_guides_draw (GimpCanvasItem *item, + cairo_t *cr); +static cairo_region_t * gimp_canvas_transform_guides_get_extents (GimpCanvasItem *item); + + +G_DEFINE_TYPE_WITH_PRIVATE (GimpCanvasTransformGuides, + gimp_canvas_transform_guides, GIMP_TYPE_CANVAS_ITEM) + +#define parent_class gimp_canvas_transform_guides_parent_class + + +static void +gimp_canvas_transform_guides_class_init (GimpCanvasTransformGuidesClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpCanvasItemClass *item_class = GIMP_CANVAS_ITEM_CLASS (klass); + + object_class->set_property = gimp_canvas_transform_guides_set_property; + object_class->get_property = gimp_canvas_transform_guides_get_property; + + item_class->draw = gimp_canvas_transform_guides_draw; + item_class->get_extents = gimp_canvas_transform_guides_get_extents; + + g_object_class_install_property (object_class, PROP_TRANSFORM, + gimp_param_spec_matrix3 ("transform", + NULL, NULL, + NULL, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_X1, + g_param_spec_double ("x1", + NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, + 0.0, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_Y1, + g_param_spec_double ("y1", + NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, + 0.0, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_X2, + g_param_spec_double ("x2", + NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, + 0.0, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_Y2, + g_param_spec_double ("y2", + NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, + 0.0, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_TYPE, + g_param_spec_enum ("type", NULL, NULL, + GIMP_TYPE_GUIDES_TYPE, + GIMP_GUIDES_NONE, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_N_GUIDES, + g_param_spec_int ("n-guides", NULL, NULL, + 1, 128, 4, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_CLIP, + g_param_spec_boolean ("clip", NULL, NULL, + FALSE, + GIMP_PARAM_READWRITE)); +} + +static void +gimp_canvas_transform_guides_init (GimpCanvasTransformGuides *transform) +{ +} + +static void +gimp_canvas_transform_guides_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpCanvasTransformGuidesPrivate *private = GET_PRIVATE (object); + + switch (property_id) + { + case PROP_TRANSFORM: + { + GimpMatrix3 *transform = g_value_get_boxed (value); + + if (transform) + private->transform = *transform; + else + gimp_matrix3_identity (&private->transform); + } + break; + + case PROP_X1: + private->x1 = g_value_get_double (value); + break; + + case PROP_Y1: + private->y1 = g_value_get_double (value); + break; + + case PROP_X2: + private->x2 = g_value_get_double (value); + break; + + case PROP_Y2: + private->y2 = g_value_get_double (value); + break; + + case PROP_TYPE: + private->type = g_value_get_enum (value); + break; + + case PROP_N_GUIDES: + private->n_guides = g_value_get_int (value); + break; + + case PROP_CLIP: + private->clip = g_value_get_boolean (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_canvas_transform_guides_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpCanvasTransformGuidesPrivate *private = GET_PRIVATE (object); + + switch (property_id) + { + case PROP_TRANSFORM: + g_value_set_boxed (value, &private->transform); + break; + + case PROP_X1: + g_value_set_double (value, private->x1); + break; + + case PROP_Y1: + g_value_set_double (value, private->y1); + break; + + case PROP_X2: + g_value_set_double (value, private->x2); + break; + + case PROP_Y2: + g_value_set_double (value, private->y2); + break; + + case PROP_TYPE: + g_value_set_enum (value, private->type); + break; + + case PROP_N_GUIDES: + g_value_set_int (value, private->n_guides); + break; + + case PROP_CLIP: + g_value_set_boolean (value, private->clip); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static gboolean +gimp_canvas_transform_guides_transform (GimpCanvasItem *item, + GimpVector2 *t_vertices, + gint *n_t_vertices) +{ + GimpCanvasTransformGuidesPrivate *private = GET_PRIVATE (item); + GimpVector2 vertices[4]; + + vertices[0] = (GimpVector2) { private->x1, private->y1 }; + vertices[1] = (GimpVector2) { private->x2, private->y1 }; + vertices[2] = (GimpVector2) { private->x2, private->y2 }; + vertices[3] = (GimpVector2) { private->x1, private->y2 }; + + if (private->clip) + { + gimp_transform_polygon (&private->transform, vertices, 4, TRUE, + t_vertices, n_t_vertices); + + return TRUE; + } + else + { + gint i; + + for (i = 0; i < 4; i++) + { + gimp_matrix3_transform_point (&private->transform, + vertices[i].x, vertices[i].y, + &t_vertices[i].x, &t_vertices[i].y); + } + + *n_t_vertices = 4; + + return gimp_transform_polygon_is_convex (t_vertices[0].x, t_vertices[0].y, + t_vertices[1].x, t_vertices[1].y, + t_vertices[3].x, t_vertices[3].y, + t_vertices[2].x, t_vertices[2].y); + } +} + +static void +draw_line (cairo_t *cr, + GimpCanvasItem *item, + GimpMatrix3 *transform, + gdouble x1, + gdouble y1, + gdouble x2, + gdouble y2) +{ + GimpCanvasTransformGuidesPrivate *private = GET_PRIVATE (item); + GimpVector2 vertices[2]; + GimpVector2 t_vertices[2]; + gint n_t_vertices; + + vertices[0] = (GimpVector2) { x1, y1 }; + vertices[1] = (GimpVector2) { x2, y2 }; + + if (private->clip) + { + gimp_transform_polygon (transform, vertices, 2, FALSE, + t_vertices, &n_t_vertices); + } + else + { + gint i; + + for (i = 0; i < 2; i++) + { + gimp_matrix3_transform_point (transform, + vertices[i].x, vertices[i].y, + &t_vertices[i].x, &t_vertices[i].y); + } + + n_t_vertices = 2; + } + + if (n_t_vertices == 2) + { + gint i; + + for (i = 0; i < 2; i++) + { + GimpVector2 v; + + gimp_canvas_item_transform_xy_f (item, + t_vertices[i].x, t_vertices[i].y, + &v.x, &v.y); + + v.x = floor (v.x) + 0.5; + v.y = floor (v.y) + 0.5; + + if (i == 0) + cairo_move_to (cr, v.x, v.y); + else + cairo_line_to (cr, v.x, v.y); + } + } +} + +static void +draw_hline (cairo_t *cr, + GimpCanvasItem *item, + GimpMatrix3 *transform, + gdouble x1, + gdouble x2, + gdouble y) +{ + draw_line (cr, item, transform, x1, y, x2, y); +} + +static void +draw_vline (cairo_t *cr, + GimpCanvasItem *item, + GimpMatrix3 *transform, + gdouble y1, + gdouble y2, + gdouble x) +{ + draw_line (cr, item, transform, x, y1, x, y2); +} + +static void +gimp_canvas_transform_guides_draw (GimpCanvasItem *item, + cairo_t *cr) +{ + GimpCanvasTransformGuidesPrivate *private = GET_PRIVATE (item); + GimpVector2 t_vertices[5]; + gint n_t_vertices; + gboolean convex; + gint i; + + convex = gimp_canvas_transform_guides_transform (item, + t_vertices, &n_t_vertices); + + if (n_t_vertices < 2) + return; + + for (i = 0; i < n_t_vertices; i++) + { + GimpVector2 v; + + gimp_canvas_item_transform_xy_f (item, t_vertices[i].x, t_vertices[i].y, + &v.x, &v.y); + + v.x = floor (v.x) + 0.5; + v.y = floor (v.y) + 0.5; + + if (i == 0) + cairo_move_to (cr, v.x, v.y); + else + cairo_line_to (cr, v.x, v.y); + } + + cairo_close_path (cr); + + if (! convex || n_t_vertices < 3) + { + _gimp_canvas_item_stroke (item, cr); + return; + } + + switch (private->type) + { + case GIMP_GUIDES_NONE: + break; + + case GIMP_GUIDES_CENTER_LINES: + draw_hline (cr, item, &private->transform, + private->x1, private->x2, (private->y1 + private->y2) / 2); + draw_vline (cr, item, &private->transform, + private->y1, private->y2, (private->x1 + private->x2) / 2); + break; + + case GIMP_GUIDES_THIRDS: + draw_hline (cr, item, &private->transform, + private->x1, private->x2, (2 * private->y1 + private->y2) / 3); + draw_hline (cr, item, &private->transform, + private->x1, private->x2, (private->y1 + 2 * private->y2) / 3); + + draw_vline (cr, item, &private->transform, + private->y1, private->y2, (2 * private->x1 + private->x2) / 3); + draw_vline (cr, item, &private->transform, + private->y1, private->y2, (private->x1 + 2 * private->x2) / 3); + break; + + case GIMP_GUIDES_FIFTHS: + for (i = 0; i < 5; i++) + { + draw_hline (cr, item, &private->transform, + private->x1, private->x2, + private->y1 + i * (private->y2 - private->y1) / 5); + draw_vline (cr, item, &private->transform, + private->y1, private->y2, + private->x1 + i * (private->x2 - private->x1) / 5); + } + break; + + case GIMP_GUIDES_GOLDEN: + draw_hline (cr, item, &private->transform, + private->x1, private->x2, + (2 * private->y1 + (1 + SQRT5) * private->y2) / (3 + SQRT5)); + draw_hline (cr, item, &private->transform, + private->x1, private->x2, + ((1 + SQRT5) * private->y1 + 2 * private->y2) / (3 + SQRT5)); + + draw_vline (cr, item, &private->transform, + private->y1, private->y2, + (2 * private->x1 + (1 + SQRT5) * private->x2) / (3 + SQRT5)); + draw_vline (cr, item, &private->transform, + private->y1, private->y2, + ((1 + SQRT5) * private->x1 + 2 * private->x2) / (3 + SQRT5)); + break; + + /* This code implements the method of diagonals discovered by + * Edwin Westhoff - see http://www.diagonalmethod.info/ + */ + case GIMP_GUIDES_DIAGONALS: + { + /* the side of the largest square that can be + * fitted in whole into the rectangle (x1, y1), (x2, y2) + */ + const gdouble square_side = MIN (private->x2 - private->x1, + private->y2 - private->y1); + + /* diagonal from the top-left edge */ + draw_line (cr, item, &private->transform, + private->x1, private->y1, + private->x1 + square_side, + private->y1 + square_side); + + /* diagonal from the top-right edge */ + draw_line (cr, item, &private->transform, + private->x2, private->y1, + private->x2 - square_side, + private->y1 + square_side); + + /* diagonal from the bottom-left edge */ + draw_line (cr, item, &private->transform, + private->x1, private->y2, + private->x1 + square_side, + private->y2 - square_side); + + /* diagonal from the bottom-right edge */ + draw_line (cr, item, &private->transform, + private->x2, private->y2, + private->x2 - square_side, + private->y2 - square_side); + } + break; + + case GIMP_GUIDES_N_LINES: + case GIMP_GUIDES_SPACING: + { + gint width, height; + gint ngx, ngy; + + width = MAX (1, private->x2 - private->x1); + height = MAX (1, private->y2 - private->y1); + + /* the MIN() in the code below limits the grid to one line + * every 5 image pixels, see bug 772667. + */ + + if (private->type == GIMP_GUIDES_N_LINES) + { + if (width <= height) + { + ngx = private->n_guides; + ngx = MIN (ngx, width / 5); + + ngy = ngx * MAX (1, height / width); + ngy = MIN (ngy, height / 5); + } + else + { + ngy = private->n_guides; + ngy = MIN (ngy, height / 5); + + ngx = ngy * MAX (1, width / height); + ngx = MIN (ngx, width / 5); + } + } + else /* GIMP_GUIDES_SPACING */ + { + gint grid_size = MAX (2, private->n_guides); + + ngx = width / grid_size; + ngx = MIN (ngx, width / 5); + + ngy = height / grid_size; + ngy = MIN (ngy, height / 5); + } + + for (i = 1; i <= ngx; i++) + { + gdouble x = private->x1 + (((gdouble) i) / (ngx + 1) * + (private->x2 - private->x1)); + + draw_line (cr, item, &private->transform, + x, private->y1, + x, private->y2); + } + + for (i = 1; i <= ngy; i++) + { + gdouble y = private->y1 + (((gdouble) i) / (ngy + 1) * + (private->y2 - private->y1)); + + draw_line (cr, item, &private->transform, + private->x1, y, + private->x2, y); + } + } + } + + _gimp_canvas_item_stroke (item, cr); +} + +static cairo_region_t * +gimp_canvas_transform_guides_get_extents (GimpCanvasItem *item) +{ + GimpVector2 t_vertices[5]; + gint n_t_vertices; + GimpVector2 top_left; + GimpVector2 bottom_right; + cairo_rectangle_int_t extents; + gint i; + + gimp_canvas_transform_guides_transform (item, t_vertices, &n_t_vertices); + + if (n_t_vertices < 2) + return cairo_region_create (); + + for (i = 0; i < n_t_vertices; i++) + { + GimpVector2 v; + + gimp_canvas_item_transform_xy_f (item, + t_vertices[i].x, t_vertices[i].y, + &v.x, &v.y); + + if (i == 0) + { + top_left = bottom_right = v; + } + else + { + top_left.x = MIN (top_left.x, v.x); + top_left.y = MIN (top_left.y, v.y); + + bottom_right.x = MAX (bottom_right.x, v.x); + bottom_right.y = MAX (bottom_right.y, v.y); + } + } + + extents.x = (gint) floor (top_left.x - 1.5); + extents.y = (gint) floor (top_left.y - 1.5); + extents.width = (gint) ceil (bottom_right.x + 1.5) - extents.x; + extents.height = (gint) ceil (bottom_right.y + 1.5) - extents.y; + + return cairo_region_create_rectangle (&extents); +} + +GimpCanvasItem * +gimp_canvas_transform_guides_new (GimpDisplayShell *shell, + const GimpMatrix3 *transform, + gdouble x1, + gdouble y1, + gdouble x2, + gdouble y2, + GimpGuidesType type, + gint n_guides, + gboolean clip) +{ + g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), NULL); + + return g_object_new (GIMP_TYPE_CANVAS_TRANSFORM_GUIDES, + "shell", shell, + "transform", transform, + "x1", x1, + "y1", y1, + "x2", x2, + "y2", y2, + "type", type, + "n-guides", n_guides, + "clip", clip, + NULL); +} + +void +gimp_canvas_transform_guides_set (GimpCanvasItem *guides, + const GimpMatrix3 *transform, + gdouble x1, + gdouble y1, + gdouble x2, + gdouble y2, + GimpGuidesType type, + gint n_guides, + gboolean clip) +{ + g_return_if_fail (GIMP_IS_CANVAS_TRANSFORM_GUIDES (guides)); + + gimp_canvas_item_begin_change (guides); + + g_object_set (guides, + "transform", transform, + "x1", x1, + "y1", y1, + "x2", x2, + "y2", y2, + "type", type, + "n-guides", n_guides, + "clip", clip, + NULL); + + gimp_canvas_item_end_change (guides); +} -- cgit v1.2.3