1 // Aseprite
2 // Copyright (C) 2001-2017 David Capello
3 //
4 // This program is distributed under the terms of
5 // the End-User License Agreement for Aseprite.
6
7 #ifdef HAVE_CONFIG_H
8 #include "config.h"
9 #endif
10
11 #include "app/cmd/set_mask.h"
12 #include "app/commands/command.h"
13 #include "app/commands/commands.h"
14 #include "app/context_access.h"
15 #include "app/modules/gui.h"
16 #include "app/transaction.h"
17 #include "base/unique_ptr.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();
clone() const28 Command* clone() const override { return new InvertMaskCommand(*this); }
29
30 protected:
31 bool onEnabled(Context* context) override;
32 void onExecute(Context* context) override;
33 };
34
InvertMaskCommand()35 InvertMaskCommand::InvertMaskCommand()
36 : Command(CommandId::InvertMask(), CmdRecordableFlag)
37 {
38 }
39
onEnabled(Context * context)40 bool InvertMaskCommand::onEnabled(Context* context)
41 {
42 return context->checkFlags(ContextFlags::ActiveDocumentIsWritable |
43 ContextFlags::HasActiveSprite);
44 }
45
onExecute(Context * context)46 void InvertMaskCommand::onExecute(Context* context)
47 {
48 bool hasMask = false;
49 {
50 const ContextReader reader(context);
51 if (reader.document()->isMaskVisible())
52 hasMask = true;
53 }
54
55 // without mask?...
56 if (!hasMask) {
57 // so we select all
58 Command* mask_all_cmd =
59 Commands::instance()->byId(CommandId::MaskAll());
60 context->executeCommand(mask_all_cmd);
61 }
62 // invert the current mask
63 else {
64 ContextWriter writer(context);
65 Doc* document(writer.document());
66 Sprite* sprite(writer.sprite());
67
68 // Select all the sprite area
69 base::UniquePtr<Mask> mask(new Mask());
70 mask->replace(sprite->bounds());
71
72 // Remove in the new mask the current sprite marked region
73 const gfx::Rect& maskBounds = document->mask()->bounds();
74 doc::fill_rect(mask->bitmap(),
75 maskBounds.x, maskBounds.y,
76 maskBounds.x + maskBounds.w-1,
77 maskBounds.y + maskBounds.h-1, 0);
78
79 Mask* curMask = document->mask();
80 if (curMask->bitmap()) {
81 // Copy the inverted region in the new mask (we just modify the
82 // document's mask temporaly here)
83 curMask->freeze();
84 curMask->invert();
85 doc::copy_image(mask->bitmap(),
86 curMask->bitmap(),
87 curMask->bounds().x,
88 curMask->bounds().y);
89 curMask->invert();
90 curMask->unfreeze();
91 }
92
93 // We need only need the area inside the sprite
94 mask->intersect(sprite->bounds());
95
96 // Set the new mask
97 Transaction transaction(writer.context(), "Mask Invert", DoesntModifyDocument);
98 transaction.execute(new cmd::SetMask(document, mask));
99 transaction.commit();
100
101 document->generateMaskBoundaries();
102 update_screen_for_document(document);
103 }
104 }
105
createInvertMaskCommand()106 Command* CommandFactory::createInvertMaskCommand()
107 {
108 return new InvertMaskCommand;
109 }
110
111 } // namespace app
112