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