1 // Aseprite
2 // Copyright (C) 2001-2018  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/tools/tool_box.h"
12 
13 #include "app/gui_xml.h"
14 #include "app/i18n/strings.h"
15 #include "app/tools/controller.h"
16 #include "app/tools/ink.h"
17 #include "app/tools/intertwine.h"
18 #include "app/tools/point_shape.h"
19 #include "app/tools/stroke.h"
20 #include "app/tools/tool_group.h"
21 #include "app/tools/tool_loop.h"
22 #include "base/bind.h"
23 #include "base/exception.h"
24 #include "doc/algo.h"
25 #include "doc/algorithm/floodfill.h"
26 #include "doc/algorithm/polygon.h"
27 #include "doc/brush.h"
28 #include "doc/compressed_image.h"
29 #include "doc/image_impl.h"
30 #include "doc/mask.h"
31 #include "fixmath/fixmath.h"
32 
33 #include <algorithm>
34 #include <cstdlib>
35 
36 #include "app/tools/controllers.h"
37 #include "app/tools/inks.h"
38 #include "app/tools/intertwiners.h"
39 #include "app/tools/point_shapes.h"
40 
41 namespace app {
42 namespace tools {
43 
44 using namespace gfx;
45 
46 const char* WellKnownTools::RectangularMarquee = "rectangular_marquee";
47 const char* WellKnownTools::Lasso = "lasso";
48 const char* WellKnownTools::Pencil = "pencil";
49 const char* WellKnownTools::Eraser = "eraser";
50 const char* WellKnownTools::Eyedropper = "eyedropper";
51 const char* WellKnownTools::Hand = "hand";
52 
53 const char* WellKnownInks::Selection = "selection";
54 const char* WellKnownInks::Paint = "paint";
55 const char* WellKnownInks::PaintFg = "paint_fg";
56 const char* WellKnownInks::PaintBg = "paint_bg";
57 const char* WellKnownInks::PaintCopy = "paint_copy";
58 const char* WellKnownInks::PaintLockAlpha = "paint_lock_alpha";
59 const char* WellKnownInks::Shading = "shading";
60 const char* WellKnownInks::Gradient = "gradient";
61 const char* WellKnownInks::Eraser = "eraser";
62 const char* WellKnownInks::ReplaceFgWithBg = "replace_fg_with_bg";
63 const char* WellKnownInks::ReplaceBgWithFg = "replace_bg_with_fg";
64 const char* WellKnownInks::PickFg = "pick_fg";
65 const char* WellKnownInks::PickBg = "pick_bg";
66 const char* WellKnownInks::Zoom = "zoom";
67 const char* WellKnownInks::Scroll = "scroll";
68 const char* WellKnownInks::Move = "move";
69 const char* WellKnownInks::Slice = "slice";
70 const char* WellKnownInks::MoveSlice = "move_slice";
71 const char* WellKnownInks::Blur = "blur";
72 const char* WellKnownInks::Jumble = "jumble";
73 
74 const char* WellKnownControllers::Freehand = "freehand";
75 const char* WellKnownControllers::PointByPoint = "point_by_point";
76 const char* WellKnownControllers::OnePoints = "one_point";
77 const char* WellKnownControllers::TwoPoints = "two_points";
78 const char* WellKnownControllers::FourPoints = "four_points";
79 const char* WellKnownControllers::LineFreehand = "line_freehand";
80 
81 const char* WellKnownIntertwiners::None = "none";
82 const char* WellKnownIntertwiners::FirstPoint = "first_point";
83 const char* WellKnownIntertwiners::AsLines = "as_lines";
84 const char* WellKnownIntertwiners::AsRectangles = "as_rectangles";
85 const char* WellKnownIntertwiners::AsEllipses = "as_ellipses";
86 const char* WellKnownIntertwiners::AsBezier = "as_bezier";
87 const char* WellKnownIntertwiners::AsPixelPerfect = "as_pixel_perfect";
88 
89 const char* WellKnownPointShapes::None = "none";
90 const char* WellKnownPointShapes::Pixel = "pixel";
91 const char* WellKnownPointShapes::Brush = "brush";
92 const char* WellKnownPointShapes::FloodFill = "floodfill";
93 const char* WellKnownPointShapes::Spray = "spray";
94 
95 namespace {
96 
97 struct deleter {
98   template<typename T>
operator ()app::tools::__anon9d13d2250111::deleter99   void operator()(T* p) { delete p; }
100 
101   template<typename A, typename B>
operator ()app::tools::__anon9d13d2250111::deleter102   void operator()(std::pair<A,B>& p) { delete p.second; }
103 };
104 
105 } // anonymous namespace
106 
ToolBox()107 ToolBox::ToolBox()
108 {
109   m_xmlTranslator.setStringIdPrefix("tools");
110 
111   m_inks[WellKnownInks::Selection]       = new SelectionInk();
112   m_inks[WellKnownInks::Paint]           = new PaintInk(PaintInk::Simple);
113   m_inks[WellKnownInks::PaintFg]         = new PaintInk(PaintInk::WithFg);
114   m_inks[WellKnownInks::PaintBg]         = new PaintInk(PaintInk::WithBg);
115   m_inks[WellKnownInks::PaintCopy]       = new PaintInk(PaintInk::Copy);
116   m_inks[WellKnownInks::PaintLockAlpha]  = new PaintInk(PaintInk::LockAlpha);
117   m_inks[WellKnownInks::Gradient]        = new GradientInk();
118   m_inks[WellKnownInks::Shading]         = new ShadingInk();
119   m_inks[WellKnownInks::Eraser]          = new EraserInk(EraserInk::Eraser);
120   m_inks[WellKnownInks::ReplaceFgWithBg] = new EraserInk(EraserInk::ReplaceFgWithBg);
121   m_inks[WellKnownInks::ReplaceBgWithFg] = new EraserInk(EraserInk::ReplaceBgWithFg);
122   m_inks[WellKnownInks::PickFg]          = new PickInk(PickInk::Fg);
123   m_inks[WellKnownInks::PickBg]          = new PickInk(PickInk::Bg);
124   m_inks[WellKnownInks::Zoom]            = new ZoomInk();
125   m_inks[WellKnownInks::Scroll]          = new ScrollInk();
126   m_inks[WellKnownInks::Move]            = new MoveInk();
127   m_inks[WellKnownInks::Slice]           = new SliceInk();
128   m_inks[WellKnownInks::Blur]            = new BlurInk();
129   m_inks[WellKnownInks::Jumble]          = new JumbleInk();
130 
131   m_controllers[WellKnownControllers::Freehand] = new FreehandController();
132   m_controllers[WellKnownControllers::PointByPoint] = new PointByPointController();
133   m_controllers[WellKnownControllers::OnePoints] = new OnePointController();
134   m_controllers[WellKnownControllers::TwoPoints] = new TwoPointsController();
135   m_controllers[WellKnownControllers::FourPoints] = new FourPointsController();
136   m_controllers[WellKnownControllers::LineFreehand] = new LineFreehandController();
137 
138   m_pointshapers[WellKnownPointShapes::None] = new NonePointShape();
139   m_pointshapers[WellKnownPointShapes::Pixel] = new PixelPointShape();
140   m_pointshapers[WellKnownPointShapes::Brush] = new BrushPointShape();
141   m_pointshapers[WellKnownPointShapes::FloodFill] = new FloodFillPointShape();
142   m_pointshapers[WellKnownPointShapes::Spray] = new SprayPointShape();
143 
144   m_intertwiners[WellKnownIntertwiners::None] = new IntertwineNone();
145   m_intertwiners[WellKnownIntertwiners::FirstPoint] = new IntertwineFirstPoint();
146   m_intertwiners[WellKnownIntertwiners::AsLines] = new IntertwineAsLines();
147   m_intertwiners[WellKnownIntertwiners::AsRectangles] = new IntertwineAsRectangles();
148   m_intertwiners[WellKnownIntertwiners::AsEllipses] = new IntertwineAsEllipses();
149   m_intertwiners[WellKnownIntertwiners::AsBezier] = new IntertwineAsBezier();
150   m_intertwiners[WellKnownIntertwiners::AsPixelPerfect] = new IntertwineAsPixelPerfect();
151 
152   loadTools();
153 
154   // When the language is change, we reload the toolbox stirngs/tooltips.
155   Strings::instance()->LanguageChange.connect(
156     [this]{ loadTools(); });
157 }
158 
~ToolBox()159 ToolBox::~ToolBox()
160 {
161   std::for_each(m_tools.begin(), m_tools.end(), deleter());
162   std::for_each(m_groups.begin(), m_groups.end(), deleter());
163   std::for_each(m_intertwiners.begin(), m_intertwiners.end(), deleter());
164   std::for_each(m_pointshapers.begin(), m_pointshapers.end(), deleter());
165   std::for_each(m_controllers.begin(), m_controllers.end(), deleter());
166   std::for_each(m_inks.begin(), m_inks.end(), deleter());
167 }
168 
getToolById(const std::string & id)169 Tool* ToolBox::getToolById(const std::string& id)
170 {
171   for (ToolIterator it = begin(), end = this->end(); it != end; ++it) {
172     Tool* tool = *it;
173     if (tool->getId() == id)
174       return tool;
175   }
176   return nullptr;
177 }
178 
getInkById(const std::string & id)179 Ink* ToolBox::getInkById(const std::string& id)
180 {
181   return m_inks[id];
182 }
183 
getControllerById(const std::string & id)184 Controller* ToolBox::getControllerById(const std::string& id)
185 {
186   return m_controllers[id];
187 }
188 
getIntertwinerById(const std::string & id)189 Intertwine* ToolBox::getIntertwinerById(const std::string& id)
190 {
191   return m_intertwiners[id];
192 }
193 
getPointShapeById(const std::string & id)194 PointShape* ToolBox::getPointShapeById(const std::string& id)
195 {
196   return m_pointshapers[id];
197 }
198 
loadTools()199 void ToolBox::loadTools()
200 {
201   LOG("TOOL: Loading tools...\n");
202 
203   XmlDocumentRef doc(GuiXml::instance()->doc());
204   TiXmlHandle handle(doc.get());
205 
206   // For each group
207   TiXmlElement* xmlGroup = handle.FirstChild("gui").FirstChild("tools").FirstChild("group").ToElement();
208   while (xmlGroup) {
209     const char* groupId = xmlGroup->Attribute("id");
210     if (!groupId)
211       throw base::Exception("The configuration file has a <group> without 'id' or 'text' attributes.");
212 
213     LOG(VERBOSE) << "TOOL: Group " << groupId << "\n";
214 
215     // Find an existent ToolGroup (this is useful in case we are
216     // reloading tool text/tooltips).
217     ToolGroup* toolGroup = nullptr;
218     for (auto g : m_groups) {
219       if (g->id() == groupId) {
220         toolGroup = g;
221         break;
222       }
223     }
224     if (toolGroup == nullptr) {
225       toolGroup = new ToolGroup(groupId);
226       m_groups.push_back(toolGroup);
227     }
228 
229     // For each tool
230     TiXmlNode* xmlToolNode = xmlGroup->FirstChild("tool");
231     TiXmlElement* xmlTool = xmlToolNode ? xmlToolNode->ToElement(): NULL;
232     while (xmlTool) {
233       const char* toolId = xmlTool->Attribute("id");
234       std::string toolText = m_xmlTranslator(xmlTool, "text");
235       std::string toolTips = m_xmlTranslator(xmlTool, "tooltip");
236       const char* defaultBrushSize = xmlTool->Attribute("default_brush_size");
237 
238       Tool* tool = nullptr;
239       for (auto t : m_tools) {
240         if (t->getId() == toolId) {
241           tool = t;
242           break;
243         }
244       }
245       if (tool == nullptr) {
246         tool = new Tool(toolGroup, toolId);
247         m_tools.push_back(tool);
248       }
249 
250       tool->setText(toolText);
251       tool->setTips(toolTips);
252       tool->setDefaultBrushSize(
253         defaultBrushSize ? std::strtol(defaultBrushSize, nullptr, 10): 1);
254 
255       LOG(VERBOSE) << "TOOL: Tool " << toolId << " in group " << groupId << " found\n";
256 
257       loadToolProperties(xmlTool, tool, 0, "left");
258       loadToolProperties(xmlTool, tool, 1, "right");
259 
260       xmlTool = xmlTool->NextSiblingElement();
261     }
262 
263     xmlGroup = xmlGroup->NextSiblingElement();
264   }
265 
266   LOG("TOOL: Done. %d tools, %d groups.\n", m_tools.size(), m_groups.size());
267 }
268 
loadToolProperties(TiXmlElement * xmlTool,Tool * tool,int button,const std::string & suffix)269 void ToolBox::loadToolProperties(TiXmlElement* xmlTool, Tool* tool, int button, const std::string& suffix)
270 {
271   const char* tool_id = tool->getId().c_str();
272   const char* fill = xmlTool->Attribute(("fill_"+suffix).c_str());
273   const char* ink = xmlTool->Attribute(("ink_"+suffix).c_str());
274   const char* controller = xmlTool->Attribute(("controller_"+suffix).c_str());
275   const char* pointshape = xmlTool->Attribute(("pointshape_"+suffix).c_str());
276   const char* intertwine = xmlTool->Attribute(("intertwine_"+suffix).c_str());
277   const char* tracepolicy = xmlTool->Attribute(("tracepolicy_"+suffix).c_str());
278 
279   if (!fill) fill = xmlTool->Attribute("fill");
280   if (!ink) ink = xmlTool->Attribute("ink");
281   if (!controller) controller = xmlTool->Attribute("controller");
282   if (!pointshape) pointshape = xmlTool->Attribute("pointshape");
283   if (!intertwine) intertwine = xmlTool->Attribute("intertwine");
284   if (!tracepolicy) tracepolicy = xmlTool->Attribute("tracepolicy");
285 
286   // Fill
287   Fill fill_value = FillNone;
288   if (fill) {
289     if (strcmp(fill, "none") == 0)
290       fill_value = FillNone;
291     else if (strcmp(fill, "always") == 0)
292       fill_value = FillAlways;
293     else if (strcmp(fill, "optional") == 0)
294       fill_value = FillOptional;
295     else
296       throw base::Exception("Invalid fill '%s' specified in '%s' tool.\n", fill, tool_id);
297   }
298 
299   // Find the ink
300   std::map<std::string, Ink*>::iterator it_ink
301     = m_inks.find(ink ? ink: "");
302   if (it_ink == m_inks.end())
303     throw base::Exception("Invalid ink '%s' specified in '%s' tool.\n", ink, tool_id);
304 
305   // Find the controller
306   std::map<std::string, Controller*>::iterator it_controller
307     = m_controllers.find(controller ? controller: "none");
308   if (it_controller == m_controllers.end())
309     throw base::Exception("Invalid controller '%s' specified in '%s' tool.\n", controller, tool_id);
310 
311   // Find the point_shape
312   std::map<std::string, PointShape*>::iterator it_pointshaper
313     = m_pointshapers.find(pointshape ? pointshape: "none");
314   if (it_pointshaper == m_pointshapers.end())
315     throw base::Exception("Invalid point-shape '%s' specified in '%s' tool.\n", pointshape, tool_id);
316 
317   // Find the intertwiner
318   std::map<std::string, Intertwine*>::iterator it_intertwiner
319     = m_intertwiners.find(intertwine ? intertwine: "none");
320   if (it_intertwiner == m_intertwiners.end())
321     throw base::Exception("Invalid intertwiner '%s' specified in '%s' tool.\n", intertwine, tool_id);
322 
323   // Trace policy
324   TracePolicy tracepolicy_value = TracePolicy::Last;
325   if (tracepolicy) {
326     if (strcmp(tracepolicy, "accumulate") == 0)
327       tracepolicy_value = TracePolicy::Accumulate;
328     else if (strcmp(tracepolicy, "last") == 0)
329       tracepolicy_value = TracePolicy::Last;
330     else if (strcmp(tracepolicy, "overlap") == 0)
331       tracepolicy_value = TracePolicy::Overlap;
332     else
333       throw base::Exception("Invalid trace-policy '%s' specified in '%s' tool.\n", tracepolicy, tool_id);
334   }
335 
336   // Setup the tool properties
337   tool->setFill(button, fill_value);
338   tool->setInk(button, it_ink->second);
339   tool->setController(button, it_controller->second);
340   tool->setPointShape(button, it_pointshaper->second);
341   tool->setIntertwine(button, it_intertwiner->second);
342   tool->setTracePolicy(button, tracepolicy_value);
343 }
344 
345 } // namespace tools
346 } // namespace app
347