Cleanup: Move scenes' foreach_id handling of toolsettings into own func.
[blender.git] / source / blender / editors / curve / editfont_undo.c
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
17 /** \file
18  * \ingroup edcurve
19  */
20
21 #include <stdlib.h>
22 #include <string.h>
23
24 #include "MEM_guardedalloc.h"
25
26 #include "BLI_array_utils.h"
27 #include "BLI_utildefines.h"
28
29 #include "DNA_curve_types.h"
30 #include "DNA_object_types.h"
31
32 #include "BKE_context.h"
33 #include "BKE_font.h"
34 #include "BKE_main.h"
35 #include "BKE_undo_system.h"
36
37 #include "DEG_depsgraph.h"
38
39 #include "ED_curve.h"
40 #include "ED_object.h"
41
42 #include "WM_api.h"
43 #include "WM_types.h"
44
45 #define USE_ARRAY_STORE
46
47 #ifdef USE_ARRAY_STORE
48 // #  define DEBUG_PRINT
49 #  include "BLI_array_store.h"
50 #  include "BLI_array_store_utils.h"
51 #  include "BLI_listbase.h"
52 #  define ARRAY_CHUNK_SIZE 32
53 #endif
54
55 /* -------------------------------------------------------------------- */
56 /** \name Undo Conversion
57  * \{ */
58
59 typedef struct UndoFont {
60   char32_t *textbuf;
61   struct CharInfo *textbufinfo;
62
63   int len, pos, selstart, selend;
64
65 #ifdef USE_ARRAY_STORE
66   struct {
67     BArrayState *textbuf;
68     BArrayState *textbufinfo;
69   } store;
70 #endif
71
72   size_t undo_size;
73 } UndoFont;
74
75 #ifdef USE_ARRAY_STORE
76
77 /** \name Array Store
78  * \{ */
79
80 static struct {
81   struct BArrayStore_AtSize bs_stride;
82   int users;
83
84   /* We could have the undo API pass in the previous state, for now store a local list */
85   ListBase local_links;
86
87 } uf_arraystore = {{NULL}};
88
89 /**
90  * \param create: When false, only free the arrays.
91  * This is done since when reading from an undo state, they must be temporarily expanded.
92  * then discarded afterwards, having this argument avoids having 2x code paths.
93  */
94 static void uf_arraystore_compact_ex(UndoFont *uf, const UndoFont *uf_ref, bool create)
95 {
96 #  define STATE_COMPACT(uf, id, len) \
97     if ((uf)->id) { \
98       BLI_assert(create == ((uf)->store.id == NULL)); \
99       if (create) { \
100         BArrayState *state_reference = uf_ref ? uf_ref->store.id : NULL; \
101         const size_t stride = sizeof(*(uf)->id); \
102         BArrayStore *bs = BLI_array_store_at_size_ensure( \
103             &uf_arraystore.bs_stride, stride, ARRAY_CHUNK_SIZE); \
104         (uf)->store.id = BLI_array_store_state_add( \
105             bs, (uf)->id, (size_t)(len)*stride, state_reference); \
106       } \
107       /* keep uf->len for validation */ \
108       MEM_freeN((uf)->id); \
109       (uf)->id = NULL; \
110     } \
111     ((void)0)
112
113   STATE_COMPACT(uf, textbuf, uf->len + 1);
114   STATE_COMPACT(uf, textbufinfo, uf->len + 1);
115
116 #  undef STATE_COMPACT
117
118   if (create) {
119     uf_arraystore.users += 1;
120   }
121 }
122
123 /**
124  * Move data from allocated arrays to de-duplicated states and clear arrays.
125  */
126 static void uf_arraystore_compact(UndoFont *um, const UndoFont *uf_ref)
127 {
128   uf_arraystore_compact_ex(um, uf_ref, true);
129 }
130
131 static void uf_arraystore_compact_with_info(UndoFont *um, const UndoFont *uf_ref)
132 {
133 #  ifdef DEBUG_PRINT
134   size_t size_expanded_prev, size_compacted_prev;
135   BLI_array_store_at_size_calc_memory_usage(
136       &uf_arraystore.bs_stride, &size_expanded_prev, &size_compacted_prev);
137 #  endif
138
139   uf_arraystore_compact(um, uf_ref);
140
141 #  ifdef DEBUG_PRINT
142   {
143     size_t size_expanded, size_compacted;
144     BLI_array_store_at_size_calc_memory_usage(
145         &uf_arraystore.bs_stride, &size_expanded, &size_compacted);
146
147     const double percent_total = size_expanded ?
148                                      (((double)size_compacted / (double)size_expanded) * 100.0) :
149                                      -1.0;
150
151     size_t size_expanded_step = size_expanded - size_expanded_prev;
152     size_t size_compacted_step = size_compacted - size_compacted_prev;
153     const double percent_step = size_expanded_step ?
154                                     (((double)size_compacted_step / (double)size_expanded_step) *
155                                      100.0) :
156                                     -1.0;
157
158     printf("overall memory use: %.8f%% of expanded size\n", percent_total);
159     printf("step memory use:    %.8f%% of expanded size\n", percent_step);
160   }
161 #  endif
162 }
163
164 /**
165  * Remove data we only expanded for temporary use.
166  */
167 static void uf_arraystore_expand_clear(UndoFont *um)
168 {
169   uf_arraystore_compact_ex(um, NULL, false);
170 }
171
172 static void uf_arraystore_expand(UndoFont *uf)
173 {
174 #  define STATE_EXPAND(uf, id, len) \
175     if ((uf)->store.id) { \
176       const size_t stride = sizeof(*(uf)->id); \
177       BArrayState *state = (uf)->store.id; \
178       size_t state_len; \
179       (uf)->id = BLI_array_store_state_data_get_alloc(state, &state_len); \
180       BLI_assert((len) == (state_len / stride)); \
181       UNUSED_VARS_NDEBUG(stride); \
182     } \
183     ((void)0)
184
185   STATE_EXPAND(uf, textbuf, uf->len + 1);
186   STATE_EXPAND(uf, textbufinfo, uf->len + 1);
187
188 #  undef STATE_EXPAND
189 }
190
191 static void uf_arraystore_free(UndoFont *uf)
192 {
193 #  define STATE_FREE(uf, id) \
194     if ((uf)->store.id) { \
195       const size_t stride = sizeof(*(uf)->id); \
196       BArrayStore *bs = BLI_array_store_at_size_get(&uf_arraystore.bs_stride, stride); \
197       BArrayState *state = (uf)->store.id; \
198       BLI_array_store_state_remove(bs, state); \
199       (uf)->store.id = NULL; \
200     } \
201     ((void)0)
202
203   STATE_FREE(uf, textbuf);
204   STATE_FREE(uf, textbufinfo);
205
206 #  undef STATE_FREE
207
208   uf_arraystore.users -= 1;
209
210   BLI_assert(uf_arraystore.users >= 0);
211
212   if (uf_arraystore.users == 0) {
213 #  ifdef DEBUG_PRINT
214     printf("editfont undo store: freeing all data!\n");
215 #  endif
216
217     BLI_array_store_at_size_clear(&uf_arraystore.bs_stride);
218   }
219 }
220
221 /** \} */
222
223 #endif /* USE_ARRAY_STORE */
224
225 static void undofont_to_editfont(UndoFont *uf, Curve *cu)
226 {
227   EditFont *ef = cu->editfont;
228
229   size_t final_size;
230
231 #ifdef USE_ARRAY_STORE
232   uf_arraystore_expand(uf);
233 #endif
234
235   final_size = sizeof(*ef->textbuf) * (uf->len + 1);
236   memcpy(ef->textbuf, uf->textbuf, final_size);
237
238   final_size = sizeof(CharInfo) * (uf->len + 1);
239   memcpy(ef->textbufinfo, uf->textbufinfo, final_size);
240
241   ef->pos = uf->pos;
242   ef->selstart = uf->selstart;
243   ef->selend = uf->selend;
244   ef->len = uf->len;
245
246 #ifdef USE_ARRAY_STORE
247   uf_arraystore_expand_clear(uf);
248 #endif
249 }
250
251 static void *undofont_from_editfont(UndoFont *uf, Curve *cu)
252 {
253   BLI_assert(BLI_array_is_zeroed(uf, 1));
254
255   EditFont *ef = cu->editfont;
256
257   size_t mem_used_prev = MEM_get_memory_in_use();
258
259   size_t final_size;
260
261   BLI_assert(sizeof(*uf->textbuf) == sizeof(*ef->textbuf));
262   final_size = sizeof(*uf->textbuf) * (ef->len + 1);
263   uf->textbuf = MEM_mallocN(final_size, __func__);
264   memcpy(uf->textbuf, ef->textbuf, final_size);
265
266   final_size = sizeof(CharInfo) * (ef->len + 1);
267   uf->textbufinfo = MEM_mallocN(final_size, __func__);
268   memcpy(uf->textbufinfo, ef->textbufinfo, final_size);
269
270   uf->pos = ef->pos;
271   uf->selstart = ef->selstart;
272   uf->selend = ef->selend;
273   uf->len = ef->len;
274
275 #ifdef USE_ARRAY_STORE
276   {
277     const UndoFont *uf_ref = uf_arraystore.local_links.last ?
278                                  ((LinkData *)uf_arraystore.local_links.last)->data :
279                                  NULL;
280
281     /* add oursrlves */
282     BLI_addtail(&uf_arraystore.local_links, BLI_genericNodeN(uf));
283
284     uf_arraystore_compact_with_info(uf, uf_ref);
285   }
286 #endif
287
288   size_t mem_used_curr = MEM_get_memory_in_use();
289
290   uf->undo_size = mem_used_prev < mem_used_curr ? mem_used_curr - mem_used_prev : sizeof(UndoFont);
291
292   return uf;
293 }
294
295 static void undofont_free_data(UndoFont *uf)
296 {
297 #ifdef USE_ARRAY_STORE
298   {
299     LinkData *link = BLI_findptr(&uf_arraystore.local_links, uf, offsetof(LinkData, data));
300     BLI_remlink(&uf_arraystore.local_links, link);
301     MEM_freeN(link);
302   }
303   uf_arraystore_free(uf);
304 #endif
305
306   if (uf->textbuf) {
307     MEM_freeN(uf->textbuf);
308   }
309   if (uf->textbufinfo) {
310     MEM_freeN(uf->textbufinfo);
311   }
312 }
313
314 static Object *editfont_object_from_context(bContext *C)
315 {
316   Object *obedit = CTX_data_edit_object(C);
317   if (obedit && obedit->type == OB_FONT) {
318     Curve *cu = obedit->data;
319     EditFont *ef = cu->editfont;
320     if (ef != NULL) {
321       return obedit;
322     }
323   }
324   return NULL;
325 }
326
327 /** \} */
328
329 /* -------------------------------------------------------------------- */
330 /** \name Implements ED Undo System
331  * \{ */
332
333 typedef struct FontUndoStep {
334   UndoStep step;
335   /* note: will split out into list for multi-object-editmode. */
336   UndoRefID_Object obedit_ref;
337   UndoFont data;
338 } FontUndoStep;
339
340 static bool font_undosys_poll(bContext *C)
341 {
342   return editfont_object_from_context(C) != NULL;
343 }
344
345 static bool font_undosys_step_encode(struct bContext *C, struct Main *bmain, UndoStep *us_p)
346 {
347   FontUndoStep *us = (FontUndoStep *)us_p;
348   us->obedit_ref.ptr = editfont_object_from_context(C);
349   Curve *cu = us->obedit_ref.ptr->data;
350   undofont_from_editfont(&us->data, cu);
351   us->step.data_size = us->data.undo_size;
352   cu->editfont->needs_flush_to_id = 1;
353   bmain->is_memfile_undo_flush_needed = true;
354
355   return true;
356 }
357
358 static void font_undosys_step_decode(
359     struct bContext *C, struct Main *bmain, UndoStep *us_p, int UNUSED(dir), bool UNUSED(is_final))
360 {
361   /* TODO(campbell): undo_system: use low-level API to set mode. */
362   ED_object_mode_set_ex(C, OB_MODE_EDIT, false, NULL);
363   BLI_assert(font_undosys_poll(C));
364
365   FontUndoStep *us = (FontUndoStep *)us_p;
366   Object *obedit = us->obedit_ref.ptr;
367   Curve *cu = obedit->data;
368   undofont_to_editfont(&us->data, cu);
369   DEG_id_tag_update(&obedit->id, ID_RECALC_GEOMETRY);
370   cu->editfont->needs_flush_to_id = 1;
371   bmain->is_memfile_undo_flush_needed = true;
372   WM_event_add_notifier(C, NC_GEOM | ND_DATA, NULL);
373 }
374
375 static void font_undosys_step_free(UndoStep *us_p)
376 {
377   FontUndoStep *us = (FontUndoStep *)us_p;
378   undofont_free_data(&us->data);
379 }
380
381 static void font_undosys_foreach_ID_ref(UndoStep *us_p,
382                                         UndoTypeForEachIDRefFn foreach_ID_ref_fn,
383                                         void *user_data)
384 {
385   FontUndoStep *us = (FontUndoStep *)us_p;
386   foreach_ID_ref_fn(user_data, ((UndoRefID *)&us->obedit_ref));
387 }
388
389 /* Export for ED_undo_sys. */
390 void ED_font_undosys_type(UndoType *ut)
391 {
392   ut->name = "Edit Font";
393   ut->poll = font_undosys_poll;
394   ut->step_encode = font_undosys_step_encode;
395   ut->step_decode = font_undosys_step_decode;
396   ut->step_free = font_undosys_step_free;
397
398   ut->step_foreach_ID_ref = font_undosys_foreach_ID_ref;
399
400   ut->use_context = true;
401
402   ut->step_size = sizeof(FontUndoStep);
403 }
404
405 /** \} */