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
27using namespace doc;
28
29namespace app {
30
31void 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