Blender  V3.3
editfont_undo.c
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 
7 #include <stdlib.h>
8 #include <string.h>
9 
10 #include "MEM_guardedalloc.h"
11 
12 #include "CLG_log.h"
13 
14 #include "BLI_array_utils.h"
15 #include "BLI_utildefines.h"
16 
17 #include "DNA_curve_types.h"
18 #include "DNA_object_types.h"
19 #include "DNA_scene_types.h"
20 
21 #include "BKE_context.h"
22 #include "BKE_main.h"
23 #include "BKE_undo_system.h"
24 #include "BKE_vfont.h"
25 
26 #include "DEG_depsgraph.h"
27 
28 #include "ED_curve.h"
29 #include "ED_object.h"
30 #include "ED_undo.h"
31 
32 #include "WM_api.h"
33 #include "WM_types.h"
34 
35 #define USE_ARRAY_STORE
36 
37 #ifdef USE_ARRAY_STORE
38 // # define DEBUG_PRINT
39 # include "BLI_array_store.h"
40 # include "BLI_array_store_utils.h"
41 # include "BLI_listbase.h"
42 # define ARRAY_CHUNK_SIZE 32
43 #endif
44 
46 static CLG_LogRef LOG = {"ed.undo.font"};
47 
48 /* -------------------------------------------------------------------- */
52 typedef struct UndoFont {
53  char32_t *textbuf;
55 
57 
58 #ifdef USE_ARRAY_STORE
59  struct {
62  } store;
63 #endif
64 
65  size_t undo_size;
67 
68 #ifdef USE_ARRAY_STORE
69 
70 /* -------------------------------------------------------------------- */
74 static struct {
76  int users;
77 
78  /* We could have the undo API pass in the previous state, for now store a local list */
80 
82 
88 static void uf_arraystore_compact_ex(UndoFont *uf, const UndoFont *uf_ref, bool create)
89 {
90 # define STATE_COMPACT(uf, id, len) \
91  if ((uf)->id) { \
92  BLI_assert(create == ((uf)->store.id == NULL)); \
93  if (create) { \
94  BArrayState *state_reference = uf_ref ? uf_ref->store.id : NULL; \
95  const size_t stride = sizeof(*(uf)->id); \
96  BArrayStore *bs = BLI_array_store_at_size_ensure( \
97  &uf_arraystore.bs_stride, stride, ARRAY_CHUNK_SIZE); \
98  (uf)->store.id = BLI_array_store_state_add( \
99  bs, (uf)->id, (size_t)(len)*stride, state_reference); \
100  } \
101  /* keep uf->len for validation */ \
102  MEM_freeN((uf)->id); \
103  (uf)->id = NULL; \
104  } \
105  ((void)0)
106 
107  STATE_COMPACT(uf, textbuf, uf->len + 1);
108  STATE_COMPACT(uf, textbufinfo, uf->len + 1);
109 
110 # undef STATE_COMPACT
111 
112  if (create) {
113  uf_arraystore.users += 1;
114  }
115 }
116 
120 static void uf_arraystore_compact(UndoFont *um, const UndoFont *uf_ref)
121 {
122  uf_arraystore_compact_ex(um, uf_ref, true);
123 }
124 
125 static void uf_arraystore_compact_with_info(UndoFont *um, const UndoFont *uf_ref)
126 {
127 # ifdef DEBUG_PRINT
128  size_t size_expanded_prev, size_compacted_prev;
130  &uf_arraystore.bs_stride, &size_expanded_prev, &size_compacted_prev);
131 # endif
132 
133  uf_arraystore_compact(um, uf_ref);
134 
135 # ifdef DEBUG_PRINT
136  {
137  size_t size_expanded, size_compacted;
139  &uf_arraystore.bs_stride, &size_expanded, &size_compacted);
140 
141  const double percent_total = size_expanded ?
142  (((double)size_compacted / (double)size_expanded) * 100.0) :
143  -1.0;
144 
145  size_t size_expanded_step = size_expanded - size_expanded_prev;
146  size_t size_compacted_step = size_compacted - size_compacted_prev;
147  const double percent_step = size_expanded_step ?
148  (((double)size_compacted_step / (double)size_expanded_step) *
149  100.0) :
150  -1.0;
151 
152  printf("overall memory use: %.8f%% of expanded size\n", percent_total);
153  printf("step memory use: %.8f%% of expanded size\n", percent_step);
154  }
155 # endif
156 }
157 
162 {
163  uf_arraystore_compact_ex(um, NULL, false);
164 }
165 
167 {
168 # define STATE_EXPAND(uf, id, len) \
169  if ((uf)->store.id) { \
170  const size_t stride = sizeof(*(uf)->id); \
171  BArrayState *state = (uf)->store.id; \
172  size_t state_len; \
173  (uf)->id = BLI_array_store_state_data_get_alloc(state, &state_len); \
174  BLI_assert((len) == (state_len / stride)); \
175  UNUSED_VARS_NDEBUG(stride); \
176  } \
177  ((void)0)
178 
179  STATE_EXPAND(uf, textbuf, uf->len + 1);
180  STATE_EXPAND(uf, textbufinfo, uf->len + 1);
181 
182 # undef STATE_EXPAND
183 }
184 
185 static void uf_arraystore_free(UndoFont *uf)
186 {
187 # define STATE_FREE(uf, id) \
188  if ((uf)->store.id) { \
189  const size_t stride = sizeof(*(uf)->id); \
190  BArrayStore *bs = BLI_array_store_at_size_get(&uf_arraystore.bs_stride, stride); \
191  BArrayState *state = (uf)->store.id; \
192  BLI_array_store_state_remove(bs, state); \
193  (uf)->store.id = NULL; \
194  } \
195  ((void)0)
196 
197  STATE_FREE(uf, textbuf);
198  STATE_FREE(uf, textbufinfo);
199 
200 # undef STATE_FREE
201 
202  uf_arraystore.users -= 1;
203 
204  BLI_assert(uf_arraystore.users >= 0);
205 
206  if (uf_arraystore.users == 0) {
207 # ifdef DEBUG_PRINT
208  printf("editfont undo store: freeing all data!\n");
209 # endif
210 
212  }
213 }
214 
217 #endif /* USE_ARRAY_STORE */
218 
219 static void undofont_to_editfont(UndoFont *uf, Curve *cu)
220 {
221  EditFont *ef = cu->editfont;
222 
223  size_t final_size;
224 
225 #ifdef USE_ARRAY_STORE
227 #endif
228 
229  final_size = sizeof(*ef->textbuf) * (uf->len + 1);
230  memcpy(ef->textbuf, uf->textbuf, final_size);
231 
232  final_size = sizeof(CharInfo) * (uf->len + 1);
233  memcpy(ef->textbufinfo, uf->textbufinfo, final_size);
234 
235  ef->pos = uf->pos;
236  ef->selstart = uf->selstart;
237  ef->selend = uf->selend;
238  ef->len = uf->len;
239 
240 #ifdef USE_ARRAY_STORE
242 #endif
243 }
244 
245 static void *undofont_from_editfont(UndoFont *uf, Curve *cu)
246 {
248 
249  EditFont *ef = cu->editfont;
250 
251  size_t mem_used_prev = MEM_get_memory_in_use();
252 
253  size_t final_size;
254 
255  BLI_assert(sizeof(*uf->textbuf) == sizeof(*ef->textbuf));
256  final_size = sizeof(*uf->textbuf) * (ef->len + 1);
257  uf->textbuf = MEM_mallocN(final_size, __func__);
258  memcpy(uf->textbuf, ef->textbuf, final_size);
259 
260  final_size = sizeof(CharInfo) * (ef->len + 1);
261  uf->textbufinfo = MEM_mallocN(final_size, __func__);
262  memcpy(uf->textbufinfo, ef->textbufinfo, final_size);
263 
264  uf->pos = ef->pos;
265  uf->selstart = ef->selstart;
266  uf->selend = ef->selend;
267  uf->len = ef->len;
268 
269 #ifdef USE_ARRAY_STORE
270  {
271  const UndoFont *uf_ref = uf_arraystore.local_links.last ?
272  ((LinkData *)uf_arraystore.local_links.last)->data :
273  NULL;
274 
275  /* Add ourselves. */
276  BLI_addtail(&uf_arraystore.local_links, BLI_genericNodeN(uf));
277 
279  }
280 #endif
281 
282  size_t mem_used_curr = MEM_get_memory_in_use();
283 
284  uf->undo_size = mem_used_prev < mem_used_curr ? mem_used_curr - mem_used_prev : sizeof(UndoFont);
285 
286  return uf;
287 }
288 
289 static void undofont_free_data(UndoFont *uf)
290 {
291 #ifdef USE_ARRAY_STORE
292  {
293  LinkData *link = BLI_findptr(&uf_arraystore.local_links, uf, offsetof(LinkData, data));
294  BLI_remlink(&uf_arraystore.local_links, link);
295  MEM_freeN(link);
296  }
297  uf_arraystore_free(uf);
298 #endif
299 
300  if (uf->textbuf) {
301  MEM_freeN(uf->textbuf);
302  }
303  if (uf->textbufinfo) {
304  MEM_freeN(uf->textbufinfo);
305  }
306 }
307 
309 {
310  ViewLayer *view_layer = CTX_data_view_layer(C);
311  Object *obedit = OBEDIT_FROM_VIEW_LAYER(view_layer);
312  if (obedit && obedit->type == OB_FONT) {
313  Curve *cu = obedit->data;
314  EditFont *ef = cu->editfont;
315  if (ef != NULL) {
316  return obedit;
317  }
318  }
319  return NULL;
320 }
321 
324 /* -------------------------------------------------------------------- */
328 typedef struct FontUndoStep {
330  /* NOTE: will split out into list for multi-object-editmode. */
331  UndoRefID_Object obedit_ref;
334 
336 {
338 }
339 
340 static bool font_undosys_step_encode(struct bContext *C, struct Main *bmain, UndoStep *us_p)
341 {
342  FontUndoStep *us = (FontUndoStep *)us_p;
344  Curve *cu = us->obedit_ref.ptr->data;
345  undofont_from_editfont(&us->data, cu);
346  us->step.data_size = us->data.undo_size;
347  cu->editfont->needs_flush_to_id = 1;
348  bmain->is_memfile_undo_flush_needed = true;
349 
350  return true;
351 }
352 
353 static void font_undosys_step_decode(struct bContext *C,
354  struct Main *bmain,
355  UndoStep *us_p,
356  const eUndoStepDir UNUSED(dir),
357  bool UNUSED(is_final))
358 {
359 
360  FontUndoStep *us = (FontUndoStep *)us_p;
361  Object *obedit = us->obedit_ref.ptr;
362 
363  /* Pass in an array of 1 (typically used for multi-object edit-mode). */
364  ED_undo_object_editmode_restore_helper(C, &obedit, 1, sizeof(Object *));
365 
366  Curve *cu = obedit->data;
367  undofont_to_editfont(&us->data, cu);
369 
371  CTX_data_scene(C), CTX_data_view_layer(C), obedit, us_p->name, &LOG);
372 
374 
375  cu->editfont->needs_flush_to_id = 1;
376  bmain->is_memfile_undo_flush_needed = true;
378 }
379 
381 {
382  FontUndoStep *us = (FontUndoStep *)us_p;
383  undofont_free_data(&us->data);
384 }
385 
387  UndoTypeForEachIDRefFn foreach_ID_ref_fn,
388  void *user_data)
389 {
390  FontUndoStep *us = (FontUndoStep *)us_p;
391  foreach_ID_ref_fn(user_data, ((UndoRefID *)&us->obedit_ref));
392 }
393 
395 {
396  ut->name = "Edit Font";
397  ut->poll = font_undosys_poll;
401 
403 
405 
406  ut->step_size = sizeof(FontUndoStep);
407 }
408 
struct Scene * CTX_data_scene(const bContext *C)
Definition: context.c:1090
struct ViewLayer * CTX_data_view_layer(const bContext *C)
Definition: context.c:1100
@ UNDOTYPE_FLAG_NEED_CONTEXT_FOR_ENCODE
eUndoStepDir
void(* UndoTypeForEachIDRefFn)(void *user_data, struct UndoRefID *id_ref)
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(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:46
struct LinkData * BLI_genericNodeN(void *data)
Definition: listbase.c:842
void BLI_addtail(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition: listbase.c:80
void BLI_remlink(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition: listbase.c:100
void * BLI_findptr(const struct ListBase *listbase, const void *ptr, int offset) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
#define UNUSED(x)
typedef double(DMatrix)[4][4]
void DEG_id_tag_update(struct ID *id, int flag)
@ ID_RECALC_GEOMETRY
Definition: DNA_ID.h:791
struct CharInfo CharInfo
Object is a sort of wrapper for general info.
@ OB_FONT
#define OBEDIT_FROM_VIEW_LAYER(view_layer)
void ED_undo_object_editmode_restore_helper(struct bContext *C, struct Object **object_array, uint object_array_len, uint object_array_stride)
Definition: ed_undo.c:817
void ED_undo_object_set_active_or_warn(struct Scene *scene, struct ViewLayer *view_layer, struct Object *ob, const char *info, struct CLG_LogRef *log)
Definition: ed_undo.c:800
Read Guarded memory(de)allocation.
#define C
Definition: RandGen.cpp:25
#define NC_GEOM
Definition: WM_types.h:343
#define ND_DATA
Definition: WM_types.h:456
void * user_data
#define STATE_COMPACT(uf, id, len)
static bool font_undosys_step_encode(struct bContext *C, struct Main *bmain, UndoStep *us_p)
static Object * editfont_object_from_context(bContext *C)
static struct @342 uf_arraystore
#define STATE_FREE(uf, id)
static void uf_arraystore_expand(UndoFont *uf)
ListBase local_links
Definition: editfont_undo.c:79
static void font_undosys_step_free(UndoStep *us_p)
static void uf_arraystore_expand_clear(UndoFont *um)
static void font_undosys_step_decode(struct bContext *C, struct Main *bmain, UndoStep *us_p, const eUndoStepDir UNUSED(dir), bool UNUSED(is_final))
static void undofont_free_data(UndoFont *uf)
struct UndoFont UndoFont
static void * undofont_from_editfont(UndoFont *uf, Curve *cu)
static void uf_arraystore_compact_with_info(UndoFont *um, const UndoFont *uf_ref)
static void uf_arraystore_compact(UndoFont *um, const UndoFont *uf_ref)
struct FontUndoStep FontUndoStep
static void uf_arraystore_free(UndoFont *uf)
int users
Definition: editfont_undo.c:76
static CLG_LogRef LOG
Definition: editfont_undo.c:46
static bool font_undosys_poll(bContext *C)
struct BArrayStore_AtSize bs_stride
Definition: editfont_undo.c:75
static void font_undosys_foreach_ID_ref(UndoStep *us_p, UndoTypeForEachIDRefFn foreach_ID_ref_fn, void *user_data)
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)
Definition: editfont_undo.c:88
void(* MEM_freeN)(void *vmemh)
Definition: mallocn.c:27
size_t(* MEM_get_memory_in_use)(void)
Definition: mallocn.c:45
void *(* MEM_mallocN)(size_t len, const char *str)
Definition: mallocn.c:33
std::unique_ptr< IDProperty, IDPropertyDeleter > create(StringRefNull prop_name, int32_t value)
Allocate a new IDProperty of type IDP_INT, set its name and value.
struct EditFont * editfont
int selend
Definition: BKE_vfont.h:43
int pos
Definition: BKE_vfont.h:42
int len
Definition: BKE_vfont.h:42
char32_t * textbuf
Definition: BKE_vfont.h:32
int selstart
Definition: BKE_vfont.h:43
struct CharInfo * textbufinfo
Definition: BKE_vfont.h:33
char needs_flush_to_id
Definition: BKE_vfont.h:49
UndoRefID_Object obedit_ref
UndoFont data
UndoStep step
Definition: BKE_main.h:121
char is_memfile_undo_flush_needed
Definition: BKE_main.h:145
void * data
char32_t * textbuf
Definition: editfont_undo.c:53
struct CharInfo * textbufinfo
Definition: editfont_undo.c:54
BArrayState * textbufinfo
Definition: editfont_undo.c:61
struct UndoFont::@343 store
int selstart
Definition: editfont_undo.c:56
size_t undo_size
Definition: editfont_undo.c:65
BArrayState * textbuf
Definition: editfont_undo.c:60
size_t data_size
char name[64]
size_t step_size
void(* step_decode)(struct bContext *C, struct Main *bmain, UndoStep *us, eUndoStepDir dir, bool is_final)
bool(* step_encode)(struct bContext *C, struct Main *bmain, UndoStep *us)
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 WM_event_add_notifier(const bContext *C, uint type, void *reference)