1 | // Aseprite |
2 | // Copyright (C) 2019 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/set_mask.h" |
13 | #include "app/commands/command.h" |
14 | #include "app/commands/commands.h" |
15 | #include "app/context_access.h" |
16 | #include "app/modules/gui.h" |
17 | #include "app/tx.h" |
18 | #include "doc/image.h" |
19 | #include "doc/mask.h" |
20 | #include "doc/primitives.h" |
21 | #include "doc/sprite.h" |
22 | |
23 | namespace app { |
24 | |
25 | class InvertMaskCommand : public Command { |
26 | public: |
27 | InvertMaskCommand(); |
28 | |
29 | protected: |
30 | bool onEnabled(Context* context) override; |
31 | void onExecute(Context* context) override; |
32 | }; |
33 | |
34 | InvertMaskCommand::InvertMaskCommand() |
35 | : Command(CommandId::InvertMask(), CmdRecordableFlag) |
36 | { |
37 | } |
38 | |
39 | bool InvertMaskCommand::onEnabled(Context* context) |
40 | { |
41 | return context->checkFlags(ContextFlags::ActiveDocumentIsWritable | |
42 | ContextFlags::HasActiveSprite); |
43 | } |
44 | |
45 | void InvertMaskCommand::onExecute(Context* context) |
46 | { |
47 | bool hasMask = false; |
48 | { |
49 | const ContextReader reader(context); |
50 | if (reader.document()->isMaskVisible()) |
51 | hasMask = true; |
52 | } |
53 | |
54 | // without mask?... |
55 | if (!hasMask) { |
56 | // so we select all |
57 | Command* mask_all_cmd = |
58 | Commands::instance()->byId(CommandId::MaskAll()); |
59 | context->executeCommand(mask_all_cmd); |
60 | } |
61 | // invert the current mask |
62 | else { |
63 | ContextWriter writer(context); |
64 | Doc* document(writer.document()); |
65 | Sprite* sprite(writer.sprite()); |
66 | |
67 | // Select all the sprite area |
68 | std::unique_ptr<Mask> mask(new Mask()); |
69 | mask->replace(sprite->bounds()); |
70 | |
71 | // Remove in the new mask the current sprite marked region |
72 | const gfx::Rect& maskBounds = document->mask()->bounds(); |
73 | doc::fill_rect(mask->bitmap(), |
74 | maskBounds.x, maskBounds.y, |
75 | maskBounds.x + maskBounds.w-1, |
76 | maskBounds.y + maskBounds.h-1, 0); |
77 | |
78 | Mask* curMask = document->mask(); |
79 | if (curMask->bitmap()) { |
80 | // Copy the inverted region in the new mask (we just modify the |
81 | // document's mask temporaly here) |
82 | curMask->freeze(); |
83 | curMask->invert(); |
84 | doc::copy_image(mask->bitmap(), |
85 | curMask->bitmap(), |
86 | curMask->bounds().x, |
87 | curMask->bounds().y); |
88 | curMask->invert(); |
89 | curMask->unfreeze(); |
90 | } |
91 | |
92 | // We need only need the area inside the sprite |
93 | mask->intersect(sprite->bounds()); |
94 | |
95 | // Set the new mask |
96 | Tx tx(writer.context(), "Mask Invert" , DoesntModifyDocument); |
97 | tx(new cmd::SetMask(document, mask.get())); |
98 | tx.commit(); |
99 | |
100 | update_screen_for_document(document); |
101 | } |
102 | } |
103 | |
104 | Command* CommandFactory::createInvertMaskCommand() |
105 | { |
106 | return new InvertMaskCommand; |
107 | } |
108 | |
109 | } // namespace app |
110 | |