Blender V4.3
editfont_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
9#include <cstdlib>
10#include <cstring>
11
12#include "MEM_guardedalloc.h"
13
14#include "CLG_log.h"
15
16#include "BLI_array_utils.h" /* For `BLI_array_is_zeroed`. */
17#include "BLI_utildefines.h"
18
19#include "DNA_curve_types.h"
20#include "DNA_object_types.h"
21
22#include "BKE_context.hh"
23#include "BKE_layer.hh"
24#include "BKE_main.hh"
25#include "BKE_undo_system.hh"
26#include "BKE_vfont.hh"
27
28#include "DEG_depsgraph.hh"
29
30#include "ED_curve.hh"
31#include "ED_undo.hh"
32
33#include "WM_api.hh"
34#include "WM_types.hh"
35
36#define USE_ARRAY_STORE
37
38#ifdef USE_ARRAY_STORE
39// # define DEBUG_PRINT
40# include "BLI_array_store.h"
42# include "BLI_listbase.h"
43# define ARRAY_CHUNK_SIZE 32
44#endif
45
47static CLG_LogRef LOG = {"ed.undo.font"};
48
49/* -------------------------------------------------------------------- */
53struct UndoFont {
54 char32_t *textbuf;
56
58
59#ifdef USE_ARRAY_STORE
60 struct {
64#endif
65
66 size_t undo_size;
67};
68
69#ifdef USE_ARRAY_STORE
70
71/* -------------------------------------------------------------------- */
75static struct {
77 int users;
78
79 /* We could have the undo API pass in the previous state, for now store a local list */
81
82} uf_arraystore = {{nullptr}};
83
89static void uf_arraystore_compact_ex(UndoFont *uf, const UndoFont *uf_ref, bool create)
90{
91# define STATE_COMPACT(uf, id, len) \
92 if ((uf)->id) { \
93 BLI_assert(create == ((uf)->store.id == nullptr)); \
94 if (create) { \
95 BArrayState *state_reference = uf_ref ? uf_ref->store.id : nullptr; \
96 const size_t stride = sizeof(*(uf)->id); \
97 BArrayStore *bs = BLI_array_store_at_size_ensure( \
98 &uf_arraystore.bs_stride, stride, ARRAY_CHUNK_SIZE); \
99 (uf)->store.id = BLI_array_store_state_add( \
100 bs, (uf)->id, size_t(len) * stride, state_reference); \
101 } \
102 /* keep uf->len for validation */ \
103 MEM_freeN((uf)->id); \
104 (uf)->id = nullptr; \
105 } \
106 ((void)0)
107
108 STATE_COMPACT(uf, textbuf, uf->len + 1);
109 STATE_COMPACT(uf, textbufinfo, uf->len + 1);
110
111# undef STATE_COMPACT
112
113 if (create) {
114 uf_arraystore.users += 1;
115 }
116}
117
121static void uf_arraystore_compact(UndoFont *um, const UndoFont *uf_ref)
122{
123 uf_arraystore_compact_ex(um, uf_ref, true);
124}
125
126static void uf_arraystore_compact_with_info(UndoFont *um, const UndoFont *uf_ref)
127{
128# ifdef DEBUG_PRINT
129 size_t size_expanded_prev, size_compacted_prev;
131 &uf_arraystore.bs_stride, &size_expanded_prev, &size_compacted_prev);
132# endif
133
134 uf_arraystore_compact(um, uf_ref);
135
136# ifdef DEBUG_PRINT
137 {
138 size_t size_expanded, size_compacted;
140 &uf_arraystore.bs_stride, &size_expanded, &size_compacted);
141
142 const double percent_total = size_expanded ?
143 ((double(size_compacted) / double(size_expanded)) * 100.0) :
144 -1.0;
145
146 size_t size_expanded_step = size_expanded - size_expanded_prev;
147 size_t size_compacted_step = size_compacted - size_compacted_prev;
148 const double percent_step = size_expanded_step ?
149 ((double(size_compacted_step) / double(size_expanded_step)) *
150 100.0) :
151 -1.0;
152
153 printf("overall memory use: %.8f%% of expanded size\n", percent_total);
154 printf("step memory use: %.8f%% of expanded size\n", percent_step);
155 }
156# endif
157}
158
163{
164 uf_arraystore_compact_ex(um, nullptr, false);
165}
166
168{
169# define STATE_EXPAND(uf, id, len) \
170 if ((uf)->store.id) { \
171 const size_t stride = sizeof(*(uf)->id); \
172 BArrayState *state = (uf)->store.id; \
173 size_t state_len; \
174 *(void **)&(uf)->id = BLI_array_store_state_data_get_alloc(state, &state_len); \
175 BLI_assert((len) == (state_len / stride)); \
176 UNUSED_VARS_NDEBUG(stride); \
177 } \
178 ((void)0)
179
180 STATE_EXPAND(uf, textbuf, uf->len + 1);
181 STATE_EXPAND(uf, textbufinfo, uf->len + 1);
182
183# undef STATE_EXPAND
184}
185
187{
188# define STATE_FREE(uf, id) \
189 if ((uf)->store.id) { \
190 const size_t stride = sizeof(*(uf)->id); \
191 BArrayStore *bs = BLI_array_store_at_size_get(&uf_arraystore.bs_stride, stride); \
192 BArrayState *state = (uf)->store.id; \
193 BLI_array_store_state_remove(bs, state); \
194 (uf)->store.id = nullptr; \
195 } \
196 ((void)0)
197
198 STATE_FREE(uf, textbuf);
199 STATE_FREE(uf, textbufinfo);
200
201# undef STATE_FREE
202
203 uf_arraystore.users -= 1;
204
205 BLI_assert(uf_arraystore.users >= 0);
206
207 if (uf_arraystore.users == 0) {
208# ifdef DEBUG_PRINT
209 printf("editfont undo store: freeing all data!\n");
210# endif
211
213 }
214}
215
218#endif /* USE_ARRAY_STORE */
219
221{
222 EditFont *ef = cu->editfont;
223
224 size_t final_size;
225
226#ifdef USE_ARRAY_STORE
228#endif
229
230 final_size = sizeof(*ef->textbuf) * (uf->len + 1);
231 memcpy(ef->textbuf, uf->textbuf, final_size);
232
233 final_size = sizeof(CharInfo) * (uf->len + 1);
234 memcpy(ef->textbufinfo, uf->textbufinfo, final_size);
235
236 ef->pos = uf->pos;
237 ef->selstart = uf->selstart;
238 ef->selend = uf->selend;
239 ef->len = uf->len;
240
241#ifdef USE_ARRAY_STORE
243#endif
244}
245
247{
249
250 EditFont *ef = cu->editfont;
251
252 size_t mem_used_prev = MEM_get_memory_in_use();
253
254 size_t final_size;
255
256 BLI_assert(sizeof(*uf->textbuf) == sizeof(*ef->textbuf));
257 final_size = sizeof(*uf->textbuf) * (ef->len + 1);
258 uf->textbuf = static_cast<char32_t *>(MEM_mallocN(final_size, __func__));
259 memcpy(uf->textbuf, ef->textbuf, final_size);
260
261 final_size = sizeof(CharInfo) * (ef->len + 1);
262 uf->textbufinfo = static_cast<CharInfo *>(MEM_mallocN(final_size, __func__));
263 memcpy(uf->textbufinfo, ef->textbufinfo, final_size);
264
265 uf->pos = ef->pos;
266 uf->selstart = ef->selstart;
267 uf->selend = ef->selend;
268 uf->len = ef->len;
269
270#ifdef USE_ARRAY_STORE
271 {
272 const UndoFont *uf_ref = static_cast<const UndoFont *>(
273 uf_arraystore.local_links.last ? ((LinkData *)uf_arraystore.local_links.last)->data :
274 nullptr);
275
276 /* Add ourselves. */
277 BLI_addtail(&uf_arraystore.local_links, BLI_genericNodeN(uf));
278
280 }
281#endif
282
283 size_t mem_used_curr = MEM_get_memory_in_use();
284
285 uf->undo_size = mem_used_prev < mem_used_curr ? mem_used_curr - mem_used_prev : sizeof(UndoFont);
286
287 return uf;
288}
289
291{
292#ifdef USE_ARRAY_STORE
293 {
294 LinkData *link = static_cast<LinkData *>(
295 BLI_findptr(&uf_arraystore.local_links, uf, offsetof(LinkData, data)));
296 BLI_remlink(&uf_arraystore.local_links, link);
297 MEM_freeN(link);
298 }
300#endif
301
302 if (uf->textbuf) {
303 MEM_freeN(uf->textbuf);
304 }
305 if (uf->textbufinfo) {
307 }
308}
309
311{
312 Scene *scene = CTX_data_scene(C);
313 ViewLayer *view_layer = CTX_data_view_layer(C);
314 BKE_view_layer_synced_ensure(scene, view_layer);
315 Object *obedit = BKE_view_layer_edit_object_get(view_layer);
316 if (obedit && obedit->type == OB_FONT) {
317 Curve *cu = static_cast<Curve *>(obedit->data);
318 EditFont *ef = cu->editfont;
319 if (ef != nullptr) {
320 return obedit;
321 }
322 }
323 return nullptr;
324}
325
328/* -------------------------------------------------------------------- */
335 UndoRefID_Scene scene_ref;
336 /* NOTE: will split out into list for multi-object-editmode. */
337 UndoRefID_Object obedit_ref;
339};
340
342{
343 return editfont_object_from_context(C) != nullptr;
344}
345
346static bool font_undosys_step_encode(bContext *C, Main *bmain, UndoStep *us_p)
347{
348 FontUndoStep *us = (FontUndoStep *)us_p;
349 us->scene_ref.ptr = CTX_data_scene(C);
351 Curve *cu = static_cast<Curve *>(us->obedit_ref.ptr->data);
353 us->step.data_size = us->data.undo_size;
355 bmain->is_memfile_undo_flush_needed = true;
356
357 return true;
358}
359
361 bContext *C, Main *bmain, UndoStep *us_p, const eUndoStepDir /*dir*/, bool /*is_final*/)
362{
363
364 FontUndoStep *us = (FontUndoStep *)us_p;
365 Object *obedit = us->obedit_ref.ptr;
366 Scene *scene = CTX_data_scene(C);
367 ViewLayer *view_layer = CTX_data_view_layer(C);
368
369 /* Pass in an array of 1 (typically used for multi-object edit-mode). */
371 CTX_wm_manager(C), us->scene_ref.ptr, &scene, &view_layer);
372 ED_undo_object_editmode_restore_helper(scene, view_layer, &obedit, 1, sizeof(Object *));
373
374 Curve *cu = static_cast<Curve *>(obedit->data);
375 undofont_to_editfont(&us->data, cu);
377
378 ED_undo_object_set_active_or_warn(scene, view_layer, obedit, us_p->name, &LOG);
379
380 /* Check after setting active (unless undoing into another scene). */
381 BLI_assert(font_undosys_poll(C) || (scene != CTX_data_scene(C)));
382
384 bmain->is_memfile_undo_flush_needed = true;
386}
387
389{
390 FontUndoStep *us = (FontUndoStep *)us_p;
392}
393
395 UndoTypeForEachIDRefFn foreach_ID_ref_fn,
396 void *user_data)
397{
398 FontUndoStep *us = (FontUndoStep *)us_p;
399 foreach_ID_ref_fn(user_data, ((UndoRefID *)&us->scene_ref));
400 foreach_ID_ref_fn(user_data, ((UndoRefID *)&us->obedit_ref));
401}
402
417
Scene * CTX_data_scene(const bContext *C)
wmWindowManager * CTX_wm_manager(const bContext *C)
ViewLayer * CTX_data_view_layer(const bContext *C)
void BKE_view_layer_synced_ensure(const Scene *scene, ViewLayer *view_layer)
Object * BKE_view_layer_edit_object_get(const ViewLayer *view_layer)
@ UNDOTYPE_FLAG_NEED_CONTEXT_FOR_ENCODE
void(*)(void *user_data, UndoRefID *id_ref) UndoTypeForEachIDRefFn
eUndoStepDir
Efficient in-memory storage of multiple similar arrays.
void BLI_array_store_at_size_clear(struct BArrayStore_AtSize *bs_stride)
void BLI_array_store_at_size_calc_memory_usage(const struct BArrayStore_AtSize *bs_stride, size_t *r_size_expanded, size_t *r_size_compacted)
Generic array manipulation API.
#define BLI_array_is_zeroed(arr, arr_len)
#define BLI_assert(a)
Definition BLI_assert.h:50
void BLI_addtail(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:110
void BLI_remlink(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:130
void * BLI_findptr(const struct ListBase *listbase, const void *ptr, int offset) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
struct LinkData * BLI_genericNodeN(void *data)
Definition listbase.cc:909
typedef double(DMatrix)[4][4]
void DEG_id_tag_update(ID *id, unsigned int flags)
@ ID_RECALC_GEOMETRY
Definition DNA_ID.h:1041
struct CharInfo CharInfo
Object is a sort of wrapper for general info.
@ OB_FONT
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:790
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:826
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:809
Read Guarded memory(de)allocation.
#define NC_GEOM
Definition WM_types.hh:360
#define ND_DATA
Definition WM_types.hh:475
#define printf
#define offsetof(t, d)
#define STATE_COMPACT(uf, id, len)
static Object * editfont_object_from_context(bContext *C)
static bool font_undosys_step_encode(bContext *C, Main *bmain, UndoStep *us_p)
#define STATE_FREE(uf, id)
static void uf_arraystore_expand(UndoFont *uf)
static void * undofont_from_editfont(UndoFont *uf, Curve *cu)
ListBase local_links
static void font_undosys_step_decode(bContext *C, Main *bmain, UndoStep *us_p, const eUndoStepDir, bool)
static void font_undosys_step_free(UndoStep *us_p)
static void uf_arraystore_expand_clear(UndoFont *um)
static void undofont_free_data(UndoFont *uf)
static void uf_arraystore_compact_with_info(UndoFont *um, const UndoFont *uf_ref)
static void uf_arraystore_compact(UndoFont *um, const UndoFont *uf_ref)
static void uf_arraystore_free(UndoFont *uf)
int users
static struct @318 uf_arraystore
static CLG_LogRef LOG
static bool font_undosys_poll(bContext *C)
static void font_undosys_foreach_ID_ref(UndoStep *us_p, UndoTypeForEachIDRefFn foreach_ID_ref_fn, void *user_data)
BArrayStore_AtSize bs_stride
void ED_font_undosys_type(UndoType *ut)
static void undofont_to_editfont(UndoFont *uf, Curve *cu)
#define STATE_EXPAND(uf, id, len)
static void uf_arraystore_compact_ex(UndoFont *uf, const UndoFont *uf_ref, bool create)
void *(* MEM_mallocN)(size_t len, const char *str)
Definition mallocn.cc:44
size_t(* MEM_get_memory_in_use)(void)
Definition mallocn.cc:62
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
struct EditFont * editfont
CharInfo * textbufinfo
Definition BKE_vfont.hh:37
int selend
Definition BKE_vfont.hh:52
char32_t * textbuf
Definition BKE_vfont.hh:35
int selstart
Definition BKE_vfont.hh:52
char needs_flush_to_id
Definition BKE_vfont.hh:65
UndoRefID_Scene scene_ref
UndoRefID_Object obedit_ref
bool is_memfile_undo_flush_needed
Definition BKE_main.hh:165
CharInfo * textbufinfo
char32_t * textbuf
BArrayState * textbufinfo
struct UndoFont::@319 store
size_t undo_size
BArrayState * textbuf
size_t data_size
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)