1 | // Aseprite |
---|---|
2 | // Copyright (C) 2020 Igara Studio S.A. |
3 | // Copyright (C) 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/util/layer_boundaries.h" |
13 | |
14 | #include "app/cmd/set_mask.h" |
15 | #include "app/console.h" |
16 | #include "app/context_access.h" |
17 | #include "app/modules/gui.h" |
18 | #include "app/tx.h" |
19 | #include "app/ui_context.h" |
20 | #include "doc/cel.h" |
21 | #include "doc/document.h" |
22 | #include "doc/image.h" |
23 | #include "doc/image_impl.h" |
24 | #include "doc/layer.h" |
25 | #include "doc/mask.h" |
26 | |
27 | using namespace doc; |
28 | |
29 | namespace app { |
30 | |
31 | void select_layer_boundaries(Layer* layer, |
32 | const frame_t frame, |
33 | const SelectLayerBoundariesOp op) |
34 | { |
35 | Mask newMask; |
36 | |
37 | const Cel* cel = layer->cel(frame); |
38 | if (cel) { |
39 | const Image* image = cel->image(); |
40 | if (image) { |
41 | newMask.replace(cel->bounds()); |
42 | newMask.freeze(); |
43 | { |
44 | LockImageBits<BitmapTraits> maskBits(newMask.bitmap()); |
45 | auto maskIt = maskBits.begin(); |
46 | auto maskEnd = maskBits.end(); |
47 | |
48 | switch (image->pixelFormat()) { |
49 | |
50 | case IMAGE_RGB: { |
51 | LockImageBits<RgbTraits> rgbBits(image); |
52 | auto rgbIt = rgbBits.begin(); |
53 | #if _DEBUG |
54 | auto rgbEnd = rgbBits.end(); |
55 | #endif |
56 | for (; maskIt != maskEnd; ++maskIt, ++rgbIt) { |
57 | ASSERT(rgbIt != rgbEnd); |
58 | color_t c = *rgbIt; |
59 | *maskIt = (rgba_geta(c) >= 128); // TODO configurable threshold |
60 | } |
61 | break; |
62 | } |
63 | |
64 | case IMAGE_GRAYSCALE: { |
65 | LockImageBits<GrayscaleTraits> grayBits(image); |
66 | auto grayIt = grayBits.begin(); |
67 | #if _DEBUG |
68 | auto grayEnd = grayBits.end(); |
69 | #endif |
70 | for (; maskIt != maskEnd; ++maskIt, ++grayIt) { |
71 | ASSERT(grayIt != grayEnd); |
72 | color_t c = *grayIt; |
73 | *maskIt = (graya_geta(c) >= 128); // TODO configurable threshold |
74 | } |
75 | break; |
76 | } |
77 | |
78 | case IMAGE_INDEXED: { |
79 | const doc::color_t maskColor = image->maskColor(); |
80 | LockImageBits<IndexedTraits> idxBits(image); |
81 | auto idxIt = idxBits.begin(); |
82 | #if _DEBUG |
83 | auto idxEnd = idxBits.end(); |
84 | #endif |
85 | for (; maskIt != maskEnd; ++maskIt, ++idxIt) { |
86 | ASSERT(idxIt != idxEnd); |
87 | color_t c = *idxIt; |
88 | *maskIt = (c != maskColor); |
89 | } |
90 | break; |
91 | } |
92 | |
93 | } |
94 | } |
95 | newMask.unfreeze(); |
96 | } |
97 | } |
98 | |
99 | try { |
100 | ContextWriter writer(UIContext::instance()); |
101 | Doc* doc = writer.document(); |
102 | ASSERT(doc == layer->sprite()->document()); |
103 | |
104 | if (doc->isMaskVisible()) { |
105 | switch (op) { |
106 | case SelectLayerBoundariesOp::REPLACE: |
107 | // newMask is the new mask |
108 | break; |
109 | case SelectLayerBoundariesOp::ADD: |
110 | newMask.add(*doc->mask()); |
111 | break; |
112 | case SelectLayerBoundariesOp::SUBTRACT: { |
113 | Mask oldMask(*doc->mask()); |
114 | oldMask.subtract(newMask); |
115 | newMask.copyFrom(&oldMask); // TODO use something like std::swap() |
116 | break; |
117 | } |
118 | case SelectLayerBoundariesOp::INTERSECT: |
119 | newMask.intersect(*doc->mask()); |
120 | break; |
121 | } |
122 | } |
123 | |
124 | Tx tx(writer.context(), "Select Layer Boundaries", DoesntModifyDocument); |
125 | tx(new cmd::SetMask(doc, &newMask)); |
126 | tx.commit(); |
127 | |
128 | update_screen_for_document(doc); |
129 | } |
130 | catch (base::Exception& e) { |
131 | Console::showException(e); |
132 | } |
133 | } |
134 | |
135 | } // namespace app |
136 |