diff --git a/src/app/script/app_object.cpp b/src/app/script/app_object.cpp index b156966b7..082a675f3 100644 --- a/src/app/script/app_object.cpp +++ b/src/app/script/app_object.cpp @@ -1,5 +1,5 @@ // Aseprite -// Copyright (C) 2018 Igara Studio S.A. +// Copyright (C) 2018-2019 Igara Studio S.A. // Copyright (C) 2015-2018 David Capello // // This program is distributed under the terms of @@ -23,9 +23,14 @@ #include "app/script/luacpp.h" #include "app/script/security.h" #include "app/site.h" +#include "app/tools/active_tool.h" +#include "app/tools/tool_box.h" +#include "app/tools/tool_loop.h" +#include "app/tools/tool_loop_manager.h" #include "app/tx.h" #include "app/ui/doc_view.h" #include "app/ui/editor/editor.h" +#include "app/ui/editor/tool_loop_impl.h" #include "app/ui/timeline/timeline.h" #include "app/ui_context.h" #include "base/fs.h" @@ -185,6 +190,71 @@ int App_refresh(lua_State* L) return 0; } +int App_drawWithTool(lua_State* L) +{ +#ifdef ENABLE_UI + // First argument must be a table + if (!lua_istable(L, 1)) + return luaL_error(L, "app.drawWithTool() must be called with a table as its first argument"); + + auto ctx = App::instance()->context(); + Doc* doc = ctx->activeDocument(); + if (!doc) + return luaL_error(L, "there is no active document to draw with the tool"); + + // Select tool by name + tools::Tool* tool = App::instance()->activeToolManager()->activeTool(); + tools::Ink* ink = App::instance()->activeToolManager()->activeInk(); + int type = lua_getfield(L, 1, "tool"); + if (type == LUA_TSTRING) { + const char* toolId = lua_tostring(L, -1); + tool = App::instance()->toolBox()->getToolById(toolId); + ink = tool->getInk(0); + } + lua_pop(L, 1); + + // Default color is the active fgColor + app::Color color = Preferences::instance().colorBar.fgColor(); + type = lua_getfield(L, 1, "color"); + if (type != LUA_TNIL) + color = convert_args_into_color(L, -1); + lua_pop(L, 1); + + // Do the tool loop + type = lua_getfield(L, 1, "points"); + if (type == LUA_TTABLE) { + std::unique_ptr loop( + create_tool_loop_for_script(ctx, tool, ink, color)); + tools::ToolLoopManager manager(loop.get()); + tools::Pointer lastPointer; + bool first = true; + + lua_pushnil(L); + while (lua_next(L, -2) != 0) { + gfx::Point pt = convert_args_into_point(L, -1); + + tools::Pointer pointer(pt, tools::Pointer::Button::Left); + if (first) { + first = false; + manager.prepareLoop(pointer); + manager.pressButton(pointer); + } + else { + manager.movement(pointer); + } + lastPointer = pointer; + lua_pop(L, 1); + } + if (!first) + manager.releaseButton(lastPointer); + + loop->commitOrRollback(); + } + lua_pop(L, 1); +#endif + return 0; +} + int App_get_activeSprite(lua_State* L) { app::Context* ctx = App::instance()->context(); @@ -430,6 +500,7 @@ const luaL_Reg App_methods[] = { { "redo", App_redo }, { "alert", App_alert }, { "refresh", App_refresh }, + { "drawWithTool", App_drawWithTool }, { nullptr, nullptr } }; diff --git a/src/app/ui/editor/tool_loop_impl.cpp b/src/app/ui/editor/tool_loop_impl.cpp index afa944645..8ae6d39c4 100644 --- a/src/app/ui/editor/tool_loop_impl.cpp +++ b/src/app/ui/editor/tool_loop_impl.cpp @@ -98,23 +98,21 @@ protected: doc::color_t m_secondaryColor; public: - ToolLoopBase(Editor* editor, - Layer* layer, - tools::Tool* tool, - tools::Ink* ink, + ToolLoopBase(Editor* editor, Site site, + tools::Tool* tool, tools::Ink* ink, tools::Controller* controller, - Doc* document, + const BrushRef& brush, tools::ToolLoop::Button button, const app::Color& fgColor, const app::Color& bgColor) : m_editor(editor) , m_tool(tool) - , m_brush(App::instance()->contextBar()->activeBrush(m_tool, ink)) + , m_brush(brush) , m_oldPatternOrigin(m_brush->patternOrigin()) - , m_document(document) - , m_sprite(editor->sprite()) - , m_layer(layer) - , m_frame(editor->frame()) + , m_document(site.document()) + , m_sprite(site.sprite()) + , m_layer(site.layer()) + , m_frame(site.frame()) , m_rgbMap(nullptr) , m_docPref(Preferences::instance().document(m_document)) , m_toolPref(Preferences::instance().tool(m_tool)) @@ -229,7 +227,10 @@ public: int getOpacity() override { return m_opacity; } int getTolerance() override { return m_tolerance; } bool getContiguous() override { return m_contiguous; } - tools::ToolLoopModifiers getModifiers() override { return m_editor->getToolLoopModifiers(); } + tools::ToolLoopModifiers getModifiers() override { + return m_editor ? m_editor->getToolLoopModifiers(): + tools::ToolLoopModifiers::kNone; + } filters::TiledMode getTiledMode() override { return m_docPref.tiled.mode(); } bool getGridVisible() override { return m_docPref.show.grid(); } bool getSnapToGrid() override { return m_docPref.grid.snap(); } @@ -278,6 +279,9 @@ public: } void updateDirtyArea(const gfx::Region& dirtyArea) override { + if (!m_editor) + return; + // This is necessary here so the "on sprite crosshair" is hidden, // we update screen pixels with the new sprite, and then we show // the crosshair saving the updated pixels. It fixes problems with @@ -290,11 +294,12 @@ public: } void updateStatusBar(const char* text) override { - StatusBar::instance()->setStatusText(0, text); + if (auto statusBar = StatusBar::instance()) + statusBar->setStatusText(0, text); } gfx::Point statusBarPositionOffset() override { - return -m_editor->mainTilePosition(); + return (m_editor ? -m_editor->mainTilePosition(): gfx::Point(0, 0)); } render::DitheringMatrix getDitheringMatrix() override { @@ -327,25 +332,19 @@ class ToolLoopImpl : public ToolLoopBase { public: ToolLoopImpl(Editor* editor, - Layer* layer, + Site site, Context* context, tools::Tool* tool, tools::Ink* ink, tools::Controller* controller, - Doc* document, + const BrushRef& brush, tools::ToolLoop::Button button, const app::Color& fgColor, const app::Color& bgColor, const bool saveLastPoint) - : ToolLoopBase(editor, - layer, - tool, - ink, - controller, - document, - button, - fgColor, - bgColor) + : ToolLoopBase(editor, site, + tool, ink, controller, brush, + button, fgColor, bgColor) , m_context(context) , m_canceled(false) , m_transaction(m_context, @@ -360,8 +359,6 @@ public: , m_floodfillSrcImage(nullptr) , m_saveLastPoint(saveLastPoint) { - ASSERT(m_context->activeDocument() == m_editor->document()); - if (m_pointShape->isFloodFill()) { // Prepare a special image for floodfill when it's configured to // stop using all visible layers. @@ -393,8 +390,7 @@ public: } m_expandCelCanvas = new ExpandCelCanvas( - editor->getSite(), - layer, + site, site.layer(), m_docPref.tiled.mode(), m_transaction, ExpandCelCanvas::Flags( @@ -567,7 +563,7 @@ tools::ToolLoop* create_tool_loop( if (!tool || !ink) return nullptr; - Layer* layer; + Site site = editor->getSite(); // For selection tools, we can use any layer (even without layers at // all), so we specify a nullptr here as the active layer. This is @@ -580,10 +576,10 @@ tools::ToolLoop* create_tool_loop( // image/pixels to stop the flood-fill algorithm. if (ink->isSelection() && !tool->getPointShape(button != tools::Pointer::Left ? 1: 0)->isFloodFill()) { - layer = nullptr; + site.layer(nullptr); } else { - layer = editor->layer(); + Layer* layer = site.layer(); if (!layer) { StatusBar::instance()->showTip( 1000, "There is no active layer"); @@ -639,14 +635,12 @@ tools::ToolLoop* create_tool_loop( (controller->isFreehand() || convertLineToFreehand)); + ASSERT(context->activeDocument() == editor->document()); return new ToolLoopImpl( - editor, layer, context, - tool, - ink, - controller, - editor->document(), - toolLoopButton, - fg, bg, + editor, site, context, + tool, ink, controller, + App::instance()->contextBar()->activeBrush(tool, ink), + toolLoopButton, fg, bg, saveLastPoint); } catch (const std::exception& ex) { @@ -655,6 +649,40 @@ tools::ToolLoop* create_tool_loop( } } +tools::ToolLoop* create_tool_loop_for_script( + Context* context, + tools::Tool* tool, + tools::Ink* ink, + const app::Color& color) +{ + ASSERT(tool); + ASSERT(ink); + + Site site = context->activeSite(); + if (!site.layer()) + return nullptr; + + try { + tools::ToolLoop::Button toolLoopButton = tools::ToolLoop::Left; + tools::Controller* controller = tool->getController(toolLoopButton); + BrushRef brush; + if (App::instance()->contextBar()) + brush = App::instance()->contextBar()->activeBrush(tool, ink); + if (!brush) + brush = BrushRef(new Brush(BrushType::kCircleBrushType, 1, 0)); + + return new ToolLoopImpl( + nullptr, site, context, + tool, ink, controller, + brush, + toolLoopButton, color, color, false); + } + catch (const std::exception& ex) { + Console::showException(ex); + return nullptr; + } +} + ////////////////////////////////////////////////////////////////////// // For preview @@ -666,20 +694,14 @@ public: Editor* editor, tools::Tool* tool, tools::Ink* ink, - Doc* document, const app::Color& fgColor, const app::Color& bgColor, Image* image, const gfx::Point& celOrigin) - : ToolLoopBase(editor, - editor->layer(), - tool, - ink, - tool->getController(tools::ToolLoop::Left), - document, - tools::ToolLoop::Left, - fgColor, - bgColor) + : ToolLoopBase(editor, editor->getSite(), + tool, ink, tool->getController(tools::ToolLoop::Left), + App::instance()->contextBar()->activeBrush(tool, ink), + tools::ToolLoop::Left, fgColor, bgColor) , m_image(image) { m_celOrigin = celOrigin; @@ -750,10 +772,7 @@ tools::ToolLoop* create_tool_loop_preview( // Create the new tool loop try { return new PreviewToolLoopImpl( - editor, - tool, - ink, - editor->document(), + editor, tool, ink, fg, bg, image, celOrigin); } catch (const std::exception&) { diff --git a/src/app/ui/editor/tool_loop_impl.h b/src/app/ui/editor/tool_loop_impl.h index 594708c3e..02f1a2674 100644 --- a/src/app/ui/editor/tool_loop_impl.h +++ b/src/app/ui/editor/tool_loop_impl.h @@ -1,4 +1,5 @@ // Aseprite +// Copyright (C) 2019 Igara Studio S.A. // Copyright (C) 2001-2017 David Capello // // This program is distributed under the terms of @@ -17,10 +18,13 @@ namespace doc { } namespace app { + class Color; class Context; class Editor; namespace tools { + class Ink; + class Tool; class ToolLoop; } @@ -30,6 +34,12 @@ namespace app { const tools::Pointer::Button button, const bool convertLineToFreehand); + tools::ToolLoop* create_tool_loop_for_script( + Context* context, + tools::Tool* tool, + tools::Ink* ink, + const app::Color& color); + tools::ToolLoop* create_tool_loop_preview( Editor* editor, doc::Image* image,