diff --git a/.travis.yml b/.travis.yml
index bc8573603..94f4fb6be 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -3,7 +3,6 @@ language: cpp
matrix:
include:
- os: osx
- osx_image: xcode9
env:
- ENABLE_UI=ON
- MATRIX_EVAL="wget https://github.com/ninja-build/ninja/releases/download/v1.8.2/ninja-mac.zip && unzip ninja-mac.zip && export PATH=$PWD:$PATH"
diff --git a/CMakeLists.txt b/CMakeLists.txt
index bcd1fee04..9cb5a2cdf 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -219,7 +219,7 @@ add_definitions(-DPNG_NO_MMX_CODE) # Do not use MMX optimizations in PNG code
# libwebp
if(WITH_WEBP_SUPPORT)
- set(WEBP_LIBRARIES webp)
+ set(WEBP_LIBRARIES webp webpdemux libwebpmux)
set(WEBP_INCLUDE_DIR ${LIBWEBP_DIR}/src)
include_directories(${WEBP_INCLUDE_DIR})
endif()
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index e224c4920..1d2600b7e 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -93,7 +93,7 @@ You can fork the GitHub repository using the Fork button at
The Pull Requests (PR) systems works in this way:
1. First of all you will need to sign our
- [Contributor License Agreement](https://github.com/aseprite/opensource/blob/master/sign-cla.md#sign-the-cla) (CLA).
+ [Contributor License Agreement](https://github.com/aseprite/sourcecode/blob/master/sign-cla.md#sign-the-cla) (CLA).
1. Then you can start working on Aseprite. Create a new branch from `master`, e.g. `fix-8` to fix the issue 8.
Check this guide about [how to name your branch](https://github.com/agis/git-style-guide#branches).
1. Start working on that new branch, and push your commits to your fork.
diff --git a/README.md b/README.md
index 638bb91be..3cd3e904f 100644
--- a/README.md
+++ b/README.md
@@ -7,21 +7,24 @@
## Introduction
-**Aseprite** is a program to create animated sprites. Its main
-features are:
+**Aseprite** is a program to create animated sprites. Its main features are:
-* Sprites are composed of [**layers** & **frames**](http://www.aseprite.org/docs/timeline/) (as separated concepts).
-* Supported [color modes](http://www.aseprite.org/docs/color/): **RGBA**, **Indexed** (palettes up to 256
- colors), and Grayscale.
-* Load/save a sequence of **PNG** files and **GIF** animations (and
- FLC, FLI, JPG, BMP, PCX, TGA).
-* Export/import animations to/from **Sprite Sheets**.
-* **Tiled** drawing mode, useful to draw **patterns** and textures.
-* **Undo/Redo** for every operation.
-* Real-time **animation preview**.
-* [**Multiple editors**](http://www.aseprite.org/docs/workspace/#drag-and-drop-tabs) support.
-* Pixel-art specific tools like filled **Contour**, **Polygon**, [**Shading**](http://www.aseprite.org/docs/shading/) mode, etc.
-* [**Onion skinning**](https://www.aseprite.org/docs/animation/#onion-skinning)
+* Sprites are composed of [layers & frames](https://www.aseprite.org/docs/timeline/) as separated concepts.
+* Support for [color profiles](https://www.aseprite.org/docs/color-profile/) and different [color modes](https://www.aseprite.org/docs/color-mode/): RGBA, Indexed (palettes up to 256 colors), Grayscale.
+* [Animation facilities](https://www.aseprite.org/docs/animation/), with real-time [preview](https://www.aseprite.org/docs/preview-window/) and [onion skinning](https://www.aseprite.org/docs/onion-skinning/).
+* [Export/import](https://www.aseprite.org/docs/exporting/) animations to/from [sprite sheets](https://www.aseprite.org/docs/sprite-sheet/), GIF files, or sequence of PNG files (and FLC, FLI, JPG, BMP, PCX, TGA).
+* [Multiple editors](https://www.aseprite.org/docs/workspace/#drag-and-drop-tabs) support.
+* [Layer groups](https://imgur.com/x3OKkGj) for organizing your work, and [reference layers](https://twitter.com/aseprite/status/806889204601016325) for rotoscoping.
+* Pixel-art specific tools like [Pixel Perfect freehand mode](https://imgur.com/0fdlNau), [Shading ink](https://www.aseprite.org/docs/shading/), [Custom Brushes](https://twitter.com/aseprite/status/1196883990080344067), [Outlines](https://twitter.com/aseprite/status/1126548469865431041), [Wide Pixels](https://imgur.com/1yZKUcs), etc.
+* Other special drawing tools like [Pressure sensitivity](https://twitter.com/aseprite/status/1253770784708886533), [Symmetry Tool](https://twitter.com/aseprite/status/659709226747625472), [Stroke and Fill](https://imgur.com/7JZQ81o) selection, [Gradients](https://twitter.com/aseprite/status/1126549217856622597).
+* [Tiled mode](https://twitter.com/pixel__toast/status/1132079817736695808) useful to draw patterns and textures.
+* [Transform multiple frames/layers](https://twitter.com/aseprite/status/1170007034651172866) at the same time.
+* [Lua scripting capabilities](https://www.aseprite.org/docs/scripting/).
+* [CLI - Command Line Interface](https://www.aseprite.org/docs/cli/) to automatize tasks.
+* [Quick Reference / Cheat Sheet](https://www.aseprite.org/quickref/) keyboard shortcuts ([customizable keys](https://imgur.com/rvAUxyF) and [mouse wheel](https://imgur.com/oNqFqVb)).
+* [Reopen closed files](https://twitter.com/aseprite/status/1202641475256881153) and [recover data](https://www.aseprite.org/docs/data-recovery/) in case of crash.
+* Undo/Redo for every operation and support for [non-linear undo](https://imgur.com/9I42fZK).
+* [More features & tips](https://twitter.com/aseprite/status/1124442198651678720)
## Issues
@@ -77,7 +80,7 @@ It tries to replicate some pixel-art algorithms:
* [Pixel perfect drawing algorithm](http://deepnight.net/pixel-perfect-drawing/) by [Sébastien Bénard](https://twitter.com/deepnightfr) and [Carduus](https://twitter.com/CarduusHimself/status/420554200737935361).
Thanks to [third-party open source projects](docs/LICENSES.md), to
-[contributors](http://www.aseprite.org/contributors/), and all the
+[contributors](https://www.aseprite.org/contributors/), and all the
people who have contributed ideas, patches, bugs report, feature
requests, donations, and help me to develop Aseprite.
@@ -95,11 +98,11 @@ This program is distributed under three different licenses:
[observable](https://github.com/aseprite/observable),
[ui](src/ui), etc.).
2. You can request a special
- [educational license](http://www.aseprite.org/faq/#is-there-an-educational-license)
+ [educational license](https://www.aseprite.org/faq/#is-there-an-educational-license)
in case you are a teacher in an educational institution and want to
use Aseprite in your classroom (in-situ).
3. Steam releases are distributed under the terms of the
[Steam Subscriber Agreement](http://store.steampowered.com/subscriber_agreement/).
You can get more information about Aseprite license in the
-[FAQ](http://www.aseprite.org/faq/#licensing-&-commercial).
+[FAQ](https://www.aseprite.org/faq/#licensing-&-commercial).
diff --git a/data/extensions/endesga-palettes/hept32.gpl b/data/extensions/endesga-palettes/hept32.gpl
new file mode 100644
index 000000000..584fa4adf
--- /dev/null
+++ b/data/extensions/endesga-palettes/hept32.gpl
@@ -0,0 +1,34 @@
+GIMP Palette
+#
+ 0 0 0 Untitled
+ 24 13 47 Untitled
+ 53 54 88 Untitled
+104 107 114 Untitled
+139 151 182 Untitled
+197 205 219 Untitled
+255 255 255 Untitled
+ 94 233 233 Untitled
+ 40 144 220 Untitled
+ 24 49 167 Untitled
+ 5 50 57 Untitled
+ 0 95 65 Untitled
+ 8 178 59 Untitled
+ 71 246 65 Untitled
+232 255 117 Untitled
+251 190 130 Untitled
+222 151 81 Untitled
+182 104 49 Untitled
+138 73 38 Untitled
+ 70 28 20 Untitled
+ 30 9 13 Untitled
+114 13 13 Untitled
+129 55 4 Untitled
+218 36 36 Untitled
+239 110 16 Untitled
+236 171 17 Untitled
+236 233 16 Untitled
+247 141 141 Untitled
+249 78 109 Untitled
+193 36 88 Untitled
+132 18 82 Untitled
+ 61 8 59 Untitled
diff --git a/data/extensions/endesga-palettes/package.json b/data/extensions/endesga-palettes/package.json
index acc847c71..a270531cc 100644
--- a/data/extensions/endesga-palettes/package.json
+++ b/data/extensions/endesga-palettes/package.json
@@ -16,7 +16,8 @@
{ "id": "EDG32", "path": "./edg32.gpl" },
{ "id": "EDG8", "path": "./edg8.gpl" },
{ "id": "EN4", "path": "./en4.gpl" },
- { "id": "ENOS16", "path": "./enos16.gpl" }
+ { "id": "ENOS16", "path": "./enos16.gpl" },
+ { "id": "HEPT32", "path": "./hept32.gpl" }
]
}
}
diff --git a/data/pref.xml b/data/pref.xml
index 200102a3a..314f36315 100644
--- a/data/pref.xml
+++ b/data/pref.xml
@@ -184,6 +184,9 @@
+
diff --git a/data/strings/en.ini b/data/strings/en.ini
index 885fb603b..bb1713d4f 100644
--- a/data/strings/en.ini
+++ b/data/strings/en.ini
@@ -1056,6 +1056,7 @@ skip = &Skip
title = Preferences
section_general = General
section_files = Files
+section_tablet = Tablet
section_alerts = Alerts
section_color = Color
section_editor = Editor
@@ -1135,6 +1136,9 @@ clear_recent_files = Clear
clear_recent_files_tooltip = Clear the list of recent files and folders
locate_file = Locate Configuration File
locate_crash_folder = Locate Crash Folder
+tablet_api_windows_pointer = Windows 8/10 Pointer API (Windows Ink)
+tablet_api_wintab_system = Wintab
+tablet_api_wintab_direct = Wintab (direct packet processing)
wheel_zoom = Zoom with scroll wheel
slide_zoom = Zoom sliding two fingers up or down
zoom_from_center_with_wheel = Zoom from center with scroll wheel
@@ -1302,12 +1306,9 @@ native_clipboard = Use native clipboard
native_file_dialog = Use native file dialog
one_finger_as_mouse_movement = Interpret one finger as mouse movement
one_finger_as_mouse_movement_tooltip = <<
+
@@ -73,6 +74,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -473,15 +493,10 @@
-
-
+ tooltip="@.load_wintab_driver_tooltip" />
diff --git a/laf b/laf
index 8032d186a..af0f8e7b5 160000
--- a/laf
+++ b/laf
@@ -1 +1 @@
-Subproject commit 8032d186a751326d0fc6436d69570ad4a3c4aaf1
+Subproject commit af0f8e7b53b9e3e689b5fa4e5ce1466c42c9e2aa
diff --git a/src/app/active_site_handler.cpp b/src/app/active_site_handler.cpp
index d470f4585..694aebb7a 100644
--- a/src/app/active_site_handler.cpp
+++ b/src/app/active_site_handler.cpp
@@ -138,6 +138,10 @@ void ActiveSiteHandler::onBeforeRemoveLayer(DocEvent& ev)
if (!selectedLayer)
return;
+ // Remove layer from range
+ data.range.eraseAndAdjust(ev.layer());
+
+ // Select other layer as active
doc::Layer* layerToSelect = candidate_if_layer_is_deleted(selectedLayer, ev.layer());
if (selectedLayer != layerToSelect) {
data.layer = (layerToSelect ? layerToSelect->id():
diff --git a/src/app/app.cpp b/src/app/app.cpp
index 56845c356..f2d0ff2d2 100644
--- a/src/app/app.cpp
+++ b/src/app/app.cpp
@@ -229,9 +229,14 @@ int App::initialize(const AppOptions& options)
#ifdef _WIN32
if (options.disableWintab() ||
- !preferences().experimental.loadWintabDriver()) {
- system->useWintabAPI(false);
+ !preferences().experimental.loadWintabDriver() ||
+ preferences().tablet.api() == "pointer") {
+ system->setTabletAPI(os::TabletAPI::WindowsPointerInput);
}
+ else if (preferences().tablet.api() == "wintab_packets")
+ system->setTabletAPI(os::TabletAPI::WintabPackets);
+ else // preferences().tablet.api() == "wintab"
+ system->setTabletAPI(os::TabletAPI::Wintab);
#endif
system->setAppName(get_app_name());
diff --git a/src/app/commands/cmd_flip.cpp b/src/app/commands/cmd_flip.cpp
index 65192fbba..e66f84e71 100644
--- a/src/app/commands/cmd_flip.cpp
+++ b/src/app/commands/cmd_flip.cpp
@@ -75,6 +75,7 @@ void FlipCommand::onExecute(Context* ctx)
CelList cels;
if (m_flipMask) {
+#ifdef ENABLE_UI
// If we want to flip the visible mask we can go to
// MovingPixelsState (even when the range is enabled, because now
// PixelsMovement support ranges).
@@ -88,6 +89,7 @@ void FlipCommand::onExecute(Context* ctx)
return;
}
}
+#endif
auto range = site.range();
if (range.enabled()) {
@@ -100,8 +102,12 @@ void FlipCommand::onExecute(Context* ctx)
}
if (cels.empty()) {
- StatusBar::instance()->showTip(
- 1000, Strings::statusbar_tips_all_layers_are_locked());
+#ifdef ENABLE_UI
+ if (ctx->isUIAvailable()) {
+ StatusBar::instance()->showTip(
+ 1000, Strings::statusbar_tips_all_layers_are_locked());
+ }
+#endif // ENABLE_UI
return;
}
}
diff --git a/src/app/commands/cmd_options.cpp b/src/app/commands/cmd_options.cpp
index 05894e2f3..35a46171f 100644
--- a/src/app/commands/cmd_options.cpp
+++ b/src/app/commands/cmd_options.cpp
@@ -50,6 +50,7 @@ namespace app {
namespace {
const char* kSectionGeneralId = "section_general";
+const char* kSectionTabletId = "section_tablet";
const char* kSectionBgId = "section_bg";
const char* kSectionGridId = "section_grid";
const char* kSectionThemeId = "section_theme";
@@ -351,9 +352,47 @@ public:
if (m_pref.experimental.useNativeFileDialog())
nativeFileDialog()->setSelected(true);
-#ifndef _WIN32
- oneFingerAsMouseMovement()->setVisible(false);
- loadWintabDriverBox()->setVisible(false);
+#ifdef _WIN32 // Show Tablet section on Windows
+ {
+ os::TabletAPI tabletAPI = os::instance()->tabletAPI();
+
+ if (tabletAPI == os::TabletAPI::Wintab) {
+ tabletApiWintabSystem()->setSelected(true);
+ loadWintabDriver()->setSelected(true);
+ loadWintabDriver2()->setSelected(true);
+ }
+ else if (tabletAPI == os::TabletAPI::WintabPackets) {
+ tabletApiWintabDirect()->setSelected(true);
+ loadWintabDriver()->setSelected(true);
+ loadWintabDriver2()->setSelected(true);
+ }
+ else {
+ tabletApiWindowsPointer()->setSelected(true);
+ loadWintabDriver()->setSelected(false);
+ loadWintabDriver2()->setSelected(false);
+ }
+
+ tabletApiWindowsPointer()->Click.connect([this](Event&){ onTabletAPIChange(); });
+ tabletApiWintabSystem()->Click.connect([this](Event&){ onTabletAPIChange(); });
+ tabletApiWintabDirect()->Click.connect([this](Event&){ onTabletAPIChange(); });
+ loadWintabDriver()->Click.connect(
+ [this](Event&){ onLoadWintabChange(loadWintabDriver()->isSelected()); });
+ loadWintabDriver2()->Click.connect(
+ [this](Event&){ onLoadWintabChange(loadWintabDriver2()->isSelected()); });
+ }
+#else // For macOS and Linux
+ {
+ // Hide the "section_tablet" item (which is only for Windows at the moment)
+ for (auto item : sectionListbox()->children()) {
+ if (static_cast(item)->getValue() == kSectionTabletId) {
+ item->setVisible(false);
+ break;
+ }
+ }
+ sectionTablet()->setVisible(false);
+ loadWintabDriverBox()->setVisible(false);
+ loadWintabDriverBox()->setVisible(false);
+ }
#endif
if (m_pref.experimental.flashLayer())
@@ -658,9 +697,35 @@ public:
m_pref.experimental.nonactiveLayersOpacity(nonactiveLayersOpacity()->getValue());
#ifdef _WIN32
- manager()->getDisplay()
- ->setInterpretOneFingerGestureAsMouseMovement(
- oneFingerAsMouseMovement()->isSelected());
+ {
+ os::TabletAPI tabletAPI = os::TabletAPI::Default;
+ std::string tabletStr;
+ bool wintabState = false;
+
+ if (tabletApiWindowsPointer()->isSelected()) {
+ tabletAPI = os::TabletAPI::WindowsPointerInput;
+ tabletStr = "pointer";
+ }
+ else if (tabletApiWintabSystem()->isSelected()) {
+ tabletAPI = os::TabletAPI::Wintab;
+ tabletStr = "wintab";
+ wintabState = true;
+ }
+ else if (tabletApiWintabDirect()->isSelected()) {
+ tabletAPI = os::TabletAPI::WintabPackets;
+ tabletStr = "wintab_packets";
+ wintabState = true;
+ }
+
+ m_pref.tablet.api(tabletStr);
+ m_pref.experimental.loadWintabDriver(wintabState);
+
+ manager()->getDisplay()
+ ->setInterpretOneFingerGestureAsMouseMovement(
+ oneFingerAsMouseMovement()->isSelected());
+
+ os::instance()->setTabletAPI(tabletAPI);
+ }
#endif
ui::set_use_native_cursors(m_pref.cursor.useNativeCursor());
@@ -1505,6 +1570,29 @@ private:
}
}
+#ifdef _WIN32
+ void onTabletAPIChange() {
+ if (tabletApiWindowsPointer()->isSelected()) {
+ loadWintabDriver()->setSelected(false);
+ loadWintabDriver2()->setSelected(false);
+ }
+ else if (tabletApiWintabSystem()->isSelected() ||
+ tabletApiWintabDirect()->isSelected()) {
+ loadWintabDriver()->setSelected(true);
+ loadWintabDriver2()->setSelected(true);
+ }
+ }
+ void onLoadWintabChange(bool state) {
+ loadWintabDriver()->setSelected(state);
+ loadWintabDriver2()->setSelected(state);
+ if (state)
+ tabletApiWintabSystem()->setSelected(true);
+ else
+ tabletApiWindowsPointer()->setSelected(true);
+ }
+
+#endif // _WIN32
+
Context* m_context;
Preferences& m_pref;
DocumentPreferences& m_globPref;
diff --git a/src/app/commands/cmd_set_ink_type.cpp b/src/app/commands/cmd_set_ink_type.cpp
index c0c04f12a..4655fed98 100644
--- a/src/app/commands/cmd_set_ink_type.cpp
+++ b/src/app/commands/cmd_set_ink_type.cpp
@@ -1,4 +1,5 @@
// Aseprite
+// Copyright (C) 2020 Igara Studio S.A.
// Copyright (C) 2001-2017 David Capello
//
// This program is distributed under the terms of
@@ -11,7 +12,7 @@
#include "app/app.h"
#include "app/commands/command.h"
#include "app/commands/commands.h"
-#include "app/commands/params.h"
+#include "app/commands/new_params.h"
#include "app/i18n/strings.h"
#include "app/tools/ink_type.h"
#include "app/ui/context_bar.h"
@@ -19,60 +20,42 @@
namespace app {
-class SetInkTypeCommand : public Command {
+struct SetInkTypeParams : public NewParams {
+ Param type { this, app::tools::InkType::DEFAULT, "type" };
+};
+
+class SetInkTypeCommand : public CommandWithNewParams {
public:
SetInkTypeCommand();
protected:
bool onNeedsParams() const override { return true; }
- void onLoadParams(const Params& params) override;
bool onChecked(Context* context) override;
void onExecute(Context* context) override;
std::string onGetFriendlyName() const override;
-
-private:
- tools::InkType m_type;
};
SetInkTypeCommand::SetInkTypeCommand()
- : Command(CommandId::SetInkType(), CmdUIOnlyFlag)
- , m_type(tools::InkType::DEFAULT)
+ : CommandWithNewParams(CommandId::SetInkType(), CmdUIOnlyFlag)
{
}
-void SetInkTypeCommand::onLoadParams(const Params& params)
-{
- std::string typeStr = params.get("type");
- if (typeStr == "simple")
- m_type = tools::InkType::SIMPLE;
- else if (typeStr == "alpha-compositing")
- m_type = tools::InkType::ALPHA_COMPOSITING;
- else if (typeStr == "copy-color")
- m_type = tools::InkType::COPY_COLOR;
- else if (typeStr == "lock-alpha")
- m_type = tools::InkType::LOCK_ALPHA;
- else if (typeStr == "shading")
- m_type = tools::InkType::SHADING;
- else
- m_type = tools::InkType::DEFAULT;
-}
-
bool SetInkTypeCommand::onChecked(Context* context)
{
tools::Tool* tool = App::instance()->activeTool();
- return (Preferences::instance().tool(tool).ink() == m_type);
+ return (Preferences::instance().tool(tool).ink() == params().type());
}
void SetInkTypeCommand::onExecute(Context* context)
{
if (App::instance()->contextBar() != nullptr)
- App::instance()->contextBar()->setInkType(m_type);
+ App::instance()->contextBar()->setInkType(params().type());
}
std::string SetInkTypeCommand::onGetFriendlyName() const
{
std::string ink;
- switch (m_type) {
+ switch (params().type()) {
case tools::InkType::SIMPLE:
ink = Strings::inks_simple_ink();
break;
diff --git a/src/app/commands/new_params.cpp b/src/app/commands/new_params.cpp
index db3abd8c4..20fb006e7 100644
--- a/src/app/commands/new_params.cpp
+++ b/src/app/commands/new_params.cpp
@@ -1,5 +1,5 @@
// Aseprite
-// Copyright (C) 2019 Igara Studio S.A.
+// Copyright (C) 2019-2020 Igara Studio S.A.
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@@ -13,6 +13,7 @@
#include "app/color.h"
#include "app/doc_exporter.h"
#include "app/sprite_sheet_type.h"
+#include "app/tools/ink_type.h"
#include "base/convert_to.h"
#include "base/split_string.h"
#include "base/string.h"
@@ -26,6 +27,7 @@
#ifdef ENABLE_SCRIPTING
#include "app/script/engine.h"
#include "app/script/luacpp.h"
+#include "app/script/values.h"
#endif
namespace app {
@@ -184,6 +186,12 @@ void Param::fromString(const std::string& value)
setValue(curve);
}
+template<>
+void Param::fromString(const std::string& value)
+{
+ setValue(tools::string_id_to_ink_type(value));
+}
+
//////////////////////////////////////////////////////////////////////
// Convert values from Lua
//////////////////////////////////////////////////////////////////////
@@ -312,6 +320,12 @@ void Param::fromLua(lua_State* L, int index)
}
}
+template<>
+void Param::fromLua(lua_State* L, int index)
+{
+ script::get_value_from_lua(L, index);
+}
+
void CommandWithNewParamsBase::loadParamsFromLuaTable(lua_State* L, int index)
{
onResetValues();
diff --git a/src/app/recent_files.cpp b/src/app/recent_files.cpp
index c187ef188..3744eb4ef 100644
--- a/src/app/recent_files.cpp
+++ b/src/app/recent_files.cpp
@@ -1,5 +1,5 @@
// Aseprite
-// Copyright (C) 2018 Igara Studio S.A.
+// Copyright (C) 2018-2020 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@@ -12,8 +12,8 @@
#include "app/recent_files.h"
#include "app/ini_file.h"
-#include "base/convert_to.h"
#include "base/fs.h"
+#include "fmt/format.h"
#include
#include
@@ -225,7 +225,7 @@ void RecentFiles::save()
for (int j=0; j(j).c_str(),
+ fmt::format("{:04d}", j).c_str(),
m_paths[i][j].c_str());
}
// Special entry that indicates that we've already converted
diff --git a/src/app/script/app_object.cpp b/src/app/script/app_object.cpp
index da4951e1f..7e0f05b4c 100644
--- a/src/app/script/app_object.cpp
+++ b/src/app/script/app_object.cpp
@@ -36,6 +36,7 @@
#include "app/ui/editor/tool_loop_impl.h"
#include "app/ui/timeline/timeline.h"
#include "app/ui_context.h"
+#include "base/clamp.h"
#include "base/fs.h"
#include "base/replace_string.h"
#include "base/version.h"
@@ -46,6 +47,7 @@
#include "ui/alert.h"
#include "ver/info.h"
+#include
#include
namespace app {
@@ -277,40 +279,113 @@ int App_useTool(lua_State* L)
if (!site.document())
return luaL_error(L, "there is no active document to draw with the tool");
+ // Options to create the ToolLoop (tool, ink, color, opacity, etc.)
+ ToolLoopParams params;
+
+ // Mouse button
+ params.button = tools::ToolLoop::Left;
+ type = lua_getfield(L, 1, "button");
+ if (type != LUA_TNIL) {
+ // Only supported button at the moment left (default) or right
+ if (lua_tointeger(L, -1) == (int)ui::kButtonRight)
+ params.button = tools::ToolLoop::Right;
+ }
+ lua_pop(L, 1);
+
// Select tool by name
- tools::Tool* tool = App::instance()->activeToolManager()->activeTool();
- tools::Ink* ink = tool->getInk(0);
+ const int buttonIdx = (params.button == tools::ToolLoop::Left ? 0: 1);
+ auto activeToolMgr = App::instance()->activeToolManager();
+ params.tool = activeToolMgr->activeTool();
+ params.ink = params.tool->getInk(buttonIdx);
+ params.controller = params.tool->getController(buttonIdx);
type = lua_getfield(L, 1, "tool");
if (type != LUA_TNIL) {
if (auto toolArg = get_tool_from_arg(L, -1)) {
- tool = toolArg;
- ink = tool->getInk(0);
+ params.tool = toolArg;
+ params.ink = params.tool->getInk(buttonIdx);
+ params.controller = params.tool->getController(buttonIdx);
}
else
return luaL_error(L, "invalid tool specified in app.useTool() function");
}
lua_pop(L, 1);
- // Default color is the active fgColor
- app::Color color = Preferences::instance().colorBar.fgColor();
- type = lua_getfield(L, 1, "color");
+ // Select ink by name
+ type = lua_getfield(L, 1, "ink");
if (type != LUA_TNIL)
- color = convert_args_into_color(L, -1);
+ params.inkType = get_value_from_lua(L, -1);
lua_pop(L, 1);
- // Default brush is the active brush in the context bar
- BrushRef brush(nullptr);
-#ifdef ENABLE_UI
- if (App::instance()->isGui() &&
- App::instance()->contextBar())
- brush = App::instance()->contextBar()->activeBrush(tool, ink);
-#endif
+ // Color
+ type = lua_getfield(L, 1, "color");
+ if (type != LUA_TNIL)
+ params.fg = convert_args_into_color(L, -1);
+ else {
+ // Default color is the active fgColor
+ params.fg = Preferences::instance().colorBar.fgColor();
+ }
+ lua_pop(L, 1);
+
+ type = lua_getfield(L, 1, "bgColor");
+ if (type != LUA_TNIL)
+ params.bg = convert_args_into_color(L, -1);
+ else
+ params.bg = params.fg;
+ lua_pop(L, 1);
+
+ // Adjust ink depending on "inkType" and "color"
+ // (e.g. InkType::SIMPLE depends on the color too, to adjust
+ // eraser/alpha compositing/opaque depending on the color alpha
+ // value).
+ params.ink = activeToolMgr->adjustToolInkDependingOnSelectedInkType(
+ params.ink, params.inkType, params.fg);
+
+ // Brush
type = lua_getfield(L, 1, "brush");
if (type != LUA_TNIL)
- brush = get_brush_from_arg(L, -1);
+ params.brush = get_brush_from_arg(L, -1);
+ else {
+ // Default brush is the active brush in the context bar
+#ifdef ENABLE_UI
+ if (App::instance()->isGui() &&
+ App::instance()->contextBar()) {
+ params.brush = App::instance()
+ ->contextBar()->activeBrush(params.tool,
+ params.ink);
+ }
+#endif
+ }
+ lua_pop(L, 1);
+ if (!params.brush) {
+ // In case the brush is nullptr (e.g. there is no UI) we use the
+ // default 1 pixel brush (e.g. to run scripts from CLI).
+ params.brush.reset(new Brush(BrushType::kCircleBrushType, 1, 0));
+ }
+
+ // Opacity, tolerance, and others
+ type = lua_getfield(L, 1, "opacity");
+ if (type != LUA_TNIL) {
+ params.opacity = lua_tointeger(L, -1);
+ params.opacity = base::clamp(params.opacity, 0, 255);
+ }
+ lua_pop(L, 1);
+
+ type = lua_getfield(L, 1, "tolerance");
+ if (type != LUA_TNIL) {
+ params.tolerance = lua_tointeger(L, -1);
+ params.tolerance = base::clamp(params.tolerance, 0, 255);
+ }
+ lua_pop(L, 1);
+
+ type = lua_getfield(L, 1, "contiguous");
+ if (type != LUA_TNIL)
+ params.contiguous = lua_toboolean(L, -1);
+ lua_pop(L, 1);
+
+ type = lua_getfield(L, 1, "freehandAlgorithm");
+ if (type != LUA_TNIL)
+ params.freehandAlgorithm = get_value_from_lua(L, -1);
lua_pop(L, 1);
- if (!brush)
- brush.reset(new Brush(BrushType::kCircleBrushType, 1, 0));
// How the tileset must be modified depending on this tool usage
type = lua_getfield(L, 1, "tilesetMode");
@@ -323,7 +398,7 @@ int App_useTool(lua_State* L)
type = lua_getfield(L, 1, "points");
if (type == LUA_TTABLE) {
std::unique_ptr loop(
- create_tool_loop_for_script(ctx, site, tool, ink, color, brush));
+ create_tool_loop_for_script(ctx, site, params));
if (!loop)
return luaL_error(L, "cannot draw in the active site");
diff --git a/src/app/script/engine.cpp b/src/app/script/engine.cpp
index 082cefe85..8f02b876a 100644
--- a/src/app/script/engine.cpp
+++ b/src/app/script/engine.cpp
@@ -19,6 +19,7 @@
#include "app/script/security.h"
#include "app/sprite_sheet_type.h"
#include "app/tileset_mode.h"
+#include "app/tools/ink_type.h"
#include "base/chrono.h"
#include "base/file_handle.h"
#include "base/fs.h"
@@ -334,6 +335,16 @@ Engine::Engine()
setfield_integer(L, "NONE", doc::BrushPattern::PAINT_BRUSH);
lua_pop(L, 1);
+ lua_newtable(L);
+ lua_pushvalue(L, -1);
+ lua_setglobal(L, "Ink");
+ setfield_integer(L, "SIMPLE", app::tools::InkType::SIMPLE);
+ setfield_integer(L, "ALPHA_COMPOSITING", app::tools::InkType::ALPHA_COMPOSITING);
+ setfield_integer(L, "COPY_COLOR", app::tools::InkType::COPY_COLOR);
+ setfield_integer(L, "LOCK_ALPHA", app::tools::InkType::LOCK_ALPHA);
+ setfield_integer(L, "SHADING", app::tools::InkType::SHADING);
+ lua_pop(L, 1);
+
lua_newtable(L);
lua_pushvalue(L, -1);
lua_setglobal(L, "FilterChannels");
diff --git a/src/app/script/engine.h b/src/app/script/engine.h
index 803379e8a..3b8b29275 100644
--- a/src/app/script/engine.h
+++ b/src/app/script/engine.h
@@ -50,14 +50,14 @@ namespace doc {
class WithUserData;
}
-namespace tools {
- class Tool;
-}
-
namespace app {
class Site;
+ namespace tools {
+ class Tool;
+ }
+
namespace script {
enum class FileAccessMode {
@@ -151,7 +151,7 @@ namespace app {
void push_tileset(lua_State* L, doc::Tileset* tileset);
void push_tileset_image(lua_State* L, doc::Tileset* tileset, doc::Image* image);
void push_tilesets(lua_State* L, doc::Tilesets* tilesets);
- void push_tool(lua_State* L, tools::Tool* tool);
+ void push_tool(lua_State* L, app::tools::Tool* tool);
void push_userdata(lua_State* L, doc::WithUserData* userData);
void push_version(lua_State* L, const base::Version& ver);
@@ -167,7 +167,7 @@ namespace app {
doc::Cel* get_image_cel_from_arg(lua_State* L, int index);
doc::frame_t get_frame_number_from_arg(lua_State* L, int index);
const doc::Mask* get_mask_from_arg(lua_State* L, int index);
- tools::Tool* get_tool_from_arg(lua_State* L, int index);
+ app::tools::Tool* get_tool_from_arg(lua_State* L, int index);
doc::BrushRef get_brush_from_arg(lua_State* L, int index);
// Used by App.open(), Sprite{ fromFile }, and Image{ fromFile }
diff --git a/src/app/script/layer_class.cpp b/src/app/script/layer_class.cpp
index 9d51a597b..b75bc33e3 100644
--- a/src/app/script/layer_class.cpp
+++ b/src/app/script/layer_class.cpp
@@ -345,9 +345,13 @@ int Layer_set_parent(lua_State* L)
else if (auto parentLayer = may_get_docobj(L, 2)) {
if (parentLayer->isGroup())
parent = static_cast(parentLayer);
+ else
+ return luaL_error(L, "the given parent is not a layer group or sprite");
}
- if (parent == layer)
+ if (!parent)
+ return luaL_error(L, "parent cannot be nil");
+ else if (parent == layer)
return luaL_error(L, "the parent of a layer cannot be the layer itself");
// TODO Why? should we be able to do this? It would require some hard work:
diff --git a/src/app/script/values.cpp b/src/app/script/values.cpp
index 4db3f81dc..311356834 100644
--- a/src/app/script/values.cpp
+++ b/src/app/script/values.cpp
@@ -126,6 +126,23 @@ gfx::Rect get_value_from_lua(lua_State* L, int index) {
return convert_args_into_rect(L, index);
}
+// ----------------------------------------------------------------------
+// tools::InkType
+
+template<>
+void push_value_to_lua(lua_State* L, const app::tools::InkType& inkType) {
+ lua_pushinteger(L, (int)inkType);
+}
+
+template<>
+app::tools::InkType get_value_from_lua(lua_State* L, int index) {
+ if (lua_type(L, index) == LUA_TSTRING) {
+ if (const char* s = lua_tostring(L, index))
+ return app::tools::string_id_to_ink_type(s);
+ }
+ return (app::tools::InkType)lua_tointeger(L, index);
+}
+
// ----------------------------------------------------------------------
// enums
@@ -163,7 +180,6 @@ FOR_ENUM(app::gen::TimelinePosition)
FOR_ENUM(app::gen::WindowColorProfile)
FOR_ENUM(app::gen::ToGrayAlgorithm)
FOR_ENUM(app::tools::FreehandAlgorithm)
-FOR_ENUM(app::tools::InkType)
FOR_ENUM(app::tools::RotationAlgorithm)
FOR_ENUM(doc::AniDir)
FOR_ENUM(doc::BrushPattern)
diff --git a/src/app/tools/active_tool.cpp b/src/app/tools/active_tool.cpp
index b46f55729..594b46cb3 100644
--- a/src/app/tools/active_tool.cpp
+++ b/src/app/tools/active_tool.cpp
@@ -1,5 +1,5 @@
// Aseprite
-// Copyright (C) 2018 Igara Studio S.A.
+// Copyright (C) 2018-2020 Igara Studio S.A.
// Copyright (C) 2016 David Capello
//
// This program is distributed under the terms of
@@ -11,6 +11,7 @@
#include "app/tools/active_tool.h"
+#include "app/color.h"
#include "app/pref/preferences.h"
#include "app/tools/active_tool_observer.h"
#include "app/tools/ink.h"
@@ -76,26 +77,34 @@ Ink* ActiveToolManager::activeInk() const
Tool* tool = activeTool();
Ink* ink = tool->getInk(m_rightClick ? 1: 0);
if (ink->isPaint() && !ink->isEffect()) {
- tools::InkType inkType = Preferences::instance().tool(tool).ink();
- const char* id = nullptr;
-
- switch (inkType) {
-
- case tools::InkType::SIMPLE: {
- id = tools::WellKnownInks::Paint;
-
+ const tools::InkType inkType = Preferences::instance().tool(tool).ink();
+ app::Color color;
#ifdef ENABLE_UI
- ColorBar* colorbar = ColorBar::instance();
- app::Color color = (m_rightClick ? colorbar->getBgColor():
- colorbar->getFgColor());
+ ColorBar* colorbar = ColorBar::instance();
+ color = (m_rightClick ? colorbar->getBgColor():
+ colorbar->getFgColor());
+#endif
+ ink = adjustToolInkDependingOnSelectedInkType(ink, inkType, color);
+ }
+
+ return ink;
+}
+
+Ink* ActiveToolManager::adjustToolInkDependingOnSelectedInkType(
+ Ink* ink,
+ const InkType inkType,
+ const app::Color& color) const
+{
+ if (ink->isPaint() && !ink->isEffect()) {
+ const char* id = nullptr;
+ switch (inkType) {
+ case tools::InkType::SIMPLE:
+ id = tools::WellKnownInks::Paint;
if (color.getAlpha() == 0)
id = tools::WellKnownInks::PaintCopy;
-#endif
break;
- }
-
case tools::InkType::ALPHA_COMPOSITING:
- id = tools::WellKnownInks::Paint;
+ id = tools::WellKnownInks::PaintAlphaCompositing;
break;
case tools::InkType::COPY_COLOR:
id = tools::WellKnownInks::PaintCopy;
@@ -107,11 +116,9 @@ Ink* ActiveToolManager::activeInk() const
id = tools::WellKnownInks::Shading;
break;
}
-
if (id)
ink = m_toolbox->getInkById(id);
}
-
return ink;
}
diff --git a/src/app/tools/active_tool.h b/src/app/tools/active_tool.h
index 9643cf03f..04731bde2 100644
--- a/src/app/tools/active_tool.h
+++ b/src/app/tools/active_tool.h
@@ -1,4 +1,5 @@
// Aseprite
+// Copyright (C) 2020 Igara Studio S.A.
// Copyright (C) 2016 David Capello
//
// This program is distributed under the terms of
@@ -8,9 +9,12 @@
#define APP_TOOLS_ACTIVE_TOOL_H_INCLUDED
#pragma once
+#include "app/tools/ink_type.h"
#include "obs/observable.h"
namespace app {
+class Color;
+
namespace tools {
class ActiveToolObserver;
@@ -44,6 +48,11 @@ public:
void releaseButtons();
void setSelectedTool(Tool* tool);
+ Ink* adjustToolInkDependingOnSelectedInkType(
+ Ink* ink,
+ const InkType inkType,
+ const app::Color& color) const;
+
private:
static bool isToolAffectedByRightClickMode(Tool* tool);
diff --git a/src/app/tools/ink_processing.h b/src/app/tools/ink_processing.h
index 131ca51a7..541f7442b 100644
--- a/src/app/tools/ink_processing.h
+++ b/src/app/tools/ink_processing.h
@@ -273,7 +273,8 @@ public:
m_palette(get_current_palette()),
m_rgbmap(loop->getRgbMap()),
m_opacity(loop->getOpacity()),
- m_maskIndex(loop->getLayer()->isBackground() ? -1: loop->sprite()->transparentColor()) {
+ m_maskIndex(loop->getLayer()->isBackground() ? -1: loop->sprite()->transparentColor()),
+ m_colorIndex(loop->getFgColor()) {
}
void prepareForPointShape(ToolLoop* loop, bool firstPoint, int x, int y) override {
@@ -281,6 +282,9 @@ public:
}
void processPixel(int x, int y) {
+ if (m_colorIndex == m_maskIndex)
+ return;
+
color_t c = *m_srcAddress;
if (int(c) == m_maskIndex)
c = m_palette->getEntry(c) & rgba_rgb_mask; // Alpha = 0
@@ -300,6 +304,7 @@ private:
const int m_opacity;
color_t m_color;
const int m_maskIndex;
+ int m_colorIndex;
};
//////////////////////////////////////////////////////////////////////
@@ -1119,6 +1124,14 @@ private:
// Brush Ink - Base
//////////////////////////////////////////////////////////////////////
+// TODO In all cases where we get the brush index and use that index
+// in m_palette->getEntry(index), the color is converted to the
+// sprite palette or to the grayscale palette (for grayscale
+// sprites), we might want to save the original palette in the
+// brush to use that one in these cases (not sure if this does
+// applies when we select a new foreground/background color in
+// the color bar and the brush color changes, or if this should
+// be a new optional flag/parameter to save on each brush)
template
class BrushInkProcessingBase : public DoubleInkProcessing, ImageTraits> {
public:
@@ -1136,7 +1149,15 @@ public:
m_height = m_brush->bounds().h;
m_u = (m_brush->patternOrigin().x - loop->getCelOrigin().x) % m_width;
m_v = (m_brush->patternOrigin().y - loop->getCelOrigin().y) % m_height;
- m_transparentColor = loop->sprite()->transparentColor();
+
+ if (loop->sprite()->colorMode() == ColorMode::INDEXED) {
+ if (loop->getLayer()->isTransparent())
+ m_transparentColor = loop->sprite()->transparentColor();
+ else
+ m_transparentColor = -1;
+ }
+ else
+ m_transparentColor = 0;
}
void prepareForPointShape(ToolLoop* loop, bool firstPoint, int x, int y) override {
@@ -1259,10 +1280,6 @@ bool BrushInkProcessingBase::preProcessPixel(int x, int y, color_t* r
break;
}
case IMAGE_INDEXED: {
- // TODO m_palette->getEntry(c) does not work because the m_palette member is
- // loaded the Graya Palette, NOT the original Indexed Palette from where m_brushImage belongs.
- // This conversion can be possible if we load the palette pointer in m_brush when
- // is created the custom brush in the Indexed Sprite.
c = get_pixel_fast(m_brushImage, x, y);
if (m_transparentColor == c)
c = 0;
@@ -1305,12 +1322,6 @@ bool BrushInkProcessingBase::preProcessPixel(int x, int y, colo
break;
}
case IMAGE_INDEXED: {
- // TODO m_palette->getEntry(c) does not work because the
- // m_palette member is loaded the Graya Palette, NOT the
- // original Indexed Palette from where m_brushImage belongs.
- // This conversion can be possible if we load the palette
- // pointer in m_brush when is created the custom brush in the
- // Indexed Sprite.
c = get_pixel_fast(m_brushImage, x, y);
if (m_transparentColor == c)
c = 0;
@@ -1348,19 +1359,43 @@ bool BrushInkProcessingBase::preProcessPixel(int x, int y, color_
switch (m_brushImage->pixelFormat()) {
case IMAGE_RGB: {
c = get_pixel_fast(m_brushImage, x, y);
- c = m_palette->findBestfit(rgba_getr(c), rgba_getg(c), rgba_getb(c), rgba_geta(c), 0);
+ color_t d = m_palette->getEntry(*m_dstAddress);
+ c = rgba_blender_normal(d, c, m_opacity);
+ c = m_palette->findBestfit(rgba_getr(c),
+ rgba_getg(c),
+ rgba_getb(c),
+ rgba_geta(c), m_transparentColor);
break;
}
case IMAGE_INDEXED: {
c = get_pixel_fast(m_brushImage, x, y);
+ if (c == m_transparentColor)
+ return false;
+
+ color_t f = m_palette->getEntry(c);
+
+ // Keep original index in special opaque case
+ if (rgba_geta(f) == 255 && m_opacity == 255)
+ break;
+
+ color_t b = m_palette->getEntry(*m_dstAddress);
+ c = rgba_blender_normal(b, f, m_opacity);
+ c = m_palette->findBestfit(rgba_getr(c),
+ rgba_getg(c),
+ rgba_getb(c),
+ rgba_geta(c), m_transparentColor);
break;
}
case IMAGE_GRAYSCALE: {
c = get_pixel_fast(m_brushImage, x, y);
+ color_t b = m_palette->getEntry(*m_dstAddress);
+ b = graya(rgba_luma(b),
+ rgba_geta(b));
+ c = graya_blender_normal(b, c, m_opacity);
c = m_palette->findBestfit(graya_getv(c),
graya_getv(c),
graya_getv(c),
- graya_geta(c), 0);
+ graya_geta(c), m_transparentColor);
break;
}
case IMAGE_BITMAP: {
@@ -1490,12 +1525,6 @@ void BrushEraserInkProcessing::processPixel(int x, int y) {
if (m_transparentColor == c)
c = 0;
else
- // TODO m_palette->getEntry(c) does not work because the
- // m_palette member is loaded the Rgba Palette, NOT the
- // original Indexed Palette from where m_brushImage belongs.
- // This conversion can be possible if we load the palette
- // pointer in m_brush when is created the custom brush in the
- // Indexed Sprite.
c = m_palette->getEntry(c);
int t;
c = doc::rgba(rgba_getr(*m_srcAddress),
@@ -1544,14 +1573,9 @@ void BrushEraserInkProcessing::processPixel(int x, int y) {
c = get_pixel_fast(m_brushImage, x, y);
if (m_transparentColor == c)
c = 0;
- else
- // TODO m_palette->getEntry(c) does not work because the
- // m_palette member is loaded the Graya Palette, NOT the
- // original Indexed Palette from where m_brushImage belongs.
- // This conversion can be possible if we load the palette
- // pointer in m_brush when is created the custom brush in the
- // Indexed Sprite.
+ else {
c = m_palette->getEntry(c);
+ }
int t;
c = graya(graya_getv(*m_srcAddress),
MUL_UN8(graya_geta(*m_dstAddress), 255 - rgba_geta(c), t));
@@ -1601,7 +1625,7 @@ void BrushEraserInkProcessing::processPixel(int x, int y) {
c = m_palette->findBestfit(graya_getv(c),
graya_getv(c),
graya_getv(c),
- graya_geta(c), 0);
+ graya_geta(c), m_transparentColor);
break;
}
case IMAGE_BITMAP: {
@@ -1726,10 +1750,6 @@ void BrushShadingInkProcessing::processPixel(int x, int y) {
break;
}
case IMAGE_INDEXED: {
- // TODO m_palette->getEntry(c) does not work because the m_palette member is
- // loaded the Graya Palette, NOT the original Indexed Palette from where m_brushImage belongs.
- // This conversion can be possible if we load the palette pointer in m_brush when
- // is created the custom brush in the Indexed Sprite.
auto c = get_pixel_fast(m_brushImage, x, y);
if (m_transparentColor != c)
*m_dstAddress = m_shading(*m_srcAddress);
@@ -1776,10 +1796,6 @@ void BrushCopyInkProcessing::processPixel(int x, int y) {
break;
}
case IMAGE_INDEXED: {
- // TODO m_palette->getEntry(c) does not work because the m_palette member is
- // loaded the Graya Palette, NOT the original Indexed Palette from where m_brushImage belongs.
- // This conversion can be possible if we load the palette pointer in m_brush when
- // is created the custom brush in the Indexed Sprite.
c = get_pixel_fast(m_brushImage, x, y);
if (c == m_transparentColor) {
*m_dstAddress = *m_srcAddress;
@@ -1819,7 +1835,10 @@ void BrushCopyInkProcessing::processPixel(int x, int y) {
c = get_pixel_fast(m_brushImage, x, y);
if (rgba_geta(c) == 0)
return;
- c = m_palette->findBestfit(rgba_getr(c), rgba_getg(c), rgba_getb(c), rgba_geta(c), 0);
+ c = m_palette->findBestfit(rgba_getr(c),
+ rgba_getg(c),
+ rgba_getb(c),
+ rgba_geta(c), m_transparentColor);
if (c == 0)
c = *m_srcAddress;
break;
@@ -1837,7 +1856,7 @@ void BrushCopyInkProcessing::processPixel(int x, int y) {
c = m_palette->findBestfit(graya_getv(c),
graya_getv(c),
graya_getv(c),
- graya_geta(c), 0);
+ graya_geta(c), m_transparentColor);
break;
}
case IMAGE_BITMAP: {
@@ -1868,12 +1887,6 @@ void BrushCopyInkProcessing::processPixel(int x, int y) {
break;
}
case IMAGE_INDEXED: {
- // TODO m_palette->getEntry(c) does not work because the
- // m_palette member is loaded the Graya Palette, NOT the
- // original Indexed Palette from where m_brushImage belongs.
- // This conversion can be possible if we load the palette
- // pointer in m_brush when is created the custom brush in the
- // Indexed Sprite.
c = get_pixel_fast(m_brushImage, x, y);
if (c == m_transparentColor) {
*m_dstAddress = *m_srcAddress;
diff --git a/src/app/tools/ink_type.cpp b/src/app/tools/ink_type.cpp
index cc142df9b..0af9d25e1 100644
--- a/src/app/tools/ink_type.cpp
+++ b/src/app/tools/ink_type.cpp
@@ -1,4 +1,5 @@
// Aseprite
+// Copyright (C) 2020 Igara Studio S.A.
// Copyright (C) 2001-2015 David Capello
//
// This program is distributed under the terms of
@@ -39,11 +40,24 @@ std::string ink_type_to_string_id(InkType inkType)
InkType string_id_to_ink_type(const std::string& s)
{
- if (s == "simple") return tools::InkType::SIMPLE;
- if (s == "alpha_compositing") return tools::InkType::ALPHA_COMPOSITING;
- if (s == "copy_color") return tools::InkType::COPY_COLOR;
- if (s == "lock_alpha") return tools::InkType::LOCK_ALPHA;
- if (s == "shading") return tools::InkType::SHADING;
+ if (s == "simple")
+ return tools::InkType::SIMPLE;
+
+ if (s == "alpha_compositing" ||
+ s == "alpha-compositing")
+ return tools::InkType::ALPHA_COMPOSITING;
+
+ if (s == "copy_color" ||
+ s == "copy-color")
+ return tools::InkType::COPY_COLOR;
+
+ if (s == "lock_alpha" ||
+ s == "lock-alpha")
+ return tools::InkType::LOCK_ALPHA;
+
+ if (s == "shading")
+ return tools::InkType::SHADING;
+
return tools::InkType::DEFAULT;
}
diff --git a/src/app/tools/inks.h b/src/app/tools/inks.h
index 7ccc2ba32..7de8580f1 100644
--- a/src/app/tools/inks.h
+++ b/src/app/tools/inks.h
@@ -66,7 +66,7 @@ private:
// (or foreground/background colors)
class PaintInk : public BaseInk {
public:
- enum Type { Simple, WithFg, WithBg, Copy, LockAlpha };
+ enum Type { Simple, WithFg, WithBg, AlphaCompositing, Copy, LockAlpha};
private:
Type m_type;
@@ -114,9 +114,11 @@ public:
}
else {
switch (m_type) {
- case Simple: {
+ case Simple:
+ case AlphaCompositing: {
bool opaque = false;
+ // Opacity is set to 255 when InkType=Simple in ToolLoopBase()
if (loop->getOpacity() == 255 &&
// The trace policy is "overlap" when the dynamics has
// a gradient between FG <-> BG
@@ -134,8 +136,16 @@ public:
opaque = (graya_geta(color) == 255);
break;
case IMAGE_INDEXED:
- color = get_current_palette()->getEntry(color);
- opaque = (rgba_geta(color) == 255);
+ // Simple ink for indexed is better to use always
+ // opaque if opacity == 255.
+ if (m_type == Simple)
+ opaque = true;
+ else if (color == loop->sprite()->transparentColor())
+ opaque = false;
+ else {
+ color = get_current_palette()->getEntry(color);
+ opaque = (rgba_geta(color) == 255);
+ }
break;
}
}
diff --git a/src/app/tools/tool_box.cpp b/src/app/tools/tool_box.cpp
index fcd169b66..d6fc8abbb 100644
--- a/src/app/tools/tool_box.cpp
+++ b/src/app/tools/tool_box.cpp
@@ -1,5 +1,5 @@
// Aseprite
-// Copyright (C) 2018 Igara Studio S.A.
+// Copyright (C) 2018-2020 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@@ -56,6 +56,7 @@ const char* WellKnownInks::Selection = "selection";
const char* WellKnownInks::Paint = "paint";
const char* WellKnownInks::PaintFg = "paint_fg";
const char* WellKnownInks::PaintBg = "paint_bg";
+const char* WellKnownInks::PaintAlphaCompositing = "paint_alpha_compositing";
const char* WellKnownInks::PaintCopy = "paint_copy";
const char* WellKnownInks::PaintLockAlpha = "paint_lock_alpha";
const char* WellKnownInks::Shading = "shading";
@@ -115,6 +116,7 @@ ToolBox::ToolBox()
m_inks[WellKnownInks::Paint] = new PaintInk(PaintInk::Simple);
m_inks[WellKnownInks::PaintFg] = new PaintInk(PaintInk::WithFg);
m_inks[WellKnownInks::PaintBg] = new PaintInk(PaintInk::WithBg);
+ m_inks[WellKnownInks::PaintAlphaCompositing] = new PaintInk(PaintInk::AlphaCompositing);
m_inks[WellKnownInks::PaintCopy] = new PaintInk(PaintInk::Copy);
m_inks[WellKnownInks::PaintLockAlpha] = new PaintInk(PaintInk::LockAlpha);
m_inks[WellKnownInks::Gradient] = new GradientInk();
diff --git a/src/app/tools/tool_box.h b/src/app/tools/tool_box.h
index df7b0b592..0514c73fa 100644
--- a/src/app/tools/tool_box.h
+++ b/src/app/tools/tool_box.h
@@ -1,5 +1,5 @@
// Aseprite
-// Copyright (C) 2018 Igara Studio S.A.
+// Copyright (C) 2018-2020 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@@ -36,6 +36,7 @@ namespace app {
extern const char* Paint;
extern const char* PaintFg;
extern const char* PaintBg;
+ extern const char* PaintAlphaCompositing;
extern const char* PaintCopy;
extern const char* PaintLockAlpha;
extern const char* Shading;
diff --git a/src/app/ui/editor/editor.cpp b/src/app/ui/editor/editor.cpp
index 31ff492ed..a107f7374 100644
--- a/src/app/ui/editor/editor.cpp
+++ b/src/app/ui/editor/editor.cpp
@@ -976,10 +976,13 @@ void Editor::drawMask(Graphics* g)
os::Paint paint;
paint.style(os::Paint::Stroke);
paint.color(gfx::rgba(0, 0, 0));
- g->setMatrix(Matrix::MakeTrans(pt.x, pt.y));
- g->concat(m_proj.scaleMatrix());
- g->drawPath(segs.path(), paint);
- g->resetMatrix();
+
+ // We translate the path instead of applying a matrix to the
+ // ui::Graphics so the "checked" pattern is not scaled too.
+ gfx::Path path;
+ segs.path().transform(m_proj.scaleMatrix(), &path);
+ path.offset(pt.x, pt.y);
+ g->drawPath(path, paint);
}
void Editor::drawMaskSafe()
diff --git a/src/app/ui/editor/tool_loop_impl.cpp b/src/app/ui/editor/tool_loop_impl.cpp
index 31bc88d54..253540e83 100644
--- a/src/app/ui/editor/tool_loop_impl.cpp
+++ b/src/app/ui/editor/tool_loop_impl.cpp
@@ -63,6 +63,22 @@ namespace app {
using namespace ui;
+#ifdef ENABLE_UI
+
+static void fill_toolloop_params_from_tool_preferences(ToolLoopParams& params)
+{
+ ToolPreferences& toolPref =
+ Preferences::instance().tool(params.tool);
+
+ params.inkType = toolPref.ink();
+ params.opacity = toolPref.opacity();
+ params.tolerance = toolPref.tolerance();
+ params.contiguous = toolPref.contiguous();
+ params.freehandAlgorithm = toolPref.freehandAlgorithm();
+}
+
+#endif // ENABLE_UI
+
//////////////////////////////////////////////////////////////////////
// Common properties between drawing/preview ToolLoop impl
@@ -107,17 +123,11 @@ protected:
public:
ToolLoopBase(Editor* editor, Site site,
- const gfx::Rect& gridBounds,
- tools::Tool* tool, tools::Ink* ink,
- tools::Controller* controller,
- const BrushRef& brush,
- tools::ToolLoop::Button button,
- const app::Color& fgColor,
- const app::Color& bgColor)
+ ToolLoopParams& params)
: m_editor(editor)
- , m_tool(tool)
- , m_brush(brush)
- , m_origBrush(brush)
+ , m_tool(params.tool)
+ , m_brush(params.brush)
+ , m_origBrush(params.brush)
, m_oldPatternOrigin(m_brush->patternOrigin())
, m_document(site.document())
, m_sprite(site.sprite())
@@ -126,15 +136,15 @@ public:
, m_rgbMap(nullptr)
, m_docPref(Preferences::instance().document(m_document))
, m_toolPref(Preferences::instance().tool(m_tool))
- , m_opacity(m_toolPref.opacity())
- , m_tolerance(m_toolPref.tolerance())
- , m_contiguous(m_toolPref.contiguous())
+ , m_opacity(params.opacity)
+ , m_tolerance(params.tolerance)
+ , m_contiguous(params.contiguous)
, m_snapToGrid(m_docPref.grid.snap())
, m_isSelectingTiles(false)
- , m_gridBounds(gridBounds)
- , m_button(button)
- , m_ink(ink->clone())
- , m_controller(controller)
+ , m_gridBounds(params.gridBounds)
+ , m_button(params.button)
+ , m_ink(params.ink->clone())
+ , m_controller(params.controller)
, m_pointShape(m_tool->getPointShape(m_button))
, m_intertwine(m_tool->getIntertwine(m_button))
, m_tracePolicy(m_tool->getTracePolicy(m_button))
@@ -143,11 +153,15 @@ public:
ColorTarget(ColorTarget::BackgroundLayer,
m_sprite->pixelFormat(),
m_sprite->transparentColor()))
- , m_fgColor(color_utils::color_for_target_mask(fgColor, m_colorTarget))
- , m_bgColor(color_utils::color_for_target_mask(bgColor, m_colorTarget))
- , m_primaryColor(button == tools::ToolLoop::Left ? m_fgColor: m_bgColor)
- , m_secondaryColor(button == tools::ToolLoop::Left ? m_bgColor: m_fgColor)
+ , m_fgColor(color_utils::color_for_target_mask(params.fg, m_colorTarget))
+ , m_bgColor(color_utils::color_for_target_mask(params.bg, m_colorTarget))
+ , m_primaryColor(m_button == tools::ToolLoop::Left ? m_fgColor: m_bgColor)
+ , m_secondaryColor(m_button == tools::ToolLoop::Left ? m_bgColor: m_fgColor)
{
+ ASSERT(m_tool);
+ ASSERT(m_ink);
+ ASSERT(m_controller);
+
#ifdef ENABLE_UI // TODO add dynamics support when UI is not enabled
if (m_controller->isFreehand() &&
!m_pointShape->isFloodFill() &&
@@ -160,7 +174,7 @@ public:
m_tracePolicy == tools::TracePolicy::AccumulateUpdateLast) {
tools::ToolBox* toolbox = App::instance()->toolBox();
- switch (m_toolPref.freehandAlgorithm()) {
+ switch (params.freehandAlgorithm) {
case tools::FreehandAlgorithm::DEFAULT:
m_intertwine = toolbox->getIntertwinerById(tools::WellKnownIntertwiners::AsLines);
m_tracePolicy = tools::TracePolicy::Accumulate;
@@ -214,18 +228,18 @@ public:
}
// Ignore opacity for these inks
- if (!tools::inkHasOpacity(m_toolPref.ink()) &&
+ if (!tools::inkHasOpacity(params.inkType) &&
m_brush->type() != kImageBrushType &&
!m_ink->isEffect()) {
m_opacity = 255;
}
#ifdef ENABLE_UI // TODO add support when UI is not enabled
- if (m_toolPref.ink() == tools::InkType::SHADING) {
+ if (params.inkType == tools::InkType::SHADING) {
m_shade = App::instance()->contextBar()->getShade();
m_shadingRemap.reset(
App::instance()->contextBar()->createShadeRemap(
- button == tools::ToolLoop::Left));
+ m_button == tools::ToolLoop::Left));
}
#endif
}
@@ -418,19 +432,10 @@ class ToolLoopImpl : public ToolLoopBase {
public:
ToolLoopImpl(Editor* editor,
Site site,
- const gfx::Rect& gridBounds,
Context* context,
- tools::Tool* tool,
- tools::Ink* ink,
- tools::Controller* controller,
- const BrushRef& brush,
- tools::ToolLoop::Button button,
- const app::Color& fgColor,
- const app::Color& bgColor,
+ ToolLoopParams& params,
const bool saveLastPoint)
- : ToolLoopBase(editor, site, gridBounds,
- tool, ink, controller, brush,
- button, fgColor, bgColor)
+ : ToolLoopBase(editor, site, params)
, m_context(context)
, m_canceled(false)
, m_tx(m_context,
@@ -487,7 +492,7 @@ public:
m_floodfillSrcImage = const_cast(getSrcImage());
// Settings
- switch (tool->getFill(m_button)) {
+ switch (m_tool->getFill(m_button)) {
case tools::FillNone:
m_filled = false;
break;
@@ -673,6 +678,9 @@ private:
};
+//////////////////////////////////////////////////////////////////////
+// For user UI painting
+
#ifdef ENABLE_UI
tools::ToolLoop* create_tool_loop(
@@ -682,14 +690,15 @@ tools::ToolLoop* create_tool_loop(
const bool convertLineToFreehand,
const bool selectTiles)
{
- tools::Tool* tool = editor->getCurrentEditorTool();
- tools::Ink* ink = editor->getCurrentEditorInk();
- if (!tool || !ink)
+ ToolLoopParams params;
+ params.tool = editor->getCurrentEditorTool();
+ params.ink = editor->getCurrentEditorInk();
+ if (!params.tool || !params.ink)
return nullptr;
if (selectTiles) {
- tool = App::instance()->toolBox()->getToolById(tools::WellKnownTools::RectangularMarquee);
- ink = tool->getInk(button == tools::Pointer::Left ? 0: 1);
+ params.tool = App::instance()->toolBox()->getToolById(tools::WellKnownTools::RectangularMarquee);
+ params.ink = params.tool->getInk(button == tools::Pointer::Left ? 0: 1);
}
Site site = editor->getSite();
@@ -698,7 +707,7 @@ tools::ToolLoop* create_tool_loop(
// site.layer(nullptr) in certain cases, we need to know if the
// active layer is a tilemap, and in that case the grid bounds can
// be different than the sprite grid bounds).
- gfx::Rect gridBounds = site.gridBounds();
+ params.gridBounds = site.gridBounds();
// 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
@@ -709,8 +718,9 @@ tools::ToolLoop* create_tool_loop(
// Anyway this cannot be used in 'magic wand' tool (isSelection +
// isFloodFill) because we need the original layer source
// image/pixels to stop the flood-fill algorithm.
- if (ink->isSelection() &&
- !tool->getPointShape(button != tools::Pointer::Left ? 1: 0)->isFloodFill()) {
+ if (params.ink->isSelection() &&
+ !params.tool->getPointShape(
+ button != tools::Pointer::Left ? 1: 0)->isFloodFill()) {
site.layer(nullptr);
}
else {
@@ -741,10 +751,11 @@ tools::ToolLoop* create_tool_loop(
// Get fg/bg colors
ColorBar* colorbar = ColorBar::instance();
- app::Color fg = colorbar->getFgColor();
- app::Color bg = colorbar->getBgColor();
+ params.fg = colorbar->getFgColor();
+ params.bg = colorbar->getBgColor();
- if (!fg.isValid() || !bg.isValid()) {
+ if (!params.fg.isValid() ||
+ !params.bg.isValid()) {
if (Preferences::instance().colorBar.showInvalidFgBgColorAlert()) {
OptionalAlert::show(
Preferences::instance().colorBar.showInvalidFgBgColorAlert,
@@ -755,28 +766,29 @@ tools::ToolLoop* create_tool_loop(
// Create the new tool loop
try {
- tools::ToolLoop::Button toolLoopButton =
+ params.button =
(button == tools::Pointer::Left ? tools::ToolLoop::Left:
tools::ToolLoop::Right);
- tools::Controller* controller =
+ params.controller =
(convertLineToFreehand ?
App::instance()->toolBox()->getControllerById(
tools::WellKnownControllers::LineFreehand):
- tool->getController(toolLoopButton));
+ params.tool->getController(params.button));
const bool saveLastPoint =
- (ink->isPaint() &&
- (controller->isFreehand() ||
+ (params.ink->isPaint() &&
+ (params.controller->isFreehand() ||
convertLineToFreehand));
+ params.brush = App::instance()->contextBar()
+ ->activeBrush(params.tool, params.ink);
+
+ fill_toolloop_params_from_tool_preferences(params);
+
ASSERT(context->activeDocument() == editor->document());
auto toolLoop = new ToolLoopImpl(
- editor, site, gridBounds, context,
- tool, ink, controller,
- App::instance()->contextBar()->activeBrush(tool, ink),
- toolLoopButton, fg, bg,
- saveLastPoint);
+ editor, site, context, params, saveLastPoint);
if (selectTiles)
toolLoop->forceSnapToTiles();
@@ -791,33 +803,33 @@ tools::ToolLoop* create_tool_loop(
#endif // ENABLE_UI
+//////////////////////////////////////////////////////////////////////
+// For scripting
+
+#ifdef ENABLE_SCRIPTING
+
tools::ToolLoop* create_tool_loop_for_script(
Context* context,
const Site& site,
- tools::Tool* tool,
- tools::Ink* ink,
- const app::Color& color,
- const doc::BrushRef& brush)
+ ToolLoopParams& params)
{
- ASSERT(tool);
- ASSERT(ink);
+ ASSERT(params.tool);
+ ASSERT(params.ink);
if (!site.layer())
return nullptr;
- try {
- const tools::ToolLoop::Button toolLoopButton = tools::ToolLoop::Left;
- tools::Controller* controller = tool->getController(toolLoopButton);
+ // TODO should gridBounds be specified by the caller?
+ params.gridBounds = site.gridBounds();
+ try {
// If we don't have the UI available, we reset the tools
// preferences, so scripts that are executed in batch mode have a
// reproducible behavior.
if (!context->isUIAvailable())
- Preferences::instance().resetToolPreferences(tool);
+ Preferences::instance().resetToolPreferences(params.tool);
return new ToolLoopImpl(
- nullptr, site, site.gridBounds(), context,
- tool, ink, controller, brush,
- toolLoopButton, color, color, false);
+ nullptr, site, context, params, false);
}
catch (const std::exception& ex) {
Console::showException(ex);
@@ -825,8 +837,10 @@ tools::ToolLoop* create_tool_loop_for_script(
}
}
+#endif // ENABLE_SCRIPTING
+
//////////////////////////////////////////////////////////////////////
-// For preview
+// For UI preview
#ifdef ENABLE_UI
@@ -836,17 +850,10 @@ class PreviewToolLoopImpl : public ToolLoopBase {
public:
PreviewToolLoopImpl(
Editor* editor,
- tools::Tool* tool,
- tools::Ink* ink,
- const BrushRef& brush,
- const app::Color& fgColor,
- const app::Color& bgColor,
+ ToolLoopParams& params,
Image* image,
const gfx::Point& celOrigin)
- : ToolLoopBase(editor, editor->getSite(),
- editor->getSite().gridBounds(),
- tool, ink, tool->getController(tools::ToolLoop::Left),
- brush, tools::ToolLoop::Left, fgColor, bgColor)
+ : ToolLoopBase(editor, editor->getSite(), params)
, m_image(image)
{
m_celOrigin = celOrigin;
@@ -900,10 +907,11 @@ tools::ToolLoop* create_tool_loop_preview(
Image* image,
const gfx::Point& celOrigin)
{
- tools::Tool* tool = editor->getCurrentEditorTool();
- tools::Ink* ink = editor->getCurrentEditorInk();
- if (!tool || !ink)
- return NULL;
+ ToolLoopParams params;
+ params.tool = editor->getCurrentEditorTool();
+ params.ink = editor->getCurrentEditorInk();
+ if (!params.tool || !params.ink)
+ return nullptr;
Layer* layer = editor->layer();
if (!layer ||
@@ -915,18 +923,26 @@ tools::ToolLoop* create_tool_loop_preview(
// Get fg/bg colors
ColorBar* colorbar = ColorBar::instance();
- app::Color fg = colorbar->getFgColor();
- app::Color bg = colorbar->getBgColor();
- if (!fg.isValid() || !bg.isValid())
+ params.fg = colorbar->getFgColor();
+ params.bg = colorbar->getBgColor();
+ if (!params.fg.isValid() ||
+ !params.bg.isValid())
return nullptr;
+ params.brush = brush;
+ params.button = tools::ToolLoop::Left;
+ params.controller = params.tool->getController(params.button);
+ params.gridBounds = editor->getSite().gridBounds();
+
// Create the new tool loop
try {
+ fill_toolloop_params_from_tool_preferences(params);
+
return new PreviewToolLoopImpl(
- editor, tool, ink, brush,
- fg, bg, image, celOrigin);
+ editor, params, image, celOrigin);
}
- catch (const std::exception&) {
+ catch (const std::exception& e) {
+ LOG(ERROR, e.what());
return nullptr;
}
}
diff --git a/src/app/ui/editor/tool_loop_impl.h b/src/app/ui/editor/tool_loop_impl.h
index 0e2be99c5..344076085 100644
--- a/src/app/ui/editor/tool_loop_impl.h
+++ b/src/app/ui/editor/tool_loop_impl.h
@@ -9,7 +9,11 @@
#define APP_UI_EDITOR_TOOL_LOOP_IMPL_H_INCLUDED
#pragma once
+#include "app/color.h"
+#include "app/tools/freehand_algorithm.h"
+#include "app/tools/ink_type.h"
#include "app/tools/pointer.h"
+#include "app/tools/tool_loop.h"
#include "doc/brush.h"
#include "doc/image_ref.h"
#include "gfx/fwd.h"
@@ -24,11 +28,32 @@ namespace app {
class Editor;
class Site;
- namespace tools {
- class Ink;
- class Tool;
- class ToolLoop;
- }
+ struct ToolLoopParams {
+ tools::Tool* tool = nullptr;
+ tools::Ink* ink = nullptr;
+ tools::Controller* controller = nullptr;
+
+ tools::ToolLoop::Button button = tools::ToolLoop::Left;
+ tools::InkType inkType = tools::InkType::DEFAULT;
+ app::Color fg;
+ app::Color bg;
+ doc::BrushRef brush;
+
+ // Options equal to (and with the same default as in)
+ // ... from
+ // "data/pref.xml" file.
+ int opacity = 255;
+ int tolerance = 0;
+ bool contiguous = true;
+ tools::FreehandAlgorithm freehandAlgorithm = tools::FreehandAlgorithm::DEFAULT;
+
+ gfx::Rect gridBounds;
+ };
+
+ //////////////////////////////////////////////////////////////////////
+ // For UI
+
+#ifdef ENABLE_UI
tools::ToolLoop* create_tool_loop(
Editor* editor,
@@ -37,20 +62,26 @@ namespace app {
const bool convertLineToFreehand,
const bool selectTiles);
- tools::ToolLoop* create_tool_loop_for_script(
- Context* context,
- const Site& site,
- tools::Tool* tool,
- tools::Ink* ink,
- const app::Color& color,
- const doc::BrushRef& brush);
-
tools::ToolLoop* create_tool_loop_preview(
Editor* editor,
const doc::BrushRef& brush,
doc::Image* image,
const gfx::Point& celOrigin);
+#endif // ENABLE_UI
+
+ //////////////////////////////////////////////////////////////////////
+ // For scripting
+
+#ifdef ENABLE_SCRIPTING
+
+ tools::ToolLoop* create_tool_loop_for_script(
+ Context* context,
+ const Site& site,
+ ToolLoopParams& params);
+
+#endif // ENABLE_SCRIPTING
+
} // namespace app
#endif
diff --git a/src/app/ui/timeline/timeline.cpp b/src/app/ui/timeline/timeline.cpp
index f90915edd..1848894dd 100644
--- a/src/app/ui/timeline/timeline.cpp
+++ b/src/app/ui/timeline/timeline.cpp
@@ -2870,7 +2870,7 @@ gfx::Rect Timeline::getRangeBounds(const Range& range) const
case Range::kLayers:
for (auto layer : range.selectedLayers()) {
layer_t layerIdx = getLayerIndex(layer);
- rc |= getPartBounds(Hit(PART_ROW, layerIdx));
+ rc |= getPartBounds(Hit(PART_ROW_TEXT, layerIdx));
}
break;
}
diff --git a/src/doc/CMakeLists.txt b/src/doc/CMakeLists.txt
index 0045431db..71523dc6c 100644
--- a/src/doc/CMakeLists.txt
+++ b/src/doc/CMakeLists.txt
@@ -1,6 +1,6 @@
# Aseprite Document Library
-# Copyright (C) 2019 Igara Studio S.A.
-# Copyright (C) 2001-2018 David Capello
+# Copyright (C) 2019-2020 Igara Studio S.A.
+# Copyright (C) 2001-2018 David Capello
if(WIN32)
add_definitions(-D_CRT_SECURE_NO_WARNINGS)
@@ -29,6 +29,7 @@ add_library(doc-lib
cel_data_io.cpp
cel_io.cpp
cels_range.cpp
+ color.cpp
compressed_image.cpp
conversion_to_surface.cpp
document.cpp
diff --git a/src/doc/algorithm/polygon_tests.cpp b/src/doc/algorithm/polygon_tests.cpp
index f273e2cf9..9f21710ee 100644
--- a/src/doc/algorithm/polygon_tests.cpp
+++ b/src/doc/algorithm/polygon_tests.cpp
@@ -224,11 +224,13 @@ TEST(Polygon, Triangle1Test)
TEST(Polygon, Triangle2Test)
{
- // P1
- // / \
- // / \
- // / \
- // P0-----P2
+ /*
+ P1
+ / \
+ / \
+ / \
+ P0-----P2
+ */
int points[6] = { 0 , 4 ,
2 , 0 ,
4 , 4 ,
@@ -262,11 +264,13 @@ TEST(Polygon, Triangle2Test)
TEST(Polygon, Triangle3Test)
{
- // P2
- // / \
- // / \
- // / \
- // P0-----P1
+ /*
+ P2
+ / \
+ / \
+ / \
+ P0-----P1
+ */
int points[6] = { 0 , 4 ,
4 , 4 ,
2 , 0 ,
@@ -300,11 +304,13 @@ TEST(Polygon, Triangle3Test)
TEST(Polygon, Triangle4Test)
{
- // P2
- // / \
- // / \
- // / \
- // P1-----P0
+ /*
+ P2
+ / \
+ / \
+ / \
+ P1-----P0
+ */
int points[6] = { 4 , 4 ,
0 , 4 ,
2 , 0 ,
@@ -408,17 +414,18 @@ TEST(Polygon, Poligon1Test)
TEST(Polygon, Polygon2Test)
{
- // P3------P4
- // P0 | /
- // \ | /
- // \ | /
- // \ / P2 P5
- // P1 / \
- // \
- // P9 /P7 \
- // \ / \ \
- // P8 \ P6
-
+ /*
+ P3------P4
+ P0 | /
+ \ | /
+ \ | /
+ \ / P2 P5
+ P1 / \
+ \
+ P9 /P7 \
+ \ / \ \
+ P8 \ P6
+ */
int points[20] = { 0 , 1 ,
2 , 4 ,
4 , 3 ,
@@ -467,23 +474,23 @@ TEST(Polygon, Polygon2Test)
EXPECT_EQ(results.scanLines[7].x1, 1);
EXPECT_EQ(results.scanLines[7].x2, 7);
EXPECT_EQ(results.scanLines[7].y, 4);
-
+
EXPECT_EQ(results.scanLines[8].x1, 2);
EXPECT_EQ(results.scanLines[8].x2, 8);
EXPECT_EQ(results.scanLines[8].y, 5);
-
+
EXPECT_EQ(results.scanLines[9].x1, 2);
EXPECT_EQ(results.scanLines[9].x2, 4);
EXPECT_EQ(results.scanLines[9].y, 6);
-
+
EXPECT_EQ(results.scanLines[10].x1, 7);
EXPECT_EQ(results.scanLines[10].x2, 8);
EXPECT_EQ(results.scanLines[10].y, 6);
-
+
EXPECT_EQ(results.scanLines[11].x1, 3);
EXPECT_EQ(results.scanLines[11].x2, 3);
EXPECT_EQ(results.scanLines[11].y, 7);
-
+
EXPECT_EQ(results.scanLines[12].x1, 9);
EXPECT_EQ(results.scanLines[12].x2, 9);
EXPECT_EQ(results.scanLines[12].y, 7);
@@ -768,7 +775,7 @@ TEST(createUnion, testC8)
pairs.push_back(2);
pairs.push_back(3);
pairs.push_back(4);
-
+
EXPECT_EQ(doc::algorithm::createUnion(pairs, x, ints), true);
EXPECT_EQ(pairs[0], 0);
EXPECT_EQ(pairs[1], 4);
diff --git a/src/doc/color.cpp b/src/doc/color.cpp
new file mode 100644
index 000000000..470d71b90
--- /dev/null
+++ b/src/doc/color.cpp
@@ -0,0 +1,46 @@
+// Aseprite Document Library
+// Copyright (c) 2020 Igara Studio S.A.
+//
+// This file is released under the terms of the MIT license.
+// Read LICENSE.txt for more information.
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "doc/color.h"
+
+#include
+
+namespace doc {
+
+color_t rgba_to_graya_using_hsv(const color_t c)
+{
+ const uint8_t M = std::max(rgba_getr(c),
+ std::max(rgba_getg(c),
+ rgba_getb(c)));
+ return graya(M,
+ rgba_geta(c));
+}
+
+color_t rgba_to_graya_using_hsl(const color_t c)
+{
+ const int m = std::min(rgba_getr(c),
+ std::min(rgba_getg(c),
+ rgba_getb(c)));
+ const int M = std::max(rgba_getr(c),
+ std::max(rgba_getg(c),
+ rgba_getb(c)));
+ return graya((M + m) / 2,
+ rgba_geta(c));
+}
+
+color_t rgba_to_graya_using_luma(const color_t c)
+{
+ return graya(rgb_luma(rgba_getr(c),
+ rgba_getg(c),
+ rgba_getb(c)),
+ rgba_geta(c));
+}
+
+} // namespace doc
diff --git a/src/doc/color.h b/src/doc/color.h
index 20b04608d..ac65b0034 100644
--- a/src/doc/color.h
+++ b/src/doc/color.h
@@ -11,8 +11,6 @@
#include "base/ints.h"
-#include
-
namespace doc {
// The greatest int type to storage a color for an image in the
@@ -95,31 +93,9 @@ namespace doc {
typedef color_t (*rgba_to_graya_func)(const color_t c);
- inline color_t rgba_to_graya_using_hsv(const color_t c) {
- const uint8_t M = std::max(rgba_getr(c),
- std::max(rgba_getg(c),
- rgba_getb(c)));
- return graya(M,
- rgba_geta(c));
- }
-
- inline color_t rgba_to_graya_using_hsl(const color_t c) {
- const int m = std::min(rgba_getr(c),
- std::min(rgba_getg(c),
- rgba_getb(c)));
- const int M = std::max(rgba_getr(c),
- std::max(rgba_getg(c),
- rgba_getb(c)));
- return graya((M + m) / 2,
- rgba_geta(c));
- }
-
- inline color_t rgba_to_graya_using_luma(const color_t c) {
- return graya(rgb_luma(rgba_getr(c),
- rgba_getg(c),
- rgba_getb(c)),
- rgba_geta(c));
- }
+ color_t rgba_to_graya_using_hsv(const color_t c);
+ color_t rgba_to_graya_using_hsl(const color_t c);
+ color_t rgba_to_graya_using_luma(const color_t c);
} // namespace doc