Blender V5.0
grease_pencil_test.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#include "testing/testing.h"
6
7#include "BLI_string.h"
8
9#include "BKE_curves.hh"
10#include "BKE_grease_pencil.hh"
11#include "BKE_idtype.hh"
12#include "BKE_lib_id.hh"
13#include "BKE_main.hh"
14
15using namespace blender::bke::greasepencil;
16
18
19/* --------------------------------------------------------------------------------------------- */
20/* Grease Pencil ID Tests. */
21
22/* NOTE: Using a struct with constructor and destructor instead of a fixture here, to have all the
23 * tests in the same group (`greasepencil`). */
37
38TEST(greasepencil, create_grease_pencil_id)
39{
41
42 GreasePencil &grease_pencil = *BKE_id_new<GreasePencil>(ctx.bmain, "GP");
43 EXPECT_EQ(grease_pencil.drawings().size(), 0);
44 EXPECT_EQ(grease_pencil.root_group().num_nodes_total(), 0);
45}
46
47/* --------------------------------------------------------------------------------------------- */
48/* Drawing Array Tests. */
49
50TEST(greasepencil, add_empty_drawings)
51{
53 GreasePencil &grease_pencil = *BKE_id_new<GreasePencil>(ctx.bmain, "GP");
54 grease_pencil.add_empty_drawings(3);
55 EXPECT_EQ(grease_pencil.drawings().size(), 3);
56}
57
58TEST(greasepencil, remove_drawings)
59{
61 GreasePencil &grease_pencil = *BKE_id_new<GreasePencil>(ctx.bmain, "GP");
62 grease_pencil.add_empty_drawings(3);
63
64 GreasePencilDrawing *drawing = reinterpret_cast<GreasePencilDrawing *>(grease_pencil.drawing(1));
65 drawing->wrap().strokes_for_write().resize(0, 10);
66
67 Layer &layer1 = grease_pencil.add_layer("Layer1");
68 Layer &layer2 = grease_pencil.add_layer("Layer2");
69
70 layer1.add_frame(0)->drawing_index = 0;
71 layer1.add_frame(10)->drawing_index = 1;
72 layer1.add_frame(20)->drawing_index = 2;
73
74 layer2.add_frame(0)->drawing_index = 1;
75 drawing->wrap().add_user();
76
77 grease_pencil.remove_frames(layer1, {10});
78 grease_pencil.remove_frames(layer2, {0});
79 EXPECT_EQ(grease_pencil.drawings().size(), 2);
80
81 static int expected_frames_size[] = {2, 0};
82 static int expected_frames_pairs_layer0[][2] = {{0, 0}, {20, 1}};
83
84 Span<const Layer *> layers = grease_pencil.layers();
85 EXPECT_EQ(layers[0]->frames().size(), expected_frames_size[0]);
86 EXPECT_EQ(layers[1]->frames().size(), expected_frames_size[1]);
87 EXPECT_EQ(layers[0]->frames().lookup(expected_frames_pairs_layer0[0][0]).drawing_index,
88 expected_frames_pairs_layer0[0][1]);
89 EXPECT_EQ(layers[0]->frames().lookup(expected_frames_pairs_layer0[1][0]).drawing_index,
90 expected_frames_pairs_layer0[1][1]);
91}
92
93TEST(greasepencil, remove_drawings_last_unused)
94{
95 GreasePencil *grease_pencil = BKE_id_new_nomain<GreasePencil>("Grease Pencil test");
96
97 /* Regression test for #129900: unused drawing at the end causes crash. */
98
99 grease_pencil->add_empty_drawings(2);
100 reinterpret_cast<const GreasePencilDrawing *>(grease_pencil->drawing(0))->wrap().remove_user();
101 reinterpret_cast<const GreasePencilDrawing *>(grease_pencil->drawing(1))->wrap().remove_user();
102
103 Layer &layer_a = grease_pencil->add_layer("LayerA");
104 layer_a.add_frame(10)->drawing_index = 0;
105 const GreasePencilDrawingBase *used_drawing = grease_pencil->drawings()[0];
106 grease_pencil->update_drawing_users_for_layer(layer_a);
107
108 EXPECT_EQ(layer_a.frames().size(), 1);
109 EXPECT_EQ(layer_a.frames().lookup(10).drawing_index, 0);
110 /* Test DNA storage data too. */
111 layer_a.prepare_for_dna_write();
112 EXPECT_EQ(layer_a.frames_storage.num, 1);
114
115 grease_pencil->remove_drawings_with_no_users();
116 EXPECT_EQ(grease_pencil->drawings().size(), 1);
117 EXPECT_EQ(grease_pencil->drawings()[0], used_drawing);
118
119 BKE_id_free(nullptr, grease_pencil);
120}
121
122/* --------------------------------------------------------------------------------------------- */
123/* Layer Tree Tests. */
124
127 {
128 this->root_group_ptr = MEM_new<greasepencil::LayerGroup>(__func__);
129 this->active_node = nullptr;
130
132
133 this->drawing_array = nullptr;
134 this->drawing_array_num = 0;
135
136 this->runtime = MEM_new<GreasePencilRuntime>(__func__);
137 }
138
140 {
141 this->attribute_storage.wrap().~AttributeStorage();
142 MEM_delete(&this->root_group());
143 MEM_delete(this->runtime);
144 this->runtime = nullptr;
145 }
146};
147
148TEST(greasepencil, layer_tree_empty)
149{
150 GreasePencilHelper grease_pencil;
151 EXPECT_EQ(grease_pencil.root_group().num_nodes_total(), 0);
152}
153
154TEST(greasepencil, layer_tree_build_simple)
155{
156 GreasePencilHelper grease_pencil;
157
158 LayerGroup &group = grease_pencil.add_layer_group(grease_pencil.root_group(), "Group1");
159 grease_pencil.add_layer(group, "Layer1");
160 grease_pencil.add_layer(group, "Layer2");
161 EXPECT_EQ(grease_pencil.root_group().num_nodes_total(), 3);
162}
163
165 StringRefNull names[7] = {"Group1", "Layer1", "Layer2", "Group2", "Layer3", "Layer4", "Layer5"};
166 const bool is_layer[7] = {false, true, true, false, true, true, true};
168
170 {
171 LayerGroup &group = grease_pencil.add_layer_group(grease_pencil.root_group(), names[0]);
172 grease_pencil.add_layer(group, names[1]);
173 grease_pencil.add_layer(group, names[2]);
174
175 LayerGroup &group2 = grease_pencil.add_layer_group(group, names[3]);
176 grease_pencil.add_layer(group2, names[4]);
177 grease_pencil.add_layer(group2, names[5]);
178
179 grease_pencil.add_layer(names[6]);
180 }
181};
182
183TEST(greasepencil, layer_tree_pre_order_iteration)
184{
186
187 Span<const TreeNode *> children = ex.grease_pencil.nodes();
188 for (const int i : children.index_range()) {
189 const TreeNode &child = *children[i];
190 EXPECT_STREQ(child.name().data(), ex.names[i].data());
191 }
192}
193
194TEST(greasepencil, layer_tree_pre_order_iteration2)
195{
197
198 Span<const Layer *> layers = ex.grease_pencil.layers();
199 char name[64];
200 for (const int i : layers.index_range()) {
201 const Layer &layer = *layers[i];
202 SNPRINTF(name, "%s%d", "Layer", i + 1);
203 EXPECT_STREQ(layer.name().data(), name);
204 }
205}
206
207TEST(greasepencil, layer_tree_total_size)
208{
210 EXPECT_EQ(ex.grease_pencil.root_group().num_nodes_total(), 7);
211}
212
213TEST(greasepencil, layer_tree_node_types)
214{
216 Span<const TreeNode *> children = ex.grease_pencil.nodes();
217 for (const int i : children.index_range()) {
218 const TreeNode &child = *children[i];
219 EXPECT_EQ(child.is_layer(), ex.is_layer[i]);
220 EXPECT_EQ(child.is_group(), !ex.is_layer[i]);
221 }
222}
223
224TEST(greasepencil, layer_tree_remove_active_node)
225{
227 TreeNode *node = ex.grease_pencil.find_node_by_name("Layer2");
228 ex.grease_pencil.set_active_node(node);
229
230 ex.grease_pencil.remove_layer(node->as_layer());
231 node = ex.grease_pencil.get_active_node();
232 EXPECT_TRUE(node != nullptr);
233 EXPECT_TRUE(node->is_layer());
234 EXPECT_TRUE(node->as_layer().name() == "Layer1");
235
236 ex.grease_pencil.remove_layer(node->as_layer());
237 node = ex.grease_pencil.get_active_node();
238 EXPECT_TRUE(node != nullptr);
239 EXPECT_TRUE(node->is_group());
240 EXPECT_TRUE(node->as_group().name() == "Group2");
241
242 ex.grease_pencil.remove_group(node->as_group());
243 node = ex.grease_pencil.get_active_node();
244 EXPECT_TRUE(node != nullptr);
245 EXPECT_TRUE(node->is_group());
246 EXPECT_TRUE(node->as_group().name() == "Group1");
247
248 ex.grease_pencil.remove_group(node->as_group());
249 node = ex.grease_pencil.get_active_node();
250 EXPECT_TRUE(node != nullptr);
251 EXPECT_TRUE(node->is_layer());
252 EXPECT_TRUE(node->as_layer().name() == "Layer5");
253
254 ex.grease_pencil.remove_layer(node->as_layer());
255 node = ex.grease_pencil.get_active_node();
256 EXPECT_TRUE(node == nullptr);
257}
258
259TEST(greasepencil, layer_tree_is_child_of)
260{
262
263 EXPECT_FALSE(ex.grease_pencil.root_group().is_child_of(ex.grease_pencil.root_group()));
264
265 const LayerGroup &group1 = ex.grease_pencil.find_node_by_name("Group1")->as_group();
266 const LayerGroup &group2 = ex.grease_pencil.find_node_by_name("Group2")->as_group();
267 const Layer &layer1 = ex.grease_pencil.find_node_by_name("Layer1")->as_layer();
268 const Layer &layer3 = ex.grease_pencil.find_node_by_name("Layer3")->as_layer();
269 const Layer &layer5 = ex.grease_pencil.find_node_by_name("Layer5")->as_layer();
270
271 EXPECT_TRUE(layer1.is_child_of(ex.grease_pencil.root_group()));
272 EXPECT_TRUE(layer1.is_child_of(group1));
273 EXPECT_TRUE(layer3.is_child_of(group1));
274 EXPECT_FALSE(layer5.is_child_of(group1));
275
276 EXPECT_TRUE(layer3.is_child_of(group2));
277 EXPECT_FALSE(layer1.is_child_of(group2));
278
279 EXPECT_TRUE(layer5.is_child_of(ex.grease_pencil.root_group()));
280}
281
282TEST(greasepencil, layer_tree_remove_group)
283{
284 /* Regression test for #130034. */
285 GreasePencilHelper grease_pencil;
286 LayerGroup &group1 = grease_pencil.add_layer_group(grease_pencil.root_group(), "Group1");
287 LayerGroup &group2 = grease_pencil.add_layer_group(group1, "Group2");
288 LayerGroup &group3 = grease_pencil.add_layer_group(group2, "Group3");
289 grease_pencil.add_layer(group3, "Layer");
290 grease_pencil.add_layer("Layer2");
291
292 /* Remove Group with children. */
293 grease_pencil.remove_group(group1, false);
294 EXPECT_EQ(grease_pencil.nodes().size(), 1);
295 EXPECT_EQ(grease_pencil.layers().size(), 1);
296 EXPECT_TRUE(grease_pencil.find_node_by_name("Layer2") != nullptr);
297}
298
299/* --------------------------------------------------------------------------------------------- */
300/* Frames Tests. */
301
308 const FramesMapKeyT sorted_keys[5] = {0, 5, 10, 12, 16};
309 GreasePencilFrame sorted_values[5] = {{0}, {1}, {-1}, {2}, {-1}};
311
313 {
314 for (int i = 0; i < 5; i++) {
315 layer.frames_for_write().add(this->sorted_keys[i], this->sorted_values[i]);
316 }
317 /* Mark the first keyframe as an implicit hold. */
318 layer.frame_at(0)->flag |= GP_FRAME_IMPLICIT_HOLD;
319 }
320};
321
322TEST(greasepencil, frame_is_end)
323{
325 EXPECT_TRUE(ex.layer.frames().lookup(10).is_end());
326}
327
328TEST(greasepencil, frame_is_implicit_hold)
329{
331 EXPECT_TRUE(ex.layer.frames().lookup(0).is_implicit_hold());
332}
333
334TEST(greasepencil, drawing_index_at)
335{
337 EXPECT_EQ(ex.layer.drawing_index_at(-100), -1);
338 EXPECT_EQ(ex.layer.drawing_index_at(100), -1);
342}
343
345{
347 EXPECT_FALSE(ex.layer.add_frame(0) != nullptr);
348 ex.layer.add_frame(10)->drawing_index = 3;
352}
353
354TEST(greasepencil, add_frame_duration_fail)
355{
357 EXPECT_FALSE(ex.layer.add_frame(0, 10) != nullptr);
358}
359
360TEST(greasepencil, add_frame_duration_override_start_null_frame)
361{
363 ex.layer.add_frame(10, 2)->drawing_index = 3;
367}
368
369TEST(greasepencil, add_frame_duration_check_duration)
370{
372 ex.layer.add_frame(17, 10)->drawing_index = 3;
373 Span<FramesMapKeyT> sorted_keys = ex.layer.sorted_keys();
374 EXPECT_EQ(sorted_keys.size(), 7);
375 EXPECT_EQ(sorted_keys[6] - sorted_keys[5], 10);
376}
377
378TEST(greasepencil, get_frame_duration_at)
379{
381 /* Before first frame. */
383 /* Implicit hold. */
386
389
390 /* No keyframe at frame 10. */
392
394
395 /* After last frame. */
398}
399
400TEST(greasepencil, add_frame_duration_override_null_frames)
401{
402 Layer layer;
403 layer.frames_for_write().add(0, {1});
404 layer.frames_for_write().add(1, {-1});
405 layer.frames_for_write().add(2, {-1});
406 layer.frames_for_write().add(3, {-1});
407
408 layer.add_frame(1, 10)->drawing_index = 3;
409 EXPECT_EQ(layer.drawing_index_at(0), 1);
410 EXPECT_EQ(layer.drawing_index_at(1), 3);
411 EXPECT_EQ(layer.drawing_index_at(11), -1);
412 Span<FramesMapKeyT> sorted_keys = layer.sorted_keys();
413 EXPECT_EQ(sorted_keys.size(), 3);
414 EXPECT_EQ(sorted_keys[0], 0);
415 EXPECT_EQ(sorted_keys[1], 1);
416 EXPECT_EQ(sorted_keys[2], 11);
417}
418
419TEST(greasepencil, remove_frame_single)
420{
421 Layer layer;
422 layer.add_frame(0)->drawing_index = 1;
423 layer.remove_frame(0);
424 EXPECT_EQ(layer.frames().size(), 0);
425}
426
427TEST(greasepencil, remove_frame_first)
428{
429 Layer layer;
430 layer.add_frame(0)->drawing_index = 1;
431 layer.add_frame(5)->drawing_index = 2;
432 layer.remove_frame(0);
433 EXPECT_EQ(layer.frames().size(), 1);
434 EXPECT_EQ(layer.frames().lookup(5).drawing_index, 2);
435}
436
437TEST(greasepencil, remove_frame_last)
438{
439 Layer layer;
440 layer.add_frame(0)->drawing_index = 1;
441 layer.add_frame(5)->drawing_index = 2;
442 layer.remove_frame(5);
443 EXPECT_EQ(layer.frames().size(), 1);
444 EXPECT_EQ(layer.frames().lookup(0).drawing_index, 1);
445}
446
447TEST(greasepencil, remove_frame_implicit_hold)
448{
449 Layer layer;
450 layer.add_frame(0, 4)->drawing_index = 1;
451 layer.add_frame(5)->drawing_index = 2;
452 layer.remove_frame(5);
453 EXPECT_EQ(layer.frames().size(), 2);
454 EXPECT_EQ(layer.frames().lookup(0).drawing_index, 1);
455 EXPECT_TRUE(layer.frames().lookup(4).is_end());
456}
457
458TEST(greasepencil, remove_frame_fixed_duration_end)
459{
460 Layer layer;
461 layer.add_frame(0, 5)->drawing_index = 1;
462 layer.add_frame(5)->drawing_index = 2;
463 layer.remove_frame(0);
464 EXPECT_EQ(layer.frames().size(), 1);
465 EXPECT_EQ(layer.frames().lookup(5).drawing_index, 2);
466}
467
468TEST(greasepencil, remove_frame_fixed_duration_overwrite_end)
469{
470 Layer layer;
471 layer.add_frame(0, 5)->drawing_index = 1;
472 layer.add_frame(5)->drawing_index = 2;
473 layer.remove_frame(5);
474 EXPECT_EQ(layer.frames().size(), 2);
475 EXPECT_EQ(layer.frames().lookup(0).drawing_index, 1);
476 EXPECT_TRUE(layer.frames().lookup(5).is_end());
477}
478
479TEST(greasepencil, remove_drawings_no_change)
480{
481 GreasePencil *grease_pencil = BKE_id_new_nomain<GreasePencil>("Grease Pencil test");
482
483 grease_pencil->add_empty_drawings(3);
484
485 Layer &layer_a = grease_pencil->add_layer("LayerA");
486 Layer &layer_b = grease_pencil->add_layer("LayerB");
487 layer_b.add_frame(10)->drawing_index = 0;
488 layer_b.add_frame(20)->drawing_index = 1;
489 layer_b.add_frame(30)->drawing_index = 2;
490
491 EXPECT_EQ(layer_a.frames().size(), 0);
492 EXPECT_EQ(layer_b.frames().size(), 3);
493 EXPECT_EQ(layer_b.frames().lookup(10).drawing_index, 0);
494 EXPECT_EQ(layer_b.frames().lookup(20).drawing_index, 1);
495 EXPECT_EQ(layer_b.frames().lookup(30).drawing_index, 2);
496 /* Test DNA storage data too. */
497 layer_a.prepare_for_dna_write();
498 layer_b.prepare_for_dna_write();
499 EXPECT_EQ(layer_a.frames_storage.num, 0);
500 EXPECT_EQ(layer_b.frames_storage.num, 3);
504
505 grease_pencil->remove_layer(layer_a);
506 EXPECT_EQ(layer_b.frames().size(), 3);
507 EXPECT_EQ(layer_b.frames().lookup(10).drawing_index, 0);
508 EXPECT_EQ(layer_b.frames().lookup(20).drawing_index, 1);
509 EXPECT_EQ(layer_b.frames().lookup(30).drawing_index, 2);
510 /* Test DNA storage data too. */
511 layer_b.prepare_for_dna_write();
512 EXPECT_EQ(layer_b.frames_storage.num, 3);
516
517 BKE_id_free(nullptr, grease_pencil);
518}
519
520TEST(greasepencil, remove_drawings_with_no_users)
521{
522 GreasePencil *grease_pencil = BKE_id_new_nomain<GreasePencil>("Grease Pencil test");
523
524 /* Test drawing index correctness: Removing users from drawings should remove those drawings, and
525 * all index references should get updated to match the changed drawing indices. */
526
527 grease_pencil->add_empty_drawings(5);
528
529 Layer &layer_a = grease_pencil->add_layer("LayerA");
530 layer_a.add_frame(10)->drawing_index = 0;
531 layer_a.add_frame(20)->drawing_index = 1;
532 layer_a.add_frame(30)->drawing_index = 2;
533 Layer &layer_b = grease_pencil->add_layer("LayerB");
534 layer_b.add_frame(10)->drawing_index = 3;
535 layer_b.add_frame(30)->drawing_index = 4;
536
537 EXPECT_EQ(layer_a.frames().size(), 3);
538 EXPECT_EQ(layer_a.frames().lookup(10).drawing_index, 0);
539 EXPECT_EQ(layer_a.frames().lookup(20).drawing_index, 1);
540 EXPECT_EQ(layer_a.frames().lookup(30).drawing_index, 2);
541 EXPECT_EQ(layer_b.frames().size(), 2);
542 EXPECT_EQ(layer_b.frames().lookup(10).drawing_index, 3);
543 EXPECT_EQ(layer_b.frames().lookup(30).drawing_index, 4);
544 /* Test DNA storage data too. */
545 layer_a.prepare_for_dna_write();
546 layer_b.prepare_for_dna_write();
547 EXPECT_EQ(layer_a.frames_storage.num, 3);
551 EXPECT_EQ(layer_b.frames_storage.num, 2);
554
555 /* Drawings 0,1,2 get removed, drawings 3,4 move up (order changes). */
556 grease_pencil->remove_layer(layer_a);
557 EXPECT_EQ(layer_b.frames().size(), 2);
558 EXPECT_EQ(layer_b.frames().lookup(10).drawing_index, 1);
559 EXPECT_EQ(layer_b.frames().lookup(30).drawing_index, 0);
560 /* Test DNA storage data too. */
561 layer_b.prepare_for_dna_write();
562 EXPECT_EQ(layer_b.frames_storage.num, 2);
565
566 BKE_id_free(nullptr, grease_pencil);
567}
568
569} // namespace blender::bke::greasepencil::tests
Low-level operations for curves.
Low-level operations for grease pencil.
void BKE_idtype_init()
Definition idtype.cc:121
void BKE_id_free(Main *bmain, void *idv)
void * BKE_id_new(Main *bmain, short type, const char *name)
Definition lib_id.cc:1514
void * BKE_id_new_nomain(short type, const char *name)
Definition lib_id.cc:1519
Main * BKE_main_new()
Definition main.cc:89
void BKE_main_free(Main *bmain)
Definition main.cc:192
EXPECT_EQ(BLI_expr_pylike_eval(expr, nullptr, 0, &result), EXPR_PYLIKE_INVALID)
#define SNPRINTF(dst, format,...)
Definition BLI_string.h:604
@ GP_FRAME_IMPLICIT_HOLD
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
bool add(const Key &key, const Value &value)
Definition BLI_map.hh:295
const Value & lookup(const Key &key) const
Definition BLI_map.hh:545
int64_t size() const
Definition BLI_map.hh:976
constexpr int64_t size() const
Definition BLI_span.hh:252
constexpr IndexRange index_range() const
Definition BLI_span.hh:401
constexpr const char * data() const
bool remove_frame(FramesMapKeyT key)
const Map< FramesMapKeyT, GreasePencilFrame > & frames() const
GreasePencilFrame * add_frame(FramesMapKeyT key, int duration=0)
int drawing_index_at(const int frame_number) const
int get_frame_duration_at(const int frame_number) const
Span< FramesMapKeyT > sorted_keys() const
Map< FramesMapKeyT, GreasePencilFrame > & frames_for_write()
const LayerGroup & as_group() const
TEST(greasepencil, create_grease_pencil_id)
const char * name
GreasePencilLayerFramesMapStorage frames_storage
GreasePencilLayerTreeNode * active_node
GreasePencilLayerTreeGroup * root_group_ptr
GreasePencilRuntimeHandle * runtime
GreasePencilDrawingBase ** drawing_array
struct AttributeStorage attribute_storage
i
Definition text_draw.cc:230