1 /* GIMP - The GNU Image Manipulation Program
2  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3  *
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
16  */
17 
18 #include "config.h"
19 
20 #include <gdk-pixbuf/gdk-pixbuf.h>
21 #include <gegl.h>
22 
23 #include "libgimpmath/gimpmath.h"
24 
25 #include "core-types.h"
26 
27 #include "gimpgrouplayer.h"
28 #include "gimpguide.h"
29 #include "gimpimage.h"
30 #include "gimpimage-pick-item.h"
31 #include "gimpimage-private.h"
32 #include "gimppickable.h"
33 #include "gimpsamplepoint.h"
34 
35 #include "text/gimptextlayer.h"
36 
37 #include "vectors/gimpstroke.h"
38 #include "vectors/gimpvectors.h"
39 
40 
41 GimpLayer *
gimp_image_pick_layer(GimpImage * image,gint x,gint y,GimpLayer * previously_picked)42 gimp_image_pick_layer (GimpImage *image,
43                        gint       x,
44                        gint       y,
45                        GimpLayer *previously_picked)
46 {
47   GList *all_layers;
48   GList *list;
49   gint   off_x, off_y;
50   gint   tries = 1;
51 
52   g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
53 
54   all_layers = gimp_image_get_layer_list (image);
55 
56   if (previously_picked)
57     {
58       gimp_item_get_offset (GIMP_ITEM (previously_picked), &off_x, &off_y);
59       if (gimp_pickable_get_opacity_at (GIMP_PICKABLE (previously_picked),
60                                         x - off_x, y - off_y) <= 0.25)
61         previously_picked = NULL;
62       else
63         tries++;
64     }
65 
66   while (tries)
67     {
68       for (list = all_layers; list; list = g_list_next (list))
69         {
70           GimpLayer *layer = list->data;
71 
72           if (previously_picked)
73             {
74               /* Take the first layer with a pixel at given coordinates
75                * after the previously picked one.
76                */
77               if (layer == previously_picked)
78                 previously_picked = NULL;
79               continue;
80             }
81 
82           gimp_item_get_offset (GIMP_ITEM (layer), &off_x, &off_y);
83 
84           if (gimp_pickable_get_opacity_at (GIMP_PICKABLE (layer),
85                                             x - off_x, y - off_y) > 0.25)
86             {
87               g_list_free (all_layers);
88 
89               return layer;
90             }
91         }
92       tries--;
93     }
94 
95   g_list_free (all_layers);
96 
97   return NULL;
98 }
99 
100 GimpLayer *
gimp_image_pick_layer_by_bounds(GimpImage * image,gint x,gint y)101 gimp_image_pick_layer_by_bounds (GimpImage *image,
102                                  gint       x,
103                                  gint       y)
104 {
105   GList *all_layers;
106   GList *list;
107 
108   g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
109 
110   all_layers = gimp_image_get_layer_list (image);
111 
112   for (list = all_layers; list; list = g_list_next (list))
113     {
114       GimpLayer *layer = list->data;
115 
116       if (gimp_item_is_visible (GIMP_ITEM (layer)))
117         {
118           gint off_x, off_y;
119           gint width, height;
120 
121           gimp_item_get_offset (GIMP_ITEM (layer), &off_x, &off_y);
122           width  = gimp_item_get_width  (GIMP_ITEM (layer));
123           height = gimp_item_get_height (GIMP_ITEM (layer));
124 
125           if (x >= off_x        &&
126               y >= off_y        &&
127               x < off_x + width &&
128               y < off_y + height)
129             {
130               g_list_free (all_layers);
131 
132               return layer;
133             }
134         }
135     }
136 
137   g_list_free (all_layers);
138 
139   return NULL;
140 }
141 
142 GimpTextLayer *
gimp_image_pick_text_layer(GimpImage * image,gint x,gint y)143 gimp_image_pick_text_layer (GimpImage *image,
144                             gint       x,
145                             gint       y)
146 {
147   GList *all_layers;
148   GList *list;
149 
150   g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
151 
152   all_layers = gimp_image_get_layer_list (image);
153 
154   for (list = all_layers; list; list = g_list_next (list))
155     {
156       GimpLayer *layer = list->data;
157       gint       off_x, off_y;
158 
159       gimp_item_get_offset (GIMP_ITEM (layer), &off_x, &off_y);
160 
161       if (GIMP_IS_TEXT_LAYER (layer) &&
162           x >= off_x &&
163           y >= off_y &&
164           x <  off_x + gimp_item_get_width  (GIMP_ITEM (layer)) &&
165           y <  off_y + gimp_item_get_height (GIMP_ITEM (layer)) &&
166           gimp_item_is_visible (GIMP_ITEM (layer)))
167         {
168           g_list_free (all_layers);
169 
170           return GIMP_TEXT_LAYER (layer);
171         }
172       else if (gimp_pickable_get_opacity_at (GIMP_PICKABLE (layer),
173                                              x - off_x, y - off_y) > 0.25)
174         {
175           /*  a normal layer covers any possible text layers below,
176            *  bail out
177            */
178 
179           break;
180         }
181     }
182 
183   g_list_free (all_layers);
184 
185   return NULL;
186 }
187 
188 GimpVectors *
gimp_image_pick_vectors(GimpImage * image,gdouble x,gdouble y,gdouble epsilon_x,gdouble epsilon_y)189 gimp_image_pick_vectors (GimpImage *image,
190                          gdouble    x,
191                          gdouble    y,
192                          gdouble    epsilon_x,
193                          gdouble    epsilon_y)
194 {
195   GimpVectors *ret = NULL;
196   GList       *all_vectors;
197   GList       *list;
198   gdouble      mindist = G_MAXDOUBLE;
199 
200   g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
201 
202   all_vectors = gimp_image_get_vectors_list (image);
203 
204   for (list = all_vectors; list; list = g_list_next (list))
205     {
206       GimpVectors *vectors = list->data;
207 
208       if (gimp_item_is_visible (GIMP_ITEM (vectors)))
209         {
210           GimpStroke *stroke = NULL;
211           GimpCoords  coords = GIMP_COORDS_DEFAULT_VALUES;
212 
213           while ((stroke = gimp_vectors_stroke_get_next (vectors, stroke)))
214             {
215               gdouble dist;
216 
217               coords.x = x;
218               coords.y = y;
219 
220               dist = gimp_stroke_nearest_point_get (stroke, &coords, 1.0,
221                                                     NULL, NULL, NULL, NULL);
222 
223               if (dist >= 0.0 &&
224                   dist <  MIN (epsilon_y, mindist))
225                 {
226                   mindist = dist;
227                   ret     = vectors;
228                 }
229             }
230         }
231     }
232 
233   g_list_free (all_vectors);
234 
235   return ret;
236 }
237 
238 static GimpGuide *
gimp_image_pick_guide_internal(GimpImage * image,gdouble x,gdouble y,gdouble epsilon_x,gdouble epsilon_y,GimpOrientationType orientation)239 gimp_image_pick_guide_internal (GimpImage           *image,
240                                 gdouble              x,
241                                 gdouble              y,
242                                 gdouble              epsilon_x,
243                                 gdouble              epsilon_y,
244                                 GimpOrientationType  orientation)
245 {
246   GList     *list;
247   GimpGuide *ret     = NULL;
248   gdouble    mindist = G_MAXDOUBLE;
249 
250   for (list = GIMP_IMAGE_GET_PRIVATE (image)->guides;
251        list;
252        list = g_list_next (list))
253     {
254       GimpGuide *guide    = list->data;
255       gint       position = gimp_guide_get_position (guide);
256       gdouble    dist;
257 
258       switch (gimp_guide_get_orientation (guide))
259         {
260         case GIMP_ORIENTATION_HORIZONTAL:
261           if (orientation == GIMP_ORIENTATION_HORIZONTAL ||
262               orientation == GIMP_ORIENTATION_UNKNOWN)
263             {
264               dist = ABS (position - y);
265               if (dist < MIN (epsilon_y, mindist))
266                 {
267                   mindist = dist;
268                   ret = guide;
269                 }
270             }
271           break;
272 
273         /* mindist always is in vertical resolution to make it comparable */
274         case GIMP_ORIENTATION_VERTICAL:
275           if (orientation == GIMP_ORIENTATION_VERTICAL ||
276               orientation == GIMP_ORIENTATION_UNKNOWN)
277             {
278               dist = ABS (position - x);
279               if (dist < MIN (epsilon_x, mindist / epsilon_y * epsilon_x))
280                 {
281                   mindist = dist * epsilon_y / epsilon_x;
282                   ret = guide;
283                 }
284             }
285           break;
286 
287         default:
288           continue;
289         }
290     }
291 
292   return ret;
293 }
294 
295 GimpGuide *
gimp_image_pick_guide(GimpImage * image,gdouble x,gdouble y,gdouble epsilon_x,gdouble epsilon_y)296 gimp_image_pick_guide (GimpImage *image,
297                        gdouble    x,
298                        gdouble    y,
299                        gdouble    epsilon_x,
300                        gdouble    epsilon_y)
301 {
302   g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
303   g_return_val_if_fail (epsilon_x > 0 && epsilon_y > 0, NULL);
304 
305   return gimp_image_pick_guide_internal (image, x, y, epsilon_x, epsilon_y,
306                                          GIMP_ORIENTATION_UNKNOWN);
307 }
308 
309 GList *
gimp_image_pick_guides(GimpImage * image,gdouble x,gdouble y,gdouble epsilon_x,gdouble epsilon_y)310 gimp_image_pick_guides (GimpImage *image,
311                         gdouble    x,
312                         gdouble    y,
313                         gdouble    epsilon_x,
314                         gdouble    epsilon_y)
315 {
316   GimpGuide *guide;
317   GList     *result = NULL;
318 
319   g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
320   g_return_val_if_fail (epsilon_x > 0 && epsilon_y > 0, NULL);
321 
322   guide = gimp_image_pick_guide_internal (image, x, y, epsilon_x, epsilon_y,
323                                           GIMP_ORIENTATION_HORIZONTAL);
324 
325   if (guide)
326     result = g_list_append (result, guide);
327 
328   guide = gimp_image_pick_guide_internal (image, x, y, epsilon_x, epsilon_y,
329                                           GIMP_ORIENTATION_VERTICAL);
330 
331   if (guide)
332     result = g_list_append (result, guide);
333 
334   return result;
335 }
336 
337 GimpSamplePoint *
gimp_image_pick_sample_point(GimpImage * image,gdouble x,gdouble y,gdouble epsilon_x,gdouble epsilon_y)338 gimp_image_pick_sample_point (GimpImage *image,
339                               gdouble    x,
340                               gdouble    y,
341                               gdouble    epsilon_x,
342                               gdouble    epsilon_y)
343 {
344   GList           *list;
345   GimpSamplePoint *ret     = NULL;
346   gdouble          mindist = G_MAXDOUBLE;
347 
348   g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
349   g_return_val_if_fail (epsilon_x > 0 && epsilon_y > 0, NULL);
350 
351   if (x < 0 || x >= gimp_image_get_width  (image) ||
352       y < 0 || y >= gimp_image_get_height (image))
353     {
354       return NULL;
355     }
356 
357   for (list = GIMP_IMAGE_GET_PRIVATE (image)->sample_points;
358        list;
359        list = g_list_next (list))
360     {
361       GimpSamplePoint *sample_point = list->data;
362       gint             sp_x;
363       gint             sp_y;
364       gdouble          dist;
365 
366       gimp_sample_point_get_position (sample_point, &sp_x, &sp_y);
367 
368       if (sp_x < 0 || sp_y < 0)
369         continue;
370 
371       dist = hypot ((sp_x + 0.5) - x,
372                     (sp_y + 0.5) - y);
373       if (dist < MIN (epsilon_y, mindist))
374         {
375           mindist = dist;
376           ret = sample_point;
377         }
378     }
379 
380   return ret;
381 }
382