lua: Add app.drawWithTool() function

This commit is contained in:
David Capello 2019-03-15 17:03:02 -03:00
parent 79601567c5
commit 1cb5949e9b
3 changed files with 153 additions and 53 deletions

View File

@ -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<tools::ToolLoop> 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 }
};

View File

@ -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&) {

View File

@ -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,