Skip to content

Commit e294c96

Browse files
tcl3AtkinsSJ
authored andcommitted
PixelPaint: Make merge up and down actions work with disjoint layers
The "Merge Active Layer Up" and "Merge Active Layer Down" actions now work with layers of different sizes. These actions now expand the bounding rect of the newly merged layer to contain all layers being merged. Layers which are not visible are now ignored by these actions.
1 parent 74dff62 commit e294c96

File tree

3 files changed

+78
-28
lines changed

3 files changed

+78
-28
lines changed

Userland/Applications/PixelPaint/Image.cpp

Lines changed: 60 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -390,38 +390,74 @@ ErrorOr<void> Image::merge_layers(LayerMergeMode layer_merge_mode)
390390
return {};
391391
}
392392

393-
void Image::merge_active_layer_up(Layer& layer)
393+
ErrorOr<void> Image::merge_active_layer_up(Layer& layer)
394394
{
395-
if (m_layers.size() < 2)
396-
return;
397-
size_t layer_index = this->index_of(layer);
398-
if ((layer_index + 1) == m_layers.size()) {
399-
dbgln("Cannot merge layer up: layer is already at the top");
400-
return; // FIXME: Notify user of error properly.
401-
}
395+
return merge_active_layer(layer, LayerMergeDirection::Up);
396+
}
402397

403-
auto& layer_above = m_layers.at(layer_index + 1);
404-
GUI::Painter painter(layer_above.content_bitmap());
405-
painter.draw_scaled_bitmap(rect(), layer.display_bitmap(), layer.rect(), (float)layer.opacity_percent() / 100.0f);
406-
remove_layer(layer);
407-
select_layer(&layer_above);
398+
ErrorOr<void> Image::merge_active_layer_down(Layer& layer)
399+
{
400+
return merge_active_layer(layer, LayerMergeDirection::Down);
408401
}
409402

