1 | // Aseprite |
2 | // Copyright (C) 2019-2022 Igara Studio S.A. |
3 | // Copyright (C) 2001-2018 David Capello |
4 | // |
5 | // This program is distributed under the terms of |
6 | // the End-User License Agreement for Aseprite. |
7 | |
8 | #ifdef HAVE_CONFIG_H |
9 | #include "config.h" |
10 | #endif |
11 | |
12 | #include "app/cmd/flatten_layers.h" |
13 | |
14 | #include "app/cmd/add_layer.h" |
15 | #include "app/cmd/configure_background.h" |
16 | #include "app/cmd/copy_rect.h" |
17 | #include "app/cmd/move_layer.h" |
18 | #include "app/cmd/remove_layer.h" |
19 | #include "app/cmd/set_layer_flags.h" |
20 | #include "app/cmd/set_layer_name.h" |
21 | #include "app/cmd/unlink_cel.h" |
22 | #include "app/doc.h" |
23 | #include "app/i18n/strings.h" |
24 | #include "app/restore_visible_layers.h" |
25 | #include "doc/algorithm/shrink_bounds.h" |
26 | #include "doc/cel.h" |
27 | #include "doc/layer.h" |
28 | #include "doc/primitives.h" |
29 | #include "doc/sprite.h" |
30 | #include "render/render.h" |
31 | |
32 | namespace app { |
33 | namespace cmd { |
34 | |
35 | FlattenLayers::FlattenLayers(doc::Sprite* sprite, |
36 | const doc::SelectedLayers& layers0, |
37 | const bool newBlend) |
38 | : WithSprite(sprite) |
39 | { |
40 | m_newBlendMethod = newBlend; |
41 | doc::SelectedLayers layers(layers0); |
42 | layers.removeChildrenIfParentIsSelected(); |
43 | |
44 | m_layerIds.reserve(layers.size()); |
45 | for (auto layer : layers) |
46 | m_layerIds.push_back(layer->id()); |
47 | } |
48 | |
49 | void FlattenLayers::onExecute() |
50 | { |
51 | Sprite* sprite = this->sprite(); |
52 | auto doc = static_cast<Doc*>(sprite->document()); |
53 | |
54 | // Set of layers to be flattened. |
55 | bool backgroundIsSel = false; |
56 | SelectedLayers layers; |
57 | for (auto layerId : m_layerIds) { |
58 | doc::Layer* layer = doc::get<doc::Layer>(layerId); |
59 | ASSERT(layer); |
60 | layers.insert(layer); |
61 | if (layer->isBackground()) |
62 | backgroundIsSel = true; |
63 | } |
64 | |
65 | LayerList list = layers.toBrowsableLayerList(); |
66 | if (list.empty()) |
67 | return; // Do nothing |
68 | |
69 | // Create a temporary image. |
70 | ImageRef image(Image::create(sprite->spec())); |
71 | |
72 | LayerImage* flatLayer; // The layer onto which everything will be flattened. |
73 | color_t bgcolor; // The background color to use for flatLayer. |
74 | bool newFlatLayer = false; |
75 | |
76 | flatLayer = sprite->backgroundLayer(); |
77 | if (backgroundIsSel && flatLayer && flatLayer->isVisible()) { |
78 | // There exists a visible background layer, so we will flatten onto that. |
79 | bgcolor = doc->bgColor(flatLayer); |
80 | } |
81 | else { |
82 | // Create a new transparent layer to flatten everything onto it. |
83 | flatLayer = new LayerImage(sprite); |
84 | ASSERT(flatLayer->isVisible()); |
85 | flatLayer->setName(Strings::layer_properties_flattened()); |
86 | newFlatLayer = true; |
87 | bgcolor = sprite->transparentColor(); |
88 | } |
89 | |
90 | render::Render render; |
91 | render.setNewBlend(m_newBlendMethod); |
92 | render.setBgOptions(render::BgOptions::MakeNone()); |
93 | |
94 | { |
95 | // Show only the layers to be flattened so other layers are hidden |
96 | // temporarily. |
97 | RestoreVisibleLayers restore; |
98 | restore.showSelectedLayers(sprite, layers); |
99 | |
100 | // Copy all frames to the background. |
101 | for (frame_t frame(0); frame<sprite->totalFrames(); ++frame) { |
102 | // Clear the image and render this frame. |
103 | clear_image(image.get(), bgcolor); |
104 | render.renderSprite(image.get(), sprite, frame); |
105 | |
106 | // TODO Keep cel links when possible |
107 | |
108 | ImageRef cel_image; |
109 | Cel* cel = flatLayer->cel(frame); |
110 | if (cel) { |
111 | if (cel->links()) |
112 | executeAndAdd(new cmd::UnlinkCel(cel)); |
113 | |
114 | cel_image = cel->imageRef(); |
115 | ASSERT(cel_image); |
116 | |
117 | executeAndAdd( |
118 | new cmd::CopyRect(cel_image.get(), image.get(), |
119 | gfx::Clip(0, 0, image->bounds()))); |
120 | } |
121 | else { |
122 | gfx::Rect bounds(image->bounds()); |
123 | if (doc::algorithm::shrink_bounds( |
124 | image.get(), image->maskColor(), nullptr, bounds)) { |
125 | cel_image.reset( |
126 | doc::crop_image(image.get(), bounds, image->maskColor())); |
127 | cel = new Cel(frame, cel_image); |
128 | cel->setPosition(bounds.origin()); |
129 | flatLayer->addCel(cel); |
130 | } |
131 | } |
132 | } |
133 | } |
134 | |
135 | // Add new flatten layer |
136 | if (newFlatLayer) |
137 | executeAndAdd(new cmd::AddLayer(list.front()->parent(), flatLayer, list.front())); |
138 | |
139 | // Delete flattened layers. |
140 | for (Layer* layer : layers) { |
141 | // layer can be == flatLayer when we are flattening on the |
142 | // background layer. |
143 | if (layer != flatLayer) { |
144 | executeAndAdd(new cmd::RemoveLayer(layer)); |
145 | } |
146 | } |
147 | } |
148 | |
149 | } // namespace cmd |
150 | } // namespace app |
151 | |