From 9e793cfe87a0aad50be9008656402dbf4b25acf9 Mon Sep 17 00:00:00 2001 From: "mr.fantastic" Date: Sat, 18 Mar 2023 21:29:11 +0300 Subject: [PATCH] Adding on canvas text for equidistance snapping visualization --- app/display/gimpcanvastext.c | 270 ++++++++++++++++++++++++++++++ app/display/gimpcanvastext.h | 58 +++++++ app/display/meson.build | 2 + app/tools/gimpdrawtool.c | 21 +++ app/tools/gimpdrawtool.h | 6 + app/tools/gimpeditselectiontool.c | 102 ++++++++--- 6 files changed, 434 insertions(+), 25 deletions(-) create mode 100644 app/display/gimpcanvastext.c create mode 100644 app/display/gimpcanvastext.h diff --git a/app/display/gimpcanvastext.c b/app/display/gimpcanvastext.c new file mode 100644 index 0000000000..4946830adb --- /dev/null +++ b/app/display/gimpcanvastext.c @@ -0,0 +1,270 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpcanvastext.c + * Copyright (C) 2023 mr.fantastic + * + * 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 "gimpcanvastext.h" +#include "gimpdisplayshell.h" + + +enum +{ + PROP_0, + PROP_X, + PROP_Y, + PROP_FONT_SIZE, + PROP_TEXT +}; + +typedef struct _GimpCanvasTextPrivate GimpCanvasTextPrivate; + +struct _GimpCanvasTextPrivate +{ + gdouble x; + gdouble y; + gdouble font_size; + gchar *text; +}; + +#define GET_PRIVATE(text_item) \ + ((GimpCanvasTextPrivate *) gimp_canvas_text_get_instance_private ((GimpCanvasText *) (text_item))) + + +/* local function prototypes */ + +static void gimp_canvas_text_finalize (GObject *object); +static void gimp_canvas_text_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_canvas_text_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); +static void gimp_canvas_text_draw (GimpCanvasItem *item, + cairo_t *cr); +static cairo_region_t * gimp_canvas_text_get_extents (GimpCanvasItem *item); + + +G_DEFINE_TYPE_WITH_PRIVATE (GimpCanvasText, gimp_canvas_text, + GIMP_TYPE_CANVAS_ITEM) + +#define parent_class gimp_canvas_text_parent_class + + +static void +gimp_canvas_text_class_init (GimpCanvasTextClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GimpCanvasItemClass *item_class = GIMP_CANVAS_ITEM_CLASS (klass); + + object_class->finalize = gimp_canvas_text_finalize; + object_class->set_property = gimp_canvas_text_set_property; + object_class->get_property = gimp_canvas_text_get_property; + + item_class->draw = gimp_canvas_text_draw; + item_class->get_extents = gimp_canvas_text_get_extents; + + g_object_class_install_property (object_class, PROP_X, + g_param_spec_double ("x", NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, 0, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_Y, + g_param_spec_double ("y", NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, 0, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_FONT_SIZE, + g_param_spec_double ("font-size", NULL, NULL, + -GIMP_MAX_IMAGE_SIZE, + GIMP_MAX_IMAGE_SIZE, 0, + GIMP_PARAM_READWRITE)); + + g_object_class_install_property (object_class, PROP_TEXT, + g_param_spec_string ("text", NULL, NULL, + NULL, + GIMP_PARAM_READWRITE)); +} + +static void +gimp_canvas_text_init (GimpCanvasText *text) +{ +} + +static void +gimp_canvas_text_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpCanvasTextPrivate *private = GET_PRIVATE (object); + + switch (property_id) + { + case PROP_X: + private->x = g_value_get_double (value); + break; + case PROP_Y: + private->y = g_value_get_double (value); + break; + case PROP_FONT_SIZE: + private->font_size = g_value_get_double (value); + break; + case PROP_TEXT: + private->text = (gchar *) g_value_dup_string (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_canvas_text_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpCanvasTextPrivate *private = GET_PRIVATE (object); + + switch (property_id) + { + case PROP_X: + g_value_set_double (value, private->x); + break; + case PROP_Y: + g_value_set_double (value, private->y); + break; + case PROP_FONT_SIZE: + g_value_set_double (value, private->font_size); + break; + case PROP_TEXT: + g_value_set_string (value, private->text); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_canvas_text_transform (GimpCanvasItem *item, + gdouble *x1, + gdouble *y1, + gdouble *x2, + gdouble *y2) +{ + GimpCanvasTextPrivate *private = GET_PRIVATE (item); + cairo_text_extents_t extents; + cairo_surface_t *dummy_surface; + cairo_t *cr; + + gimp_canvas_item_transform_xy_f (item, + private->x, private->y, + x1, y1); + + dummy_surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, 0, 0); + cr = cairo_create (dummy_surface); + + cairo_set_font_size (cr, private->font_size); + cairo_text_extents (cr, private->text, &extents); + + cairo_surface_destroy (dummy_surface); + cairo_destroy (cr); + + *x1 = floor (*x1) + 0.5; + *y1 = floor (*y1) + 0.5; + *x2 = extents.width; + *y2 = extents.height; +} + +static void +gimp_canvas_text_draw (GimpCanvasItem *item, + cairo_t *cr) +{ + gdouble x, y, ignore_x2, ignore_y2; + + GimpCanvasTextPrivate *private = GET_PRIVATE (item); + + gimp_canvas_text_transform (item, &x, &y, &ignore_x2, &ignore_y2); + + cairo_set_font_size (cr, private->font_size); + cairo_move_to (cr, x,y); + cairo_text_path (cr, private->text); + + _gimp_canvas_item_fill (item, cr); +} + +static cairo_region_t * +gimp_canvas_text_get_extents (GimpCanvasItem *item) +{ + cairo_rectangle_int_t rectangle; + gdouble x1, y1, x2, y2; + + gimp_canvas_text_transform (item, &x1, &y1, &x2, &y2); + + rectangle.x = (gint) x1-1; + rectangle.y = (gint) (y1-y2-1); + rectangle.width = (gint) x2+3; + rectangle.height = (gint) y2+3; + + return cairo_region_create_rectangle (&rectangle); +} + +GimpCanvasItem * +gimp_canvas_text_new (GimpDisplayShell *shell, + gdouble x, + gdouble y, + gdouble font_size, + gchar *text) +{ + g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), NULL); + + return g_object_new (GIMP_TYPE_CANVAS_TEXT, + "shell", shell, + "x", x, + "y", y, + "font-size", font_size, + "text", text, + NULL); +} + +static void +gimp_canvas_text_finalize (GObject *object) +{ + GimpCanvasTextPrivate *private = GET_PRIVATE (object); + + g_free (private->text); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} diff --git a/app/display/gimpcanvastext.h b/app/display/gimpcanvastext.h new file mode 100644 index 0000000000..cb19d0890c --- /dev/null +++ b/app/display/gimpcanvastext.h @@ -0,0 +1,58 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpcanvastext.h + * Copyright (C) 2023 mr.fantastic + * + * 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 . + */ + +#ifndef __GIMPCANVASTEXT_H__ +#define __GIMPCANVASTEXT_H__ + + +#include "gimpcanvasitem.h" + + +#define GIMP_TYPE_CANVAS_TEXT (gimp_canvas_text_get_type ()) +#define GIMP_CANVAS_TEXT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_CANVAS_TEXT, GimpCanvasText)) +#define GIMP_CANVAS_TEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_CANVAS_TEXT, GimpCanvasTextClass)) +#define GIMP_IS_CANVAS_TEXT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((klass), GIMP_TYPE_CANVAS_TEXT)) +#define GIMP_IS_CANVAS_TEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_CANVAS_TEXT)) +#define GIMP_CANVAS_TEXT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_CANVAS_TEXT, GimpCanvasTextClass)) + + +typedef struct _GimpCanvasText GimpCanvasText; +typedef struct _GimpCanvasTextClass GimpCanvasTextClass; + +struct _GimpCanvasText +{ + GimpCanvasItem parent_instance; +}; + +struct _GimpCanvasTextClass +{ + GimpCanvasItemClass parent_class; +}; + + +GType gimp_canvas_text_get_type (void) G_GNUC_CONST; + +GimpCanvasItem * gimp_canvas_text_new (GimpDisplayShell *shell, + gdouble x, + gdouble y, + gdouble font_size, + gchar *text); + +#endif diff --git a/app/display/meson.build b/app/display/meson.build index 3ca11a6a83..63cc40eca8 100644 --- a/app/display/meson.build +++ b/app/display/meson.build @@ -47,6 +47,8 @@ libappdisplay_sources = [ 'gimpcanvastextcursor.c', 'gimpcanvastransformguides.c', 'gimpcanvastransformpreview.c', + 'gimpcanvastext.c', + 'gimpcanvastext.h', 'gimpcursorview.c', 'gimpdisplay-foreach.c', 'gimpdisplay-handlers.c', diff --git a/app/tools/gimpdrawtool.c b/app/tools/gimpdrawtool.c index c9acd2ffd6..6ceb751807 100644 --- a/app/tools/gimpdrawtool.c +++ b/app/tools/gimpdrawtool.c @@ -43,6 +43,7 @@ #include "display/gimpcanvassamplepoint.h" #include "display/gimpcanvastextcursor.h" #include "display/gimpcanvastransformpreview.h" +#include "display/gimpcanvastext.h" #include "display/gimpdisplay.h" #include "display/gimpdisplayshell.h" #include "display/gimpdisplayshell-items.h" @@ -1243,6 +1244,26 @@ gimp_draw_tool_add_transform_preview (GimpDrawTool *draw_tool, return item; } +GimpCanvasItem * +gimp_draw_tool_add_text (GimpDrawTool *draw_tool, + gdouble x, + gdouble y, + gdouble font_size, + gchar *text) +{ + GimpCanvasItem *item; + + g_return_val_if_fail (GIMP_IS_DRAW_TOOL (draw_tool), NULL); + + item = gimp_canvas_text_new (gimp_display_get_shell (draw_tool->display), + x, y, font_size, text); + + gimp_draw_tool_add_item (draw_tool, item); + g_object_unref (item); + + return item; +} + gboolean gimp_draw_tool_on_handle (GimpDrawTool *draw_tool, GimpDisplay *display, diff --git a/app/tools/gimpdrawtool.h b/app/tools/gimpdrawtool.h index 68b72d5742..489fb74ff7 100644 --- a/app/tools/gimpdrawtool.h +++ b/app/tools/gimpdrawtool.h @@ -191,6 +191,12 @@ GimpCanvasItem * gimp_draw_tool_add_text_cursor (GimpDrawTool *draw_too gboolean overwrite, GimpTextDirection direction); +GimpCanvasItem * gimp_draw_tool_add_text (GimpDrawTool *draw_tool, + gdouble x, + gdouble y, + gdouble font_size, + gchar *text); + gboolean gimp_draw_tool_on_handle (GimpDrawTool *draw_tool, GimpDisplay *display, gdouble x, diff --git a/app/tools/gimpeditselectiontool.c b/app/tools/gimpeditselectiontool.c index ef681c3c53..d4efd3f198 100644 --- a/app/tools/gimpeditselectiontool.c +++ b/app/tools/gimpeditselectiontool.c @@ -20,6 +20,9 @@ #include #include +#include +#include + #include #include #include @@ -631,13 +634,14 @@ gimp_edit_selection_tool_active_modifier_key (GimpTool *tool, static void gimp_edit_selection_tool_draw (GimpDrawTool *draw_tool) { - GimpEditSelectionTool *edit_select = GIMP_EDIT_SELECTION_TOOL (draw_tool); - GimpDisplay *display = GIMP_TOOL (draw_tool)->display; - GimpImage *image = gimp_display_get_image (display); + GimpEditSelectionTool *edit_select = GIMP_EDIT_SELECTION_TOOL (draw_tool); + GimpDisplay *display = GIMP_TOOL (draw_tool)->display; + GimpImage *image = gimp_display_get_image (display); + GimpDisplayShell *shell = gimp_display_get_shell (display); GList *selected_items; GList *iter; - gint off_x = G_MAXINT; - gint off_y = G_MAXINT; + gint off_x = G_MAXINT; + gint off_y = G_MAXINT; selected_items = gimp_edit_selection_tool_get_selected_items (edit_select, image); g_return_if_fail (selected_items != NULL); @@ -776,16 +780,23 @@ gimp_edit_selection_tool_draw (GimpDrawTool *draw_tool) if (equidistance_side_horizontal != GIMP_ARRANGE_HFILL) { - GimpLayer *near_layer1 = gimp_image_get_near_layer_horizontal1 (image); - GimpLayer *near_layer2 = gimp_image_get_near_layer_horizontal2 (image); - gint gx1 = 0; - gint gy1 = 0; - gint gw1 = 0; - gint gh1 = 0; - gint gx2 = 0; - gint gy2 = 0; - gint gw2 = 0; - gint gh2 = 0; + GimpLayer *near_layer1 = shell->near_layer_horizontal1; + GimpLayer *near_layer2 = shell->near_layer_horizontal2; + GtkStyleContext *style = gtk_widget_get_style_context (GTK_WIDGET (shell)); + gdouble font_size; + gint gx1 = 0; + gint gy1 = 0; + gint gw1 = 0; + gint gh1 = 0; + gint gx2 = 0; + gint gy2 = 0; + gint gw2 = 0; + gint gh2 = 0; + gint distance; + gchar distance_string[32]; + + gtk_style_context_get (style, gtk_style_context_get_state (style), + "font-size", &font_size, NULL); gimp_item_bounds (GIMP_ITEM (near_layer1), &gx1, &gy1, &gw1, &gh1); gimp_item_get_offset (GIMP_ITEM (near_layer1), &gx1, &gy1); @@ -793,16 +804,33 @@ gimp_edit_selection_tool_draw (GimpDrawTool *draw_tool) gimp_item_bounds (GIMP_ITEM (near_layer2), &gx2, &gy2, &gw2, &gh2); gimp_item_get_offset (GIMP_ITEM (near_layer2), &gx2, &gy2); + gimp_draw_tool_add_rectangle (draw_tool, FALSE, gx1, gy1, gw1, gh1); + gimp_draw_tool_add_rectangle (draw_tool, FALSE, gx2, gy2, gw2, gh2); + switch (equidistance_side_horizontal) { case GIMP_ALIGN_LEFT: + distance = x - (gx1+gw1); + gimp_draw_tool_add_line (draw_tool, x, y+h/2, gx1+gw1, y+h/2); gimp_draw_tool_add_line (draw_tool, gx1, gy1+gh1/2, gx2+gw2, gy1+gh1/2); + + g_sprintf (distance_string, "%d px", distance); + gimp_draw_tool_add_text (draw_tool, x - distance/2, y+h/2, font_size, distance_string); + gimp_draw_tool_add_text (draw_tool, gx1 - distance/2, gy1+gh1/2, font_size, distance_string); + break; case GIMP_ALIGN_RIGHT: + distance = gx1 - (x+w); + gimp_draw_tool_add_line (draw_tool, x+w, y+h/2, gx1, y+h/2); gimp_draw_tool_add_line (draw_tool, gx1+gw1, gy1+gh1/2, gx2, gy1+gh1/2); + + g_sprintf (distance_string, "%d px", distance); + gimp_draw_tool_add_text (draw_tool, (x+w) + distance/2, y+h/2, font_size, distance_string); + gimp_draw_tool_add_text (draw_tool, (gx1+gw1) + distance/2, gy1+gh1/2, font_size, distance_string); + break; default: @@ -812,16 +840,23 @@ gimp_edit_selection_tool_draw (GimpDrawTool *draw_tool) if (equidistance_side_vertical != GIMP_ARRANGE_HFILL) { - GimpLayer *near_layer1 = gimp_image_get_near_layer_vertical1 (image); - GimpLayer *near_layer2 = gimp_image_get_near_layer_vertical2 (image); - gint gx1 = 0; - gint gy1 = 0; - gint gw1 = 0; - gint gh1 = 0; - gint gx2 = 0; - gint gy2 = 0; - gint gw2 = 0; - gint gh2 = 0; + GimpLayer *near_layer1 = shell->near_layer_vertical1; + GimpLayer *near_layer2 = shell->near_layer_vertical2; + GtkStyleContext *style = gtk_widget_get_style_context (GTK_WIDGET (shell)); + gdouble font_size; + gint gx1 = 0; + gint gy1 = 0; + gint gw1 = 0; + gint gh1 = 0; + gint gx2 = 0; + gint gy2 = 0; + gint gw2 = 0; + gint gh2 = 0; + gint distance; + gchar distance_string[32]; + + gtk_style_context_get (style, gtk_style_context_get_state (style), + "font-size", &font_size, NULL); gimp_item_bounds (GIMP_ITEM (near_layer1), &gx1, &gy1, &gw1, &gh1); gimp_item_get_offset (GIMP_ITEM (near_layer1), &gx1, &gy1); @@ -829,16 +864,33 @@ gimp_edit_selection_tool_draw (GimpDrawTool *draw_tool) gimp_item_bounds (GIMP_ITEM (near_layer2), &gx2, &gy2, &gw2, &gh2); gimp_item_get_offset (GIMP_ITEM (near_layer2), &gx2, &gy2); + gimp_draw_tool_add_rectangle (draw_tool, FALSE, gx1, gy1, gw1, gh1); + gimp_draw_tool_add_rectangle (draw_tool, FALSE, gx2, gy2, gw2, gh2); + switch (equidistance_side_vertical) { case GIMP_ALIGN_TOP: + distance = y - (gy1+gh1); + gimp_draw_tool_add_line (draw_tool, x+w/2, y, x+w/2, gy1+gh1); gimp_draw_tool_add_line (draw_tool, gx1+gw1/2, gy1, gx1+gw1/2, gy2+gh2); + + g_sprintf (distance_string, "%d px", distance); + gimp_draw_tool_add_text (draw_tool, x+w/2, y - distance/2, font_size, distance_string); + gimp_draw_tool_add_text (draw_tool, gx1+gw1/2, gy1 - distance/2, font_size, distance_string); + break; case GIMP_ALIGN_BOTTOM: + distance = gy1 - (y+h); + gimp_draw_tool_add_line (draw_tool, x+w/2, y+h, x+w/2, gy1); gimp_draw_tool_add_line (draw_tool, gx1+gw1/2, gy1+gh1, gx1+gw1/2, gy2); + + g_sprintf (distance_string, "%d px", distance); + gimp_draw_tool_add_text (draw_tool, x+w/2, (y+h) + distance/2, font_size, distance_string); + gimp_draw_tool_add_text (draw_tool, gx1+gw1/2, (gy1+gh1) + distance/2, font_size, distance_string); + break; default: