Blender  V2.93
editfont_undo.c
Go to the documentation of this file.
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  */
16 
21 #include <stdlib.h>
22 #include <string.h>
23 
24 #include "MEM_guardedalloc.h"
25 
26 #include "CLG_log.h"
27 
28 #include "BLI_array_utils.h"
29 #include "BLI_utildefines.h"
30 
31 #include "DNA_curve_types.h"
32 #include "DNA_object_types.h"
33 #include "DNA_scene_types.h"
34 
35 #include "BKE_context.h"
36 #include "BKE_font.h"
37 #include "BKE_main.h"
38 #include "BKE_undo_system.h"
39 
40 #include "DEG_depsgraph.h"
41 
42 #include "ED_curve.h"
43 #include "ED_object.h"
44 #include "ED_undo.h"
45 
46 #include "WM_api.h"
47 #include "WM_types.h"
48 
49 #define USE_ARRAY_STORE
50 
51 #ifdef USE_ARRAY_STORE
52 // # define DEBUG_PRINT
53 # include "BLI_array_store.h"
54 # include "BLI_array_store_utils.h"
55 # include "BLI_listbase.h"
56 # define ARRAY_CHUNK_SIZE 32
57 #endif
58 
60 static CLG_LogRef LOG = {"ed.undo.font"};
61 
62 /* -------------------------------------------------------------------- */
66 typedef struct UndoFont {
67  char32_t *textbuf;
69 
71 
72 #ifdef USE_ARRAY_STORE
73  struct {
76  } store;
77 #endif
78 
79  size_t undo_size;
81 
82 #ifdef USE_ARRAY_STORE
83 
84 /* -------------------------------------------------------------------- */
88 static struct {
90  int users;
91 
92  /* We could have the undo API pass in the previous state, for now store a local list */
94 
96 
102 static void uf_arraystore_compact_ex(UndoFont *uf, const UndoFont *uf_ref, bool create)
103 {
104 # define STATE_COMPACT(uf, id, len) \
105  if ((uf)->id) { \
106  BLI_assert(create == ((uf)->store.id == NULL)); \
107  if (create) { \
108  BArrayState *state_reference = uf_ref ? uf_ref->store.id : NULL; \
109  const size_t stride = sizeof(*(uf)->id); \
110  BArrayStore *bs = BLI_array_store_at_size_ensure( \
111  &uf_arraystore.bs_stride, stride, ARRAY_CHUNK_SIZE); \
112  (uf)->store.id = BLI_array_store_state_add( \
113  bs, (uf)->id, (size_t)(len)*stride, state_reference); \
114  } \
115  /* keep uf->len for validation */ \
116  MEM_freeN((uf)->id); \
117  (uf)->id = NULL; \
118  } \
119  ((void)0)
120 
121  STATE_COMPACT(uf, textbuf, uf->len + 1);
122  STATE_COMPACT(uf, textbufinfo, uf->len + 1);
123 
124 # undef STATE_COMPACT
125 
126  if (create) {
127  uf_arraystore.users += 1;
128  }
129 }
130 
134 static void uf_arraystore_compact(UndoFont *um, const UndoFont *uf_ref)
135 {
136  uf_arraystore_compact_ex(um, uf_ref, true);
137 }
138 
139 static void uf_arraystore_compact_with_info(UndoFont *um, const UndoFont *uf_ref)
140 {
141 # ifdef DEBUG_PRINT
142  size_t size_expanded_prev, size_compacted_prev;
144  &uf_arraystore.bs_stride, &size_expanded_prev, &size_compacted_prev);
145 # endif
146 
147  uf_arraystore_compact(um, uf_ref);
148 
149 # ifdef DEBUG_PRINT
150  {
151  size_t size_expanded, size_compacted;
153  &uf_arraystore.bs_stride, &size_expanded, &size_compacted);
154 
155  const double percent_total = size_expanded ?
156  (((double)size_compacted / (double)size_expanded) * 100.0) :
157  -1.0;
158 
159  size_t size_expanded_step = size_expanded - size_expanded_prev;
160  size_t size_compacted_step = size_compacted - size_compacted_prev;
161  const double percent_step = size_expanded_step ?
162  (((double)size_compacted_step / (double)size_expanded_step) *
163  100.0) :
164  -1.0;
165 
166  printf("overall memory use: %.8f%% of expanded size\n", percent_total);
167  printf("step memory use: %.8f%% of expanded size\n", percent_step);
168  }
169 # endif
170 }
171 
176 {
177  uf_arraystore_compact_ex(um, NULL, false);
178 }
179 
181 {
182 # define STATE_EXPAND(uf, id, len) \
183  if ((uf)->store.id) { \
184  const size_t stride = sizeof(*(uf)->id); \
185  BArrayState *state = (uf)->store.id; \
186  size_t state_len; \
187  (uf)->id = BLI_array_store_state_data_get_alloc(state, &state_len); \
188  BLI_assert((len) == (state_len / stride)); \
189  UNUSED_VARS_NDEBUG(stride); \
190  } \
191  ((void)0)
192 
193  STATE_EXPAND(uf, textbuf, uf->len + 1);
194  STATE_EXPAND(uf, textbufinfo, uf->len + 1);
195 
196 # undef STATE_EXPAND
197 }
198 
199 static void uf_arraystore_free(UndoFont *uf)
200 {
201 # define STATE_FREE(uf, id) \
202  if ((uf)->store.id) { \
203  const size_t stride = sizeof(*(uf)->id); \
204  BArrayStore *bs = BLI_array_store_at_size_get(&uf_arraystore.bs_stride, stride); \
205  BArrayState *state = (uf)->store.id; \
206  BLI_array_store_state_remove(bs, state); \
207  (uf)->store.id = NULL; \
208  } \
209  ((void)0)
210 
211  STATE_FREE(uf, textbuf);
212  STATE_FREE(uf, textbufinfo);
213 
214 # undef STATE_FREE
215 
216  uf_arraystore.users -= 1;
217 
218  BLI_assert(uf_arraystore.users >= 0);
219 
220  if (uf_arraystore.users == 0) {
221 # ifdef DEBUG_PRINT
222  printf("editfont undo store: freeing all data!\n");
223 # endif
224 
226  }
227 }
228 
231 #endif /* USE_ARRAY_STORE */
232 
233 static void undofont_to_editfont(UndoFont *uf, Curve *cu)
234 {
235  EditFont *ef = cu->editfont;
236 
237  size_t final_size;
238 
239 #ifdef USE_ARRAY_STORE
241 #endif
242 
243  final_size = sizeof(*ef->textbuf) * (uf->len + 1);
244  memcpy(ef->textbuf, uf->textbuf, final_size);
245 
246  final_size = sizeof(CharInfo) * (uf->len + 1);
247  memcpy(ef->textbufinfo, uf->textbufinfo, final_size);
248 
249  ef->pos = uf->pos;
250  ef->selstart = uf->selstart;
251  ef->selend = uf->selend;
252  ef->len = uf->len;
253 
254 #ifdef USE_ARRAY_STORE
256 #endif
257 }
258 
259 static void *undofont_from_editfont(UndoFont *uf, Curve *cu)
260 {
262 
263  EditFont *ef = cu->editfont;
264 
265  size_t mem_used_prev = MEM_get_memory_in_use();
266 
267  size_t final_size;
268 
269  BLI_assert(sizeof(*uf->textbuf) == sizeof(*ef->textbuf));
270  final_size = sizeof(*uf->textbuf) * (ef->len + 1);
271  uf->textbuf = MEM_mallocN(final_size, __func__);
272  memcpy(uf->textbuf, ef->textbuf, final_size);
273 
274  final_size = sizeof(CharInfo) * (ef->len + 1);
275  uf->textbufinfo = MEM_mallocN(final_size, __func__);
276  memcpy(uf->textbufinfo, ef->textbufinfo, final_size);
277 
278  uf->pos = ef->pos;
279  uf->selstart = ef->selstart;
280  uf->selend = ef->selend;
281  uf->len = ef->len;
282 
283 #ifdef USE_ARRAY_STORE
284  {
285  const UndoFont *uf_ref = uf_arraystore.local_links.last ?
286  ((LinkData *)uf_arraystore.local_links.last)->data :
287  NULL;
288 
289  /* Add ourselves. */
290  BLI_addtail(&uf_arraystore.local_links, BLI_genericNodeN(uf));
291 
293  }
294 #endif
295 
296  size_t mem_used_curr = MEM_get_memory_in_use();
297 
298  uf->undo_size = mem_used_prev < mem_used_curr ? mem_used_curr - mem_used_prev : sizeof(UndoFont);
299 
300  return uf;
301 }
302 
303 static void undofont_free_data(UndoFont *uf)
304 {
305 #ifdef USE_ARRAY_STORE
306  {
307  LinkData *link = BLI_findptr(&uf_arraystore.local_links, uf, offsetof(LinkData, data));
308  BLI_remlink(&uf_arraystore.local_links, link);
309  MEM_freeN(link);
310  }
311  uf_arraystore_free(uf);
312 #endif
313 
314  if (uf->textbuf) {
315  MEM_freeN(uf->textbuf);
316  }
317  if (uf->textbufinfo) {
318  MEM_freeN(uf->textbufinfo);
319  }
320 }
321 
323 {
324  ViewLayer *view_layer = CTX_data_view_layer(C);
325  Object *obedit = OBEDIT_FROM_VIEW_LAYER(view_layer);
326  if (obedit && obedit->type == OB_FONT) {
327  Curve *cu = obedit->data;
328  EditFont *ef = cu->editfont;
329  if (ef != NULL) {
330  return obedit;
331  }
332  }
333  return NULL;
334 }
335 
338 /* -------------------------------------------------------------------- */
342 typedef struct FontUndoStep {
344  /* note: will split out into list for multi-object-editmode. */
345  UndoRefID_Object obedit_ref;
348 
350 {
352 }
353 
354 static bool font_undosys_step_encode(struct bContext *C, struct Main *bmain, UndoStep *us_p)
355 {
356  FontUndoStep *us = (FontUndoStep *)us_p;
358  Curve *cu = us->obedit_ref.ptr->data;
359  undofont_from_editfont(&us->data, cu);
360  us->step.data_size = us->data.undo_size;
361  cu->editfont->needs_flush_to_id = 1;
362  bmain->is_memfile_undo_flush_needed = true;
363 
364  return true;
365 }
366 
367 static void font_undosys_step_decode(struct bContext *C,
368  struct Main *bmain,
369  UndoStep *us_p,
370  const eUndoStepDir UNUSED(dir),
371  bool UNUSED(is_final))
372 {
373 
374  FontUndoStep *us = (FontUndoStep *)us_p;
375  Object *obedit = us->obedit_ref.ptr;
376 
377  /* Pass in an array of 1 (typically used for multi-object edit-mode). */
378  ED_undo_object_editmode_restore_helper(C, &obedit, 1, sizeof(Object *));
379 
380  Curve *cu = obedit->data;
381  undofont_to_editfont(&us->data, cu);
383 
385  CTX_data_scene(C), CTX_data_view_layer(C), obedit, us_p->name, &LOG);
386 
388 
389  cu->editfont->needs_flush_to_id = 1;
390  bmain->is_memfile_undo_flush_needed = true;
392 }
393 
395 {
396  FontUndoStep *us = (FontUndoStep *)us_p;
397  undofont_free_data(&us->data);
398 }
399 
401  UndoTypeForEachIDRefFn foreach_ID_ref_fn,
402  void *user_data)
403 {
404  FontUndoStep *us = (FontUndoStep *)us_p;
405  foreach_ID_ref_fn(user_data, ((UndoRefID *)&us->obedit_ref));
406 }
407 
408 /* Export for ED_undo_sys. */
410 {
411  ut->name = "Edit Font";
412  ut->poll = font_undosys_poll;
416 
418 
420 
421  ut->step_size = sizeof(FontUndoStep);
422 }
423 
struct Scene * CTX_data_scene(const bContext *C)
Definition: context.c:1034
struct ViewLayer * CTX_data_view_layer(const bContext *C)
Definition: context.c:1044
eUndoStepDir
@ UNDOTYPE_FLAG_NEED_CONTEXT_FOR_ENCODE
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:58
struct LinkData * BLI_genericNodeN(void *data)
Definition: listbase.c:923
void BLI_addtail(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition: listbase.c:110
void BLI_remlink(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition: listbase.c:133
void * BLI_findptr(const struct ListBase *listbase, const void *ptr, const 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:611
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:897
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:877
Read Guarded memory(de)allocation.
#define C
Definition: RandGen.cpp:39
#define NC_GEOM
Definition: WM_types.h:294
#define ND_DATA
Definition: WM_types.h:408
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)
#define STATE_FREE(uf, id)
static void uf_arraystore_expand(UndoFont *uf)
ListBase local_links
Definition: editfont_undo.c:93
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:90
static struct @318 uf_arraystore
static CLG_LogRef LOG
Definition: editfont_undo.c:60
static bool font_undosys_poll(bContext *C)
struct BArrayStore_AtSize bs_stride
Definition: editfont_undo.c:89
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)
void(* MEM_freeN)(void *vmemh)
Definition: mallocn.c:41
size_t(* MEM_get_memory_in_use)(void)
Definition: mallocn.c:59
void *(* MEM_mallocN)(size_t len, const char *str)
Definition: mallocn.c:47
struct EditFont * editfont
int selend
Definition: BKE_font.h:59
int pos
Definition: BKE_font.h:58
int len
Definition: BKE_font.h:58
char32_t * textbuf
Definition: BKE_font.h:48
int selstart
Definition: BKE_font.h:59
struct CharInfo * textbufinfo
Definition: BKE_font.h:49
char needs_flush_to_id
Definition: BKE_font.h:65
UndoRefID_Object obedit_ref
UndoFont data
UndoStep step
Definition: BKE_main.h:116
char is_memfile_undo_flush_needed
Definition: BKE_main.h:130
void * data
char32_t * textbuf
Definition: editfont_undo.c:67
struct CharInfo * textbufinfo
Definition: editfont_undo.c:68
BArrayState * textbufinfo
Definition: editfont_undo.c:75
struct UndoFont::@319 store
int selstart
Definition: editfont_undo.c:70
size_t undo_size
Definition: editfont_undo.c:79
BArrayState * textbuf
Definition: editfont_undo.c:74
size_t data_size
char name[64]
size_t step_size
void(* step_decode)(struct bContext *C, struct Main *bmain, UndoStep *us, const 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)