mirror of
https://github.com/aseprite/aseprite.git
synced 2025-03-29 19:20:09 +00:00
Merge branch 'master' into tilemap-editor
This commit is contained in:
commit
a10efb187b
@ -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"
|
||||
|
@ -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()
|
||||
|
@ -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.
|
||||
|
37
README.md
37
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).
|
||||
|
34
data/extensions/endesga-palettes/hept32.gpl
Normal file
34
data/extensions/endesga-palettes/hept32.gpl
Normal file
@ -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
|
@ -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" }
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -184,6 +184,9 @@
|
||||
<option id="font" type="std::string" />
|
||||
<option id="mini_font" type="std::string" />
|
||||
</section>
|
||||
<section id="tablet" text="Tablet">
|
||||
<option id="api" type="std::string" />
|
||||
</section>
|
||||
<section id="experimental" text="Experimental">
|
||||
<option id="new_render_engine" type="bool" default="true" />
|
||||
<option id="new_blend" type="bool" default="true" />
|
||||
|
@ -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 = <<<END
|
||||
Only for Windows 8/10 tablets: Interprets one finger as mouse movement
|
||||
Only for Windows 8/10 Pointer API: Interprets one finger as mouse movement
|
||||
and two fingers as pan/scroll. Uncheck this to use the old behavior:
|
||||
One finger pans/scrolls.
|
||||
--
|
||||
Note: This option is available just to get the old behavior but
|
||||
will be removed in future versions.
|
||||
END
|
||||
load_wintab_driver = Load wintab32 library
|
||||
load_wintab_driver_tooltip = <<<END
|
||||
|
@ -8,6 +8,7 @@
|
||||
<view maxsize="true">
|
||||
<listbox id="section_listbox">
|
||||
<listitem text="@.section_general" value="section_general" />
|
||||
<listitem text="@.section_tablet" value="section_tablet" />
|
||||
<listitem text="@.section_files" value="section_files" />
|
||||
<listitem text="@.section_color" value="section_color" />
|
||||
<listitem text="@.section_alerts" value="section_alerts" />
|
||||
@ -73,6 +74,25 @@
|
||||
<link id="locate_crash_folder" text="@.locate_crash_folder" />
|
||||
</vbox>
|
||||
|
||||
<!-- Tablet -->
|
||||
<vbox id="section_tablet">
|
||||
<separator text="@.section_tablet" horizontal="true" />
|
||||
<radio id="tablet_api_windows_pointer" text="@.tablet_api_windows_pointer" group="1" />
|
||||
<radio id="tablet_api_wintab_system" text="@.tablet_api_wintab_system" group="1" />
|
||||
<radio id="tablet_api_wintab_direct" text="@.tablet_api_wintab_direct" group="1" />
|
||||
<separator horizontal="true" />
|
||||
<check id="one_finger_as_mouse_movement"
|
||||
text="@.one_finger_as_mouse_movement"
|
||||
tooltip="@.one_finger_as_mouse_movement_tooltip"
|
||||
pref="experimental.one_finger_as_mouse_movement" />
|
||||
<hbox>
|
||||
<check id="load_wintab_driver"
|
||||
text="@.load_wintab_driver"
|
||||
tooltip="@.load_wintab_driver_tooltip" />
|
||||
<link text="@.wintab_more_info" url="https://www.aseprite.org/docs/wintab/" />
|
||||
</hbox>
|
||||
</vbox>
|
||||
|
||||
<!-- Files -->
|
||||
<vbox id="section_files">
|
||||
<separator text="@.section_files" horizontal="true" />
|
||||
@ -473,15 +493,10 @@
|
||||
</hbox>
|
||||
<check id="native_clipboard" text="@.native_clipboard" />
|
||||
<check id="native_file_dialog" text="@.native_file_dialog" />
|
||||
<check id="one_finger_as_mouse_movement"
|
||||
text="@.one_finger_as_mouse_movement"
|
||||
tooltip="@.one_finger_as_mouse_movement_tooltip"
|
||||
pref="experimental.one_finger_as_mouse_movement" />
|
||||
<hbox id="load_wintab_driver_box">
|
||||
<check id="load_wintab_driver"
|
||||
<check id="load_wintab_driver2"
|
||||
text="@.load_wintab_driver"
|
||||
tooltip="@.load_wintab_driver_tooltip"
|
||||
pref="experimental.load_wintab_driver" />
|
||||
tooltip="@.load_wintab_driver_tooltip" />
|
||||
<link text="@.wintab_more_info" url="https://www.aseprite.org/docs/wintab/" />
|
||||
</hbox>
|
||||
<check id="flash_layer" text="@.flash_selected_layer" />
|
||||
|
2
laf
2
laf
@ -1 +1 @@
|
||||
Subproject commit 8032d186a751326d0fc6436d69570ad4a3c4aaf1
|
||||
Subproject commit af0f8e7b53b9e3e689b5fa4e5ce1466c42c9e2aa
|
@ -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():
|
||||
|
@ -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());
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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<ListItem*>(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;
|
||||
|
@ -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<app::tools::InkType> type { this, app::tools::InkType::DEFAULT, "type" };
|
||||
};
|
||||
|
||||
class SetInkTypeCommand : public CommandWithNewParams<SetInkTypeParams> {
|
||||
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;
|
||||
|
@ -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<filters::ColorCurve>::fromString(const std::string& value)
|
||||
setValue(curve);
|
||||
}
|
||||
|
||||
template<>
|
||||
void Param<tools::InkType>::fromString(const std::string& value)
|
||||
{
|
||||
setValue(tools::string_id_to_ink_type(value));
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// Convert values from Lua
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
@ -312,6 +320,12 @@ void Param<filters::ColorCurve>::fromLua(lua_State* L, int index)
|
||||
}
|
||||
}
|
||||
|
||||
template<>
|
||||
void Param<tools::InkType>::fromLua(lua_State* L, int index)
|
||||
{
|
||||
script::get_value_from_lua<tools::InkType>(L, index);
|
||||
}
|
||||
|
||||
void CommandWithNewParamsBase::loadParamsFromLuaTable(lua_State* L, int index)
|
||||
{
|
||||
onResetValues();
|
||||
|
@ -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 <cstdio>
|
||||
#include <cstring>
|
||||
@ -225,7 +225,7 @@ void RecentFiles::save()
|
||||
|
||||
for (int j=0; j<m_paths[i].size(); ++j) {
|
||||
set_config_string(section,
|
||||
base::convert_to<std::string>(j).c_str(),
|
||||
fmt::format("{:04d}", j).c_str(),
|
||||
m_paths[i][j].c_str());
|
||||
}
|
||||
// Special entry that indicates that we've already converted
|
||||
|
@ -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 <cstring>
|
||||
#include <iostream>
|
||||
|
||||
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<tools::InkType>(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<tools::FreehandAlgorithm>(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<tools::ToolLoop> 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");
|
||||
|
||||
|
@ -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");
|
||||
|
@ -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 }
|
||||
|
@ -345,9 +345,13 @@ int Layer_set_parent(lua_State* L)
|
||||
else if (auto parentLayer = may_get_docobj<Layer>(L, 2)) {
|
||||
if (parentLayer->isGroup())
|
||||
parent = static_cast<LayerGroup*>(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:
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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<typename ImageTraits>
|
||||
class BrushInkProcessingBase : public DoubleInkProcessing<BrushInkProcessingBase<ImageTraits>, 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<RgbTraits>::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<IndexedTraits>(m_brushImage, x, y);
|
||||
if (m_transparentColor == c)
|
||||
c = 0;
|
||||
@ -1305,12 +1322,6 @@ bool BrushInkProcessingBase<GrayscaleTraits>::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<IndexedTraits>(m_brushImage, x, y);
|
||||
if (m_transparentColor == c)
|
||||
c = 0;
|
||||
@ -1348,19 +1359,43 @@ bool BrushInkProcessingBase<IndexedTraits>::preProcessPixel(int x, int y, color_
|
||||
switch (m_brushImage->pixelFormat()) {
|
||||
case IMAGE_RGB: {
|
||||
c = get_pixel_fast<RgbTraits>(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<IndexedTraits>(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<GrayscaleTraits>(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<RgbTraits>::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<GrayscaleTraits>::processPixel(int x, int y) {
|
||||
c = get_pixel_fast<IndexedTraits>(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<IndexedTraits>::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<GrayscaleTraits>::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<IndexedTraits>(m_brushImage, x, y);
|
||||
if (m_transparentColor != c)
|
||||
*m_dstAddress = m_shading(*m_srcAddress);
|
||||
@ -1776,10 +1796,6 @@ void BrushCopyInkProcessing<RgbTraits>::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<IndexedTraits>(m_brushImage, x, y);
|
||||
if (c == m_transparentColor) {
|
||||
*m_dstAddress = *m_srcAddress;
|
||||
@ -1819,7 +1835,10 @@ void BrushCopyInkProcessing<IndexedTraits>::processPixel(int x, int y) {
|
||||
c = get_pixel_fast<RgbTraits>(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<IndexedTraits>::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<GrayscaleTraits>::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<IndexedTraits>(m_brushImage, x, y);
|
||||
if (c == m_transparentColor) {
|
||||
*m_dstAddress = *m_srcAddress;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
@ -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()
|
||||
|
@ -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<Image*>(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;
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
// <preferences><tool>...</tool></preferences> 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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
46
src/doc/color.cpp
Normal file
46
src/doc/color.cpp
Normal file
@ -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 <algorithm>
|
||||
|
||||
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
|
@ -11,8 +11,6 @@
|
||||
|
||||
#include "base/ints.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
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
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user