Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/editors/space_node/node_gizmo.cc')
-rw-r--r--source/blender/editors/space_node/node_gizmo.cc642
1 files changed, 642 insertions, 0 deletions
diff --git a/source/blender/editors/space_node/node_gizmo.cc b/source/blender/editors/space_node/node_gizmo.cc
new file mode 100644
index 00000000000..8c60d100b26
--- /dev/null
+++ b/source/blender/editors/space_node/node_gizmo.cc
@@ -0,0 +1,642 @@
+/*
+ * 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 2
+ * 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, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/** \file
+ * \ingroup spnode
+ */
+
+#include <cmath>
+
+#include "BLI_math_matrix.h"
+#include "BLI_math_vector.h"
+#include "BLI_rect.h"
+#include "BLI_utildefines.h"
+
+#include "BKE_context.h"
+#include "BKE_image.h"
+#include "BKE_main.h"
+
+#include "ED_gizmo_library.h"
+#include "ED_screen.h"
+
+#include "IMB_imbuf_types.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "RNA_access.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "node_intern.hh"
+
+namespace blender::ed::space_node {
+
+/* -------------------------------------------------------------------- */
+/** \name Local Utilities
+ * \{ */
+
+static void node_gizmo_calc_matrix_space(const SpaceNode *snode,
+ const ARegion *region,
+ float matrix_space[4][4])
+{
+ unit_m4(matrix_space);
+ mul_v3_fl(matrix_space[0], snode->zoom);
+ mul_v3_fl(matrix_space[1], snode->zoom);
+ matrix_space[3][0] = (region->winx / 2) + snode->xof;
+ matrix_space[3][1] = (region->winy / 2) + snode->yof;
+}
+
+static void node_gizmo_calc_matrix_space_with_image_dims(const SpaceNode *snode,
+ const ARegion *region,
+ const float image_dims[2],
+ float matrix_space[4][4])
+{
+ unit_m4(matrix_space);
+ mul_v3_fl(matrix_space[0], snode->zoom * image_dims[0]);
+ mul_v3_fl(matrix_space[1], snode->zoom * image_dims[1]);
+ matrix_space[3][0] = ((region->winx / 2) + snode->xof) - ((image_dims[0] / 2.0f) * snode->zoom);
+ matrix_space[3][1] = ((region->winy / 2) + snode->yof) - ((image_dims[1] / 2.0f) * snode->zoom);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Backdrop Gizmo
+ * \{ */
+
+static void gizmo_node_backdrop_prop_matrix_get(const wmGizmo *UNUSED(gz),
+ wmGizmoProperty *gz_prop,
+ void *value_p)
+{
+ float(*matrix)[4] = (float(*)[4])value_p;
+ BLI_assert(gz_prop->type->array_length == 16);
+ const SpaceNode *snode = (const SpaceNode *)gz_prop->custom_func.user_data;
+ matrix[0][0] = snode->zoom;
+ matrix[1][1] = snode->zoom;
+ matrix[3][0] = snode->xof;
+ matrix[3][1] = snode->yof;
+}
+
+static void gizmo_node_backdrop_prop_matrix_set(const wmGizmo *UNUSED(gz),
+ wmGizmoProperty *gz_prop,
+ const void *value_p)
+{
+ const float(*matrix)[4] = (const float(*)[4])value_p;
+ BLI_assert(gz_prop->type->array_length == 16);
+ SpaceNode *snode = (SpaceNode *)gz_prop->custom_func.user_data;
+ snode->zoom = matrix[0][0];
+ snode->xof = matrix[3][0];
+ snode->yof = matrix[3][1];
+}
+
+static bool WIDGETGROUP_node_transform_poll(const bContext *C, wmGizmoGroupType *UNUSED(gzgt))
+{
+ SpaceNode *snode = CTX_wm_space_node(C);
+
+ if ((snode->flag & SNODE_BACKDRAW) == 0) {
+ return false;
+ }
+
+ if (snode && snode->edittree && snode->edittree->type == NTREE_COMPOSIT) {
+ bNode *node = nodeGetActive(snode->edittree);
+
+ if (node && ELEM(node->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static void WIDGETGROUP_node_transform_setup(const bContext *UNUSED(C), wmGizmoGroup *gzgroup)
+{
+ wmGizmoWrapper *wwrapper = (wmGizmoWrapper *)MEM_mallocN(sizeof(wmGizmoWrapper), __func__);
+
+ wwrapper->gizmo = WM_gizmo_new("GIZMO_GT_cage_2d", gzgroup, nullptr);
+
+ RNA_enum_set(wwrapper->gizmo->ptr,
+ "transform",
+ ED_GIZMO_CAGE2D_XFORM_FLAG_TRANSLATE | ED_GIZMO_CAGE2D_XFORM_FLAG_SCALE_UNIFORM);
+
+ gzgroup->customdata = wwrapper;
+}
+
+static void WIDGETGROUP_node_transform_refresh(const bContext *C, wmGizmoGroup *gzgroup)
+{
+ Main *bmain = CTX_data_main(C);
+ wmGizmo *cage = ((wmGizmoWrapper *)gzgroup->customdata)->gizmo;
+ const ARegion *region = CTX_wm_region(C);
+ /* center is always at the origin */
+ const float origin[3] = {float(region->winx / 2), float(region->winy / 2), 0.0f};
+
+ void *lock;
+ Image *ima = BKE_image_ensure_viewer(bmain, IMA_TYPE_COMPOSITE, "Viewer Node");
+ ImBuf *ibuf = BKE_image_acquire_ibuf(ima, nullptr, &lock);
+
+ if (ibuf) {
+ const float dims[2] = {
+ (ibuf->x > 0) ? ibuf->x : 64.0f,
+ (ibuf->y > 0) ? ibuf->y : 64.0f,
+ };
+
+ RNA_float_set_array(cage->ptr, "dimensions", dims);
+ WM_gizmo_set_matrix_location(cage, origin);
+ WM_gizmo_set_flag(cage, WM_GIZMO_HIDDEN, false);
+
+ /* Need to set property here for undo. TODO: would prefer to do this in _init. */
+ SpaceNode *snode = CTX_wm_space_node(C);
+#if 0
+ PointerRNA nodeptr;
+ RNA_pointer_create(snode->id, &RNA_SpaceNodeEditor, snode, &nodeptr);
+ WM_gizmo_target_property_def_rna(cage, "offset", &nodeptr, "backdrop_offset", -1);
+ WM_gizmo_target_property_def_rna(cage, "scale", &nodeptr, "backdrop_zoom", -1);
+#endif
+
+ wmGizmoPropertyFnParams params{};
+ params.value_get_fn = gizmo_node_backdrop_prop_matrix_get;
+ params.value_set_fn = gizmo_node_backdrop_prop_matrix_set;
+ params.range_get_fn = nullptr;
+ params.user_data = snode;
+ WM_gizmo_target_property_def_func(cage, "matrix", &params);
+ }
+ else {
+ WM_gizmo_set_flag(cage, WM_GIZMO_HIDDEN, true);
+ }
+
+ BKE_image_release_ibuf(ima, ibuf, lock);
+}
+
+void NODE_GGT_backdrop_transform(wmGizmoGroupType *gzgt)
+{
+ gzgt->name = "Backdrop Transform Widget";
+ gzgt->idname = "NODE_GGT_backdrop_transform";
+
+ gzgt->flag |= WM_GIZMOGROUPTYPE_PERSISTENT;
+
+ gzgt->poll = WIDGETGROUP_node_transform_poll;
+ gzgt->setup = WIDGETGROUP_node_transform_setup;
+ gzgt->setup_keymap = WM_gizmogroup_setup_keymap_generic_maybe_drag;
+ gzgt->refresh = WIDGETGROUP_node_transform_refresh;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Crop Gizmo
+ * \{ */
+
+struct NodeCropWidgetGroup {
+ wmGizmo *border;
+
+ struct {
+ float dims[2];
+ } state;
+
+ struct {
+ PointerRNA ptr;
+ PropertyRNA *prop;
+ bContext *context;
+ } update_data;
+};
+
+static void gizmo_node_crop_update(struct NodeCropWidgetGroup *crop_group)
+{
+ RNA_property_update(
+ crop_group->update_data.context, &crop_group->update_data.ptr, crop_group->update_data.prop);
+}
+
+static void two_xy_to_rect(const NodeTwoXYs *nxy,
+ rctf *rect,
+ const float dims[2],
+ bool is_relative)
+{
+ if (is_relative) {
+ rect->xmin = nxy->fac_x1;
+ rect->xmax = nxy->fac_x2;
+ rect->ymin = nxy->fac_y1;
+ rect->ymax = nxy->fac_y2;
+ }
+ else {
+ rect->xmin = nxy->x1 / dims[0];
+ rect->xmax = nxy->x2 / dims[0];
+ rect->ymin = nxy->y1 / dims[1];
+ rect->ymax = nxy->y2 / dims[1];
+ }
+}
+
+static void two_xy_from_rect(NodeTwoXYs *nxy,
+ const rctf *rect,
+ const float dims[2],
+ bool is_relative)
+{
+ if (is_relative) {
+ nxy->fac_x1 = rect->xmin;
+ nxy->fac_x2 = rect->xmax;
+ nxy->fac_y1 = rect->ymin;
+ nxy->fac_y2 = rect->ymax;
+ }
+ else {
+ nxy->x1 = rect->xmin * dims[0];
+ nxy->x2 = rect->xmax * dims[0];
+ nxy->y1 = rect->ymin * dims[1];
+ nxy->y2 = rect->ymax * dims[1];
+ }
+}
+
+/* scale callbacks */
+static void gizmo_node_crop_prop_matrix_get(const wmGizmo *gz,
+ wmGizmoProperty *gz_prop,
+ void *value_p)
+{
+ float(*matrix)[4] = (float(*)[4])value_p;
+ BLI_assert(gz_prop->type->array_length == 16);
+ NodeCropWidgetGroup *crop_group = (NodeCropWidgetGroup *)gz->parent_gzgroup->customdata;
+ const float *dims = crop_group->state.dims;
+ const bNode *node = (const bNode *)gz_prop->custom_func.user_data;
+ const NodeTwoXYs *nxy = (const NodeTwoXYs *)node->storage;
+ bool is_relative = (bool)node->custom2;
+ rctf rct;
+ two_xy_to_rect(nxy, &rct, dims, is_relative);
+ matrix[0][0] = fabsf(BLI_rctf_size_x(&rct));
+ matrix[1][1] = fabsf(BLI_rctf_size_y(&rct));
+ matrix[3][0] = (BLI_rctf_cent_x(&rct) - 0.5f) * dims[0];
+ matrix[3][1] = (BLI_rctf_cent_y(&rct) - 0.5f) * dims[1];
+}
+
+static void gizmo_node_crop_prop_matrix_set(const wmGizmo *gz,
+ wmGizmoProperty *gz_prop,
+ const void *value_p)
+{
+ const float(*matrix)[4] = (const float(*)[4])value_p;
+ BLI_assert(gz_prop->type->array_length == 16);
+ NodeCropWidgetGroup *crop_group = (NodeCropWidgetGroup *)gz->parent_gzgroup->customdata;
+ const float *dims = crop_group->state.dims;
+ bNode *node = (bNode *)gz_prop->custom_func.user_data;
+ NodeTwoXYs *nxy = (NodeTwoXYs *)node->storage;
+ bool is_relative = (bool)node->custom2;
+ rctf rct;
+ two_xy_to_rect(nxy, &rct, dims, is_relative);
+ const bool nx = rct.xmin > rct.xmax;
+ const bool ny = rct.ymin > rct.ymax;
+ BLI_rctf_resize(&rct, fabsf(matrix[0][0]), fabsf(matrix[1][1]));
+ BLI_rctf_recenter(&rct, (matrix[3][0] / dims[0]) + 0.5f, (matrix[3][1] / dims[1]) + 0.5f);
+ rctf rct_isect{};
+ rct_isect.xmin = 0;
+ rct_isect.xmax = 1;
+ rct_isect.ymin = 0;
+ rct_isect.ymax = 1;
+ BLI_rctf_isect(&rct_isect, &rct, &rct);
+ if (nx) {
+ SWAP(float, rct.xmin, rct.xmax);
+ }
+ if (ny) {
+ SWAP(float, rct.ymin, rct.ymax);
+ }
+ two_xy_from_rect(nxy, &rct, dims, is_relative);
+ gizmo_node_crop_update(crop_group);
+}
+
+static bool WIDGETGROUP_node_crop_poll(const bContext *C, wmGizmoGroupType *UNUSED(gzgt))
+{
+ SpaceNode *snode = CTX_wm_space_node(C);
+
+ if ((snode->flag & SNODE_BACKDRAW) == 0) {
+ return false;
+ }
+
+ if (snode && snode->edittree && snode->edittree->type == NTREE_COMPOSIT) {
+ bNode *node = nodeGetActive(snode->edittree);
+
+ if (node && ELEM(node->type, CMP_NODE_CROP)) {
+ /* ignore 'use_crop_size', we can't usefully edit the crop in this case. */
+ if ((node->custom1 & (1 << 0)) == 0) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+static void WIDGETGROUP_node_crop_setup(const bContext *UNUSED(C), wmGizmoGroup *gzgroup)
+{
+ struct NodeCropWidgetGroup *crop_group = (NodeCropWidgetGroup *)MEM_mallocN(
+ sizeof(struct NodeCropWidgetGroup), __func__);
+
+ crop_group->border = WM_gizmo_new("GIZMO_GT_cage_2d", gzgroup, nullptr);
+
+ RNA_enum_set(crop_group->border->ptr,
+ "transform",
+ ED_GIZMO_CAGE2D_XFORM_FLAG_TRANSLATE | ED_GIZMO_CAGE2D_XFORM_FLAG_SCALE);
+
+ gzgroup->customdata = crop_group;
+}
+
+static void WIDGETGROUP_node_crop_draw_prepare(const bContext *C, wmGizmoGroup *gzgroup)
+{
+ ARegion *region = CTX_wm_region(C);
+ wmGizmo *gz = (wmGizmo *)gzgroup->gizmos.first;
+
+ SpaceNode *snode = CTX_wm_space_node(C);
+
+ node_gizmo_calc_matrix_space(snode, region, gz->matrix_space);
+}
+
+static void WIDGETGROUP_node_crop_refresh(const bContext *C, wmGizmoGroup *gzgroup)
+{
+ Main *bmain = CTX_data_main(C);
+ NodeCropWidgetGroup *crop_group = (NodeCropWidgetGroup *)gzgroup->customdata;
+ wmGizmo *gz = crop_group->border;
+
+ void *lock;
+ Image *ima = BKE_image_ensure_viewer(bmain, IMA_TYPE_COMPOSITE, "Viewer Node");
+ ImBuf *ibuf = BKE_image_acquire_ibuf(ima, nullptr, &lock);
+
+ if (ibuf) {
+ crop_group->state.dims[0] = (ibuf->x > 0) ? ibuf->x : 64.0f;
+ crop_group->state.dims[1] = (ibuf->y > 0) ? ibuf->y : 64.0f;
+
+ RNA_float_set_array(gz->ptr, "dimensions", crop_group->state.dims);
+ WM_gizmo_set_flag(gz, WM_GIZMO_HIDDEN, false);
+
+ SpaceNode *snode = CTX_wm_space_node(C);
+ bNode *node = nodeGetActive(snode->edittree);
+
+ crop_group->update_data.context = (bContext *)C;
+ RNA_pointer_create(
+ (ID *)snode->edittree, &RNA_CompositorNodeCrop, node, &crop_group->update_data.ptr);
+ crop_group->update_data.prop = RNA_struct_find_property(&crop_group->update_data.ptr,
+ "relative");
+
+ wmGizmoPropertyFnParams params{};
+ params.value_get_fn = gizmo_node_crop_prop_matrix_get;
+ params.value_set_fn = gizmo_node_crop_prop_matrix_set;
+ params.range_get_fn = nullptr;
+ params.user_data = node;
+ WM_gizmo_target_property_def_func(gz, "matrix", &params);
+ }
+ else {
+ WM_gizmo_set_flag(gz, WM_GIZMO_HIDDEN, true);
+ }
+
+ BKE_image_release_ibuf(ima, ibuf, lock);
+}
+
+void NODE_GGT_backdrop_crop(wmGizmoGroupType *gzgt)
+{
+ gzgt->name = "Backdrop Crop Widget";
+ gzgt->idname = "NODE_GGT_backdrop_crop";
+
+ gzgt->flag |= WM_GIZMOGROUPTYPE_PERSISTENT;
+
+ gzgt->poll = WIDGETGROUP_node_crop_poll;
+ gzgt->setup = WIDGETGROUP_node_crop_setup;
+ gzgt->setup_keymap = WM_gizmogroup_setup_keymap_generic_maybe_drag;
+ gzgt->draw_prepare = WIDGETGROUP_node_crop_draw_prepare;
+ gzgt->refresh = WIDGETGROUP_node_crop_refresh;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Sun Beams
+ * \{ */
+
+struct NodeSunBeamsWidgetGroup {
+ wmGizmo *gizmo;
+
+ struct {
+ float dims[2];
+ } state;
+};
+
+static bool WIDGETGROUP_node_sbeam_poll(const bContext *C, wmGizmoGroupType *UNUSED(gzgt))
+{
+ SpaceNode *snode = CTX_wm_space_node(C);
+
+ if ((snode->flag & SNODE_BACKDRAW) == 0) {
+ return false;
+ }
+
+ if (snode && snode->edittree && snode->edittree->type == NTREE_COMPOSIT) {
+ bNode *node = nodeGetActive(snode->edittree);
+
+ if (node && ELEM(node->type, CMP_NODE_SUNBEAMS)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static void WIDGETGROUP_node_sbeam_setup(const bContext *UNUSED(C), wmGizmoGroup *gzgroup)
+{
+ NodeSunBeamsWidgetGroup *sbeam_group = (NodeSunBeamsWidgetGroup *)MEM_mallocN(
+ sizeof(NodeSunBeamsWidgetGroup), __func__);
+
+ sbeam_group->gizmo = WM_gizmo_new("GIZMO_GT_move_3d", gzgroup, nullptr);
+ wmGizmo *gz = sbeam_group->gizmo;
+
+ RNA_enum_set(gz->ptr, "draw_style", ED_GIZMO_MOVE_STYLE_CROSS_2D);
+
+ gz->scale_basis = 0.05f / 75.0f;
+
+ gzgroup->customdata = sbeam_group;
+}
+
+static void WIDGETGROUP_node_sbeam_draw_prepare(const bContext *C, wmGizmoGroup *gzgroup)
+{
+ NodeSunBeamsWidgetGroup *sbeam_group = (NodeSunBeamsWidgetGroup *)gzgroup->customdata;
+ ARegion *region = CTX_wm_region(C);
+ wmGizmo *gz = (wmGizmo *)gzgroup->gizmos.first;
+
+ SpaceNode *snode = CTX_wm_space_node(C);
+
+ node_gizmo_calc_matrix_space_with_image_dims(
+ snode, region, sbeam_group->state.dims, gz->matrix_space);
+}
+
+static void WIDGETGROUP_node_sbeam_refresh(const bContext *C, wmGizmoGroup *gzgroup)
+{
+ Main *bmain = CTX_data_main(C);
+ NodeSunBeamsWidgetGroup *sbeam_group = (NodeSunBeamsWidgetGroup *)gzgroup->customdata;
+ wmGizmo *gz = sbeam_group->gizmo;
+
+ void *lock;
+ Image *ima = BKE_image_ensure_viewer(bmain, IMA_TYPE_COMPOSITE, "Viewer Node");
+ ImBuf *ibuf = BKE_image_acquire_ibuf(ima, nullptr, &lock);
+
+ if (ibuf) {
+ sbeam_group->state.dims[0] = (ibuf->x > 0) ? ibuf->x : 64.0f;
+ sbeam_group->state.dims[1] = (ibuf->y > 0) ? ibuf->y : 64.0f;
+
+ SpaceNode *snode = CTX_wm_space_node(C);
+ bNode *node = nodeGetActive(snode->edittree);
+
+ /* Need to set property here for undo. TODO: would prefer to do this in _init. */
+ PointerRNA nodeptr;
+ RNA_pointer_create((ID *)snode->edittree, &RNA_CompositorNodeSunBeams, node, &nodeptr);
+ WM_gizmo_target_property_def_rna(gz, "offset", &nodeptr, "source", -1);
+
+ WM_gizmo_set_flag(gz, WM_GIZMO_DRAW_MODAL, true);
+ }
+ else {
+ WM_gizmo_set_flag(gz, WM_GIZMO_HIDDEN, true);
+ }
+
+ BKE_image_release_ibuf(ima, ibuf, lock);
+}
+
+void NODE_GGT_backdrop_sun_beams(wmGizmoGroupType *gzgt)
+{
+ gzgt->name = "Sun Beams Widget";
+ gzgt->idname = "NODE_GGT_sbeam";
+
+ gzgt->flag |= WM_GIZMOGROUPTYPE_PERSISTENT;
+
+ gzgt->poll = WIDGETGROUP_node_sbeam_poll;
+ gzgt->setup = WIDGETGROUP_node_sbeam_setup;
+ gzgt->setup_keymap = WM_gizmogroup_setup_keymap_generic_maybe_drag;
+ gzgt->draw_prepare = WIDGETGROUP_node_sbeam_draw_prepare;
+ gzgt->refresh = WIDGETGROUP_node_sbeam_refresh;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Corner Pin
+ * \{ */
+
+struct NodeCornerPinWidgetGroup {
+ wmGizmo *gizmos[4];
+
+ struct {
+ float dims[2];
+ } state;
+};
+
+static bool WIDGETGROUP_node_corner_pin_poll(const bContext *C, wmGizmoGroupType *UNUSED(gzgt))
+{
+ SpaceNode *snode = CTX_wm_space_node(C);
+
+ if ((snode->flag & SNODE_BACKDRAW) == 0) {
+ return false;
+ }
+
+ if (snode && snode->edittree && snode->edittree->type == NTREE_COMPOSIT) {
+ bNode *node = nodeGetActive(snode->edittree);
+
+ if (node && ELEM(node->type, CMP_NODE_CORNERPIN)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static void WIDGETGROUP_node_corner_pin_setup(const bContext *UNUSED(C), wmGizmoGroup *gzgroup)
+{
+ NodeCornerPinWidgetGroup *cpin_group = (NodeCornerPinWidgetGroup *)MEM_mallocN(
+ sizeof(NodeCornerPinWidgetGroup), __func__);
+ const wmGizmoType *gzt_move_3d = WM_gizmotype_find("GIZMO_GT_move_3d", false);
+
+ for (int i = 0; i < 4; i++) {
+ cpin_group->gizmos[i] = WM_gizmo_new_ptr(gzt_move_3d, gzgroup, nullptr);
+ wmGizmo *gz = cpin_group->gizmos[i];
+
+ RNA_enum_set(gz->ptr, "draw_style", ED_GIZMO_MOVE_STYLE_CROSS_2D);
+
+ gz->scale_basis = 0.01f / 75.0;
+ }
+
+ gzgroup->customdata = cpin_group;
+}
+
+static void WIDGETGROUP_node_corner_pin_draw_prepare(const bContext *C, wmGizmoGroup *gzgroup)
+{
+ NodeCornerPinWidgetGroup *cpin_group = (NodeCornerPinWidgetGroup *)gzgroup->customdata;
+ ARegion *region = CTX_wm_region(C);
+
+ SpaceNode *snode = CTX_wm_space_node(C);
+
+ float matrix_space[4][4];
+ node_gizmo_calc_matrix_space_with_image_dims(
+ snode, region, cpin_group->state.dims, matrix_space);
+
+ for (int i = 0; i < 4; i++) {
+ wmGizmo *gz = cpin_group->gizmos[i];
+ copy_m4_m4(gz->matrix_space, matrix_space);
+ }
+}
+
+static void WIDGETGROUP_node_corner_pin_refresh(const bContext *C, wmGizmoGroup *gzgroup)
+{
+ Main *bmain = CTX_data_main(C);
+ NodeCornerPinWidgetGroup *cpin_group = (NodeCornerPinWidgetGroup *)gzgroup->customdata;
+
+ void *lock;
+ Image *ima = BKE_image_ensure_viewer(bmain, IMA_TYPE_COMPOSITE, "Viewer Node");
+ ImBuf *ibuf = BKE_image_acquire_ibuf(ima, nullptr, &lock);
+
+ if (ibuf) {
+ cpin_group->state.dims[0] = (ibuf->x > 0) ? ibuf->x : 64.0f;
+ cpin_group->state.dims[1] = (ibuf->y > 0) ? ibuf->y : 64.0f;
+
+ SpaceNode *snode = CTX_wm_space_node(C);
+ bNode *node = nodeGetActive(snode->edittree);
+
+ /* need to set property here for undo. TODO: would prefer to do this in _init. */
+ int i = 0;
+ for (bNodeSocket *sock = (bNodeSocket *)node->inputs.first; sock && i < 4; sock = sock->next) {
+ if (sock->type == SOCK_VECTOR) {
+ wmGizmo *gz = cpin_group->gizmos[i++];
+
+ PointerRNA sockptr;
+ RNA_pointer_create((ID *)snode->edittree, &RNA_NodeSocket, sock, &sockptr);
+ WM_gizmo_target_property_def_rna(gz, "offset", &sockptr, "default_value", -1);
+
+ WM_gizmo_set_flag(gz, WM_GIZMO_DRAW_MODAL, true);
+ }
+ }
+ }
+ else {
+ for (int i = 0; i < 4; i++) {
+ wmGizmo *gz = cpin_group->gizmos[i];
+ WM_gizmo_set_flag(gz, WM_GIZMO_HIDDEN, true);
+ }
+ }
+
+ BKE_image_release_ibuf(ima, ibuf, lock);
+}
+
+void NODE_GGT_backdrop_corner_pin(wmGizmoGroupType *gzgt)
+{
+ gzgt->name = "Corner Pin Widget";
+ gzgt->idname = "NODE_GGT_backdrop_corner_pin";
+
+ gzgt->flag |= WM_GIZMOGROUPTYPE_PERSISTENT;
+
+ gzgt->poll = WIDGETGROUP_node_corner_pin_poll;
+ gzgt->setup = WIDGETGROUP_node_corner_pin_setup;
+ gzgt->setup_keymap = WM_gizmogroup_setup_keymap_generic_maybe_drag;
+ gzgt->draw_prepare = WIDGETGROUP_node_corner_pin_draw_prepare;
+ gzgt->refresh = WIDGETGROUP_node_corner_pin_refresh;
+}
+
+/** \} */
+
+} // namespace blender::ed::space_node