Blender V5.0
grease_pencil_undo.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include "BLI_task.hh"
10
11#include "BKE_context.hh"
12#include "BKE_curves.hh"
13#include "BKE_customdata.hh"
14#include "BKE_grease_pencil.hh"
15#include "BKE_lib_id.hh"
16#include "BKE_main.hh"
17#include "BKE_object.hh"
18#include "BKE_undo_system.hh"
19
20#include "CLG_log.h"
21
22#include "DEG_depsgraph.hh"
24
26#include "ED_grease_pencil.hh"
27#include "ED_undo.hh"
28
29#include "MEM_guardedalloc.h"
30
31#include "WM_api.hh"
32#include "WM_types.hh"
33
34static CLG_LogRef LOG = {"undo.greasepencil"};
35
37
38/* -------------------------------------------------------------------- */
43
52
53/* Store contextual data and status info during undo step encoding or decoding. */
55
63
65 protected:
66 /* Index of this drawing in the original combined array of all drawings in GreasePencil ID. */
67 int index_;
68
69 /* Data from #GreasePencilDrawingBase that needs to be saved in undo steps. */
70 uint32_t flag_;
71
82 const GreasePencilDrawingType drawing_type) const
83 {
84 /* TODO: Maybe that code should rather be part of GreasePencil:: API, together with
85 * `add_empty_drawings` and such? */
86 GreasePencilDrawingBase *drawing = drawings[index_];
87 if (drawing != nullptr) {
88 if (drawing->type == drawing_type) {
89 return;
90 }
91 switch (drawing->type) {
92 case GP_DRAWING:
93 MEM_delete(&reinterpret_cast<GreasePencilDrawing *>(drawing)->wrap());
94 break;
96 MEM_delete(&reinterpret_cast<GreasePencilDrawingReference *>(drawing)->wrap());
97 break;
98 }
99 drawing = nullptr;
100 }
101 if (drawing == nullptr) {
102 switch (drawing_type) {
103 case GP_DRAWING:
104 drawings[index_] = reinterpret_cast<GreasePencilDrawingBase *>(
105 MEM_new<bke::greasepencil::Drawing>(__func__));
106 break;
108 drawings[index_] = reinterpret_cast<GreasePencilDrawingBase *>(
109 MEM_new<bke::greasepencil::DrawingReference>(__func__));
110 break;
111 }
112 }
113 }
114};
115
117 bke::CurvesGeometry geometry_;
118
119 public:
120 void encode(const GreasePencilDrawing &drawing_geometry,
121 const int64_t drawing_index,
122 StepEncodeStatus & /*encode_status*/)
123 {
124 BLI_assert(drawing_index >= 0 && drawing_index < INT32_MAX);
125 index_ = int(drawing_index);
126
127 flag_ = drawing_geometry.base.flag;
128 geometry_ = drawing_geometry.geometry.wrap();
129 }
130
131 void decode(GreasePencil &grease_pencil, StepDecodeStatus & /*decode_status*/) const
132 {
133 MutableSpan<GreasePencilDrawingBase *> drawings = grease_pencil.drawings();
135 BLI_assert(drawings[index_]->type == GP_DRAWING);
136
137 GreasePencilDrawing &drawing_geometry = *reinterpret_cast<GreasePencilDrawing *>(
138 drawings[index_]);
139
140 drawing_geometry.base.flag = flag_;
141 drawing_geometry.geometry.wrap() = geometry_;
142
143 /* TODO: Check if there is a way to tell if both stored and current geometry are still the
144 * same, to avoid recomputing the caches all the time for all drawings? */
145 drawing_geometry.wrap().tag_topology_changed();
146 }
147};
148
150 UndoRefID_GreasePencil grease_pencil_ref_ = {};
151
152 public:
153 void encode(const GreasePencilDrawingReference &drawing_reference,
154 const int64_t drawing_index,
155 StepEncodeStatus & /*encode_status*/)
156 {
157 BLI_assert(drawing_index >= 0 && drawing_index < INT32_MAX);
158 index_ = int(drawing_index);
159
160 flag_ = drawing_reference.base.flag;
161 grease_pencil_ref_.ptr = drawing_reference.id_reference;
162 }
163
164 void decode(GreasePencil &grease_pencil, StepDecodeStatus &decode_status) const
165 {
166 MutableSpan<GreasePencilDrawingBase *> drawings = grease_pencil.drawings();
168 BLI_assert(drawings[index_]->type == GP_DRAWING_REFERENCE);
169
170 GreasePencilDrawingReference &drawing_reference =
171 *reinterpret_cast<GreasePencilDrawingReference *>(drawings[index_]);
172 drawing_reference.base.flag = flag_;
173
174 if (drawing_reference.id_reference != grease_pencil_ref_.ptr) {
175 id_us_min(reinterpret_cast<ID *>(drawing_reference.id_reference));
176 drawing_reference.id_reference = grease_pencil_ref_.ptr;
177 id_us_plus(reinterpret_cast<ID *>(drawing_reference.id_reference));
178 decode_status.needs_relationships_update = true;
179 }
180 }
181
182 void foreach_id_ref(UndoTypeForEachIDRefFn foreach_ID_ref_fn, void *user_data)
183 {
184 foreach_ID_ref_fn(user_data, reinterpret_cast<UndoRefID *>(&grease_pencil_ref_));
185 }
186};
187
189 public:
190 UndoRefID_Object obedit_ref = {};
191
192 private:
193 Array<StepDrawingGeometry> drawings_geometry_;
194 Array<StepDrawingReference> drawings_reference_;
195
196 int layers_num_ = 0;
198 std::string active_node_name_;
199 bke::AttributeStorage layer_attributes_;
200
201 void encode_drawings(const GreasePencil &grease_pencil, StepEncodeStatus &encode_status)
202 {
203 const Span<const GreasePencilDrawingBase *> drawings = grease_pencil.drawings();
204
205 int64_t drawings_geometry_num = 0;
206 int64_t drawings_reference_num = 0;
207 for (const int64_t idx : drawings.index_range()) {
208 const GreasePencilDrawingBase &drawing = *drawings[idx];
209 switch (drawing.type) {
210 case GP_DRAWING:
211 drawings_geometry_num++;
212 break;
214 drawings_reference_num++;
215 break;
216 }
217 }
218
219 drawings_geometry_.reinitialize(drawings_geometry_num);
220 drawings_reference_.reinitialize(drawings_reference_num);
221
222 int drawings_geometry_idx = 0;
223 int drawings_reference_idx = 0;
224 for (const int64_t idx : drawings.index_range()) {
225 const GreasePencilDrawingBase &drawing = *drawings[idx];
226 switch (drawing.type) {
227 case GP_DRAWING:
228 drawings_geometry_[drawings_geometry_idx++].encode(
229 reinterpret_cast<const GreasePencilDrawing &>(drawing), idx, encode_status);
230 break;
232 drawings_reference_[drawings_reference_idx++].encode(
233 reinterpret_cast<const GreasePencilDrawingReference &>(drawing), idx, encode_status);
234 break;
235 }
236 }
237 }
238
239 void decode_drawings(GreasePencil &grease_pencil, StepDecodeStatus &decode_status) const
240 {
241 const int drawing_array_num = int(drawings_geometry_.size() + drawings_reference_.size());
242 grease_pencil.resize_drawings(drawing_array_num);
243
244 for (const StepDrawingGeometry &drawing : drawings_geometry_) {
245 drawing.decode(grease_pencil, decode_status);
246 }
247 for (const StepDrawingReference &drawing : drawings_reference_) {
248 drawing.decode(grease_pencil, decode_status);
249 }
250 }
251
252 void encode_layers(const GreasePencil &grease_pencil, StepEncodeStatus & /*encode_status*/)
253 {
254 layers_num_ = int(grease_pencil.layers().size());
255 layer_attributes_ = grease_pencil.attribute_storage.wrap();
256
257 if (grease_pencil.active_node != nullptr) {
258 active_node_name_ = grease_pencil.get_active_node()->name();
259 }
260
261 root_group_ = grease_pencil.root_group();
262 }
263
264 void decode_layers(GreasePencil &grease_pencil, StepDecodeStatus & /*decode_status*/) const
265 {
266 if (grease_pencil.root_group_ptr) {
267 MEM_delete(&grease_pencil.root_group());
268 }
269 grease_pencil.set_active_node(nullptr);
270
271 grease_pencil.root_group_ptr = MEM_new<bke::greasepencil::LayerGroup>(__func__, root_group_);
272 BLI_assert(layers_num_ == grease_pencil.layers().size());
273
274 if (!active_node_name_.empty()) {
275 if (bke::greasepencil::TreeNode *active_node = grease_pencil.root_group().find_node_by_name(
276 active_node_name_))
277 {
278 grease_pencil.set_active_node(active_node);
279 }
280 }
281
282 grease_pencil.attribute_storage.wrap() = layer_attributes_;
283 }
284
285 public:
286 ~StepObject() = default;
287
288 void encode(Object *ob, StepEncodeStatus &encode_status)
289 {
290 const GreasePencil &grease_pencil = *static_cast<GreasePencil *>(ob->data);
291 this->obedit_ref.ptr = ob;
292
293 this->encode_drawings(grease_pencil, encode_status);
294 this->encode_layers(grease_pencil, encode_status);
295 }
296
297 void decode(StepDecodeStatus &decode_status) const
298 {
299 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(this->obedit_ref.ptr->data);
300
301 this->decode_drawings(grease_pencil, decode_status);
302 this->decode_layers(grease_pencil, decode_status);
303
304 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
305 }
306
307 void foreach_id_ref(UndoTypeForEachIDRefFn foreach_ID_ref_fn, void *user_data)
308 {
309 foreach_ID_ref_fn(user_data, reinterpret_cast<UndoRefID *>(&this->obedit_ref));
310 for (StepDrawingReference &drawing_ref : drawings_reference_) {
311 drawing_ref.foreach_id_ref(foreach_ID_ref_fn, user_data);
312 }
313 }
314};
315
322
323static bool step_encode(bContext *C, Main *bmain, UndoStep *us_p)
324{
325 GreasePencilUndoStep *us = reinterpret_cast<GreasePencilUndoStep *>(us_p);
326 StepEncodeStatus encode_status;
327
328 Scene *scene = CTX_data_scene(C);
329 ViewLayer *view_layer = CTX_data_view_layer(C);
331
332 us->scene_ref.ptr = scene;
333 new (&us->objects) Array<StepObject>(objects.size());
334
335 threading::parallel_for(us->objects.index_range(), 8, [&](const IndexRange range) {
336 for (const int64_t i : range) {
337 Object *ob = objects[i];
338 us->objects[i].encode(ob, encode_status);
339 }
340 });
341
342 bmain->is_memfile_undo_flush_needed = true;
343
344 return true;
345}
346
347static void step_decode(
348 bContext *C, Main *bmain, UndoStep *us_p, const eUndoStepDir /*dir*/, bool /*is_final*/)
349{
350 GreasePencilUndoStep *us = reinterpret_cast<GreasePencilUndoStep *>(us_p);
351 StepDecodeStatus decode_status;
352
353 Scene *scene = CTX_data_scene(C);
354 ViewLayer *view_layer = CTX_data_view_layer(C);
355
357 CTX_wm_manager(C), us->scene_ref.ptr, &scene, &view_layer);
359 view_layer,
360 &us->objects.first().obedit_ref.ptr,
361 uint(us->objects.size()),
362 sizeof(decltype(us->objects)::value_type));
363
364 BLI_assert(BKE_object_is_in_editmode(us->objects.first().obedit_ref.ptr));
365
366 for (const StepObject &step_object : us->objects) {
367 step_object.decode(decode_status);
368 }
369
370 if (decode_status.needs_relationships_update) {
372 }
373
375 scene, view_layer, us->objects.first().obedit_ref.ptr, us_p->name, &LOG);
376
377 bmain->is_memfile_undo_flush_needed = true;
378
380}
381
382static void step_free(UndoStep *us_p)
383{
384 GreasePencilUndoStep *us = reinterpret_cast<GreasePencilUndoStep *>(us_p);
385 us->objects.~Array();
386}
387
388static void foreach_ID_ref(UndoStep *us_p,
389 UndoTypeForEachIDRefFn foreach_ID_ref_fn,
390 void *user_data)
391{
392 GreasePencilUndoStep *us = reinterpret_cast<GreasePencilUndoStep *>(us_p);
393
394 foreach_ID_ref_fn(user_data, reinterpret_cast<UndoRefID *>(&us->scene_ref));
395 for (StepObject &object : us->objects) {
396 object.foreach_id_ref(foreach_ID_ref_fn, user_data);
397 }
398}
399
401
402} // namespace blender::ed::greasepencil::undo
403
Scene * CTX_data_scene(const bContext *C)
wmWindowManager * CTX_wm_manager(const bContext *C)
ViewLayer * CTX_data_view_layer(const bContext *C)
Low-level operations for curves.
CustomData interface, see also DNA_customdata_types.h.
Low-level operations for grease pencil.
void id_us_plus(ID *id)
Definition lib_id.cc:358
void id_us_min(ID *id)
Definition lib_id.cc:366
General operations, lookup, etc. for blender objects.
bool BKE_object_is_in_editmode(const Object *ob)
@ UNDOTYPE_FLAG_NEED_CONTEXT_FOR_ENCODE
void(*)(void *user_data, UndoRefID *id_ref) UndoTypeForEachIDRefFn
eUndoStepDir
#define BLI_assert(a)
Definition BLI_assert.h:46
unsigned int uint
void DEG_id_tag_update(ID *id, unsigned int flags)
void DEG_relations_tag_update(Main *bmain)
@ ID_RECALC_GEOMETRY
Definition DNA_ID.h:1074
struct GreasePencil GreasePencil
GreasePencilDrawingType
@ GP_DRAWING_REFERENCE
void ED_undo_object_set_active_or_warn(Scene *scene, ViewLayer *view_layer, Object *ob, const char *info, CLG_LogRef *log)
Definition ed_undo.cc:775
void ED_undo_object_editmode_restore_helper(Scene *scene, ViewLayer *view_layer, Object **object_array, uint object_array_len, uint object_array_stride)
Definition ed_undo.cc:811
blender::Vector< Object * > ED_undo_editmode_objects_from_view_layer(const Scene *scene, ViewLayer *view_layer)
Definition ed_undo.cc:856
void ED_undo_object_editmode_validate_scene_from_windows(wmWindowManager *wm, const Scene *scene_ref, Scene **scene_p, ViewLayer **view_layer_p)
Definition ed_undo.cc:794
Read Guarded memory(de)allocation.
#define C
Definition RandGen.cpp:29
#define NC_GEOM
Definition WM_types.hh:393
#define ND_DATA
Definition WM_types.hh:509
long long int int64_t
void reinitialize(const int64_t new_size)
Definition BLI_array.hh:419
constexpr IndexRange index_range() const
Definition BLI_span.hh:401
int64_t size() const
void decode_valid_drawingtype_at_index_ensure(MutableSpan< GreasePencilDrawingBase * > drawings, const GreasePencilDrawingType drawing_type) const
void encode(const GreasePencilDrawing &drawing_geometry, const int64_t drawing_index, StepEncodeStatus &)
void decode(GreasePencil &grease_pencil, StepDecodeStatus &) const
void foreach_id_ref(UndoTypeForEachIDRefFn foreach_ID_ref_fn, void *user_data)
void encode(const GreasePencilDrawingReference &drawing_reference, const int64_t drawing_index, StepEncodeStatus &)
void decode(GreasePencil &grease_pencil, StepDecodeStatus &decode_status) const
void foreach_id_ref(UndoTypeForEachIDRefFn foreach_ID_ref_fn, void *user_data)
void decode(StepDecodeStatus &decode_status) const
void encode(Object *ob, StepEncodeStatus &encode_status)
#define INT32_MAX
void ED_undosys_type_grease_pencil(UndoType *ut)
#define LOG(level)
Definition log.h:97
static void foreach_ID_ref(UndoStep *us_p, UndoTypeForEachIDRefFn foreach_ID_ref_fn, void *user_data)
static bool step_encode(bContext *C, Main *bmain, UndoStep *us_p)
static void step_free(UndoStep *us_p)
static void step_decode(bContext *C, Main *bmain, UndoStep *us_p, const eUndoStepDir, bool)
bool grease_pencil_edit_poll(bContext *C)
void parallel_for(const IndexRange range, const int64_t grain_size, const Function &function, const TaskSizeHints &size_hints=detail::TaskSizeHints_Static(1))
Definition BLI_task.hh:93
float wrap(float value, float max, float min)
Definition node_math.h:103
GreasePencilDrawingBase base
GreasePencilLayerTreeNode * active_node
GreasePencilLayerTreeGroup * root_group_ptr
struct AttributeStorage attribute_storage
Definition DNA_ID.h:414
bool is_memfile_undo_flush_needed
Definition BKE_main.hh:213
char name[64]
void(* step_foreach_ID_ref)(UndoStep *us, UndoTypeForEachIDRefFn foreach_ID_ref_fn, void *user_data)
const char * name
void(* step_free)(UndoStep *us)
bool(* poll)(struct bContext *C)
void(* step_decode)(bContext *C, Main *bmain, UndoStep *us, eUndoStepDir dir, bool is_final)
bool(* step_encode)(bContext *C, Main *bmain, UndoStep *us)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)