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