1 /* This file is part of GEGL
2  *
3  * GEGL is free software; you can redistribute it and/or
4  * modify it under the terms of the GNU Lesser General Public
5  * License as published by the Free Software Foundation; either
6  * version 3 of the License, or (at your option) any later version.
7  *
8  * GEGL is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11  * Lesser General Public License for more details.
12  *
13  * You should have received a copy of the GNU Lesser General Public
14  * License along with GEGL; if not, see <https://www.gnu.org/licenses/>.
15  *
16  * Copyright 2020 Øyvind Kolås
17  */
18 
19 #include "config.h"
20 #include <glib/gi18n-lib.h>
21 
22 #ifdef GEGL_PROPERTIES
23 
24 property_double (x, _("X"), 0.5)
25     description (_("Horizontal justification 0.0 is left 0.5 centered and 1.0 right."))
26     value_range (-2.0, 3.0)
27     ui_range (0.0, 1.0)
28     ui_meta ("axis", "x")
29 
30 property_double (y, _("Y"), 0.5)
31     description (_("Vertical justification 0.0 is top 0.5 middle and 1.0 bottom."))
32     value_range (-2.0, 3.0)
33     ui_range (0.0, 1.0)
34     ui_meta ("axis", "y")
35 
36 property_double (horizontal_margin, "Horizontal Margin", 0.0)
37 property_double (vertical_margin, "Vertical Margin", 0.0)
38 
39 property_boolean (snap_integer, "snap to integer position", TRUE)
40 
41 
42 #else
43 
44 #include "gegl-operation-filter.h"
45 #include "transform-core.h"
46 #define GEGL_OP_NO_SOURCE
47 #define GEGL_OP_Parent  OpTransform
48 #define GEGL_OP_PARENT  TYPE_OP_TRANSFORM
49 #define GEGL_OP_NAME    border_align
50 #define GEGL_OP_BUNDLE
51 #define GEGL_OP_C_FILE  "border-align.c"
52 
53 #include "gegl-op.h"
54 
55 #include <stdio.h>
56 
57 static GeglNode *gegl_node_get_consumer_no (GeglNode *node,
58                                             const char *output_pad,
59                                             const char **consumer_pad,
60                                             int no)
61 {
62   GeglNode *consumer = NULL;
63   GeglNode **nodes = NULL;
64   const gchar **consumer_names = NULL;
65   int count;
66   if (node == NULL)
67     return NULL;
68 
69   count = gegl_node_get_consumers (node, "output", &nodes, &consumer_names);
70   if (count > no){
71      /* XXX : look into inverting list in get_consumers */
72      //consumer = nodes[count-no-1];
73      consumer = nodes[no];
74      if (consumer_pad)
75        *consumer_pad = g_intern_string (consumer_names[count-no-1]);
76   }
77   g_free (nodes);
78   g_free (consumer_names);
79   return consumer;
80 }
81 
82 static GeglNode *gegl_node_find_composite_target (GeglNode *node)
83 {
84   const char *dest_pad = NULL;
85   GeglNode *iter = node;
86   iter = gegl_node_get_consumer_no (iter, "output", &dest_pad, 0);
87   while (iter && dest_pad && g_str_equal (dest_pad, "input"))
88   {
89     iter = gegl_node_get_consumer_no (iter, "output", &dest_pad, 0);
90   }
91   if (dest_pad && !strcmp (dest_pad, "aux"))
92   {
93     return gegl_node_get_producer (iter, "input", NULL);
94   }
95   return NULL;
96 }
97 
98 static void
99 create_matrix (OpTransform *op,
100                GeglMatrix3 *matrix)
101 {
102   GeglOperation *operation = GEGL_OPERATION (op);
103   GeglProperties *o = GEGL_PROPERTIES (op);
104 
105   gdouble x = 0.0;
106   gdouble y = 0.0;
107 
108   GeglNode *border_node = gegl_operation_get_source_node (operation, "aux");
109   GeglNode *box_node    = gegl_operation_get_source_node (operation, "input");
110 
111   GeglRectangle box_rect = {0,};
112   GeglRectangle border_rect = {0,};
113 
114   if (box_node)
115     box_rect = gegl_node_get_bounding_box (box_node);
116 
117   if (border_node)
118   {
119     border_rect = gegl_node_get_bounding_box (border_node);
120   }
121   else
122   {
123      border_node = gegl_node_find_composite_target (operation->node);
124      if (border_node)
125        border_rect = gegl_node_get_bounding_box (border_node);
126   }
127 
128   x = o->x * (border_rect.width - box_rect.width - o->horizontal_margin * 2) +
129              o->horizontal_margin;
130   y = o->y * (border_rect.height - box_rect.height - o->vertical_margin * 2)+
131              o->vertical_margin;
132 
133   x -= box_rect.x;
134   y -= box_rect.y;
135 
136   if (o->snap_integer)
137   {
138     x = roundf (x);
139     y = roundf (y);
140   }
141 
142   matrix->coeff [0][2] = x;
143   matrix->coeff [1][2] = y;
144 }
145 
146 static void attach (GeglOperation *operation)
147 {
148   GeglOperationComposerClass *klass = GEGL_OPERATION_COMPOSER_GET_CLASS (operation);
149   GParamSpec    *pspec;
150   GeglOperationClass *parent_class = g_type_class_peek_parent (klass);
151 
152   if (parent_class->attach)
153     parent_class->attach (operation);
154 
155   pspec = g_param_spec_object ("aux",
156       klass->aux_label?klass->aux_label:"Aux",
157       klass->aux_description?klass->aux_description:_("Auxiliary image buffer input pad."),
158       GEGL_TYPE_BUFFER,
159       G_PARAM_READWRITE |
160       GEGL_PARAM_PAD_INPUT);
161   gegl_operation_create_pad (operation, pspec);
162   g_param_spec_sink (pspec);
163 
164 }
165 
166 static void
167 gegl_op_class_init (GeglOpClass *klass)
168 {
169   GeglOperationClass *operation_class;
170   OpTransformClass   *transform_class;
171 
172   operation_class = GEGL_OPERATION_CLASS (klass);
173   transform_class = OP_TRANSFORM_CLASS (klass);
174 
175   operation_class->attach = attach;
176   transform_class->create_matrix = create_matrix;
177 
178   gegl_operation_class_set_keys (operation_class,
179     "name", "gegl:border-align",
180     "title", _("Border Align"),
181     "categories", "transform",
182     "reference-hash", "109c3f3685488a9952ca07ef18387850",
183     "description", _("Aligns box of input rectangle with border of compositing target or aux' bounding-box border, if aux pad is not connected the op tries to figure out which bounding box' border applies."),
184     NULL);
185 }
186 
187 #endif
188