WIP: Brush assets project #106303

Draft
Julian Eisel wants to merge 354 commits from brush-assets-project into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
10 changed files with 179 additions and 36 deletions
Showing only changes of commit 0acf98a0e0 - Show all commits

View File

@ -427,9 +427,9 @@ class PREFERENCES_OT_keyconfig_remove(Operator):
# Add-on Operators
class PREFERENCES_OT_addon_enable(Operator):
"""Enable an add-on"""
"""Turn on this extension"""
bl_idname = "preferences.addon_enable"
bl_label = "Enable Add-on"
bl_label = "Enable Extension"
module: StringProperty(
name="Module",
@ -473,9 +473,9 @@ class PREFERENCES_OT_addon_enable(Operator):
class PREFERENCES_OT_addon_disable(Operator):
"""Disable an add-on"""
"""Turn off this extension"""
bl_idname = "preferences.addon_disable"
bl_label = "Disable Add-on"
bl_label = "Disable Extension"
module: StringProperty(
name="Module",

View File

@ -21,6 +21,7 @@
#include "DNA_ID.h"
#include "BLI_function_ref.hh"
#include "BLI_sys_types.h"
#include <array>
@ -324,15 +325,24 @@ void BKE_library_ID_test_usages(Main *bmain,
/** Parameters and result data structure for the 'unused IDs' functions below. */
struct LibQueryUnusedIDsData {
/** Process local data-blocks. */
bool do_local_ids;
bool do_local_ids = false;
/** Process linked data-blocks. */
bool do_linked_ids;
bool do_linked_ids = false;
/**
* Process all actually unused data-blocks, including these that are currently only used by
* other unused data-blocks, and 'dependency islands' of several data-blocks using each-other,
* without any external valid user.
*/
bool do_recursive;
bool do_recursive = false;
/**
* Callback filter, if defined and it returns `true`, the given `id` may be considered as unused,
* otherwise it will always be considered as used.
*
* Allows for more complex handling of which IDs should be deleted, on top of the basic
* local/linked choices.
*/
blender::FunctionRef<bool(ID *id)> filter_fn = nullptr;
/**
* Amount of detected as unused data-blocks, per type and total as the last value of the array

View File

@ -238,7 +238,6 @@ static ID **collection_owner_pointer_get(ID *id)
if ((id->flag & LIB_EMBEDDED_DATA) == 0) {
return nullptr;
}
BLI_assert((id->tag & LIB_TAG_NO_MAIN) == 0);
Collection *master_collection = (Collection *)id;
BLI_assert((master_collection->flag & COLLECTION_IS_MASTER) != 0);

View File

@ -1708,6 +1708,13 @@ template<typename T> static void shrink_array(T **array, int *num, const int shr
{
BLI_assert(shrink_num > 0);
const int new_array_num = *num - shrink_num;
if (new_array_num == 0) {
MEM_freeN(*array);
*array = nullptr;
*num = 0;
return;
}
T *new_array = reinterpret_cast<T *>(MEM_cnew_array<T *>(new_array_num, __func__));
blender::uninitialized_move_n(*array, new_array_num, new_array);
@ -1924,6 +1931,7 @@ static void remove_drawings_unchecked(GreasePencil &grease_pencil,
for (auto [key, value] : layer->frames_for_write().items()) {
if (value.drawing_index == swap_index) {
value.drawing_index = index_to_remove;
layer->tag_frames_map_changed();
}
}
}
@ -2521,7 +2529,7 @@ void GreasePencil::remove_layer(blender::bke::greasepencil::Layer &layer)
layer.parent_group().unlink_node(layer.as_node());
/* Remove drawings. */
for (GreasePencilFrame frame : layer.frames_for_write().values()) {
for (const GreasePencilFrame frame : layer.frames().values()) {
GreasePencilDrawingBase *drawing_base = this->drawing(frame.drawing_index);
if (drawing_base->type != GP_DRAWING) {
continue;
@ -2598,7 +2606,8 @@ static void write_drawing_array(GreasePencil &grease_pencil, BlendWriter *writer
static void free_drawing_array(GreasePencil &grease_pencil)
{
if (grease_pencil.drawing_array == nullptr || grease_pencil.drawing_array_num == 0) {
if (grease_pencil.drawing_array == nullptr) {
BLI_assert(grease_pencil.drawing_array_num == 0);
return;
}
for (int i = 0; i < grease_pencil.drawing_array_num; i++) {

View File

@ -363,4 +363,96 @@ TEST(greasepencil, remove_frame_fixed_duration_overwrite_end)
EXPECT_TRUE(layer.frames().lookup(5).is_null());
}
TEST(greasepencil, remove_drawings_no_change)
{
GreasePencil *grease_pencil = reinterpret_cast<GreasePencil *>(
BKE_id_new_nomain(ID_GP, "Grease Pencil test"));
grease_pencil->add_empty_drawings(3);
Layer &layer_a = grease_pencil->add_layer("LayerA");
Layer &layer_b = grease_pencil->add_layer("LayerB");
layer_b.add_frame(10, 0);
layer_b.add_frame(20, 1);
layer_b.add_frame(30, 2);
EXPECT_EQ(layer_a.frames().size(), 0);
EXPECT_EQ(layer_b.frames().size(), 3);
EXPECT_EQ(layer_b.frames().lookup(10).drawing_index, 0);
EXPECT_EQ(layer_b.frames().lookup(20).drawing_index, 1);
EXPECT_EQ(layer_b.frames().lookup(30).drawing_index, 2);
/* Test DNA storage data too. */
layer_a.prepare_for_dna_write();
layer_b.prepare_for_dna_write();
EXPECT_EQ(layer_a.frames_storage.num, 0);
EXPECT_EQ(layer_b.frames_storage.num, 3);
EXPECT_EQ(layer_b.frames_storage.values[0].drawing_index, 0);
EXPECT_EQ(layer_b.frames_storage.values[1].drawing_index, 1);
EXPECT_EQ(layer_b.frames_storage.values[2].drawing_index, 2);
grease_pencil->remove_layer(layer_a);
EXPECT_EQ(layer_b.frames().size(), 3);
EXPECT_EQ(layer_b.frames().lookup(10).drawing_index, 0);
EXPECT_EQ(layer_b.frames().lookup(20).drawing_index, 1);
EXPECT_EQ(layer_b.frames().lookup(30).drawing_index, 2);
/* Test DNA storage data too. */
layer_b.prepare_for_dna_write();
EXPECT_EQ(layer_b.frames_storage.num, 3);
EXPECT_EQ(layer_b.frames_storage.values[0].drawing_index, 0);
EXPECT_EQ(layer_b.frames_storage.values[1].drawing_index, 1);
EXPECT_EQ(layer_b.frames_storage.values[2].drawing_index, 2);
BKE_id_free(nullptr, grease_pencil);
}
TEST(greasepencil, remove_drawings_with_no_users)
{
GreasePencil *grease_pencil = reinterpret_cast<GreasePencil *>(
BKE_id_new_nomain(ID_GP, "Grease Pencil test"));
/* Test drawing index correctness: Removing users from drawings should remove those drawings, and
* all index references should get updated to match the changed drawing indices. */
grease_pencil->add_empty_drawings(5);
Layer &layer_a = grease_pencil->add_layer("LayerA");
layer_a.add_frame(10, 0);
layer_a.add_frame(20, 1);
layer_a.add_frame(30, 2);
Layer &layer_b = grease_pencil->add_layer("LayerB");
layer_b.add_frame(10, 3);
layer_b.add_frame(30, 4);
EXPECT_EQ(layer_a.frames().size(), 3);
EXPECT_EQ(layer_a.frames().lookup(10).drawing_index, 0);
EXPECT_EQ(layer_a.frames().lookup(20).drawing_index, 1);
EXPECT_EQ(layer_a.frames().lookup(30).drawing_index, 2);
EXPECT_EQ(layer_b.frames().size(), 2);
EXPECT_EQ(layer_b.frames().lookup(10).drawing_index, 3);
EXPECT_EQ(layer_b.frames().lookup(30).drawing_index, 4);
/* Test DNA storage data too. */
layer_a.prepare_for_dna_write();
layer_b.prepare_for_dna_write();
EXPECT_EQ(layer_a.frames_storage.num, 3);
EXPECT_EQ(layer_a.frames_storage.values[0].drawing_index, 0);
EXPECT_EQ(layer_a.frames_storage.values[1].drawing_index, 1);
EXPECT_EQ(layer_a.frames_storage.values[2].drawing_index, 2);
EXPECT_EQ(layer_b.frames_storage.num, 2);
EXPECT_EQ(layer_b.frames_storage.values[0].drawing_index, 3);
EXPECT_EQ(layer_b.frames_storage.values[1].drawing_index, 4);
/* Drawings 0,1,2 get removed, drawings 3,4 move up (order changes). */
grease_pencil->remove_layer(layer_a);
EXPECT_EQ(layer_b.frames().size(), 2);
EXPECT_EQ(layer_b.frames().lookup(10).drawing_index, 1);
EXPECT_EQ(layer_b.frames().lookup(30).drawing_index, 0);
/* Test DNA storage data too. */
layer_b.prepare_for_dna_write();
EXPECT_EQ(layer_b.frames_storage.num, 2);
EXPECT_EQ(layer_b.frames_storage.values[0].drawing_index, 1);
EXPECT_EQ(layer_b.frames_storage.values[1].drawing_index, 0);
BKE_id_free(nullptr, grease_pencil);
}
} // namespace blender::bke::greasepencil::tests

View File

@ -10,6 +10,7 @@
#include "DNA_anim_types.h"
#include "BLI_function_ref.hh"
#include "BLI_ghash.h"
#include "BLI_linklist_stack.h"
#include "BLI_listbase.h"
@ -600,7 +601,7 @@ void BKE_library_ID_test_usages(Main *bmain,
* user feedback ('what would be the amounts of IDs detected as unused if this option was
* enabled').
*/
struct UnusedIdsData {
struct UnusedIDsData {
Main *bmain;
const int id_tag;
@ -609,12 +610,26 @@ struct UnusedIdsData {
bool do_linked_ids;
bool do_recursive;
blender::FunctionRef<bool(ID *id)> filter_fn;
std::array<int, INDEX_ID_MAX> *num_total;
std::array<int, INDEX_ID_MAX> *num_local;
std::array<int, INDEX_ID_MAX> *num_linked;
blender::Set<ID *> unused_ids{};
UnusedIDsData(Main *bmain, const int id_tag, LibQueryUnusedIDsData &parameters)
: bmain(bmain),
id_tag(id_tag),
do_local_ids(parameters.do_local_ids),
do_linked_ids(parameters.do_linked_ids),
do_recursive(parameters.do_recursive),
num_total(&parameters.num_total),
num_local(&parameters.num_local),
num_linked(&parameters.num_linked)
{
}
void reset(const bool do_local_ids,
const bool do_linked_ids,
const bool do_recursive,
@ -632,8 +647,11 @@ struct UnusedIdsData {
}
};
static void lib_query_unused_ids_tag_id(ID *id, UnusedIdsData &data)
static void lib_query_unused_ids_tag_id(ID *id, UnusedIDsData &data)
{
if (data.filter_fn && !data.filter_fn(id)) {
return;
}
id->tag |= data.id_tag;
data.unused_ids.add(id);
@ -652,7 +670,7 @@ static void lib_query_unused_ids_tag_id(ID *id, UnusedIdsData &data)
/* Returns `true` if given ID is detected as part of at least one dependency loop, false otherwise.
*/
static bool lib_query_unused_ids_tag_recurse(ID *id, UnusedIdsData &data)
static bool lib_query_unused_ids_tag_recurse(ID *id, UnusedIDsData &data)
{
/* We should never deal with embedded, not-in-main IDs here. */
BLI_assert((id->flag & LIB_EMBEDDED_DATA) == 0);
@ -764,7 +782,7 @@ static bool lib_query_unused_ids_tag_recurse(ID *id, UnusedIdsData &data)
return is_part_of_dependency_loop;
}
static void lib_query_unused_ids_tag(UnusedIdsData &data)
static void lib_query_unused_ids_tag(UnusedIDsData &data)
{
BKE_main_relations_tag_set(data.bmain, MAINIDRELATIONS_ENTRY_TAGS_PROCESSED, false);
@ -851,15 +869,14 @@ void BKE_lib_query_unused_ids_amounts(Main *bmain, LibQueryUnusedIDsData &parame
* this call.
*/
UnusedIdsData data{
bmain,
0,
true,
parameters.do_linked_ids,
parameters.do_recursive,
parameters.do_local_ids ? &parameters.num_total : &num_dummy,
&parameters.num_local,
(parameters.do_local_ids && parameters.do_linked_ids) ? &parameters.num_linked : &num_dummy};
UnusedIDsData data(bmain, 0, parameters);
data.do_local_ids = true;
if (!parameters.do_local_ids) {
data.num_total = &num_dummy;
}
if (!(parameters.do_local_ids && parameters.do_linked_ids)) {
data.num_linked = &num_dummy;
}
lib_query_unused_ids_tag(data);
if (!(parameters.do_local_ids && parameters.do_linked_ids)) {
@ -887,14 +904,7 @@ void BKE_lib_query_unused_ids_tag(Main *bmain, const int tag, LibQueryUnusedIDsD
parameters.num_local.fill(0);
parameters.num_linked.fill(0);
UnusedIdsData data{bmain,
tag,
parameters.do_local_ids,
parameters.do_linked_ids,
parameters.do_recursive,
&parameters.num_total,
&parameters.num_local,
&parameters.num_linked};
UnusedIDsData data(bmain, tag, parameters);
if (parameters.do_recursive) {
BKE_main_relations_create(bmain, 0);

View File

@ -34,6 +34,7 @@ void set_selected_frames_type(bke::greasepencil::Layer &layer,
for (GreasePencilFrame &frame : layer.frames_for_write().values()) {
if (frame.is_selected()) {
frame.type = key_type;
layer.tag_frames_map_changed();
}
}
}
@ -170,6 +171,10 @@ bool duplicate_selected_frames(GreasePencil &grease_pencil, bke::greasepencil::L
changed = true;
}
if (changed) {
layer.tag_frames_map_changed();
}
return changed;
}
@ -210,6 +215,7 @@ bool select_frame_at(bke::greasepencil::Layer &layer,
return false;
}
select_frame(*frame, select_mode);
layer.tag_frames_map_changed();
return true;
}
@ -232,6 +238,7 @@ void select_all_frames(bke::greasepencil::Layer &layer, const short select_mode)
{
for (auto item : layer.frames_for_write().items()) {
select_frame(item.value, select_mode);
layer.tag_frames_map_changed();
}
}
@ -269,6 +276,8 @@ void select_frames_region(KeyframeEditData *ked,
select_frame(frame, select_mode);
}
}
node.as_layer().tag_frames_map_changed();
}
}
else if (node.is_group()) {
@ -288,6 +297,7 @@ void select_frames_range(bke::greasepencil::TreeNode &node,
for (auto [frame_number, frame] : node.as_layer().frames_for_write().items()) {
if (IN_RANGE(float(frame_number), min, max)) {
select_frame(frame, select_mode);
node.as_layer().tag_frames_map_changed();
}
}
}

View File

@ -234,14 +234,24 @@ static Array<int> get_frame_numbers_for_layer(const bke::greasepencil::Layer &la
const int current_frame,
const bool use_multi_frame_editing)
{
Vector<int> frame_numbers({current_frame});
Vector<int> frame_numbers;
if (use_multi_frame_editing) {
bool current_frame_is_covered = false;
const int drawing_index_at_current_frame = layer.drawing_index_at(current_frame);
for (const auto [frame_number, frame] : layer.frames().items()) {
if (frame_number != current_frame && frame.is_selected()) {
frame_numbers.append_unchecked(frame_number);
if (!frame.is_selected()) {
continue;
}
frame_numbers.append(frame_number);
current_frame_is_covered |= (frame.drawing_index == drawing_index_at_current_frame);
}
if (current_frame_is_covered) {
return frame_numbers.as_span();
}
}
frame_numbers.append(current_frame);
return frame_numbers.as_span();
}

View File

@ -2163,7 +2163,10 @@ static int unused_message_popup_width_compute(bContext *C)
/* Computation of unused data amounts, with all options ON.
* Used to estimate the maximum required width for the dialog. */
Main *bmain = CTX_data_main(C);
LibQueryUnusedIDsData data = {true, true, true, {}, {}, {}};
LibQueryUnusedIDsData data;
data.do_local_ids = true;
data.do_linked_ids = true;
data.do_recursive = true;
BKE_lib_query_unused_ids_amounts(bmain, data);
std::string unused_message = "";

View File

@ -82,7 +82,7 @@ static void createTransGreasePencilVerts(bContext *C, TransInfo *t)
continue;
}
Object *object_eval = DEG_get_evaluated_object(depsgraph, tc.obedit);
GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object_eval->data);
GreasePencil &grease_pencil = *static_cast<GreasePencil *>(tc.obedit->data);
Span<const bke::greasepencil::Layer *> layers = grease_pencil.layers();
int layer_points_offset = 0;