410-
void Image::merge_active_layer_down(Layer& layer)
403+
ErrorOr<void> Image::merge_active_layer(NonnullRefPtr<Layer> const& layer, LayerMergeDirection layer_merge_direction)
411404
{
412405
if (m_layers.size() < 2)
413-
return;
414-
int layer_index = this->index_of(layer);
415-
if (layer_index == 0) {
416-
dbgln("Cannot merge layer down: layer is already at the bottom");
417-
return; // FIXME: Notify user of error properly.
406+
return {};
407+
408+
if (!layer->is_visible())
409+
return Error::from_string_literal("Layer must be visible");
410+
411+
auto layer_index = index_of(layer);
412+
auto direction = layer_merge_direction == LayerMergeDirection::Up ? 1 : -1;
413+
ssize_t layer_to_merge_index = layer_index + direction;
414+
ssize_t layer_count = m_layers.size();
415+
416+
if (layer_to_merge_index < 0)
417+
return Error::from_string_literal("Layer is already at the bottom");
418+
if (layer_to_merge_index >= layer_count)
419+
return Error::from_string_literal("Layer is already at the top");
420+
421+
Optional<NonnullRefPtr<Layer>> maybe_adjacent_layer;
422+
while (layer_to_merge_index >= 0 && layer_to_merge_index < layer_count) {
423+
auto& layer = m_layers.at(layer_to_merge_index);
424+
if (layer.is_visible()) {
425+
maybe_adjacent_layer = layer;
426+
break;
427+
}
428+
layer_to_merge_index += direction;
418429
}
419430

420-
auto& layer_below = m_layers.at(layer_index - 1);
421-
GUI::Painter painter(layer_below.content_bitmap());
422-
painter.draw_scaled_bitmap(rect(), layer.display_bitmap(), layer.rect(), (float)layer.opacity_percent() / 100.0f);
423-
remove_layer(layer);
424-
select_layer(&layer_below);
431+
if (!maybe_adjacent_layer.has_value()) {
432+
auto error_message = layer_merge_direction == LayerMergeDirection::Up ? "No visible layers above this layer"sv : "No visible layers below this layer"sv;
433+
return Error::from_string_view(error_message);
434+
}
435+
436+
auto adjacent_layer = maybe_adjacent_layer.value();
437+
auto bottom_layer = layer_merge_direction == LayerMergeDirection::Down ? adjacent_layer : layer;
438+
auto top_layer = layer_merge_direction == LayerMergeDirection::Down ? layer : adjacent_layer;
439+
auto merged_layer_bounding_rect = bottom_layer->relative_rect().united(top_layer->relative_rect());
440+
auto merged_layer = bottom_layer;
441+
if (!bottom_layer->relative_rect().contains(top_layer->relative_rect())) {
442+
merged_layer = TRY(Layer::create_with_size(*this, merged_layer_bounding_rect.size(), adjacent_layer->name()));
443+
merged_layer->set_location(merged_layer_bounding_rect.location());
444+
} else if (merged_layer.ptr() != adjacent_layer.ptr()) {
445+
merged_layer->set_name(adjacent_layer->name());
446+
}
447+
448+
GUI::Painter painter(merged_layer->content_bitmap());
449+
if (merged_layer.ptr() != bottom_layer.ptr())
450+
painter.blit(bottom_layer->location() - merged_layer->location(), bottom_layer->display_bitmap(), bottom_layer->rect(), static_cast<float>(bottom_layer->opacity_percent()) / 100.0f);
451+
painter.blit(top_layer->location() - merged_layer->location(), top_layer->display_bitmap(), top_layer->rect(), static_cast<float>(top_layer->opacity_percent()) / 100.0f);
452+
453+
auto top_layer_index = max(layer_index, layer_to_merge_index);
454+
auto bottom_layer_index = min(layer_index, layer_to_merge_index);
455+
m_layers.remove(top_layer_index);
456+
m_layers.remove(bottom_layer_index);
457+
m_layers.insert(top_layer_index - 1, merged_layer);
458+
select_layer(merged_layer);
459+
did_modify_layer_stack();
460+
return {};
425461
}
426462

427463
void Image::select_layer(Layer* layer)

Userland/Applications/PixelPaint/Image.h

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,8 +85,8 @@ class Image : public RefCounted<Image> {
8585
void select_layer(Layer*);
8686
ErrorOr<void> flatten_all_layers();
8787
ErrorOr<void> merge_visible_layers();
88-
void merge_active_layer_up(Layer& layer);
89-
void merge_active_layer_down(Layer& layer);
88+
ErrorOr<void> merge_active_layer_up(Layer& layer);
89+
ErrorOr<void> merge_active_layer_down(Layer& layer);
9090

9191
void add_client(ImageClient&);
9292
void remove_client(ImageClient&);
@@ -111,13 +111,19 @@ class Image : public RefCounted<Image> {
111111
VisibleOnly
112112
};
113113

114+
enum class LayerMergeDirection {
115+
Up,
116+
Down
117+
};
118+
114119
explicit Image(Gfx::IntSize);
115120

116121
void did_change(Gfx::IntRect const& modified_rect = {});
117122
void did_change_rect(Gfx::IntRect const& modified_rect = {});
118123
void did_modify_layer_stack();
119124

120125
ErrorOr<void> merge_layers(LayerMergeMode);
126+
ErrorOr<void> merge_active_layer(NonnullRefPtr<Layer> const&, LayerMergeDirection);
121127

122128
Gfx::IntSize m_size;
123129
NonnullRefPtrVector<Layer> m_layers;

Userland/Applications/PixelPaint/MainWidget.cpp

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -870,7 +870,11 @@ ErrorOr<void> MainWidget::initialize_menubar(GUI::Window& window)
870870
auto active_layer = editor->active_layer();
871871
if (!active_layer)
872872
return;
873-
editor->image().merge_active_layer_up(*active_layer);
873+
874+
if (auto maybe_error = editor->image().merge_active_layer_up(*active_layer); maybe_error.is_error()) {
875+
GUI::MessageBox::show_error(&window, DeprecatedString::formatted("Failed to merge active layer up: {}", maybe_error.release_error()));
876+
return;
877+
}
874878
editor->did_complete_action("Merge Active Layer Up"sv);
875879
}));
876880

@@ -881,7 +885,11 @@ ErrorOr<void> MainWidget::initialize_menubar(GUI::Window& window)
881885
auto active_layer = editor->active_layer();
882886
if (!active_layer)
883887
return;
884-
editor->image().merge_active_layer_down(*active_layer);
888+
889+
if (auto maybe_error = editor->image().merge_active_layer_down(*active_layer); maybe_error.is_error()) {
890+
GUI::MessageBox::show_error(&window, DeprecatedString::formatted("Failed to merge active layer down: {}", maybe_error.release_error()));
891+
return;
892+
}
885893
editor->did_complete_action("Merge Active Layer Down"sv);
886894
}));
887895

0 commit comments

Comments
 (0)