Cleanup: add trailing commas to structs
[blender.git] / source / blender / editors / space_node / node_gizmo.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (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, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * ***** END GPL LICENSE BLOCK *****
19  */
20
21 /** \file blender/editors/space_node/node_gizmo.c
22  *  \ingroup spnode
23  */
24
25 #include <math.h>
26
27 #include "BLI_utildefines.h"
28 #include "BLI_math_matrix.h"
29 #include "BLI_math_vector.h"
30 #include "BLI_rect.h"
31
32 #include "BKE_context.h"
33 #include "BKE_image.h"
34 #include "BKE_main.h"
35
36 #include "ED_screen.h"
37 #include "ED_gizmo_library.h"
38
39 #include "IMB_imbuf_types.h"
40
41 #include "MEM_guardedalloc.h"
42
43 #include "RNA_access.h"
44
45 #include "WM_api.h"
46 #include "WM_types.h"
47
48 #include "node_intern.h"
49
50
51 /* -------------------------------------------------------------------- */
52
53 /** \name Local Utilities
54  * \{ */
55
56 static void node_gizmo_calc_matrix_space(
57         const SpaceNode *snode, const ARegion *ar, float matrix_space[4][4])
58 {
59         unit_m4(matrix_space);
60         mul_v3_fl(matrix_space[0], snode->zoom);
61         mul_v3_fl(matrix_space[1], snode->zoom);
62         matrix_space[3][0] = (ar->winx / 2) + snode->xof;
63         matrix_space[3][1] = (ar->winy / 2) + snode->yof;
64 }
65
66 static void node_gizmo_calc_matrix_space_with_image_dims(
67         const SpaceNode *snode, const ARegion *ar, const float image_dims[2], float matrix_space[4][4])
68 {
69         unit_m4(matrix_space);
70         mul_v3_fl(matrix_space[0], snode->zoom * image_dims[0]);
71         mul_v3_fl(matrix_space[1], snode->zoom * image_dims[1]);
72         matrix_space[3][0] = ((ar->winx / 2) + snode->xof) - ((image_dims[0] / 2.0f) * snode->zoom);
73         matrix_space[3][1] = ((ar->winy / 2) + snode->yof) - ((image_dims[1] / 2.0f) * snode->zoom);
74 }
75
76 /** \} */
77
78
79
80 /* -------------------------------------------------------------------- */
81
82 /** \name Backdrop Gizmo
83  * \{ */
84
85 static void gizmo_node_backdrop_prop_matrix_get(
86         const wmGizmo *UNUSED(gz), wmGizmoProperty *gz_prop,
87         void *value_p)
88 {
89         float (*matrix)[4] = value_p;
90         BLI_assert(gz_prop->type->array_length == 16);
91         const SpaceNode *snode = gz_prop->custom_func.user_data;
92         matrix[0][0] = snode->zoom;
93         matrix[1][1] = snode->zoom;
94         matrix[3][0] = snode->xof;
95         matrix[3][1] = snode->yof;
96 }
97
98 static void gizmo_node_backdrop_prop_matrix_set(
99         const wmGizmo *UNUSED(gz), wmGizmoProperty *gz_prop,
100         const void *value_p)
101 {
102         const float (*matrix)[4] = value_p;
103         BLI_assert(gz_prop->type->array_length == 16);
104         SpaceNode *snode = gz_prop->custom_func.user_data;
105         snode->zoom = matrix[0][0];
106         snode->zoom = matrix[1][1];
107         snode->xof  = matrix[3][0];
108         snode->yof  = matrix[3][1];
109 }
110
111 static bool WIDGETGROUP_node_transform_poll(const bContext *C, wmGizmoGroupType *UNUSED(gzgt))
112 {
113         SpaceNode *snode = CTX_wm_space_node(C);
114
115         if ((snode->flag & SNODE_BACKDRAW) == 0) {
116                 return false;
117         }
118
119         if (snode && snode->edittree && snode->edittree->type == NTREE_COMPOSIT) {
120                 bNode *node = nodeGetActive(snode->edittree);
121
122                 if (node && ELEM(node->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER)) {
123                         return true;
124                 }
125         }
126
127         return false;
128 }
129
130 static void WIDGETGROUP_node_transform_setup(const bContext *UNUSED(C), wmGizmoGroup *gzgroup)
131 {
132         wmGizmoWrapper *wwrapper = MEM_mallocN(sizeof(wmGizmoWrapper), __func__);
133
134         wwrapper->gizmo = WM_gizmo_new("GIZMO_GT_cage_2d", gzgroup, NULL);
135
136         RNA_enum_set(wwrapper->gizmo->ptr, "transform",
137                      ED_GIZMO_CAGE2D_XFORM_FLAG_TRANSLATE | ED_GIZMO_CAGE2D_XFORM_FLAG_SCALE_UNIFORM);
138
139         gzgroup->customdata = wwrapper;
140 }
141
142 static void WIDGETGROUP_node_transform_refresh(const bContext *C, wmGizmoGroup *gzgroup)
143 {
144         Main *bmain = CTX_data_main(C);
145         wmGizmo *cage = ((wmGizmoWrapper *)gzgroup->customdata)->gizmo;
146         const ARegion *ar = CTX_wm_region(C);
147         /* center is always at the origin */
148         const float origin[3] = {ar->winx / 2, ar->winy / 2};
149
150         void *lock;
151         Image *ima = BKE_image_verify_viewer(bmain, IMA_TYPE_COMPOSITE, "Viewer Node");
152         ImBuf *ibuf = BKE_image_acquire_ibuf(ima, NULL, &lock);
153
154         if (ibuf) {
155                 const float dims[2] = {
156                         (ibuf->x > 0) ? ibuf->x : 64.0f,
157                         (ibuf->y > 0) ? ibuf->y : 64.0f,
158                 };
159
160                 RNA_float_set_array(cage->ptr, "dimensions", dims);
161                 WM_gizmo_set_matrix_location(cage, origin);
162                 WM_gizmo_set_flag(cage, WM_GIZMO_HIDDEN, false);
163
164                 /* need to set property here for undo. TODO would prefer to do this in _init */
165                 SpaceNode *snode = CTX_wm_space_node(C);
166 #if 0
167                 PointerRNA nodeptr;
168                 RNA_pointer_create(snode->id, &RNA_SpaceNodeEditor, snode, &nodeptr);
169                 WM_gizmo_target_property_def_rna(cage, "offset", &nodeptr, "backdrop_offset", -1);
170                 WM_gizmo_target_property_def_rna(cage, "scale", &nodeptr, "backdrop_zoom", -1);
171 #endif
172
173                 WM_gizmo_target_property_def_func(
174                         cage, "matrix",
175                         &(const struct wmGizmoPropertyFnParams) {
176                             .value_get_fn = gizmo_node_backdrop_prop_matrix_get,
177                             .value_set_fn = gizmo_node_backdrop_prop_matrix_set,
178                             .range_get_fn = NULL,
179                             .user_data = snode,
180                         });
181         }
182         else {
183                 WM_gizmo_set_flag(cage, WM_GIZMO_HIDDEN, true);
184         }
185
186         BKE_image_release_ibuf(ima, ibuf, lock);
187 }
188
189 void NODE_GGT_backdrop_transform(wmGizmoGroupType *gzgt)
190 {
191         gzgt->name = "Backdrop Transform Widget";
192         gzgt->idname = "NODE_GGT_backdrop_transform";
193
194         gzgt->flag |= WM_GIZMOGROUPTYPE_PERSISTENT;
195
196         gzgt->poll = WIDGETGROUP_node_transform_poll;
197         gzgt->setup = WIDGETGROUP_node_transform_setup;
198         gzgt->refresh = WIDGETGROUP_node_transform_refresh;
199 }
200
201 /** \} */
202
203 /* -------------------------------------------------------------------- */
204
205 /** \name Crop Gizmo
206  * \{ */
207
208 struct NodeCropWidgetGroup {
209         wmGizmo *border;
210
211         struct {
212                 float dims[2];
213         } state;
214
215         struct {
216                 PointerRNA ptr;
217                 PropertyRNA *prop;
218                 bContext *context;
219         } update_data;
220 };
221
222 static void gizmo_node_crop_update(struct NodeCropWidgetGroup *crop_group)
223 {
224         RNA_property_update(crop_group->update_data.context, &crop_group->update_data.ptr, crop_group->update_data.prop);
225 }
226
227 static void two_xy_to_rect(const NodeTwoXYs *nxy, rctf *rect, const float dims[2], bool is_relative)
228 {
229         if (is_relative) {
230                 rect->xmin = nxy->fac_x1;
231                 rect->xmax = nxy->fac_x2;
232                 rect->ymin = nxy->fac_y1;
233                 rect->ymax = nxy->fac_y2;
234         }
235         else {
236                 rect->xmin = nxy->x1 / dims[0];
237                 rect->xmax = nxy->x2 / dims[0];
238                 rect->ymin = nxy->y1 / dims[1];
239                 rect->ymax = nxy->y2 / dims[1];
240         }
241 }
242
243 static void two_xy_from_rect(NodeTwoXYs *nxy, const rctf *rect, const float dims[2], bool is_relative)
244 {
245         if (is_relative) {
246                 nxy->fac_x1 = rect->xmin;
247                 nxy->fac_x2 = rect->xmax;
248                 nxy->fac_y1 = rect->ymin;
249                 nxy->fac_y2 = rect->ymax;
250         }
251         else {
252                 nxy->x1 = rect->xmin * dims[0];
253                 nxy->x2 = rect->xmax * dims[0];
254                 nxy->y1 = rect->ymin * dims[1];
255                 nxy->y2 = rect->ymax * dims[1];
256         }
257 }
258
259 /* scale callbacks */
260 static void gizmo_node_crop_prop_matrix_get(
261         const wmGizmo *gz, wmGizmoProperty *gz_prop,
262         void *value_p)
263 {
264         float (*matrix)[4] = value_p;
265         BLI_assert(gz_prop->type->array_length == 16);
266         struct NodeCropWidgetGroup *crop_group = gz->parent_gzgroup->customdata;
267         const float *dims = crop_group->state.dims;
268         const bNode *node = gz_prop->custom_func.user_data;
269         const NodeTwoXYs *nxy = node->storage;
270         bool is_relative = (bool)node->custom2;
271         rctf rct;
272         two_xy_to_rect(nxy, &rct, dims, is_relative);
273         matrix[0][0] = fabsf(BLI_rctf_size_x(&rct));
274         matrix[1][1] = fabsf(BLI_rctf_size_y(&rct));
275         matrix[3][0] = (BLI_rctf_cent_x(&rct) - 0.5f) * dims[0];
276         matrix[3][1] = (BLI_rctf_cent_y(&rct) - 0.5f) * dims[1];
277 }
278
279 static void gizmo_node_crop_prop_matrix_set(
280         const wmGizmo *gz, wmGizmoProperty *gz_prop,
281         const void *value_p)
282 {
283         const float (*matrix)[4] = value_p;
284         BLI_assert(gz_prop->type->array_length == 16);
285         struct NodeCropWidgetGroup *crop_group = gz->parent_gzgroup->customdata;
286         const float *dims = crop_group->state.dims;
287         bNode *node = gz_prop->custom_func.user_data;
288         NodeTwoXYs *nxy = node->storage;
289         bool is_relative = (bool)node->custom2;
290         rctf rct;
291         two_xy_to_rect(nxy, &rct, dims, is_relative);
292         const bool nx = rct.xmin > rct.xmax;
293         const bool ny = rct.ymin > rct.ymax;
294         BLI_rctf_resize(&rct, fabsf(matrix[0][0]), fabsf(matrix[1][1]));
295         BLI_rctf_recenter(&rct, (matrix[3][0] / dims[0]) + 0.5f, (matrix[3][1] / dims[1]) + 0.5f);
296         BLI_rctf_isect(&(rctf){ .xmin = 0, .ymin = 0, .xmax = 1, .ymax = 1, }, &rct, &rct);
297         if (nx) {
298                 SWAP(float, rct.xmin, rct.xmax);
299         }
300         if (ny) {
301                 SWAP(float, rct.ymin, rct.ymax);
302         }
303         two_xy_from_rect(nxy, &rct, dims, is_relative);
304         gizmo_node_crop_update(crop_group);
305 }
306
307 static bool WIDGETGROUP_node_crop_poll(const bContext *C, wmGizmoGroupType *UNUSED(gzgt))
308 {
309         SpaceNode *snode = CTX_wm_space_node(C);
310
311         if ((snode->flag & SNODE_BACKDRAW) == 0) {
312                 return false;
313         }
314
315         if (snode && snode->edittree && snode->edittree->type == NTREE_COMPOSIT) {
316                 bNode *node = nodeGetActive(snode->edittree);
317
318                 if (node && ELEM(node->type, CMP_NODE_CROP)) {
319                         /* ignore 'use_crop_size', we can't usefully edit the crop in this case. */
320                         if ((node->custom1 & (1 << 0)) == 0) {
321                                 return true;
322                         }
323                 }
324         }
325
326         return false;
327 }
328
329 static void WIDGETGROUP_node_crop_setup(const bContext *UNUSED(C), wmGizmoGroup *gzgroup)
330 {
331         struct NodeCropWidgetGroup *crop_group = MEM_mallocN(sizeof(struct NodeCropWidgetGroup), __func__);
332
333         crop_group->border = WM_gizmo_new("GIZMO_GT_cage_2d", gzgroup, NULL);
334
335         RNA_enum_set(crop_group->border->ptr, "transform",
336                      ED_GIZMO_CAGE2D_XFORM_FLAG_TRANSLATE | ED_GIZMO_CAGE2D_XFORM_FLAG_SCALE);
337
338         gzgroup->customdata = crop_group;
339 }
340
341 static void WIDGETGROUP_node_crop_draw_prepare(const bContext *C, wmGizmoGroup *gzgroup)
342 {
343         ARegion *ar = CTX_wm_region(C);
344         wmGizmo *gz = gzgroup->gizmos.first;
345
346         SpaceNode *snode = CTX_wm_space_node(C);
347
348         node_gizmo_calc_matrix_space(snode, ar, gz->matrix_space);
349 }
350
351 static void WIDGETGROUP_node_crop_refresh(const bContext *C, wmGizmoGroup *gzgroup)
352 {
353         Main *bmain = CTX_data_main(C);
354         struct NodeCropWidgetGroup *crop_group = gzgroup->customdata;
355         wmGizmo *gz = crop_group->border;
356
357         void *lock;
358         Image *ima = BKE_image_verify_viewer(bmain, IMA_TYPE_COMPOSITE, "Viewer Node");
359         ImBuf *ibuf = BKE_image_acquire_ibuf(ima, NULL, &lock);
360
361         if (ibuf) {
362                 crop_group->state.dims[0] = (ibuf->x > 0) ? ibuf->x : 64.0f;
363                 crop_group->state.dims[1] = (ibuf->y > 0) ? ibuf->y : 64.0f;
364
365                 RNA_float_set_array(gz->ptr, "dimensions", crop_group->state.dims);
366                 WM_gizmo_set_flag(gz, WM_GIZMO_HIDDEN, false);
367
368                 SpaceNode *snode = CTX_wm_space_node(C);
369                 bNode *node = nodeGetActive(snode->edittree);
370
371                 crop_group->update_data.context = (bContext *)C;
372                 RNA_pointer_create((ID *)snode->edittree, &RNA_CompositorNodeCrop, node, &crop_group->update_data.ptr);
373                 crop_group->update_data.prop = RNA_struct_find_property(&crop_group->update_data.ptr, "relative");
374
375                 WM_gizmo_target_property_def_func(
376                         gz, "matrix",
377                         &(const struct wmGizmoPropertyFnParams) {
378                             .value_get_fn = gizmo_node_crop_prop_matrix_get,
379                             .value_set_fn = gizmo_node_crop_prop_matrix_set,
380                             .range_get_fn = NULL,
381                             .user_data = node,
382                         });
383         }
384         else {
385                 WM_gizmo_set_flag(gz, WM_GIZMO_HIDDEN, true);
386         }
387
388         BKE_image_release_ibuf(ima, ibuf, lock);
389 }
390
391 void NODE_GGT_backdrop_crop(wmGizmoGroupType *gzgt)
392 {
393         gzgt->name = "Backdrop Crop Widget";
394         gzgt->idname = "NODE_GGT_backdrop_crop";
395
396         gzgt->flag |= WM_GIZMOGROUPTYPE_PERSISTENT;
397
398         gzgt->poll = WIDGETGROUP_node_crop_poll;
399         gzgt->setup = WIDGETGROUP_node_crop_setup;
400         gzgt->draw_prepare = WIDGETGROUP_node_crop_draw_prepare;
401         gzgt->refresh = WIDGETGROUP_node_crop_refresh;
402 }
403
404 /** \} */
405
406 /* -------------------------------------------------------------------- */
407
408 /** \name Sun Beams
409  * \{ */
410
411 struct NodeSunBeamsWidgetGroup {
412         wmGizmo *gizmo;
413
414         struct {
415                 float dims[2];
416         } state;
417 };
418
419 static bool WIDGETGROUP_node_sbeam_poll(const bContext *C, wmGizmoGroupType *UNUSED(gzgt))
420 {
421         SpaceNode *snode = CTX_wm_space_node(C);
422
423         if ((snode->flag & SNODE_BACKDRAW) == 0) {
424                 return false;
425         }
426
427         if (snode && snode->edittree && snode->edittree->type == NTREE_COMPOSIT) {
428                 bNode *node = nodeGetActive(snode->edittree);
429
430                 if (node && ELEM(node->type, CMP_NODE_SUNBEAMS)) {
431                         return true;
432                 }
433         }
434
435         return false;
436 }
437
438 static void WIDGETGROUP_node_sbeam_setup(const bContext *UNUSED(C), wmGizmoGroup *gzgroup)
439 {
440         struct NodeSunBeamsWidgetGroup *sbeam_group = MEM_mallocN(sizeof(struct NodeSunBeamsWidgetGroup), __func__);
441
442         sbeam_group->gizmo = WM_gizmo_new("GIZMO_GT_move_3d", gzgroup, NULL);
443         wmGizmo *gz = sbeam_group->gizmo;
444
445         RNA_enum_set(gz->ptr, "draw_style",  ED_GIZMO_MOVE_STYLE_CROSS_2D);
446
447         gz->scale_basis = 0.05f;
448
449         gzgroup->customdata = sbeam_group;
450 }
451
452 static void WIDGETGROUP_node_sbeam_draw_prepare(const bContext *C, wmGizmoGroup *gzgroup)
453 {
454         struct NodeSunBeamsWidgetGroup *sbeam_group = gzgroup->customdata;
455         ARegion *ar = CTX_wm_region(C);
456         wmGizmo *gz = gzgroup->gizmos.first;
457
458         SpaceNode *snode = CTX_wm_space_node(C);
459
460         node_gizmo_calc_matrix_space_with_image_dims(snode, ar, sbeam_group->state.dims, gz->matrix_space);
461 }
462
463 static void WIDGETGROUP_node_sbeam_refresh(const bContext *C, wmGizmoGroup *gzgroup)
464 {
465         Main *bmain = CTX_data_main(C);
466         struct NodeSunBeamsWidgetGroup *sbeam_group = gzgroup->customdata;
467         wmGizmo *gz = sbeam_group->gizmo;
468
469         void *lock;
470         Image *ima = BKE_image_verify_viewer(bmain, IMA_TYPE_COMPOSITE, "Viewer Node");
471         ImBuf *ibuf = BKE_image_acquire_ibuf(ima, NULL, &lock);
472
473         if (ibuf) {
474                 sbeam_group->state.dims[0] = (ibuf->x > 0) ? ibuf->x : 64.0f;
475                 sbeam_group->state.dims[1] = (ibuf->y > 0) ? ibuf->y : 64.0f;
476
477                 SpaceNode *snode = CTX_wm_space_node(C);
478                 bNode *node = nodeGetActive(snode->edittree);
479
480                 /* need to set property here for undo. TODO would prefer to do this in _init */
481                 PointerRNA nodeptr;
482                 RNA_pointer_create((ID *)snode->edittree, &RNA_CompositorNodeSunBeams, node, &nodeptr);
483                 WM_gizmo_target_property_def_rna(gz, "offset", &nodeptr, "source", -1);
484
485                 WM_gizmo_set_flag(gz, WM_GIZMO_DRAW_MODAL, true);
486         }
487         else {
488                 WM_gizmo_set_flag(gz, WM_GIZMO_HIDDEN, true);
489         }
490
491         BKE_image_release_ibuf(ima, ibuf, lock);
492 }
493
494 void NODE_GGT_backdrop_sun_beams(wmGizmoGroupType *gzgt)
495 {
496         gzgt->name = "Sun Beams Widget";
497         gzgt->idname = "NODE_GGT_sbeam";
498
499         gzgt->flag |= WM_GIZMOGROUPTYPE_PERSISTENT;
500
501         gzgt->poll = WIDGETGROUP_node_sbeam_poll;
502         gzgt->setup = WIDGETGROUP_node_sbeam_setup;
503         gzgt->draw_prepare = WIDGETGROUP_node_sbeam_draw_prepare;
504         gzgt->refresh = WIDGETGROUP_node_sbeam_refresh;
505 }
506
507 /** \} */
508
509
510
511 /* -------------------------------------------------------------------- */
512
513 /** \name Corner Pin
514  * \{ */
515
516 struct NodeCornerPinWidgetGroup {
517         wmGizmo *gizmos[4];
518
519         struct {
520                 float dims[2];
521         } state;
522 };
523
524 static bool WIDGETGROUP_node_corner_pin_poll(const bContext *C, wmGizmoGroupType *UNUSED(gzgt))
525 {
526         SpaceNode *snode = CTX_wm_space_node(C);
527
528         if ((snode->flag & SNODE_BACKDRAW) == 0) {
529                 return false;
530         }
531
532         if (snode && snode->edittree && snode->edittree->type == NTREE_COMPOSIT) {
533                 bNode *node = nodeGetActive(snode->edittree);
534
535                 if (node && ELEM(node->type, CMP_NODE_CORNERPIN)) {
536                         return true;
537                 }
538         }
539
540         return false;
541 }
542
543 static void WIDGETGROUP_node_corner_pin_setup(const bContext *UNUSED(C), wmGizmoGroup *gzgroup)
544 {
545         struct NodeCornerPinWidgetGroup *cpin_group = MEM_mallocN(sizeof(struct NodeCornerPinWidgetGroup), __func__);
546         const wmGizmoType *gzt_move_3d = WM_gizmotype_find("GIZMO_GT_move_3d", false);
547
548         for (int i = 0; i < 4; i++) {
549                 cpin_group->gizmos[i] = WM_gizmo_new_ptr(gzt_move_3d, gzgroup, NULL);
550                 wmGizmo *gz = cpin_group->gizmos[i];
551
552                 RNA_enum_set(gz->ptr, "draw_style",  ED_GIZMO_MOVE_STYLE_CROSS_2D);
553
554                 gz->scale_basis = 0.01f;
555         }
556
557         gzgroup->customdata = cpin_group;
558 }
559
560 static void WIDGETGROUP_node_corner_pin_draw_prepare(const bContext *C, wmGizmoGroup *gzgroup)
561 {
562         struct NodeCornerPinWidgetGroup *cpin_group = gzgroup->customdata;
563         ARegion *ar = CTX_wm_region(C);
564
565         SpaceNode *snode = CTX_wm_space_node(C);
566
567         float matrix_space[4][4];
568         node_gizmo_calc_matrix_space_with_image_dims(snode, ar, cpin_group->state.dims, matrix_space);
569
570         for (int i = 0; i < 4; i++) {
571                 wmGizmo *gz = cpin_group->gizmos[i];
572                 copy_m4_m4(gz->matrix_space, matrix_space);
573         }
574 }
575
576 static void WIDGETGROUP_node_corner_pin_refresh(const bContext *C, wmGizmoGroup *gzgroup)
577 {
578         Main *bmain = CTX_data_main(C);
579         struct NodeCornerPinWidgetGroup *cpin_group = gzgroup->customdata;
580
581         void *lock;
582         Image *ima = BKE_image_verify_viewer(bmain, IMA_TYPE_COMPOSITE, "Viewer Node");
583         ImBuf *ibuf = BKE_image_acquire_ibuf(ima, NULL, &lock);
584
585         if (ibuf) {
586                 cpin_group->state.dims[0] = (ibuf->x > 0) ? ibuf->x : 64.0f;
587                 cpin_group->state.dims[1] = (ibuf->y > 0) ? ibuf->y : 64.0f;
588
589                 SpaceNode *snode = CTX_wm_space_node(C);
590                 bNode *node = nodeGetActive(snode->edittree);
591
592                 /* need to set property here for undo. TODO would prefer to do this in _init */
593                 int i = 0;
594                 for (bNodeSocket *sock = node->inputs.first; sock && i < 4; sock = sock->next) {
595                         if (sock->type == SOCK_VECTOR) {
596                                 wmGizmo *gz = cpin_group->gizmos[i++];
597
598                                 PointerRNA sockptr;
599                                 RNA_pointer_create((ID *)snode->edittree, &RNA_NodeSocket, sock, &sockptr);
600                                 WM_gizmo_target_property_def_rna(gz, "offset", &sockptr, "default_value", -1);
601
602                                 WM_gizmo_set_flag(gz, WM_GIZMO_DRAW_MODAL, true);
603                         }
604                 }
605         }
606         else {
607                 for (int i = 0; i < 4; i++) {
608                         wmGizmo *gz = cpin_group->gizmos[i];
609                         WM_gizmo_set_flag(gz, WM_GIZMO_HIDDEN, true);
610                 }
611         }
612
613         BKE_image_release_ibuf(ima, ibuf, lock);
614 }
615
616 void NODE_GGT_backdrop_corner_pin(wmGizmoGroupType *gzgt)
617 {
618         gzgt->name = "Corner Pin Widget";
619         gzgt->idname = "NODE_GGT_backdrop_corner_pin";
620
621         gzgt->flag |= WM_GIZMOGROUPTYPE_PERSISTENT;
622
623         gzgt->poll = WIDGETGROUP_node_corner_pin_poll;
624         gzgt->setup = WIDGETGROUP_node_corner_pin_setup;
625         gzgt->draw_prepare = WIDGETGROUP_node_corner_pin_draw_prepare;
626         gzgt->refresh = WIDGETGROUP_node_corner_pin_refresh;
627 }
628
629 /** \} */