a0453f9694d2c8372f907c6b2dbeee7fb6df62b7
[blender.git] / source / blender / editors / curve / editfont_undo.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * ***** END GPL LICENSE BLOCK *****
19  */
20
21 /** \file blender/editors/curve/editfont_undo.c
22  *  \ingroup edcurve
23  */
24
25 #include <stdlib.h>
26 #include <string.h>
27 #include <wchar.h>
28
29 #include "MEM_guardedalloc.h"
30
31 #include "BLI_utildefines.h"
32
33 #include "DNA_curve_types.h"
34 #include "DNA_object_types.h"
35 #include "DNA_scene_types.h"
36
37 #include "BKE_context.h"
38 #include "BKE_font.h"
39
40 #include "ED_curve.h"
41 #include "ED_util.h"
42
43 #define USE_ARRAY_STORE
44
45 #ifdef USE_ARRAY_STORE
46 // #  define DEBUG_PRINT
47 #  include "BLI_array_store.h"
48 #  include "BLI_array_store_utils.h"
49 #  include "BLI_listbase.h"
50 #  define ARRAY_CHUNK_SIZE 32
51 #endif
52
53 typedef struct UndoFont {
54         wchar_t *textbuf;
55         struct CharInfo *textbufinfo;
56
57         int len, pos;
58
59 #ifdef USE_ARRAY_STORE
60         struct {
61                 BArrayState *textbuf;
62                 BArrayState *textbufinfo;
63         } store;
64 #endif
65 } UndoFont;
66
67
68 #ifdef USE_ARRAY_STORE
69
70 /** \name Array Store
71  * \{ */
72
73 static struct {
74         struct BArrayStore_AtSize bs_stride;
75         int users;
76
77         /* We could have the undo API pass in the previous state, for now store a local list */
78         ListBase local_links;
79
80 } uf_arraystore = {NULL};
81
82 /**
83  * \param create: When false, only free the arrays.
84  * This is done since when reading from an undo state, they must be temporarily expanded.
85  * then discarded afterwards, having this argument avoids having 2x code paths.
86  */
87 static void uf_arraystore_compact_ex(
88         UndoFont *uf, const UndoFont *uf_ref,
89         bool create)
90 {
91 #define STATE_COMPACT(uf, id, len) \
92         if ((uf)->id) { \
93                 BLI_assert(create == ((uf)->store.id == NULL)); \
94                 if (create) { \
95                         BArrayState *state_reference = uf_ref ? uf_ref->store.id : NULL; \
96                         const size_t stride = sizeof(*(uf)->id); \
97                         BArrayStore *bs = BLI_array_store_at_size_ensure(&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         } ((void)0)
105
106         STATE_COMPACT(uf, textbuf, uf->len + 1);
107         STATE_COMPACT(uf, textbufinfo, uf->len + 1);
108
109 #undef STATE_COMPACT
110
111         if (create) {
112                 uf_arraystore.users += 1;
113         }
114 }
115
116 /**
117  * Move data from allocated arrays to de-duplicated states and clear arrays.
118  */
119 static void uf_arraystore_compact(UndoFont *um, const UndoFont *uf_ref)
120 {
121         uf_arraystore_compact_ex(um, uf_ref, true);
122 }
123
124 static void uf_arraystore_compact_with_info(UndoFont *um, const UndoFont *uf_ref)
125 {
126 #ifdef DEBUG_PRINT
127         size_t size_expanded_prev, size_compacted_prev;
128         BLI_array_store_at_size_calc_memory_usage(&uf_arraystore.bs_stride, &size_expanded_prev, &size_compacted_prev);
129 #endif
130
131         uf_arraystore_compact(um, uf_ref);
132
133 #ifdef DEBUG_PRINT
134         {
135                 size_t size_expanded, size_compacted;
136                 BLI_array_store_at_size_calc_memory_usage(&uf_arraystore.bs_stride, &size_expanded, &size_compacted);
137
138                 const double percent_total = size_expanded ?
139                         (((double)size_compacted / (double)size_expanded) * 100.0) : -1.0;
140
141                 size_t size_expanded_step = size_expanded - size_expanded_prev;
142                 size_t size_compacted_step = size_compacted - size_compacted_prev;
143                 const double percent_step = size_expanded_step ?
144                         (((double)size_compacted_step / (double)size_expanded_step) * 100.0) : -1.0;
145
146                 printf("overall memory use: %.8f%% of expanded size\n", percent_total);
147                 printf("step memory use:    %.8f%% of expanded size\n", percent_step);
148         }
149 #endif
150 }
151
152 /**
153  * Remove data we only expanded for temporary use.
154  */
155 static void uf_arraystore_expand_clear(UndoFont *um)
156 {
157         uf_arraystore_compact_ex(um, NULL, false);
158 }
159
160 static void uf_arraystore_expand(UndoFont *uf)
161 {
162 #define STATE_EXPAND(uf, id, len) \
163         if ((uf)->store.id) { \
164                 const size_t stride = sizeof(*(uf)->id); \
165                 BArrayState *state = (uf)->store.id; \
166                 size_t state_len; \
167                 (uf)->id = BLI_array_store_state_data_get_alloc(state, &state_len); \
168                 BLI_assert((len) == (state_len / stride)); \
169                 UNUSED_VARS_NDEBUG(stride); \
170         } ((void)0)
171
172         STATE_EXPAND(uf, textbuf, uf->len + 1);
173         STATE_EXPAND(uf, textbufinfo, uf->len + 1);
174
175 #undef STATE_EXPAND
176 }
177
178 static void uf_arraystore_free(UndoFont *uf)
179 {
180 #define STATE_FREE(uf, id) \
181         if ((uf)->store.id) { \
182                 const size_t stride = sizeof(*(uf)->id); \
183                 BArrayStore *bs = BLI_array_store_at_size_get(&uf_arraystore.bs_stride, stride); \
184                 BArrayState *state = (uf)->store.id; \
185                 BLI_array_store_state_remove(bs, state); \
186                 (uf)->store.id = NULL; \
187         } ((void)0)
188
189         STATE_FREE(uf, textbuf);
190         STATE_FREE(uf, textbufinfo);
191
192 #undef STATE_FREE
193
194         uf_arraystore.users -= 1;
195
196         BLI_assert(uf_arraystore.users >= 0);
197
198         if (uf_arraystore.users == 0) {
199 #ifdef DEBUG_PRINT
200                 printf("editfont undo store: freeing all data!\n");
201 #endif
202
203                 BLI_array_store_at_size_clear(&uf_arraystore.bs_stride);
204         }
205
206 }
207
208 /** \} */
209
210 #endif  /* USE_ARRAY_STORE */
211
212 static void undoFont_to_editFont(void *uf_v, void *ecu, void *UNUSED(obdata))
213 {
214         Curve *cu = (Curve *)ecu;
215         EditFont *ef = cu->editfont;
216         const UndoFont *uf = uf_v;
217
218         size_t final_size;
219
220 #ifdef USE_ARRAY_STORE
221         uf_arraystore_expand(uf_v);
222 #endif
223
224         final_size = sizeof(wchar_t) * (uf->len + 1);
225         memcpy(ef->textbuf, uf->textbuf, final_size);
226
227         final_size = sizeof(CharInfo) * (uf->len + 1);
228         memcpy(ef->textbufinfo, uf->textbufinfo, final_size);
229
230         ef->pos = uf->pos;
231         ef->len = uf->len;
232
233         ef->selstart = ef->selend = 0;
234
235 #ifdef USE_ARRAY_STORE
236         uf_arraystore_expand_clear(uf_v);
237 #endif
238 }
239
240 static void *editFont_to_undoFont(void *ecu, void *UNUSED(obdata))
241 {
242         Curve *cu = (Curve *)ecu;
243         EditFont *ef = cu->editfont;
244
245         UndoFont *uf = MEM_callocN(sizeof(*uf), __func__);
246
247         size_t final_size;
248
249         final_size = sizeof(wchar_t) * (ef->len + 1);
250         uf->textbuf = MEM_mallocN(final_size, __func__);
251         memcpy(uf->textbuf, ef->textbuf, final_size);
252
253         final_size = sizeof(CharInfo) * (ef->len + 1);
254         uf->textbufinfo = MEM_mallocN(final_size, __func__);
255         memcpy(uf->textbufinfo, ef->textbufinfo, final_size);
256
257         uf->pos = ef->pos;
258         uf->len = ef->len;
259
260 #ifdef USE_ARRAY_STORE
261         {
262                 const UndoFont *uf_ref = uf_arraystore.local_links.last ?
263                                          ((LinkData *)uf_arraystore.local_links.last)->data : NULL;
264
265                 /* add oursrlves */
266                 BLI_addtail(&uf_arraystore.local_links, BLI_genericNodeN(uf));
267
268                 uf_arraystore_compact_with_info(uf, uf_ref);
269         }
270 #endif
271
272         return uf;
273 }
274
275 static void free_undoFont(void *uf_v)
276 {
277         UndoFont *uf = uf_v;
278
279 #ifdef USE_ARRAY_STORE
280         {
281                 LinkData *link = BLI_findptr(&uf_arraystore.local_links, uf, offsetof(LinkData, data));
282                 BLI_remlink(&uf_arraystore.local_links, link);
283                 MEM_freeN(link);
284         }
285         uf_arraystore_free(uf);
286 #endif
287
288         if (uf->textbuf) {
289                 MEM_freeN(uf->textbuf);
290         }
291         if (uf->textbufinfo) {
292                 MEM_freeN(uf->textbufinfo);
293         }
294
295         MEM_freeN(uf);
296 }
297
298 static void *get_undoFont(bContext *C)
299 {
300         Object *obedit = CTX_data_edit_object(C);
301         if (obedit && obedit->type == OB_FONT) {
302                 return obedit->data;
303         }
304         return NULL;
305 }
306
307 /* and this is all the undo system needs to know */
308 void undo_push_font(bContext *C, const char *name)
309 {
310         undo_editmode_push(C, name, get_undoFont, free_undoFont, undoFont_to_editFont, editFont_to_undoFont, NULL);
311 }