Merge branch 'beta' into tilemap-editor

This commit is contained in:
David Capello 2020-07-13 17:32:42 -03:00
commit 515dace441
195 changed files with 2554 additions and 1293 deletions

View File

@ -8,6 +8,7 @@
* [Linux dependencies](#linux-dependencies)
* [Compiling](#compiling)
* [Windows details](#windows-details)
* [MinGW](#mingw)
* [macOS details](#macos-details)
* [Issues with Retina displays](#issues-with-retina-displays)
* [Linux details](#linux-details)
@ -18,7 +19,7 @@
You should be able to compile Aseprite successfully on the following
platforms:
* Windows 10 + [Visual Studio Community 2019 + Windows 10.0.18362.0 SDK](https://imgur.com/a/7zs51IT)
* Windows 10 + [Visual Studio Community 2019 + Windows 10.0.18362.0 SDK](https://imgur.com/a/7zs51IT) (we don't support [MinGW](#mingw))
* macOS 10.15.3 Mojave + Xcode 11.2.1 + macOS 10.15 SDK (older version might work)
* Linux + gcc 9.2 or clang 9.0
@ -58,8 +59,8 @@ To compile Aseprite you will need:
## Windows dependencies
* Windows 10 (**we don't support cross-compiling and don't know if this would be possible**)
* [Visual Studio Community 2019](https://visualstudio.microsoft.com/downloads/)
* Windows 10 (we don't support cross-compiling)
* [Visual Studio Community 2019](https://visualstudio.microsoft.com/downloads/) (we don't support [MinGW](#mingw))
* The [Desktop development with C++ item + Windows 10.0.18362.0 SDK](https://imgur.com/a/7zs51IT)
from the Visual Studio installer
@ -130,6 +131,23 @@ And then
In this case, `C:\deps\skia` is the directory where Skia was compiled
or uncompressed.
### MinGW
We don't support MinGW compiler and it might bring some problems into
the compilation process. If you see that the detected C++ compiler by
cmake is `C:\MinGW\bin\c++.exe` or something similar, you have to get
rid of MinGW path (`C:\MinGW\bin`) from the `PATH` environment
variable and run cmake again from scratch, so the Visual Studio C++
compiler (`cl.exe`) is used instead.
You can define the `CMAKE_IGNORE_PATH` variable when running cmake for
the first time in case that you don't know or don't want to modify the
`PATH` variable, e.g.:
cmake -DCMAKE_IGNORE_PATH=C:\MinGW\bin ...
More information in [issue #2449](https://github.com/aseprite/aseprite/issues/2449)
## macOS details
Run `cmake` with the following parameters and then `ninja`:

View File

@ -77,7 +77,7 @@ Aseprite includes color palettes created by:
It tries to replicate some pixel-art algorithms:
* [RotSprite](http://forums.sonicretro.org/index.php?showtopic=8848&st=15&p=159754&#entry159754) by Xenowhirl.
* [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).
* [Pixel perfect drawing algorithm](https://deepnight.net/blog/tools/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](https://www.aseprite.org/contributors/), and all the

View File

@ -132,6 +132,7 @@
<key command="Refresh" shortcut="F5" />
<key command="TogglePreview" shortcut="F7" />
<key command="FullscreenPreview" shortcut="F8" />
<key command="FullscreenMode" shortcut="F11" mac="Cmd+Ctrl+F" />
<key command="ShowGrid" shortcut="Ctrl+'" mac="Cmd+'" />
<key command="ShowPixelGrid" shortcut="Ctrl+Shift+'" mac="Cmd+Shift+'" />
<key command="SnapToGrid" shortcut="Shift+S" />
@ -949,7 +950,8 @@
<param name="switch" value="true" />
</item>
<item command="TogglePreview" text="@.view_preview" />
<item command="AdvancedMode" text="@.view_full_screen_mode" />
<item command="AdvancedMode" text="@.view_advanced_mode" />
<item command="FullscreenMode" text="@.view_full_screen_mode" />
<item command="FullscreenPreview" text="@.view_full_screen_preview" />
<item command="Home" text="@.view_home" group="view_controls" />
<separator />

View File

@ -197,6 +197,7 @@
<option id="load_wintab_driver" type="bool" default="true" />
<option id="flash_layer" type="bool" default="false" />
<option id="nonactive_layers_opacity" type="int" default="255" />
<option id="rgbmap_algorithm" type="doc::RgbMapAlgorithm" default="doc::RgbMapAlgorithm::DEFAULT" />
</section>
<section id="news">
<option id="cache_file" type="std::string" />
@ -266,6 +267,7 @@
<option id="dithering_algorithm" type="std::string" />
<option id="dithering_factor" type="int" default="100" />
<option id="to_gray" type="ToGrayAlgorithm" default="ToGrayAlgorithm::DEFAULT" />
<option id="advanced" type="bool" default="false" />
</section>
<section id="eyedropper" text="Editor">
<option id="channel" type="EyedropperChannel" default="EyedropperChannel::COLOR_ALPHA" />
@ -329,6 +331,7 @@
<option id="show_alert" type="bool" default="true" />
<option id="interlaced" type="bool" default="false" />
<option id="loop" type="bool" default="true" />
<option id="preserve_palette_order" type="bool" default="true" />
</section>
<section id="jpeg">
<option id="show_alert" type="bool" default="true" />

View File

@ -306,6 +306,7 @@ Flip_Selection = Selection
Flip_Vertically = Vertically
FrameProperties = Frame Properties
FrameTagProperties = Tag Properties
FullscreenMode = Toggle Fullscreen Mode
FullscreenPreview = Fullscreen Preview
GotoFirstFrame = Go to First Frame
GotoFirstFrameInTag = Go to First Frame In Tag
@ -736,12 +737,14 @@ Check in case that you want to establish
the given option as the default option.
END
reset = Reset
advanced_options = Advanced Options
[gif_options]
title = GIF Options
general_options = General Options:
interlaced = &Interlaced
animation_loop = Animation &Loop
preserve_palette_order = &Preserve palette order
ok = &OK
cancel = &Cancel
@ -983,6 +986,7 @@ view_set_loop_section = Set &Loop Section
view_show_onion_skin = Show &Onion Skin
view_timeline = &Timeline
view_preview = Previe&w
view_advanced_mode = &Advanced Mode
view_full_screen_mode = &Full Screen Mode
view_full_screen_preview = F&ull Screen Preview
view_home = &Home
@ -1042,12 +1046,16 @@ background = Background:
transparent = &Transparent
white = &White
black = &Black
advanced_options = Advanced Options
pixel_ratio = Pixel Aspect Ratio:
square_pixels = Square Pixels (1:1)
double_wide = Double-wide Pixels (2:1)
double_high = Double-high Pixels (1:2)
[rgbmap_algorithm_selector]
label = RGB to palette index mapping:
rgb5a3 = Table RGB 5 bits + Alpha 3 bits
octree = Octree without Alpha
[open_sequence]
title = Notice
description = Do you want to load the following files as an animation?
@ -1337,7 +1345,7 @@ bg_color = Background Color:
[palette_from_sprite]
title = Palette from Sprite
new_palette = Create a new palette with a specific number of colors (or less):
new_palette = Create new palette, color count limit:
replace_palette = Replace current palette
replace_range = Replace current range
alpha_channel = Create entries with alpha component

View File

@ -13,12 +13,21 @@
<slider min="0" max="100" id="factor" minwidth="100" />
<label text="%" />
</hbox>
<combobox id="to_gray_combobox">
<listitem text="!Luminance" />
<listitem text="!HSV" />
<listitem text="!HSL" />
</combobox>
<check text="@.flatten" id="flatten" />
<check id="advanced_check" text="@general.advanced_options" cell_hspan="2" />
<hbox id="advanced" cell_hspan="2">
<label text="@rgbmap_algorithm_selector.label" />
<hbox id="rgbmap_algorithm_placeholder" />
</hbox>
<separator horizontal="true" />
<hbox>
<slider min="0" max="100" id="progress" minwidth="100" />

View File

@ -1,11 +1,13 @@
<!-- Aseprite -->
<!-- Copyright (C) 2014-2018 by David Capello -->
<!-- Copyright (C) 2020 Igara Studio S.A. -->
<!-- Copyright (C) 2014-2018 David Capello -->
<gui>
<window id="gif_options" text="@.title">
<vbox>
<separator text="@.general_options" left="true" horizontal="true" />
<check text="@.interlaced" id="interlaced" />
<check text="@.animation_loop" id="loop" />
<check text="@.preserve_palette_order" id="preserve_palette_order" />
<separator horizontal="true" />

View File

@ -1,5 +1,6 @@
<!-- Aseprite -->
<!-- Copyright (C) 2001-2018 by David Capello -->
<!-- Copyright (c) 2020 Igara Studio S.A. -->
<!-- Copyright (c) 2001-2018 David Capello -->
<gui>
<window id="new_sprite" text="@.title">
<box vertical="true">
@ -26,7 +27,7 @@
<item text="@.black" icon="icon_black" />
</buttonset>
<check id="advanced_check" text="@.advanced_options" />
<check id="advanced_check" text="@general.advanced_options" />
<vbox id="advanced">
<label text="@.pixel_ratio" />
<combobox id="pixel_ratio" cell_align="horizontal">

View File

@ -504,6 +504,10 @@
<label text="@.non_active_layer_opacity" />
<slider id="nonactive_layers_opacity" min="0" max="255" width="128" />
</hbox>
<hbox>
<label text="@rgbmap_algorithm_selector.label" />
<hbox id="rgbmap_algorithm_placeholder" />
</hbox>
</vbox>
</panel>

View File

@ -1,5 +1,6 @@
<!-- Aseprite -->
<!-- Copyright (C) 2015-2018 by David Capello -->
<!-- Copyright (c) 2020 Igara Studio S.A. -->
<!-- Copyright (c) 2015-2018 David Capello -->
<gui>
<window id="palette_from_sprite" text="@.title">
<grid columns="2">
@ -7,7 +8,15 @@
<expr expansive="true" id="ncolors" magnet="true" />
<radio id="current_palette" text="@.replace_palette" group="1" cell_hspan="2" />
<radio id="current_range" text="@.replace_range" group="1" cell_hspan="2" />
<separator horizontal="true" cell_hspan="2" />
<check id="alpha_channel" text="@.alpha_channel" cell_hspan="2" />
<check id="advanced_check" text="@general.advanced_options" cell_hspan="2" />
<hbox id="advanced" cell_hspan="2">
<label text="@rgbmap_algorithm_selector.label" />
<hbox id="rgbmap_algorithm_placeholder" />
</hbox>
<separator horizontal="true" cell_hspan="2" />

View File

@ -85,10 +85,10 @@ We are using some C++11 features, mainly:
* Use range-based for loops (`for (const auto& item : values) { ... }`)
* Use template alias (`template<typename T> alias = orig<T>;`)
* Use non-generic lambda functions
* Use `std::shared_ptr` and `std::unique_ptr`
* Use `std::shared_ptr`, `std::unique_ptr`, or `base::Ref`
* Use `base::clamp` (no `std::clamp` yet)
* Use `static constexpr T v = ...;`
* You can use `<atomic>`, `<thread>`, `<mutex>`, and `<condition_variable>`
* We can use `using T = ...;` instead of `typedef ... T`
* Prefer `using T = ...;` instead of `typedef ... T`
* We use gcc 9.2 or clang 9.0 on Linux, so check the features available in
https://developer.mozilla.org/en-US/docs/Mozilla/Using_CXX_in_Mozilla_code

2
laf

@ -1 +1 @@
Subproject commit e1fb70a069c6482be282bf6f6d9190e3a0def14a
Subproject commit e1eec6912548498b87a17e01e88249cbc9328ad3

View File

@ -227,6 +227,7 @@ if(ENABLE_UI)
commands/cmd_fit_screen.cpp
commands/cmd_frame_properties.cpp
commands/cmd_frame_tag_properties.cpp
commands/cmd_fullscreen_mode.cpp
commands/cmd_fullscreen_preview.cpp
commands/cmd_goto_frame.cpp
commands/cmd_goto_layer.cpp
@ -372,6 +373,7 @@ if(ENABLE_UI)
ui/preview_editor.cpp
ui/recent_listbox.cpp
ui/resources_listbox.cpp
ui/rgbmap_algorithm_selector.cpp
ui/search_entry.cpp
ui/select_accelerator.cpp
ui/selection_mode_field.cpp

View File

@ -373,16 +373,13 @@ void App::run()
ResourceFinder rf;
rf.includeDataDir(fmt::format("icons/ase{0}.png", size).c_str());
if (rf.findFirst()) {
os::Surface* surf = os::instance()->loadRgbaSurface(rf.filename().c_str());
os::SurfaceRef surf = os::instance()->loadRgbaSurface(rf.filename().c_str());
if (surf)
icons.push_back(surf);
}
}
display->setIcons(icons);
for (auto surf : icons)
surf->dispose();
}
catch (const std::exception&) {
// Just ignore the exception, we couldn't change the app icon, no

View File

@ -26,7 +26,6 @@
#include "app/ui/main_window.h"
#include "app/ui_context.h"
#include "app/util/filetoks.h"
#include "base/bind.h"
#include "base/fs.h"
#include "base/string.h"
#include "fmt/format.h"
@ -312,7 +311,7 @@ AppMenus* AppMenus::instance()
static AppMenus* instance = NULL;
if (!instance) {
instance = new AppMenus;
App::instance()->Exit.connect(base::Bind<void>(&destroy_instance, instance));
App::instance()->Exit.connect([]{ destroy_instance(instance); });
}
return instance;
}
@ -323,13 +322,7 @@ AppMenus::AppMenus()
{
m_recentFilesConn =
App::instance()->recentFiles()->Changed.connect(
base::Bind(&AppMenus::rebuildRecentList, this));
}
AppMenus::~AppMenus()
{
if (m_osMenu)
m_osMenu->dispose();
[this]{ rebuildRecentList(); });
}
void AppMenus::reload()
@ -563,10 +556,10 @@ bool AppMenus::rebuildRecentList()
// Sync native menus
if (owner->native() &&
owner->native()->menuItem) {
os::Menus* menus = os::instance()->menus();
os::Menu* osMenu = (menus ? menus->createMenu(): nullptr);
auto menus = os::instance()->menus();
os::MenuRef osMenu = (menus ? menus->makeMenu(): nullptr);
if (osMenu) {
createNativeSubmenus(osMenu, menu);
createNativeSubmenus(osMenu.get(), menu);
owner->native()->menuItem->setSubmenu(osMenu);
}
}
@ -826,8 +819,9 @@ void AppMenus::createNativeMenus()
if (!menus) // This platform doesn't support native menu items
return;
os::Menu* oldOSMenu = m_osMenu;
m_osMenu = menus->createMenu();
// Save a reference to the old menu to avoid destroying it.
os::MenuRef oldOSMenu = m_osMenu;
m_osMenu = menus->makeMenu();
#ifdef __APPLE__ // Create default macOS app menus (App ... Window)
{
@ -863,24 +857,24 @@ void AppMenus::createNativeMenus()
os::MenuItemInfo quit(fmt::format("Quit {}", get_app_name()), os::MenuItemInfo::Quit);
quit.shortcut = os::Shortcut('q', os::kKeyCmdModifier);
os::Menu* appMenu = menus->createMenu();
appMenu->addItem(menus->createMenuItem(about));
appMenu->addItem(menus->createMenuItem(os::MenuItemInfo(os::MenuItemInfo::Separator)));
appMenu->addItem(menus->createMenuItem(preferences));
appMenu->addItem(menus->createMenuItem(os::MenuItemInfo(os::MenuItemInfo::Separator)));
appMenu->addItem(menus->createMenuItem(hide));
appMenu->addItem(menus->createMenuItem(os::MenuItemInfo("Hide Others", os::MenuItemInfo::HideOthers)));
appMenu->addItem(menus->createMenuItem(os::MenuItemInfo("Show All", os::MenuItemInfo::ShowAll)));
appMenu->addItem(menus->createMenuItem(os::MenuItemInfo(os::MenuItemInfo::Separator)));
appMenu->addItem(menus->createMenuItem(quit));
os::MenuRef appMenu = menus->makeMenu();
appMenu->addItem(menus->makeMenuItem(about));
appMenu->addItem(menus->makeMenuItem(os::MenuItemInfo(os::MenuItemInfo::Separator)));
appMenu->addItem(menus->makeMenuItem(preferences));
appMenu->addItem(menus->makeMenuItem(os::MenuItemInfo(os::MenuItemInfo::Separator)));
appMenu->addItem(menus->makeMenuItem(hide));
appMenu->addItem(menus->makeMenuItem(os::MenuItemInfo("Hide Others", os::MenuItemInfo::HideOthers)));
appMenu->addItem(menus->makeMenuItem(os::MenuItemInfo("Show All", os::MenuItemInfo::ShowAll)));
appMenu->addItem(menus->makeMenuItem(os::MenuItemInfo(os::MenuItemInfo::Separator)));
appMenu->addItem(menus->makeMenuItem(quit));
os::MenuItem* appItem = menus->createMenuItem(os::MenuItemInfo("App"));
os::MenuItemRef appItem = menus->makeMenuItem(os::MenuItemInfo("App"));
appItem->setSubmenu(appMenu);
m_osMenu->addItem(appItem);
}
#endif
createNativeSubmenus(m_osMenu, m_rootMenu.get());
createNativeSubmenus(m_osMenu.get(), m_rootMenu.get());
#ifdef __APPLE__
{
@ -898,11 +892,11 @@ void AppMenus::createNativeMenus()
os::MenuItemInfo minimize("Minimize", os::MenuItemInfo::Minimize);
minimize.shortcut = os::Shortcut('m', os::kKeyCmdModifier);
os::Menu* windowMenu = menus->createMenu();
windowMenu->addItem(menus->createMenuItem(minimize));
windowMenu->addItem(menus->createMenuItem(os::MenuItemInfo("Zoom", os::MenuItemInfo::Zoom)));
os::MenuRef windowMenu = menus->makeMenu();
windowMenu->addItem(menus->makeMenuItem(minimize));
windowMenu->addItem(menus->makeMenuItem(os::MenuItemInfo("Zoom", os::MenuItemInfo::Zoom)));
os::MenuItem* windowItem = menus->createMenuItem(os::MenuItemInfo("Window"));
os::MenuItemRef windowItem = menus->makeMenuItem(os::MenuItemInfo("Window"));
windowItem->setSubmenu(windowMenu);
// We use helpIndex+1 because the first index in m_osMenu is the
@ -913,10 +907,11 @@ void AppMenus::createNativeMenus()
menus->setAppMenu(m_osMenu);
if (oldOSMenu)
oldOSMenu->dispose();
oldOSMenu.reset();
}
void AppMenus::createNativeSubmenus(os::Menu* osMenu, const ui::Menu* uiMenu)
void AppMenus::createNativeSubmenus(os::Menu* osMenu,
const ui::Menu* uiMenu)
{
os::Menus* menus = os::instance()->menus();
@ -960,7 +955,7 @@ void AppMenus::createNativeSubmenus(os::Menu* osMenu, const ui::Menu* uiMenu)
continue;
}
os::MenuItem* osItem = menus->createMenuItem(info);
os::MenuItemRef osItem = menus->makeMenuItem(info);
if (osItem) {
osMenu->addItem(osItem);
if (appMenuItem) {
@ -970,8 +965,8 @@ void AppMenus::createNativeSubmenus(os::Menu* osMenu, const ui::Menu* uiMenu)
if (child->type() == ui::kMenuItemWidget &&
((ui::MenuItem*)child)->hasSubmenu()) {
os::Menu* osSubmenu = menus->createMenu();
createNativeSubmenus(osSubmenu, ((ui::MenuItem*)child)->getSubmenu());
os::MenuRef osSubmenu = menus->makeMenu();
createNativeSubmenus(osSubmenu.get(), ((ui::MenuItem*)child)->getSubmenu());
osItem->setSubmenu(osSubmenu);
}
}

View File

@ -14,6 +14,7 @@
#include "app/widget_type_mismatch.h"
#include "base/disable_copying.h"
#include "obs/connection.h"
#include "os/menus.h"
#include "ui/base.h"
#include "ui/menu.h"
@ -22,11 +23,6 @@
class TiXmlElement;
class TiXmlHandle;
namespace os {
class Menu;
class Shortcut;
}
namespace app {
class Command;
class Params;
@ -41,8 +37,6 @@ namespace app {
public:
static AppMenus* instance();
~AppMenus();
void reload();
void initTheme();
@ -83,7 +77,8 @@ namespace app {
void syncNativeMenuItemKeyShortcuts(Menu* menu);
void updateMenusList();
void createNativeMenus();
void createNativeSubmenus(os::Menu* osMenu, const ui::Menu* uiMenu);
void createNativeSubmenus(os::Menu* osMenu,
const ui::Menu* uiMenu);
#ifdef ENABLE_SCRIPTING
void loadScriptsSubmenu(ui::Menu* menu,
@ -118,7 +113,7 @@ namespace app {
std::map<std::string, GroupInfo> m_groups;
// Native main menu bar (== nullptr if the platform doesn't
// support native menus)
os::Menu* m_osMenu;
os::MenuRef m_osMenu;
XmlTranslator m_xmlTranslator;
};

View File

@ -15,7 +15,6 @@
#include "app/check_update_delegate.h"
#include "app/pref/preferences.h"
#include "base/bind.h"
#include "base/convert_to.h"
#include "base/launcher.h"
#include "base/replace_string.h"
@ -127,7 +126,7 @@ void CheckUpdateThreadLauncher::launch()
m_delegate->onCheckingUpdates();
m_bgJob.reset(new CheckUpdateBackgroundJob);
m_thread.reset(new base::thread(base::Bind<void>(&CheckUpdateThreadLauncher::checkForUpdates, this)));
m_thread.reset(new base::thread([this]{ checkForUpdates(); }));
// Start a timer to monitoring the progress of the background job
// executed in "m_thread". The "onMonitoringTick" method will be

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2018 Igara Studio S.A.
// Copyright (C) 2018-2020 Igara Studio S.A.
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -17,7 +17,7 @@
namespace app {
namespace cmd {
AssignColorProfile::AssignColorProfile(doc::Sprite* sprite, const gfx::ColorSpacePtr& cs)
AssignColorProfile::AssignColorProfile(doc::Sprite* sprite, const gfx::ColorSpaceRef& cs)
: WithSprite(sprite)
, m_oldCS(sprite->colorSpace())
, m_newCS(cs)

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2018 Igara Studio S.A.
// Copyright (C) 2018-2020 Igara Studio S.A.
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -18,7 +18,7 @@ namespace cmd {
class AssignColorProfile : public Cmd,
public WithSprite {
public:
AssignColorProfile(doc::Sprite* sprite, const gfx::ColorSpacePtr& cs);
AssignColorProfile(doc::Sprite* sprite, const gfx::ColorSpaceRef& cs);
protected:
void onExecute() override;
@ -32,8 +32,8 @@ namespace cmd {
}
private:
gfx::ColorSpacePtr m_oldCS;
gfx::ColorSpacePtr m_newCS;
gfx::ColorSpaceRef m_oldCS;
gfx::ColorSpaceRef m_newCS;
};
} // namespace cmd

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2018-2019 Igara Studio S.A.
// Copyright (C) 2018-2020 Igara Studio S.A.
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -24,7 +24,7 @@ namespace app {
namespace cmd {
static doc::ImageRef convert_image_color_space(const doc::Image* srcImage,
const gfx::ColorSpacePtr& newCS,
const gfx::ColorSpaceRef& newCS,
os::ColorSpaceConversion* conversion)
{
ImageSpec spec = srcImage->spec();
@ -71,14 +71,14 @@ static doc::ImageRef convert_image_color_space(const doc::Image* srcImage,
}
void convert_color_profile(doc::Sprite* sprite,
const gfx::ColorSpacePtr& newCS)
const gfx::ColorSpaceRef& newCS)
{
ASSERT(sprite->colorSpace());
ASSERT(newCS);
os::System* system = os::instance();
auto srcOCS = system->createColorSpace(sprite->colorSpace());
auto dstOCS = system->createColorSpace(newCS);
auto srcOCS = system->makeColorSpace(sprite->colorSpace());
auto dstOCS = system->makeColorSpace(newCS);
ASSERT(srcOCS);
ASSERT(dstOCS);
@ -123,15 +123,15 @@ void convert_color_profile(doc::Sprite* sprite,
void convert_color_profile(doc::Image* image,
doc::Palette* palette,
const gfx::ColorSpacePtr& oldCS,
const gfx::ColorSpacePtr& newCS)
const gfx::ColorSpaceRef& oldCS,
const gfx::ColorSpaceRef& newCS)
{
ASSERT(oldCS);
ASSERT(newCS);
os::System* system = os::instance();
auto srcOCS = system->createColorSpace(oldCS);
auto dstOCS = system->createColorSpace(newCS);
auto srcOCS = system->makeColorSpace(oldCS);
auto dstOCS = system->makeColorSpace(newCS);
ASSERT(srcOCS);
ASSERT(dstOCS);
@ -161,7 +161,7 @@ void convert_color_profile(doc::Image* image,
}
}
ConvertColorProfile::ConvertColorProfile(doc::Sprite* sprite, const gfx::ColorSpacePtr& newCS)
ConvertColorProfile::ConvertColorProfile(doc::Sprite* sprite, const gfx::ColorSpaceRef& newCS)
: WithSprite(sprite)
{
os::System* system = os::instance();
@ -169,8 +169,8 @@ ConvertColorProfile::ConvertColorProfile(doc::Sprite* sprite, const gfx::ColorSp
ASSERT(sprite->colorSpace());
ASSERT(newCS);
auto srcOCS = system->createColorSpace(sprite->colorSpace());
auto dstOCS = system->createColorSpace(newCS);
auto srcOCS = system->makeColorSpace(sprite->colorSpace());
auto dstOCS = system->makeColorSpace(newCS);
ASSERT(srcOCS);
ASSERT(dstOCS);

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2018-2019 Igara Studio S.A.
// Copyright (C) 2018-2020 Igara Studio S.A.
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -28,7 +28,7 @@ namespace cmd {
class ConvertColorProfile : public Cmd,
public WithSprite {
public:
ConvertColorProfile(doc::Sprite* sprite, const gfx::ColorSpacePtr& newCS);
ConvertColorProfile(doc::Sprite* sprite, const gfx::ColorSpaceRef& newCS);
protected:
void onExecute() override;
@ -45,12 +45,12 @@ namespace cmd {
// Converts the sprite to the new color profile without undo information.
// TODO how to merge this function with cmd::ConvertColorProfile
void convert_color_profile(doc::Sprite* sprite,
const gfx::ColorSpacePtr& newCS);
const gfx::ColorSpaceRef& newCS);
void convert_color_profile(doc::Image* image,
doc::Palette* palette,
const gfx::ColorSpacePtr& oldCS,
const gfx::ColorSpacePtr& newCS);
const gfx::ColorSpaceRef& oldCS,
const gfx::ColorSpaceRef& newCS);
} // namespace cmd
} // namespace app

View File

@ -70,6 +70,7 @@ private:
SetPixelFormat::SetPixelFormat(Sprite* sprite,
const PixelFormat newFormat,
const render::Dithering& dithering,
const doc::RgbMapAlgorithm mapAlgorithm,
doc::rgba_to_graya_func toGray,
render::TaskDelegate* delegate)
: WithSprite(sprite)
@ -102,6 +103,7 @@ SetPixelFormat::SetPixelFormat(Sprite* sprite,
oldImage,
cel->frame(),
cel->layer()->isBackground(),
mapAlgorithm,
toGray,
&superDel);
@ -118,6 +120,7 @@ SetPixelFormat::SetPixelFormat(Sprite* sprite,
oldImage,
0, // TODO select a frame or generate other tilesets?
false, // TODO is background? it depends of the layer where this tileset is used
mapAlgorithm,
toGray,
&superDel);
}
@ -191,6 +194,7 @@ void SetPixelFormat::convertImage(doc::Sprite* sprite,
const doc::ImageRef& oldImage,
const doc::frame_t frame,
const bool isBackground,
const doc::RgbMapAlgorithm mapAlgorithm,
doc::rgba_to_graya_func toGray,
render::TaskDelegate* delegate)
{
@ -201,7 +205,7 @@ void SetPixelFormat::convertImage(doc::Sprite* sprite,
render::convert_pixel_format
(oldImage.get(), nullptr, m_newFormat,
dithering,
sprite->rgbMap(frame),
sprite->rgbMap(frame, sprite->rgbMapForSprite(), mapAlgorithm),
sprite->palette(frame),
isBackground,
oldImage->maskColor(),

View File

@ -15,6 +15,7 @@
#include "doc/frame.h"
#include "doc/image_ref.h"
#include "doc/pixel_format.h"
#include "doc/rgbmap_algorithm.h"
namespace doc {
class Sprite;
@ -34,6 +35,7 @@ namespace cmd {
SetPixelFormat(doc::Sprite* sprite,
const doc::PixelFormat newFormat,
const render::Dithering& dithering,
const doc::RgbMapAlgorithm mapAlgorithm,
doc::rgba_to_graya_func toGray,
render::TaskDelegate* delegate);
@ -52,6 +54,7 @@ namespace cmd {
const doc::ImageRef& oldImage,
const doc::frame_t frame,
const bool isBackground,
const doc::RgbMapAlgorithm mapAlgorithm,
doc::rgba_to_graya_func toGray,
render::TaskDelegate* delegate);

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2018-2019 Igara Studio S.A.
// Copyright (C) 2018-2020 Igara Studio S.A.
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -33,12 +33,12 @@ void initialize_color_spaces(Preferences& pref)
});
}
os::ColorSpacePtr get_screen_color_space()
os::ColorSpaceRef get_screen_color_space()
{
return os::instance()->defaultDisplay()->colorSpace();
}
os::ColorSpacePtr get_current_color_space()
os::ColorSpaceRef get_current_color_space()
{
#ifdef ENABLE_UI
if (current_editor)
@ -48,14 +48,14 @@ os::ColorSpacePtr get_current_color_space()
return get_screen_color_space();
}
gfx::ColorSpacePtr get_working_rgb_space_from_preferences()
gfx::ColorSpaceRef get_working_rgb_space_from_preferences()
{
if (Preferences::instance().color.manage()) {
const std::string name = Preferences::instance().color.workingRgbSpace();
if (name == "sRGB")
return gfx::ColorSpace::MakeSRGB();
std::vector<os::ColorSpacePtr> colorSpaces;
std::vector<os::ColorSpaceRef> colorSpaces;
os::instance()->listColorSpaces(colorSpaces);
for (auto& cs : colorSpaces) {
if (cs->gfxColorSpace()->name() == name)
@ -78,8 +78,8 @@ ConvertCS::ConvertCS()
}
}
ConvertCS::ConvertCS(const os::ColorSpacePtr& srcCS,
const os::ColorSpacePtr& dstCS)
ConvertCS::ConvertCS(const os::ColorSpaceRef& srcCS,
const os::ColorSpaceRef& dstCS)
{
if (g_manage) {
m_conversion = os::instance()->convertBetweenColorSpace(srcCS, dstCS);
@ -108,10 +108,10 @@ ConvertCS convert_from_current_to_screen_color_space()
return ConvertCS();
}
ConvertCS convert_from_custom_to_srgb(const os::ColorSpacePtr& from)
ConvertCS convert_from_custom_to_srgb(const os::ColorSpaceRef& from)
{
return ConvertCS(from,
os::instance()->createColorSpace(gfx::ColorSpace::MakeSRGB()));
os::instance()->makeColorSpace(gfx::ColorSpace::MakeSRGB()));
}
} // namespace app

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2018-2019 Igara Studio S.A.
// Copyright (c) 2018-2020 Igara Studio S.A.
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -12,8 +12,6 @@
#include "gfx/color_space.h"
#include "os/color_space.h"
#include <memory>
namespace doc {
class Sprite;
}
@ -23,27 +21,27 @@ namespace app {
void initialize_color_spaces(Preferences& pref);
os::ColorSpacePtr get_screen_color_space();
os::ColorSpaceRef get_screen_color_space();
// Returns the color space of the current document.
os::ColorSpacePtr get_current_color_space();
os::ColorSpaceRef get_current_color_space();
gfx::ColorSpacePtr get_working_rgb_space_from_preferences();
gfx::ColorSpaceRef get_working_rgb_space_from_preferences();
class ConvertCS {
public:
ConvertCS();
ConvertCS(const os::ColorSpacePtr& srcCS,
const os::ColorSpacePtr& dstCS);
ConvertCS(const os::ColorSpaceRef& srcCS,
const os::ColorSpaceRef& dstCS);
ConvertCS(ConvertCS&&);
ConvertCS& operator=(const ConvertCS&) = delete;
gfx::Color operator()(const gfx::Color c);
private:
std::unique_ptr<os::ColorSpaceConversion> m_conversion;
os::Ref<os::ColorSpaceConversion> m_conversion;
};
ConvertCS convert_from_current_to_screen_color_space();
ConvertCS convert_from_custom_to_srgb(const os::ColorSpacePtr& from);
ConvertCS convert_from_custom_to_srgb(const os::ColorSpaceRef& from);
} // namespace app

View File

@ -13,7 +13,6 @@
#include "app/commands/command.h"
#include "app/modules/gui.h"
#include "app/ui/main_window.h"
#include "base/bind.h"
#include "fmt/format.h"
#include "ver/info.h"

View File

@ -23,7 +23,6 @@
#include "app/ui/editor/select_box_state.h"
#include "app/ui/skin/skin_theme.h"
#include "app/ui_context.h"
#include "base/bind.h"
#include "base/clamp.h"
#include "doc/image.h"
#include "doc/mask.h"
@ -75,13 +74,13 @@ public:
setTop(0);
setBottom(0);
width() ->Change.connect(base::Bind<void>(&CanvasSizeWindow::onSizeChange, this));
height()->Change.connect(base::Bind<void>(&CanvasSizeWindow::onSizeChange, this));
dir() ->ItemChange.connect(base::Bind<void>(&CanvasSizeWindow::onDirChange, this));;
left() ->Change.connect(base::Bind<void>(&CanvasSizeWindow::onBorderChange, this));
right() ->Change.connect(base::Bind<void>(&CanvasSizeWindow::onBorderChange, this));
top() ->Change.connect(base::Bind<void>(&CanvasSizeWindow::onBorderChange, this));
bottom()->Change.connect(base::Bind<void>(&CanvasSizeWindow::onBorderChange, this));
width() ->Change.connect([this]{ onSizeChange(); });
height()->Change.connect([this]{ onSizeChange(); });
dir() ->ItemChange.connect([this]{ onDirChange(); });
left() ->Change.connect([this]{ onBorderChange(); });
right() ->Change.connect([this]{ onBorderChange(); });
top() ->Change.connect([this]{ onBorderChange(); });
bottom()->Change.connect([this]{ onBorderChange(); });
m_editor->setState(m_selectBoxState);

View File

@ -1,4 +1,5 @@
// Aseprite
// Copyright (C) 2020 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@ -21,7 +22,6 @@
#include "app/ui/timeline/timeline.h"
#include "app/ui/user_data_popup.h"
#include "app/ui_context.h"
#include "base/bind.h"
#include "base/mem_utils.h"
#include "base/scoped_value.h"
#include "doc/cel.h"
@ -50,9 +50,9 @@ public:
, m_cel(nullptr)
, m_selfUpdate(false)
, m_newUserData(false) {
opacity()->Change.connect(base::Bind<void>(&CelPropertiesWindow::onStartTimer, this));
userData()->Click.connect(base::Bind<void>(&CelPropertiesWindow::onPopupUserData, this));
m_timer.Tick.connect(base::Bind<void>(&CelPropertiesWindow::onCommitChange, this));
opacity()->Change.connect([this]{ onStartTimer(); });
userData()->Click.connect([this]{ onPopupUserData(); });
m_timer.Tick.connect([this]{ onCommitChange(); });
remapWindow();
centerWindow();

View File

@ -26,8 +26,8 @@
#include "app/ui/dithering_selector.h"
#include "app/ui/editor/editor.h"
#include "app/ui/editor/editor_render.h"
#include "app/ui/rgbmap_algorithm_selector.h"
#include "app/ui/skin/skin_theme.h"
#include "base/bind.h"
#include "base/thread.h"
#include "doc/image.h"
#include "doc/layer.h"
@ -89,6 +89,7 @@ public:
const doc::frame_t frame,
const doc::PixelFormat pixelFormat,
const render::Dithering& dithering,
const doc::RgbMapAlgorithm rgbMapAlgorithm,
const gen::ToGrayAlgorithm toGray,
const gfx::Point& pos,
const bool newBlend)
@ -102,11 +103,13 @@ public:
sprite, frame,
pixelFormat,
dithering,
rgbMapAlgorithm,
toGray,
newBlend]() { // Copy the matrix
run(sprite, frame,
pixelFormat,
dithering,
rgbMapAlgorithm,
toGray,
newBlend);
})
@ -131,6 +134,7 @@ private:
const doc::frame_t frame,
const doc::PixelFormat pixelFormat,
const render::Dithering& dithering,
const doc::RgbMapAlgorithm rgbMapAlgorithm,
const gen::ToGrayAlgorithm toGray,
const bool newBlend) {
doc::ImageRef tmp(
@ -152,7 +156,9 @@ private:
m_image.get(),
pixelFormat,
dithering,
sprite->rgbMap(frame),
sprite->rgbMap(frame,
sprite->rgbMapForSprite(),
rgbMapAlgorithm),
sprite->palette(frame),
(sprite->backgroundLayer() != nullptr),
0,
@ -189,9 +195,11 @@ public:
, m_imageBuffer(new doc::ImageBuffer)
, m_selectedItem(nullptr)
, m_ditheringSelector(nullptr)
, m_mapAlgorithmSelector(nullptr)
, m_imageJustCreated(true)
{
doc::PixelFormat from = m_editor->sprite()->pixelFormat();
const auto& pref = Preferences::instance();
const doc::PixelFormat from = m_editor->sprite()->pixelFormat();
// Add the color mode in the window title
switch (from) {
@ -209,40 +217,63 @@ public:
m_ditheringSelector = new DitheringSelector(DitheringSelector::SelectBoth);
m_ditheringSelector->setExpansive(true);
m_mapAlgorithmSelector = new RgbMapAlgorithmSelector;
m_mapAlgorithmSelector->setExpansive(true);
// Select default dithering method
{
int index = m_ditheringSelector->findItemIndex(
Preferences::instance().quantization.ditheringAlgorithm());
pref.quantization.ditheringAlgorithm());
if (index >= 0)
m_ditheringSelector->setSelectedItemIndex(index);
}
m_ditheringSelector->Change.connect(
base::Bind<void>(&ColorModeWindow::onDithering, this));
ditheringPlaceholder()->addChild(m_ditheringSelector);
// Select default RgbMap algorithm
m_mapAlgorithmSelector->algorithm(pref.experimental.rgbmapAlgorithm());
factor()->Change.connect(base::Bind<void>(&ColorModeWindow::onDithering, this));
ditheringPlaceholder()->addChild(m_ditheringSelector);
rgbmapAlgorithmPlaceholder()->addChild(m_mapAlgorithmSelector);
const bool adv = pref.quantization.advanced();
advancedCheck()->setSelected(adv);
advanced()->setVisible(adv);
// Signals
m_ditheringSelector->Change.connect([this]{ onIndexParamChange(); });
m_mapAlgorithmSelector->Change.connect([this]{ onIndexParamChange(); });
factor()->Change.connect([this]{ onIndexParamChange(); });
advancedCheck()->Click.connect(
[this](ui::Event&){
advanced()->setVisible(advancedCheck()->isSelected());
const gfx::Rect origBounds = bounds();
setBounds(gfx::Rect(bounds().origin(), sizeHint()));
manager()->invalidateRect(origBounds);
});
}
else {
amount()->setVisible(false);
advancedCheck()->setVisible(false);
advanced()->setVisible(false);
}
if (from != IMAGE_GRAYSCALE) {
colorMode()->addChild(new ConversionItem(IMAGE_GRAYSCALE));
toGrayCombobox()->Change.connect(base::Bind<void>(&ColorModeWindow::onToGrayChange, this));
toGrayCombobox()->Change.connect([this]{ onToGrayChange(); });
}
colorModeView()->setMinSize(
colorModeView()->sizeHint() +
colorMode()->sizeHint());
colorMode()->Change.connect(base::Bind<void>(&ColorModeWindow::onChangeColorMode, this));
m_timer.Tick.connect(base::Bind<void>(&ColorModeWindow::onMonitorProgress, this));
colorMode()->Change.connect([this]{ onChangeColorMode(); });
m_timer.Tick.connect([this]{ onMonitorProgress(); });
progress()->setReadOnly(true);
// Default dithering factor
factor()->setValue(Preferences::instance().quantization.ditheringFactor());
factor()->setValue(pref.quantization.ditheringFactor());
// Select first option
colorMode()->selectIndex(0);
@ -267,6 +298,13 @@ public:
return d;
}
doc::RgbMapAlgorithm rgbMapAlgorithm() const {
if (m_mapAlgorithmSelector)
return m_mapAlgorithmSelector->algorithm();
else
return doc::RgbMapAlgorithm::DEFAULT;
}
gen::ToGrayAlgorithm toGray() const {
static_assert(
int(gen::ToGrayAlgorithm::LUMA) == 0 &&
@ -280,20 +318,25 @@ public:
return flatten()->isSelected();
}
// Save the dithering method used for the future
void saveDitheringOptions() {
void saveOptions() {
auto& pref = Preferences::instance();
// Save the dithering method used for the future
if (m_ditheringSelector) {
if (auto item = m_ditheringSelector->getSelectedItem()) {
Preferences::instance().quantization.ditheringAlgorithm(
pref.quantization.ditheringAlgorithm(
item->text());
if (m_ditheringSelector->ditheringAlgorithm() ==
render::DitheringAlgorithm::ErrorDiffusion) {
Preferences::instance().quantization.ditheringFactor(
pref.quantization.ditheringFactor(
factor()->getValue());
}
}
}
if (m_mapAlgorithmSelector)
pref.quantization.advanced(advancedCheck()->isSelected());
}
private:
@ -368,6 +411,7 @@ private:
m_editor->frame(),
dstPixelFormat,
dithering(),
rgbMapAlgorithm(),
toGray(),
visibleBounds.origin(),
Preferences::instance().experimental.newBlend()));
@ -375,7 +419,7 @@ private:
m_timer.start();
}
void onDithering() {
void onIndexParamChange() {
stop();
m_selectedItem = nullptr;
onChangeColorMode();
@ -421,6 +465,7 @@ private:
std::unique_ptr<ConvertThread> m_bgThread;
ConversionItem* m_selectedItem;
DitheringSelector* m_ditheringSelector;
RgbMapAlgorithmSelector* m_mapAlgorithmSelector;
bool m_imageJustCreated;
};
@ -441,6 +486,7 @@ private:
bool m_useUI;
doc::PixelFormat m_format;
render::Dithering m_dithering;
doc::RgbMapAlgorithm m_rgbmap;
gen::ToGrayAlgorithm m_toGray;
};
@ -450,6 +496,7 @@ ChangePixelFormatCommand::ChangePixelFormatCommand()
m_useUI = true;
m_format = IMAGE_RGB;
m_dithering = render::Dithering();
m_rgbmap = doc::RgbMapAlgorithm::DEFAULT;
m_toGray = gen::ToGrayAlgorithm::DEFAULT;
}
@ -497,6 +544,15 @@ void ChangePixelFormatCommand::onLoadParams(const Params& params)
m_dithering.matrix(render::BayerMatrix(8));
}
// TODO change this with NewParams as in ColorQuantizationParams
std::string rgbmap = params.get("rgbmap");
if (rgbmap == "octree")
m_rgbmap = doc::RgbMapAlgorithm::OCTREE;
else if (rgbmap == "rgb5a3")
m_rgbmap = doc::RgbMapAlgorithm::RGB5A3;
else
m_rgbmap = doc::RgbMapAlgorithm::DEFAULT;
std::string toGray = params.get("toGray");
if (toGray == "luma")
m_toGray = gen::ToGrayAlgorithm::LUMA;
@ -570,10 +626,11 @@ void ChangePixelFormatCommand::onExecute(Context* context)
m_format = window.pixelFormat();
m_dithering = window.dithering();
m_rgbmap = window.rgbMapAlgorithm();
m_toGray = window.toGray();
flatten = window.flattenEnabled();
window.saveDitheringOptions();
window.saveOptions();
}
#endif // ENABLE_UI
@ -607,6 +664,7 @@ void ChangePixelFormatCommand::onExecute(Context* context)
new cmd::SetPixelFormat(
sprite, m_format,
m_dithering,
m_rgbmap,
get_gray_func(m_toGray),
&job)); // SpriteJob is a render::TaskDelegate
});

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2019 Igara Studio S.A.
// Copyright (C) 2019-2020 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@ -20,10 +20,12 @@
#include "app/sprite_job.h"
#include "app/transaction.h"
#include "app/ui/color_bar.h"
#include "app/ui/rgbmap_algorithm_selector.h"
#include "app/ui_context.h"
#include "doc/palette.h"
#include "doc/sprite.h"
#include "render/quantization.h"
#include "ui/manager.h"
#include "palette_from_sprite.xml.h"
@ -36,8 +38,53 @@ struct ColorQuantizationParams : public NewParams {
Param<bool> withAlpha { this, true, "withAlpha" };
Param<int> maxColors { this, 256, "maxColors" };
Param<bool> useRange { this, false, "useRange" };
Param<RgbMapAlgorithm> algorithm { this, RgbMapAlgorithm::DEFAULT, "algorithm" };
};
#if ENABLE_UI
class PaletteFromSpriteWindow : public app::gen::PaletteFromSprite {
public:
PaletteFromSpriteWindow() {
rgbmapAlgorithmPlaceholder()->addChild(&m_algoSelector);
advancedCheck()->Click.connect(
[this](ui::Event&){
advanced()->setVisible(advancedCheck()->isSelected());
const gfx::Rect origBounds = bounds();
setBounds(gfx::Rect(bounds().origin(), sizeHint()));
manager()->invalidateRect(origBounds);
});
m_algoSelector.Change.connect(
[this](){
switch (algorithm()) {
case RgbMapAlgorithm::RGB5A3:
alphaChannel()->setEnabled(true);
break;
case RgbMapAlgorithm::OCTREE:
alphaChannel()->setSelected(false);
alphaChannel()->setEnabled(false);
break;
}
});
}
doc::RgbMapAlgorithm algorithm() {
return m_algoSelector.algorithm();
}
void algorithm(const doc::RgbMapAlgorithm mapAlgo) {
m_algoSelector.algorithm(mapAlgo);
}
private:
RgbMapAlgorithmSelector m_algoSelector;
};
#endif
class ColorQuantizationCommand : public CommandWithNewParams<ColorQuantizationParams> {
public:
ColorQuantizationCommand();
@ -65,8 +112,10 @@ void ColorQuantizationCommand::onExecute(Context* ctx)
const bool ui = (params().ui() && ctx->isUIAvailable());
#endif
auto& pref = Preferences::instance();
bool withAlpha = params().withAlpha();
int maxColors = params().maxColors();
RgbMapAlgorithm algorithm = params().algorithm();
bool createPal;
Site site = ctx->activeSite();
@ -74,16 +123,22 @@ void ColorQuantizationCommand::onExecute(Context* ctx)
#ifdef ENABLE_UI
if (ui) {
app::gen::PaletteFromSprite window;
PaletteFromSpriteWindow window;
{
ContextReader reader(ctx);
const Palette* curPalette = site.sprite()->palette(site.frame());
if (!params().algorithm.isSet())
algorithm = pref.experimental.rgbmapAlgorithm();
if (!params().withAlpha.isSet())
withAlpha = App::instance()->preferences().quantization.withAlpha();
withAlpha = pref.quantization.withAlpha();
const bool advanced = pref.quantization.advanced();
window.advancedCheck()->setSelected(advanced);
window.advanced()->setVisible(advanced);
window.algorithm(algorithm);
window.newPalette()->setSelected(true);
window.alphaChannel()->setSelected(withAlpha);
window.ncolors()->setTextf("%d", maxColors);
if (entries.picks() > 1) {
@ -107,7 +162,10 @@ void ColorQuantizationCommand::onExecute(Context* ctx)
maxColors = window.ncolors()->textInt();
withAlpha = window.alphaChannel()->isSelected();
App::instance()->preferences().quantization.withAlpha(withAlpha);
algorithm = window.algorithm();
pref.quantization.withAlpha(withAlpha);
pref.quantization.advanced(window.advancedCheck()->isSelected());
if (window.newPalette()->isSelected()) {
createPal = true;
@ -139,14 +197,15 @@ void ColorQuantizationCommand::onExecute(Context* ctx)
Palette tmpPalette(frame, entries.picks());
SpriteJob job(reader, "Color Quantization");
const bool newBlend = Preferences::instance().experimental.newBlend();
const bool newBlend = pref.experimental.newBlend();
job.startJobWithCallback(
[sprite, withAlpha, &tmpPalette, &job, newBlend]{
[sprite, withAlpha, &tmpPalette, &job, newBlend, algorithm]{
render::create_palette_from_sprite(
sprite, 0, sprite->lastFrame(),
withAlpha, &tmpPalette,
&job,
newBlend); // SpriteJob is a render::TaskDelegate
&job, // SpriteJob is a render::TaskDelegate
newBlend,
algorithm);
});
job.waitJob();
if (job.isCanceled())

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2019 Igara Studio S.A.
// Copyright (C) 2019-2020 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@ -32,7 +32,6 @@
#include "app/ui/optional_alert.h"
#include "app/ui/status_bar.h"
#include "app/ui/timeline/timeline.h"
#include "base/bind.h"
#include "base/clamp.h"
#include "base/convert_to.h"
#include "base/fs.h"
@ -318,11 +317,11 @@ public:
, m_executionID(0)
, m_filenameFormat(params.filenameFormat())
{
sectionTabs()->ItemChange.connect(base::Bind<void>(&ExportSpriteSheetWindow::onChangeSection, this));
expandSections()->Click.connect(base::Bind<void>(&ExportSpriteSheetWindow::onExpandSections, this));
closeSpriteSection()->Click.connect(base::Bind<void>(&ExportSpriteSheetWindow::onCloseSection, this, kSectionSprite));
closeBordersSection()->Click.connect(base::Bind<void>(&ExportSpriteSheetWindow::onCloseSection, this, kSectionBorders));
closeOutputSection()->Click.connect(base::Bind<void>(&ExportSpriteSheetWindow::onCloseSection, this, kSectionOutput));
sectionTabs()->ItemChange.connect([this]{ onChangeSection(); });
expandSections()->Click.connect([this]{ onExpandSections(); });
closeSpriteSection()->Click.connect([this]{ onCloseSection(kSectionSprite); });
closeBordersSection()->Click.connect([this]{ onCloseSection(kSectionBorders); });
closeOutputSection()->Click.connect([this]{ onCloseSection(kSectionOutput); });
static_assert(
(int)app::SpriteSheetType::None == 0 &&
@ -430,32 +429,32 @@ public:
m_dataFilename == kSpecifiedFilename)
m_dataFilename = base + ".json";
exportButton()->Click.connect(base::Bind<void>(&ExportSpriteSheetWindow::onExport, this));
sheetType()->Change.connect(&ExportSpriteSheetWindow::onSheetTypeChange, this);
constraintType()->Change.connect(&ExportSpriteSheetWindow::onConstraintTypeChange, this);
widthConstraint()->Change.connect(&ExportSpriteSheetWindow::generatePreview, this);
heightConstraint()->Change.connect(&ExportSpriteSheetWindow::generatePreview, this);
borderPadding()->Change.connect(base::Bind<void>(&ExportSpriteSheetWindow::generatePreview, this));
shapePadding()->Change.connect(base::Bind<void>(&ExportSpriteSheetWindow::generatePreview, this));
innerPadding()->Change.connect(base::Bind<void>(&ExportSpriteSheetWindow::generatePreview, this));
extrudeEnabled()->Click.connect(base::Bind<void>(&ExportSpriteSheetWindow::generatePreview, this));
mergeDups()->Click.connect(base::Bind<void>(&ExportSpriteSheetWindow::generatePreview, this));
ignoreEmpty()->Click.connect(base::Bind<void>(&ExportSpriteSheetWindow::generatePreview, this));
imageEnabled()->Click.connect(base::Bind<void>(&ExportSpriteSheetWindow::onImageEnabledChange, this));
imageFilename()->Click.connect(base::Bind<void>(&ExportSpriteSheetWindow::onImageFilename, this));
dataEnabled()->Click.connect(base::Bind<void>(&ExportSpriteSheetWindow::onDataEnabledChange, this));
dataFilename()->Click.connect(base::Bind<void>(&ExportSpriteSheetWindow::onDataFilename, this));
trimSpriteEnabled()->Click.connect(base::Bind<void>(&ExportSpriteSheetWindow::onTrimEnabledChange, this));
trimEnabled()->Click.connect(base::Bind<void>(&ExportSpriteSheetWindow::onTrimEnabledChange, this));
gridTrimEnabled()->Click.connect(base::Bind<void>(&ExportSpriteSheetWindow::generatePreview, this));
layers()->Change.connect(base::Bind<void>(&ExportSpriteSheetWindow::generatePreview, this));
splitLayers()->Click.connect(base::Bind<void>(&ExportSpriteSheetWindow::onSplitLayersOrFrames, this));
splitTags()->Click.connect(base::Bind<void>(&ExportSpriteSheetWindow::onSplitLayersOrFrames, this));
frames()->Change.connect(base::Bind<void>(&ExportSpriteSheetWindow::generatePreview, this));
dataFilenameFormat()->Change.connect(base::Bind<void>(&ExportSpriteSheetWindow::onDataFilenameFormatChange, this));
openGenerated()->Click.connect(base::Bind<void>(&ExportSpriteSheetWindow::onOpenGeneratedChange, this));
preview()->Click.connect(base::Bind<void>(&ExportSpriteSheetWindow::generatePreview, this));
m_genTimer.Tick.connect(base::Bind<void>(&ExportSpriteSheetWindow::onGenTimerTick, this));
exportButton()->Click.connect([this]{ onExport(); });
sheetType()->Change.connect([this]{ onSheetTypeChange(); });
constraintType()->Change.connect([this]{ onConstraintTypeChange(); });
widthConstraint()->Change.connect([this]{ generatePreview(); });
heightConstraint()->Change.connect([this]{ generatePreview(); });
borderPadding()->Change.connect([this]{ generatePreview(); });
shapePadding()->Change.connect([this]{ generatePreview(); });
innerPadding()->Change.connect([this]{ generatePreview(); });
extrudeEnabled()->Click.connect([this]{ generatePreview(); });
mergeDups()->Click.connect([this]{ generatePreview(); });
ignoreEmpty()->Click.connect([this]{ generatePreview(); });
imageEnabled()->Click.connect([this]{ onImageEnabledChange(); });
imageFilename()->Click.connect([this]{ onImageFilename(); });
dataEnabled()->Click.connect([this]{ onDataEnabledChange(); });
dataFilename()->Click.connect([this]{ onDataFilename(); });
trimSpriteEnabled()->Click.connect([this]{ onTrimEnabledChange(); });
trimEnabled()->Click.connect([this]{ onTrimEnabledChange(); });
gridTrimEnabled()->Click.connect([this]{ generatePreview(); });
layers()->Change.connect([this]{ generatePreview(); });
splitLayers()->Click.connect([this]{ onSplitLayersOrFrames(); });
splitTags()->Click.connect([this]{ onSplitLayersOrFrames(); });
frames()->Change.connect([this]{ generatePreview(); });
dataFilenameFormat()->Change.connect([this]{ onDataFilenameFormatChange(); });
openGenerated()->Click.connect([this]{ onOpenGeneratedChange(); });
preview()->Click.connect([this]{ generatePreview(); });
m_genTimer.Tick.connect([this]{ onGenTimerTick(); });
// Select tabs
{

View File

@ -0,0 +1,60 @@
// Aseprite
// Copyright (C) 2020 Igara Studio S.A.
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "ui/ui.h"
#include "app/app.h"
#include "app/commands/command.h"
#include "app/commands/commands.h"
#include "app/context.h"
#include "os/display.h"
#include "os/system.h"
namespace app {
class FullscreenModeCommand : public Command {
public:
FullscreenModeCommand();
protected:
void onExecute(Context* context) override;
};
FullscreenModeCommand::FullscreenModeCommand()
: Command(CommandId::FullscreenMode(), CmdUIOnlyFlag)
{
}
// Shows the sprite using the complete screen.
void FullscreenModeCommand::onExecute(Context* ctx)
{
if (!ctx->isUIAvailable())
return;
ui::Manager* manager = ui::Manager::getDefault();
ASSERT(manager);
if (!manager)
return;
os::Display* display = manager->getDisplay();
ASSERT(display);
if (!display)
return;
display->setFullscreen(
!display->isFullscreen());
}
Command* CommandFactory::createFullscreenModeCommand()
{
return new FullscreenModeCommand;
}
} // namespace app

View File

@ -26,7 +26,6 @@
#include "doc/palette.h"
#include "doc/primitives.h"
#include "doc/sprite.h"
#include "os/scoped_handle.h"
#include "os/surface.h"
#include "os/system.h"
@ -53,7 +52,7 @@ public:
, m_proj(editor->projection())
, m_index_bg_color(-1)
, m_doublebuf(Image::create(IMAGE_RGB, ui::display_w(), ui::display_h()))
, m_doublesur(os::instance()->createRgbaSurface(ui::display_w(), ui::display_h())) {
, m_doublesur(os::instance()->makeRgbaSurface(ui::display_w(), ui::display_h())) {
// Do not use DocWriter (do not lock the document) because we
// will call other sub-commands (e.g. previous frame, next frame,
// etc.).
@ -232,8 +231,8 @@ protected:
}
convert_image_to_surface(m_doublebuf.get(), m_pal,
m_doublesur, 0, 0, 0, 0, m_doublebuf->width(), m_doublebuf->height());
g->blit(m_doublesur, 0, 0, 0, 0, m_doublesur->width(), m_doublesur->height());
m_doublesur.get(), 0, 0, 0, 0, m_doublebuf->width(), m_doublebuf->height());
g->blit(m_doublesur.get(), 0, 0, 0, 0, m_doublesur->width(), m_doublesur->height());
}
private:
@ -249,7 +248,7 @@ private:
int m_index_bg_color;
std::unique_ptr<Image> m_render;
std::unique_ptr<Image> m_doublebuf;
os::ScopedHandle<os::Surface> m_doublesur;
os::SurfaceRef m_doublesur;
filters::TiledMode m_tiled;
};

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2019 Igara Studio S.A.
// Copyright (C) 2019-2020 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@ -29,7 +29,6 @@
#include "app/ui/editor/select_box_state.h"
#include "app/ui/editor/standby_state.h"
#include "app/ui/workspace.h"
#include "base/bind.h"
#include "doc/cel.h"
#include "doc/image.h"
#include "doc/layer.h"
@ -69,16 +68,16 @@ public:
sheetType()->addItem("By Columns");
sheetType()->setSelectedItemIndex((int)app::SpriteSheetType::Rows-1);
sheetType()->Change.connect(base::Bind<void>(&ImportSpriteSheetWindow::onSheetTypeChange, this));
x()->Change.connect(base::Bind<void>(&ImportSpriteSheetWindow::onEntriesChange, this));
y()->Change.connect(base::Bind<void>(&ImportSpriteSheetWindow::onEntriesChange, this));
width()->Change.connect(base::Bind<void>(&ImportSpriteSheetWindow::onEntriesChange, this));
height()->Change.connect(base::Bind<void>(&ImportSpriteSheetWindow::onEntriesChange, this));
paddingEnabled()->Click.connect(base::Bind<void>(&ImportSpriteSheetWindow::onPaddingEnabledChange, this));
horizontalPadding()->Change.connect(base::Bind<void>(&ImportSpriteSheetWindow::onEntriesChange, this));
verticalPadding()->Change.connect(base::Bind<void>(&ImportSpriteSheetWindow::onEntriesChange, this));
partialTiles()->Click.connect(base::Bind<void>(&ImportSpriteSheetWindow::onEntriesChange, this));
selectFile()->Click.connect(base::Bind<void>(&ImportSpriteSheetWindow::onSelectFile, this));
sheetType()->Change.connect([this]{ onSheetTypeChange(); });
x()->Change.connect([this]{ onEntriesChange(); });
y()->Change.connect([this]{ onEntriesChange(); });
width()->Change.connect([this]{ onEntriesChange(); });
height()->Change.connect([this]{ onEntriesChange(); });
paddingEnabled()->Click.connect([this]{ onPaddingEnabledChange(); });
horizontalPadding()->Change.connect([this]{ onEntriesChange(); });
verticalPadding()->Change.connect([this]{ onEntriesChange(); });
partialTiles()->Click.connect([this]{ onEntriesChange(); });
selectFile()->Click.connect([this]{ onSelectFile(); });
remapWindow();
centerWindow();

View File

@ -26,7 +26,6 @@
#include "app/ui/select_accelerator.h"
#include "app/ui/separator_in_view.h"
#include "app/ui/skin/skin_theme.h"
#include "base/bind.h"
#include "base/fs.h"
#include "base/scoped_value.h"
#include "base/split_string.h"
@ -376,13 +375,13 @@ private:
m_changeConn = obs::connection();
m_changeButton.reset(new Button(""));
m_changeConn = m_changeButton->Click.connect(base::Bind<void>(&KeyItem::onChangeAccel, this, i));
m_changeConn = m_changeButton->Click.connect([this, i]{ onChangeAccel(i); });
m_changeButton->setStyle(SkinTheme::instance()->styles.miniButton());
addChild(m_changeButton.get());
m_deleteConn = obs::connection();
m_deleteButton.reset(new Button(""));
m_deleteConn = m_deleteButton->Click.connect(base::Bind<void>(&KeyItem::onDeleteAccel, this, i));
m_deleteConn = m_deleteButton->Click.connect([this, i]{ onDeleteAccel(i); });
m_deleteButton->setStyle(SkinTheme::instance()->styles.miniButton());
addChild(m_deleteButton.get());
@ -409,7 +408,7 @@ private:
(!m_menuitem || m_menuitem->getCommand())) {
m_addConn = obs::connection();
m_addButton.reset(new Button(""));
m_addConn = m_addButton->Click.connect(base::Bind<void>(&KeyItem::onAddAccel, this));
m_addConn = m_addButton->Click.connect([this]{ onAddAccel(); });
m_addButton->setStyle(SkinTheme::instance()->styles.miniButton());
addChild(m_addButton.get());
@ -511,14 +510,14 @@ public:
onWheelBehaviorChange();
wheelBehavior()->ItemChange.connect(base::Bind<void>(&KeyboardShortcutsWindow::onWheelBehaviorChange, this));
wheelZoom()->Click.connect(base::Bind<void>(&KeyboardShortcutsWindow::onWheelZoomChange, this));
wheelBehavior()->ItemChange.connect([this]{ onWheelBehaviorChange(); });
wheelZoom()->Click.connect([this]{ onWheelZoomChange(); });
search()->Change.connect(base::Bind<void>(&KeyboardShortcutsWindow::onSearchChange, this));
section()->Change.connect(base::Bind<void>(&KeyboardShortcutsWindow::onSectionChange, this));
importButton()->Click.connect(base::Bind<void>(&KeyboardShortcutsWindow::onImport, this));
exportButton()->Click.connect(base::Bind<void>(&KeyboardShortcutsWindow::onExport, this));
resetButton()->Click.connect(base::Bind<void>(&KeyboardShortcutsWindow::onReset, this));
search()->Change.connect([this]{ onSearchChange(); });
section()->Change.connect([this]{ onSectionChange(); });
importButton()->Click.connect([this]{ onImport(); });
exportButton()->Click.connect([this]{ onExport(); });
resetButton()->Click.connect([this]{ onReset(); });
fillAllLists();

View File

@ -1,4 +1,5 @@
// Aseprite
// Copyright (C) 2020 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@ -24,7 +25,6 @@
#include "app/ui/timeline/timeline.h"
#include "app/ui/user_data_popup.h"
#include "app/ui_context.h"
#include "base/bind.h"
#include "base/scoped_value.h"
#include "doc/image.h"
#include "doc/layer.h"
@ -99,11 +99,11 @@ public:
mode()->addItem(new BlendModeItem("Color", doc::BlendMode::HSL_COLOR));
mode()->addItem(new BlendModeItem("Luminosity", doc::BlendMode::HSL_LUMINOSITY));
name()->Change.connect(base::Bind<void>(&LayerPropertiesWindow::onStartTimer, this));
mode()->Change.connect(base::Bind<void>(&LayerPropertiesWindow::onStartTimer, this));
opacity()->Change.connect(base::Bind<void>(&LayerPropertiesWindow::onStartTimer, this));
m_timer.Tick.connect(base::Bind<void>(&LayerPropertiesWindow::onCommitChange, this));
userData()->Click.connect(base::Bind<void>(&LayerPropertiesWindow::onPopupUserData, this));
name()->Change.connect([this]{ onStartTimer(); });
mode()->Change.connect([this]{ onStartTimer(); });
opacity()->Change.connect([this]{ onStartTimer(); });
m_timer.Tick.connect([this]{ onCommitChange(); });
userData()->Click.connect([this]{ onPopupUserData(); });
remapWindow();
centerWindow();

View File

@ -24,7 +24,6 @@
#include "app/ui/color_bar.h"
#include "app/ui/color_button.h"
#include "app/ui/selection_mode_field.h"
#include "base/bind.h"
#include "base/chrono.h"
#include "base/convert_to.h"
#include "base/scoped_value.h"
@ -136,13 +135,13 @@ void MaskByColorCommand::onExecute(Context* context)
if (get_config_bool("MaskColor", "Preview", true))
m_checkPreview->setSelected(true);
button_ok->Click.connect(base::Bind<void>(&Window::closeWindow, m_window, button_ok));
button_cancel->Click.connect(base::Bind<void>(&Window::closeWindow, m_window, button_cancel));
button_ok->Click.connect([this, button_ok]{ m_window->closeWindow(button_ok); });
button_cancel->Click.connect([this, button_cancel]{ m_window->closeWindow(button_cancel); });
m_buttonColor->Change.connect(base::Bind<void>(&MaskByColorCommand::maskPreview, this, base::Ref(reader)));
m_sliderTolerance->Change.connect(base::Bind<void>(&MaskByColorCommand::maskPreview, this, base::Ref(reader)));
m_checkPreview->Click.connect(base::Bind<void>(&MaskByColorCommand::maskPreview, this, base::Ref(reader)));
m_selMode->ModeChange.connect(base::Bind<void>(&MaskByColorCommand::maskPreview, this, base::Ref(reader)));
m_buttonColor->Change.connect([&]{ maskPreview(reader); });
m_sliderTolerance->Change.connect([&]{ maskPreview(reader); });
m_checkPreview->Click.connect([&]{ maskPreview(reader); });
m_selMode->ModeChange.connect([&]{ maskPreview(reader); });
button_ok->setFocusMagnet(true);
m_buttonColor->setExpansive(true);

View File

@ -27,7 +27,6 @@
#include "app/util/clipboard.h"
#include "app/util/clipboard.h"
#include "app/util/pixel_ratio.h"
#include "base/bind.h"
#include "base/clamp.h"
#include "doc/cel.h"
#include "doc/image.h"
@ -155,16 +154,15 @@ void NewFileCommand::onExecute(Context* ctx)
bool advanced = pref.newFile.advanced();
window.advancedCheck()->setSelected(advanced);
window.advancedCheck()->Click.connect(
base::Bind<void>(
[&]{
gfx::Rect bounds = window.bounds();
window.advanced()->setVisible(window.advancedCheck()->isSelected());
window.setBounds(gfx::Rect(window.bounds().origin(),
window.sizeHint()));
window.layout();
[&]{
gfx::Rect bounds = window.bounds();
window.advanced()->setVisible(window.advancedCheck()->isSelected());
window.setBounds(gfx::Rect(window.bounds().origin(),
window.sizeHint()));
window.layout();
window.manager()->invalidateRect(bounds);
}));
window.manager()->invalidateRect(bounds);
});
window.advanced()->setVisible(advanced);
if (advanced)
window.pixelRatio()->setValue(pref.newFile.pixelRatio());
@ -275,7 +273,8 @@ void NewFileCommand::onExecute(Context* ctx)
if (clipboardPalette.isBlack()) {
render::create_palette_from_sprite(
sprite.get(), 0, sprite->lastFrame(), true,
&clipboardPalette, nullptr, true);
&clipboardPalette, nullptr, true,
Preferences::instance().experimental.rgbmapAlgorithm());
}
sprite->setPalette(&clipboardPalette, false);
}

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2019 Igara Studio S.A.
// Copyright (C) 2019-2020 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@ -25,7 +25,6 @@
#include "app/recent_files.h"
#include "app/ui/status_bar.h"
#include "app/ui_context.h"
#include "base/bind.h"
#include "base/fs.h"
#include "base/thread.h"
#include "doc/sprite.h"

View File

@ -28,9 +28,9 @@
#include "app/tx.h"
#include "app/ui/color_button.h"
#include "app/ui/pref_widget.h"
#include "app/ui/rgbmap_algorithm_selector.h"
#include "app/ui/separator_in_view.h"
#include "app/ui/skin/skin_theme.h"
#include "base/bind.h"
#include "base/clamp.h"
#include "base/convert_to.h"
#include "base/fs.h"
@ -99,13 +99,13 @@ class OptionsWindow : public app::gen::Options {
class ColorSpaceItem : public ListItem {
public:
ColorSpaceItem(const os::ColorSpacePtr& cs)
ColorSpaceItem(const os::ColorSpaceRef& cs)
: ListItem(cs->gfxColorSpace()->name()),
m_cs(cs) {
}
os::ColorSpacePtr cs() const { return m_cs; }
os::ColorSpaceRef cs() const { return m_cs; }
private:
os::ColorSpacePtr m_cs;
os::ColorSpaceRef m_cs;
};
class ThemeItem : public ListItem {
@ -202,7 +202,7 @@ public:
, m_restoreScreenScaling(m_pref.general.screenScale())
, m_restoreUIScaling(m_pref.general.uiScale())
{
sectionListbox()->Change.connect(base::Bind<void>(&OptionsWindow::onChangeSection, this));
sectionListbox()->Change.connect([this]{ onChangeSection(); });
// Default extension to save files
fillExtensionsCombobox(defaultExtension(), m_pref.saveFile.defaultExtension());
@ -212,15 +212,15 @@ public:
// Number of recent items
recentFiles()->setValue(m_pref.general.recentItems());
clearRecentFiles()->Click.connect(base::Bind<void>(&OptionsWindow::onClearRecentFiles, this));
clearRecentFiles()->Click.connect([this]{ onClearRecentFiles(); });
// Template item for active display color profiles
m_templateTextForDisplayCS = windowCs()->getItem(2)->text();
windowCs()->deleteItem(2);
// Color profiles
resetColorManagement()->Click.connect(base::Bind<void>(&OptionsWindow::onResetColorManagement, this));
colorManagement()->Click.connect(base::Bind<void>(&OptionsWindow::onColorManagement, this));
resetColorManagement()->Click.connect([this]{ onResetColorManagement(); });
colorManagement()->Click.connect([this]{ onColorManagement(); });
{
os::instance()->listColorSpaces(m_colorSpaces);
for (auto& cs : m_colorSpaces) {
@ -236,7 +236,7 @@ public:
}
// Alerts
resetAlerts()->Click.connect(base::Bind<void>(&OptionsWindow::onResetAlerts, this));
resetAlerts()->Click.connect([this]{ onResetAlerts(); });
// Cursor
paintingCursorType()->setSelectedItemIndex(int(m_pref.cursor.paintingCursorType()));
@ -250,7 +250,7 @@ public:
cursorColorType()->setSelectedItemIndex(1);
cursorColor()->setVisible(true);
}
cursorColorType()->Change.connect(base::Bind<void>(&OptionsWindow::onCursorColorType, this));
cursorColorType()->Change.connect([this]{ onCursorColorType(); });
// Brush preview
brushPreview()->setSelectedItemIndex(
@ -334,7 +334,7 @@ public:
int(os::Capabilities::CustomNativeMouseCursor)) != 0) {
if (m_pref.cursor.useNativeCursor())
nativeCursor()->setSelected(true);
nativeCursor()->Click.connect(base::Bind<void>(&OptionsWindow::onNativeCursorChange, this));
nativeCursor()->Click.connect([this]{ onNativeCursorChange(); });
cursorScale()->setSelectedItemIndex(
cursorScale()->findItemIndexByValue(
@ -400,6 +400,10 @@ public:
nonactiveLayersOpacity()->setValue(m_pref.experimental.nonactiveLayersOpacity());
rgbmapAlgorithmPlaceholder()->addChild(&m_rgbmapAlgorithmSelector);
m_rgbmapAlgorithmSelector.setExpansive(true);
m_rgbmapAlgorithmSelector.algorithm(m_pref.experimental.rgbmapAlgorithm());
if (m_pref.editor.showScrollbars())
showScrollbars()->setSelected(true);
@ -418,11 +422,11 @@ public:
if (context->activeDocument()) {
bgScope()->addItem("Background for the Active Document");
bgScope()->setSelectedItemIndex(1);
bgScope()->Change.connect(base::Bind<void>(&OptionsWindow::onChangeBgScope, this));
bgScope()->Change.connect([this]{ onChangeBgScope(); });
gridScope()->addItem("Grid for the Active Document");
gridScope()->setSelectedItemIndex(1);
gridScope()->Change.connect(base::Bind<void>(&OptionsWindow::onChangeGridScope, this));
gridScope()->Change.connect([this]{ onChangeGridScope(); });
}
selectScalingItems();
@ -477,21 +481,21 @@ public:
checkedBgSize()->addItem("2x2");
checkedBgSize()->addItem("1x1");
checkedBgSize()->addItem("Custom");
checkedBgSize()->Change.connect(base::Bind<void>(&OptionsWindow::onCheckedBgSizeChange, this));
checkedBgSize()->Change.connect([this]{ onCheckedBgSizeChange(); });
// Reset buttons
resetBg()->Click.connect(base::Bind<void>(&OptionsWindow::onResetBg, this));
resetGrid()->Click.connect(base::Bind<void>(&OptionsWindow::onResetGrid, this));
resetBg()->Click.connect([this]{ onResetBg(); });
resetGrid()->Click.connect([this]{ onResetGrid(); });
// Links
locateFile()->Click.connect(base::Bind<void>(&OptionsWindow::onLocateConfigFile, this));
locateFile()->Click.connect([this]{ onLocateConfigFile(); });
if (!App::instance()->memoryDumpFilename().empty())
locateCrashFolder()->Click.connect(base::Bind<void>(&OptionsWindow::onLocateCrashFolder, this));
locateCrashFolder()->Click.connect([this]{ onLocateCrashFolder(); });
else
locateCrashFolder()->setVisible(false);
// Undo preferences
limitUndo()->Click.connect(base::Bind<void>(&OptionsWindow::onLimitUndoCheck, this));
limitUndo()->Click.connect([this]{ onLimitUndoCheck(); });
limitUndo()->setSelected(m_pref.undo.sizeLimit() != 0);
onLimitUndoCheck();
@ -499,20 +503,20 @@ public:
undoAllowNonlinearHistory()->setSelected(m_pref.undo.allowNonlinearHistory());
// Theme buttons
themeList()->Change.connect(base::Bind<void>(&OptionsWindow::onThemeChange, this));
themeList()->DoubleClickItem.connect(base::Bind<void>(&OptionsWindow::onSelectTheme, this));
selectTheme()->Click.connect(base::Bind<void>(&OptionsWindow::onSelectTheme, this));
openThemeFolder()->Click.connect(base::Bind<void>(&OptionsWindow::onOpenThemeFolder, this));
themeList()->Change.connect([this]{ onThemeChange(); });
themeList()->DoubleClickItem.connect([this]{ onSelectTheme(); });
selectTheme()->Click.connect([this]{ onSelectTheme(); });
openThemeFolder()->Click.connect([this]{ onOpenThemeFolder(); });
// Extensions buttons
extensionsList()->Change.connect(base::Bind<void>(&OptionsWindow::onExtensionChange, this));
addExtension()->Click.connect(base::Bind<void>(&OptionsWindow::onAddExtension, this));
disableExtension()->Click.connect(base::Bind<void>(&OptionsWindow::onDisableExtension, this));
uninstallExtension()->Click.connect(base::Bind<void>(&OptionsWindow::onUninstallExtension, this));
openExtensionFolder()->Click.connect(base::Bind<void>(&OptionsWindow::onOpenExtensionFolder, this));
extensionsList()->Change.connect([this]{ onExtensionChange(); });
addExtension()->Click.connect([this]{ onAddExtension(); });
disableExtension()->Click.connect([this]{ onDisableExtension(); });
uninstallExtension()->Click.connect([this]{ onUninstallExtension(); });
openExtensionFolder()->Click.connect([this]{ onOpenExtensionFolder(); });
// Apply button
buttonApply()->Click.connect(base::Bind<void>(&OptionsWindow::onApply, this));
buttonApply()->Click.connect([this]{ onApply(); });
onChangeBgScope();
onChangeGridScope();
@ -521,12 +525,12 @@ public:
// Refill languages combobox when extensions are enabled/disabled
m_extLanguagesChanges =
App::instance()->extensions().LanguagesChange.connect(
base::Bind<void>(&OptionsWindow::refillLanguages, this));
[this]{ refillLanguages(); });
// Reload themes when extensions are enabled/disabled
m_extThemesChanges =
App::instance()->extensions().ThemesChange.connect(
base::Bind<void>(&OptionsWindow::reloadThemes, this));
[this]{ reloadThemes(); });
}
bool ok() {
@ -695,6 +699,7 @@ public:
m_pref.experimental.useNativeFileDialog(nativeFileDialog()->isSelected());
m_pref.experimental.flashLayer(flashLayer()->isSelected());
m_pref.experimental.nonactiveLayersOpacity(nonactiveLayersOpacity()->getValue());
m_pref.experimental.rgbmapAlgorithm(m_rgbmapAlgorithmSelector.algorithm());
#ifdef _WIN32
{
@ -1604,8 +1609,9 @@ private:
std::string m_restoreThisTheme;
int m_restoreScreenScaling;
int m_restoreUIScaling;
std::vector<os::ColorSpacePtr> m_colorSpaces;
std::vector<os::ColorSpaceRef> m_colorSpaces;
std::string m_templateTextForDisplayCS;
RgbMapAlgorithmSelector m_rgbmapAlgorithmSelector;
};
class OptionsCommand : public Command {

View File

@ -21,7 +21,6 @@
#include "app/ui/font_popup.h"
#include "app/ui/timeline/timeline.h"
#include "app/util/freetype_utils.h"
#include "base/bind.h"
#include "base/clamp.h"
#include "base/fs.h"
#include "base/string.h"
@ -69,8 +68,8 @@ public:
updateFontFaceButton();
fontSize()->setTextf("%d", size);
fontFace()->Click.connect(base::Bind<void>(&PasteTextWindow::onSelectFontFile, this));
fontFace()->DropDownClick.connect(base::Bind<void>(&PasteTextWindow::onSelectSystemFont, this));
fontFace()->Click.connect([this]{ onSelectFontFile(); });
fontFace()->DropDownClick.connect([this]{ onSelectSystemFont(); });
fontColor()->setColor(color);
this->antialias()->setSelected(antialias);
}
@ -116,7 +115,7 @@ private:
try {
m_fontPopup.reset(new FontPopup());
m_fontPopup->Load.connect(&PasteTextWindow::setFontFace, this);
m_fontPopup->Close.connect(base::Bind<void>(&PasteTextWindow::onCloseFontPopup, this));
m_fontPopup->Close.connect([this]{ onCloseFontPopup(); });
}
catch (const std::exception& ex) {
Console::showException(ex);

View File

@ -33,7 +33,6 @@
#include "app/ui/layer_frame_comboboxes.h"
#include "app/ui/optional_alert.h"
#include "app/ui/status_bar.h"
#include "base/bind.h"
#include "base/convert_to.h"
#include "base/fs.h"
#include "base/scoped_value.h"

View File

@ -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
@ -22,7 +22,6 @@
#include "app/tx.h"
#include "app/ui/color_button.h"
#include "app/util/pixel_ratio.h"
#include "base/bind.h"
#include "base/mem_utils.h"
#include "doc/image.h"
#include "doc/palette.h"
@ -64,7 +63,7 @@ void SpritePropertiesCommand::onExecute(Context* context)
ColorButton* color_button = nullptr;
// List of available color profiles
std::vector<os::ColorSpacePtr> colorSpaces;
std::vector<os::ColorSpaceRef> colorSpaces;
os::instance()->listColorSpaces(colorSpaces);
// Load the window widget
@ -153,7 +152,7 @@ void SpritePropertiesCommand::onExecute(Context* context)
++i;
}
if (selectedColorProfile < 0) {
colorSpaces.push_back(os::instance()->createColorSpace(sprite->colorSpace()));
colorSpaces.push_back(os::instance()->makeColorSpace(sprite->colorSpace()));
selectedColorProfile = colorSpaces.size()-1;
}

View File

@ -20,7 +20,6 @@
#include "app/modules/palettes.h"
#include "app/sprite_job.h"
#include "app/util/resize_image.h"
#include "base/bind.h"
#include "base/clamp.h"
#include "base/convert_to.h"
#include "doc/algorithm/resize_image.h"
@ -184,11 +183,11 @@ protected:
class SpriteSizeWindow : public app::gen::SpriteSize {
public:
SpriteSizeWindow(Context* ctx, const SpriteSizeParams& params) : m_ctx(ctx) {
lockRatio()->Click.connect(base::Bind<void>(&SpriteSizeWindow::onLockRatioClick, this));
widthPx()->Change.connect(base::Bind<void>(&SpriteSizeWindow::onWidthPxChange, this));
heightPx()->Change.connect(base::Bind<void>(&SpriteSizeWindow::onHeightPxChange, this));
widthPerc()->Change.connect(base::Bind<void>(&SpriteSizeWindow::onWidthPercChange, this));
heightPerc()->Change.connect(base::Bind<void>(&SpriteSizeWindow::onHeightPercChange, this));
lockRatio()->Click.connect([this]{ onLockRatioClick(); });
widthPx()->Change.connect([this]{ onWidthPxChange(); });
heightPx()->Change.connect([this]{ onHeightPxChange(); });
widthPerc()->Change.connect([this]{ onWidthPercChange(); });
heightPerc()->Change.connect([this]{ onHeightPercChange(); });
widthPx()->setTextf("%d", params.width());
heightPx()->setTextf("%d", params.height());

View File

@ -1,4 +1,5 @@
// Aseprite
// Copyright (C) 2020 Igara Studio S.A.
// Copyright (C) 2015-2018 David Capello
//
// This program is distributed under the terms of
@ -21,7 +22,6 @@
#include "app/modules/gui.h"
#include "app/modules/palettes.h"
#include "app/site.h"
#include "base/bind.h"
#include "base/mem_utils.h"
#include "ui/listitem.h"
#include "ui/message.h"

View File

@ -72,6 +72,7 @@ FOR_EACH_COMMAND(Fill)
FOR_EACH_COMMAND(FitScreen)
FOR_EACH_COMMAND(FrameProperties)
FOR_EACH_COMMAND(FrameTagProperties)
FOR_EACH_COMMAND(FullscreenMode)
FOR_EACH_COMMAND(FullscreenPreview)
FOR_EACH_COMMAND(GotoFirstFrame)
FOR_EACH_COMMAND(GotoFirstFrameInTag)

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2019 Igara Studio S.A.
// Copyright (C) 2019-2020 Igara Studio S.A.
// Copyright (C) 2017 David Capello
//
// This program is distributed under the terms of
@ -21,7 +21,6 @@
#include "app/ui/color_button.h"
#include "app/ui/color_sliders.h"
#include "app/ui/slider2.h"
#include "base/bind.h"
#include "doc/image.h"
#include "doc/mask.h"
#include "doc/sprite.h"
@ -59,8 +58,8 @@ public:
getContainer()->addChild(&m_brightness);
getContainer()->addChild(new ui::Label("Contrast:"));
getContainer()->addChild(&m_contrast);
m_brightness.Change.connect(base::Bind<void>(&BrightnessContrastWindow::onChange, this));
m_contrast.Change.connect(base::Bind<void>(&BrightnessContrastWindow::onChange, this));
m_brightness.Change.connect([this]{ onChange(); });
m_contrast.Change.connect([this]{ onChange(); });
}
private:

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2019 Igara Studio S.A.
// Copyright (C) 2019-2020 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@ -23,7 +23,6 @@
#include "app/ini_file.h"
#include "app/load_widget.h"
#include "app/pref/preferences.h"
#include "base/bind.h"
#include "doc/mask.h"
#include "doc/sprite.h"
#include "filters/convolution_matrix.h"
@ -58,7 +57,9 @@ static const char* ConfigSection = "ConvolutionMatrix";
class ConvolutionMatrixWindow : public FilterWindow {
public:
ConvolutionMatrixWindow(ConvolutionMatrixFilter& filter, FilterManagerImpl& filterMgr, ConvolutionMatrixStock& stock)
ConvolutionMatrixWindow(ConvolutionMatrixFilter& filter,
FilterManagerImpl& filterMgr,
ConvolutionMatrixStock& stock)
: FilterWindow("Convolution Matrix", ConfigSection, &filterMgr,
WithChannelsSelector,
WithTiledCheckBox,
@ -72,14 +73,14 @@ public:
{
getContainer()->addChild(m_controlsWidget.get());
m_reloadButton->Click.connect(&ConvolutionMatrixWindow::onReloadStock, this);
m_stockListBox->Change.connect(base::Bind<void>(&ConvolutionMatrixWindow::onMatrixChange, this));
m_reloadButton->Click.connect([this]{ onReloadStock(); });
m_stockListBox->Change.connect([this]{ onMatrixChange(); });
fillStockListBox();
}
private:
void onReloadStock(Event& ev) {
void onReloadStock() {
m_stock.reloadStock();
fillStockListBox();
}

View File

@ -22,7 +22,6 @@
#include "app/ui/button_set.h"
#include "app/ui/color_button.h"
#include "app/ui/color_sliders.h"
#include "base/bind.h"
#include "doc/image.h"
#include "doc/mask.h"
#include "doc/sprite.h"
@ -75,11 +74,11 @@ public:
m_colorType.addItem("HSV+")->setFocusStop(false);
m_colorType.addItem("HSL+")->setFocusStop(false);
m_colorType.setSelectedItem(int(mode));
m_colorType.ItemChange.connect(base::Bind<void>(&HueSaturationWindow::onChangeMode, this));
m_colorType.ItemChange.connect([this]{ onChangeMode(); });
m_sliders.setColorType(app::Color::HslType);
m_sliders.setMode(ColorSliders::Mode::Relative);
m_sliders.ColorChange.connect(base::Bind<void>(&HueSaturationWindow::onChangeControls, this));
m_sliders.ColorChange.connect([this]{ onChangeControls(); });
onChangeMode();
}

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2019 Igara Studio S.A.
// Copyright (C) 2019-2020 Igara Studio S.A.
// Copyright (C) 2001-2017 David Capello
//
// This program is distributed under the terms of
@ -19,7 +19,6 @@
#include "app/ini_file.h"
#include "app/modules/gui.h"
#include "app/ui/color_button.h"
#include "base/bind.h"
#include "doc/image.h"
#include "doc/mask.h"
#include "doc/sprite.h"

View File

@ -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.
@ -22,7 +22,6 @@
#include "app/ui/color_bar.h"
#include "app/ui/color_button.h"
#include "app/ui/skin/skin_theme.h"
#include "base/bind.h"
#include "doc/image.h"
#include "doc/mask.h"
#include "doc/sprite.h"

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2019 Igara Studio S.A.
// Copyright (C) 2019-2020 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@ -30,7 +30,6 @@
#include "app/ui/color_button.h"
#include "app/ui/keyboard_shortcuts.h"
#include "app/ui_context.h"
#include "base/bind.h"
#include "doc/image.h"
#include "doc/mask.h"
#include "doc/sprite.h"

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2019 Igara Studio S.A.
// Copyright (C) 2019-2020 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@ -15,7 +15,6 @@
#include "app/modules/gfx.h"
#include "app/modules/gui.h"
#include "app/ui/skin/skin_theme.h"
#include "base/bind.h"
#include "doc/image.h"
#include "ui/box.h"
#include "ui/button.h"

View File

@ -1,4 +1,5 @@
// Aseprite
// Copyright (C) 2020 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@ -17,7 +18,6 @@
#include "app/modules/gui.h"
#include "app/pref/preferences.h"
#include "app/ui/editor/editor.h"
#include "base/bind.h"
namespace app {
@ -73,7 +73,7 @@ FilterWindow::FilterWindow(const char* title, const char* cfgSection,
if (m_tiledCheck) {
m_tiledCheck->setSelected(tiledMode != TiledMode::NONE);
m_tiledCheck->Click.connect(base::Bind<void>(&FilterWindow::onTiledChange, this));
m_tiledCheck->Click.connect([this]{ onTiledChange(); });
m_vbox.addChild(m_tiledCheck);
}

View File

@ -19,6 +19,7 @@
#include "base/string.h"
#include "doc/algorithm/resize_image.h"
#include "doc/color_mode.h"
#include "doc/rgbmap_algorithm.h"
#include "filters/color_curve.h"
#include "filters/hue_saturation_filter.h"
#include "filters/outline_filter.h"
@ -192,6 +193,17 @@ void Param<tools::InkType>::fromString(const std::string& value)
setValue(tools::string_id_to_ink_type(value));
}
template<>
void Param<doc::RgbMapAlgorithm>::fromString(const std::string& value)
{
if (base::utf8_icmp(value, "octree") == 0)
setValue(doc::RgbMapAlgorithm::OCTREE);
else if (base::utf8_icmp(value, "rgb5a3") == 0)
setValue(doc::RgbMapAlgorithm::RGB5A3);
else
setValue(doc::RgbMapAlgorithm::DEFAULT);
}
//////////////////////////////////////////////////////////////////////
// Convert values from Lua
//////////////////////////////////////////////////////////////////////
@ -326,6 +338,15 @@ void Param<tools::InkType>::fromLua(lua_State* L, int index)
script::get_value_from_lua<tools::InkType>(L, index);
}
template<>
void Param<doc::RgbMapAlgorithm>::fromLua(lua_State* L, int index)
{
if (lua_type(L, index) == LUA_TSTRING)
fromString(lua_tostring(L, index));
else
setValue((doc::RgbMapAlgorithm)lua_tointeger(L, index));
}
void CommandWithNewParamsBase::loadParamsFromLuaTable(lua_State* L, int index)
{
onResetValues();

View File

@ -70,7 +70,7 @@ void ScreenshotCommand::onExecute(Context* ctx)
rf.includeDesktopDir("");
os::Display* display = ui::Manager::getDefault()->getDisplay();
os::Surface* surface = display->getSurface();
os::Surface* surface = display->surface();
std::string fn;
if (params().save()) {

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2018-2019 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
@ -13,7 +13,6 @@
#include <cstdio>
#include <vector>
#include "base/bind.h"
#include "base/memory.h"
#include "base/string.h"
#include "ui/ui.h"
@ -34,14 +33,13 @@ public:
ConsoleWindow() : Window(Window::WithTitleBar, "Console"),
m_textbox("", WORDWRAP),
m_button("Cancel") {
m_button.Click.connect(base::Bind<void>(&ConsoleWindow::closeWindow, this, &m_button));
m_button.Click.connect([this]{ closeWindow(&m_button); });
// When the window is closed, we clear the text
Close.connect(
base::Bind<void>(
[this] {
m_textbox.setText(std::string());
}));
[this]{
m_textbox.setText(std::string());
});
m_view.attachToView(&m_textbox);
m_button.setMinSize(gfx::Size(60*ui::guiscale(), 0));

View File

@ -26,7 +26,6 @@
#include "app/doc_access.h"
#include "app/doc_diff.h"
#include "app/pref/preferences.h"
#include "base/bind.h"
#include "base/chrono.h"
#include "base/remove_from_container.h"
#include "ui/system.h"
@ -63,7 +62,7 @@ BackupObserver::BackupObserver(RecoveryConfig* config,
, m_session(session)
, m_ctx(ctx)
, m_done(false)
, m_thread(base::Bind<void>(&BackupObserver::backgroundThread, this))
, m_thread([this]{ backgroundThread(); })
{
m_ctx->add_observer(this);
m_ctx->documents().add_observer(this);

View File

@ -368,7 +368,7 @@ private:
// Read color space
if (!s.eof()) {
gfx::ColorSpacePtr colorSpace = readColorSpace(s);
gfx::ColorSpaceRef colorSpace = readColorSpace(s);
if (colorSpace)
spr->setColorSpace(colorSpace);
}
@ -383,7 +383,7 @@ private:
return spr.release();
}
gfx::ColorSpacePtr readColorSpace(std::ifstream& s) {
gfx::ColorSpaceRef readColorSpace(std::ifstream& s) {
const gfx::ColorSpace::Type type = (gfx::ColorSpace::Type)read16(s);
const gfx::ColorSpace::Flag flags = (gfx::ColorSpace::Flag)read16(s);
const double gamma = fixmath::fixtof(read32(s));
@ -399,7 +399,7 @@ private:
s.read((char*)&buf[0], n);
std::string name = read_string(s);
auto colorSpace = std::make_shared<gfx::ColorSpace>(
auto colorSpace = base::make_ref<gfx::ColorSpace>(
type, flags, gamma, std::move(buf));
colorSpace->setName(name);
return colorSpace;

View File

@ -20,7 +20,6 @@
#include "app/doc_access.h"
#include "app/file/file.h"
#include "app/ui_context.h"
#include "base/bind.h"
#include "base/convert_to.h"
#include "base/fs.h"
#include "base/fstream_path.h"

View File

@ -205,7 +205,7 @@ private:
return true;
}
bool writeColorSpace(std::ofstream& s, const gfx::ColorSpacePtr& colorSpace) {
bool writeColorSpace(std::ofstream& s, const gfx::ColorSpaceRef& colorSpace) {
write16(s, colorSpace->type());
write16(s, colorSpace->flags());
write32(s, fixmath::ftofix(colorSpace->gamma()));

View File

@ -602,7 +602,7 @@ void Doc::updateOSColorSpace(bool appWideSignal)
{
auto system = os::instance();
if (system) {
m_osColorSpace = system->createColorSpace(sprite()->colorSpace());
m_osColorSpace = system->makeColorSpace(sprite()->colorSpace());
if (!m_osColorSpace && system->defaultDisplay())
m_osColorSpace = system->defaultDisplay()->colorSpace();
}

View File

@ -100,7 +100,7 @@ namespace app {
color_t bgColor() const;
color_t bgColor(Layer* layer) const;
os::ColorSpacePtr osColorSpace() const { return m_osColorSpace; }
os::ColorSpaceRef osColorSpace() const { return m_osColorSpace; }
//////////////////////////////////////////////////////////////////////
// Notifications
@ -261,7 +261,7 @@ namespace app {
gfx::Point m_lastDrawingPoint;
// Last used color space to render a sprite.
os::ColorSpacePtr m_osColorSpace;
os::ColorSpaceRef m_osColorSpace;
DISABLE_COPYING(Doc);
};

View File

@ -1094,7 +1094,7 @@ Doc* DocExporter::createEmptyTexture(const Samples& samples,
ColorMode colorMode = ColorMode::INDEXED;
Palette* palette = nullptr;
int maxColors = 256;
gfx::ColorSpacePtr colorSpace;
gfx::ColorSpaceRef colorSpace;
color_t transparentColor = 0;
for (const auto& sample : samples) {
@ -1184,6 +1184,7 @@ void DocExporter::renderTexture(Context* ctx,
sample.sprite(),
textureImage->pixelFormat(),
render::Dithering(),
Sprite::DefaultRgbMapAlgorithm(), // TODO add rgbmap algorithm preference
nullptr, // toGray is not needed because the texture is Indexed or RGB
nullptr) // TODO add a delegate to show progress
.execute(ctx);

View File

@ -995,7 +995,7 @@ static void ase_file_write_color_profile(FILE* f,
dio::AsepriteFrameHeader* frame_header,
const doc::Sprite* sprite)
{
const gfx::ColorSpacePtr& cs = sprite->colorSpace();
const gfx::ColorSpaceRef& cs = sprite->colorSpace();
if (!cs) // No color
return;

View File

@ -906,7 +906,8 @@ void FileOp::postLoad()
std::shared_ptr<Palette> palette(
render::create_palette_from_sprite(
sprite, frame_t(0), sprite->lastFrame(), true,
nullptr, nullptr, m_config.newBlend));
nullptr, nullptr, m_config.newBlend,
m_config.rgbMapAlgorithm));
sprite->resetPalettes();
sprite->setPalette(palette.get(), false);
@ -914,7 +915,7 @@ void FileOp::postLoad()
}
// What to do with the sprite color profile?
gfx::ColorSpacePtr spriteCS = sprite->colorSpace();
gfx::ColorSpaceRef spriteCS = sprite->colorSpace();
app::gen::ColorProfileBehavior behavior =
app::gen::ColorProfileBehavior::DISABLE;

View File

@ -22,6 +22,7 @@ void FileOpConfig::fillFromPreferences()
newBlend = Preferences::instance().experimental.newBlend();
defaultSliceColor = Preferences::instance().slices.defaultColor();
workingCS = get_working_rgb_space_from_preferences();
rgbMapAlgorithm = Preferences::instance().experimental.rgbmapAlgorithm();
}
} // namespace app

View File

@ -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.
@ -10,6 +10,7 @@
#include "app/color.h"
#include "app/pref/preferences.h"
#include "doc/rgbmap_algorithm.h"
#include "gfx/color_space.h"
namespace app {
@ -24,7 +25,7 @@ namespace app {
// profile or without a color profile.
app::gen::ColorProfileBehavior filesWithProfile = app::gen::ColorProfileBehavior::EMBEDDED;
app::gen::ColorProfileBehavior missingProfile = app::gen::ColorProfileBehavior::ASSIGN;
gfx::ColorSpacePtr workingCS = gfx::ColorSpace::MakeSRGB();
gfx::ColorSpaceRef workingCS = gfx::ColorSpace::MakeSRGB();
// True if we should render each frame to save it with the new
// blend mode.h
@ -32,6 +33,9 @@ namespace app {
app::Color defaultSliceColor = app::Color::fromRgb(0, 0, 255);
// Algorithm used to create a palette from RGB files.
doc::RgbMapAlgorithm rgbMapAlgorithm = doc::RgbMapAlgorithm::DEFAULT;
void fillFromPreferences();
};

View File

@ -356,7 +356,9 @@ private:
if (!frameBounds.isEmpty())
frameImage.reset(readFrameIndexedImage(frameBounds));
GIF_TRACE("GIF: Frame[%d] transparent index = %d\n", (int)m_frameNum, m_localTransparentIndex);
GIF_TRACE("GIF: Frame[%d] transparentIndex=%d localMap=%d\n",
(int)m_frameNum, m_localTransparentIndex,
m_gifFile->Image.ColorMap ? m_gifFile->Image.ColorMap->ColorCount: 0);
if (m_frameNum == 0) {
if (m_localTransparentIndex >= 0)
@ -456,9 +458,14 @@ private:
}
else if (!m_hasLocalColormaps) {
if (!global) {
if (!m_firstLocalColormap)
m_firstLocalColormap = GifMakeMapObject(colormap->ColorCount,
colormap->Colors);
if (!m_firstLocalColormap) {
m_firstLocalColormap = GifMakeMapObject(256, nullptr);
for (int i=0; i<colormap->ColorCount; ++i) {
m_firstLocalColormap->Colors[i].Red = colormap->Colors[i].Red;
m_firstLocalColormap->Colors[i].Green = colormap->Colors[i].Green;
m_firstLocalColormap->Colors[i].Blue = colormap->Colors[i].Blue;
}
}
global = m_firstLocalColormap;
}
@ -502,13 +509,13 @@ private:
// With this we avoid discarding the transparent index when a
// frame indicates that it uses a specific index as transparent
// but the image is completely opaque anyway.
if (m_localTransparentIndex >= 0 &&
if (!m_opaque && m_frameNum == 0 && m_localTransparentIndex >= 0 &&
m_localTransparentIndex < ncolors) {
usedEntries[m_localTransparentIndex] = true;
}
for (const auto& i : LockImageBits<IndexedTraits>(frameImage)) {
if (i >= 0 && i < ncolors)
if (i >= 0 && i < ncolors && i != m_localTransparentIndex)
usedEntries[i] = true;
}
}
@ -523,11 +530,16 @@ private:
// Check if we need an extra color equal to the bg color in a
// transparent frameImage.
bool needsExtraBgColor = false;
bool needCheckLocalTransparent = m_bgIndex != m_localTransparentIndex ||
(ncolors > m_localTransparentIndex
&& m_localTransparentIndex >= 0
&& usedEntries[m_localTransparentIndex]);
if (m_sprite->pixelFormat() == IMAGE_INDEXED &&
!m_opaque && m_bgIndex != m_localTransparentIndex) {
!m_opaque &&
needCheckLocalTransparent) {
for (const auto& i : LockImageBits<IndexedTraits>(frameImage)) {
if (i == m_bgIndex &&
i != m_localTransparentIndex) {
if (i == m_bgIndex) {
needsExtraBgColor = true;
break;
}
@ -617,6 +629,20 @@ private:
int i = m_bgIndex;
int j = base++;
palette->setEntry(j, colormap2rgba(colormap, i));
// m_firstLocalColorMap, is used only if we have no global color map in the gif source,
// and we want to preserve original color indexes, as much we can.
// If the palette size is > 256, m_firstLocalColormal is no more useful, because
// the sprite pixel format will be converted in RGBA image, and the colors will
// be picked from the sprite palette, instead of m_firstLocalColorMap.
if (m_firstLocalColormap && m_firstLocalColormap->ColorCount > j) {
// We need add this extra color to m_firstLocalColormap, because
// it might has not been considered in the first getFrameColormap execution.
// (this happen when: in the first execution of getFrameColormap function
// an extra color was not needed)
m_firstLocalColormap->Colors[j].Red = rgba_getr(palette->getEntry(j));
m_firstLocalColormap->Colors[j].Green = rgba_getg(palette->getEntry(j));
m_firstLocalColormap->Colors[j].Blue = rgba_getb(palette->getEntry(j));
}
m_remap.map(i, j);
}
@ -892,6 +918,34 @@ bool GifFormat::onLoad(FileOp* fop)
#ifdef ENABLE_SAVE
// Our stragegy to encode GIF files depends of the sprite color mode:
//
// 1) If the sprite is indexed, we have two paths:
// * For opaque an opaque sprite we can save it as it is (with the
// same indexes/pixels and same color palette). This brings us
// the best possible to compress the GIF file (using the best
// disposal method to update only the differences between each
// frame).
// * For transparent sprites we offer to the user the option to
// preserve the original palette or not
// (m_preservePaletteOrders). If the palette must be preserve,
// some level of compression will be sacrificed.
//
// 2) For RGB sprites the palette is created on each frame depending
// on the updated rectangle between frames, i.e. each to new frame
// incorporates a minimal rectangular region with changes from the
// previous frame, we can calculate the palette required for this
// rectangle and use it as a local colormap for the frame (if each
// frame uses previous color in the palette there is no need to
// introduce a new palette).
//
// Note: In the following algorithm you will find the "pixel clearing"
// term, this happens when we need to clear an opaque color with the
// gif transparent bg color. This is the worst possible case, because
// on transparent gif files, the only way to get the transparent color
// (bg color) is using the RESTORE_BGCOLOR disposal method (so we lost
// the chance to use DO_NOT_DISPOSE in these cases).
//
class GifEncoder {
public:
typedef int gifframe_t;
@ -902,10 +956,22 @@ public:
, m_document(fop->document())
, m_sprite(fop->document()->sprite())
, m_spriteBounds(m_sprite->bounds())
, m_hasBackground(m_sprite->backgroundLayer() ? true: false)
, m_hasBackground(m_sprite->isOpaque())
, m_bitsPerPixel(1)
, m_globalColormap(nullptr)
, m_quantizeColormaps(false) {
, m_globalColormapPalette(*m_sprite->palette(0))
, m_preservePaletteOrder(false) {
const auto gifOptions = std::static_pointer_cast<GifOptions>(fop->formatOptions());
LOG("GIF: Saving with options: interlaced=%d loop=%d\n",
gifOptions->interlaced(), gifOptions->loop());
m_interlaced = gifOptions->interlaced();
m_loop = (gifOptions->loop() ? 0: -1);
m_lastFrameBounds = m_spriteBounds;
m_lastDisposal = DisposalMethod::NONE;
if (m_sprite->pixelFormat() == IMAGE_INDEXED) {
for (Palette* palette : m_sprite->getPalettes()) {
int bpp = GifBitSizeLimited(palette->size());
@ -920,46 +986,107 @@ public:
m_sprite->getPalettes().size() == 1) {
// If some layer has opacity < 255 or a different blend mode, we
// need to create color palettes.
bool quantizeColormaps = false;
for (const Layer* layer : m_sprite->allVisibleLayers()) {
if (layer->isVisible() && layer->isImage()) {
const LayerImage* imageLayer = static_cast<const LayerImage*>(layer);
if (imageLayer->opacity() < 255 ||
imageLayer->blendMode() != BlendMode::NORMAL) {
m_quantizeColormaps = true;
quantizeColormaps = true;
break;
}
}
}
if (!m_quantizeColormaps) {
m_globalColormap = createColorMap(m_sprite->palette(0));
if (!quantizeColormaps) {
m_globalColormap = createColorMap(&m_globalColormapPalette);
m_bgIndex = m_sprite->transparentColor();
// For indexed and opaque sprite, we can preserve the exact
// palette order without lossing compression rate.
if (m_hasBackground)
m_preservePaletteOrder = true;
// Only for transparent indexed images the user can choose to
// preserve or not the palette order.
else
m_preservePaletteOrder = gifOptions->preservePaletteOrder();
}
else
m_bgIndex = 0;
}
else {
m_bgIndex = 0;
m_quantizeColormaps = true;
}
// This is the transparent index to use as "local transparent"
// index for each gif frame. In case that we use a global colormap
// (and we don't need to preserve the original palette), we can
// try to find a place for a global transparent index.
m_transparentIndex = (m_hasBackground ? -1: m_bgIndex);
if (m_globalColormap) {
// The variable m_globalColormap is != nullptr only on indexed images
ASSERT(m_sprite->pixelFormat() == IMAGE_INDEXED);
if (m_hasBackground)
m_clearColor = m_sprite->palette(0)->getEntry(m_bgIndex);
else
m_clearColor = rgba(0, 0, 0, 0);
const Palette* pal = m_sprite->palette(0);
bool maskColorFounded = false;
for (int i=0; i<pal->size(); i++) {
if (doc::rgba_geta(pal->getEntry(i)) == 0) {
maskColorFounded = true;
m_transparentIndex = i;
break;
}
}
const auto gifOptions = std::static_pointer_cast<GifOptions>(fop->formatOptions());
#if 0
// If the palette contains room for one extra color for the
// mask, we can use that index.
if (!maskColorFounded && pal->size() < 256) {
maskColorFounded = true;
LOG("GIF: Saving with options: interlaced=%d loop=%d\n",
gifOptions->interlaced(), gifOptions->loop());
Palette newPalette(*pal);
newPalette.addEntry(0);
ASSERT(newPalette.size() <= 256);
m_interlaced = gifOptions->interlaced();
m_loop = (gifOptions->loop() ? 0: -1);
m_transparentIndex = newPalette.size() - 1;
m_globalColormapPalette = newPalette;
m_globalColormap = createColorMap(&m_globalColormapPalette);
}
else
#endif
if (// If all colors are opaque/used in the sprite
!maskColorFounded &&
// We aren't obligated to preserve the original palette
!m_preservePaletteOrder &&
// And the sprite is transparent
!m_hasBackground) {
// We create a new palette with 255 colors + one extra entry
// for the transparent color
Palette newPalette(0, 255);
render::create_palette_from_sprite(
m_sprite,
0,
totalFrames()-1,
false,
&newPalette,
nullptr,
m_fop->newBlend(),
RgbMapAlgorithm::DEFAULT, // TODO configurable?
false); // Do not add the transparent color yet
// We will use the last palette entry (e.g. index=255) as the
// transparent index
newPalette.addEntry(0);
ASSERT(newPalette.size() <= 256);
m_transparentIndex = newPalette.size() - 1;
m_globalColormapPalette = newPalette;
m_globalColormap = createColorMap(&m_globalColormapPalette);
}
}
// Create the 3 temporary images (previous/current/next) to
// compare pixels between them.
for (int i=0; i<3; ++i)
m_images[i].reset(Image::create(IMAGE_RGB,
m_images[i].reset(Image::create((m_preservePaletteOrder)? IMAGE_INDEXED : IMAGE_RGB,
m_spriteBounds.w,
m_spriteBounds.h));
}
@ -1005,25 +1132,22 @@ public:
if (gifFrame+1 < nframes)
renderFrame(*frame_it, m_nextImage);
gfx::Rect frameBounds;
DisposalMethod disposal;
calculateBestDisposalMethod(gifFrame, frameBounds, disposal);
gfx::Rect frameBounds = m_spriteBounds;
DisposalMethod disposal = DisposalMethod::DO_NOT_DISPOSE;
// TODO We could join both frames in a longer one (with more duration)
if (frameBounds.isEmpty())
frameBounds = gfx::Rect(0, 0, 1, 1);
// Creation of the deltaImage (difference image result respect
// to current VS previous frame image). At the same time we
// must scan the next image, to check if some pixel turns to
// transparent (0), if the case, we need to force disposal
// method of the current image to RESTORE_BG. Further, at the
// same time, we must check if we can go without color zero (0).
calculateDeltaImageFrameBoundsDisposal(gifFrame, frameBounds, disposal);
writeImage(gifFrame, frame, frameBounds, disposal,
// Only the last frame in the animation needs the fix
(fix_last_frame_duration && gifFrame == nframes-1));
// Dispose/clear frame content
process_disposal_method(m_previousImage,
m_currentImage,
disposal,
frameBounds,
m_clearColor);
m_fop->setProgress(double(gifFrame+1) / double(nframes));
}
return true;
@ -1031,6 +1155,126 @@ public:
private:
void calculateDeltaImageFrameBoundsDisposal(gifframe_t gifFrame,
gfx::Rect& frameBounds,
DisposalMethod& disposal) {
if (gifFrame == 0) {
m_deltaImage.reset(Image::createCopy(m_currentImage));
frameBounds = m_spriteBounds;
// The first frame (frame 0) is good to force to disposal = DO_NOT_DISPOSE,
// but when the next frame (frame 1) has a "pixel clearing",
// we must change disposal to RESTORE_BGCOLOR.
// "Pixel clearing" detection:
if (!m_hasBackground && !m_preservePaletteOrder) {
const LockImageBits<RgbTraits> bits2(m_currentImage);
const LockImageBits<RgbTraits> bits3(m_nextImage);
typename LockImageBits<RgbTraits>::const_iterator it2, it3, end2, end3;
for (it2 = bits2.begin(), end2 = bits2.end(),
it3 = bits3.begin(), end3 = bits3.end();
it2 != end2 && it3 != end3; ++it2, ++it3) {
if (*it2 != 0 && *it3 == 0) {
disposal = DisposalMethod::RESTORE_BGCOLOR;
break;
}
}
}
else if (m_preservePaletteOrder)
disposal = DisposalMethod::RESTORE_BGCOLOR;
}
else {
int x1 = 0;
int y1 = 0;
int x2 = 0;
int y2 = 0;
if (!m_preservePaletteOrder) {
// When m_lastDisposal was RESTORE_BGBOLOR it implies
// we will have to cover with colors the entire previous frameBounds plus
// the current frameBounds due to color changes, so we must start with
// a frameBounds equal to the previous frame iteration (saved in m_lastFrameBounds).
// Then we must cover all the resultant frameBounds with full color
// in m_currentImage, the output image will be saved in deltaImage.
if (m_lastDisposal == DisposalMethod::RESTORE_BGCOLOR) {
x1 = m_lastFrameBounds.x;
y1 = m_lastFrameBounds.y;
x2 = m_lastFrameBounds.x + m_lastFrameBounds.w - 1;
y2 = m_lastFrameBounds.y + m_lastFrameBounds.h - 1;
}
else {
x1 = m_spriteBounds.w - 1;
y1 = m_spriteBounds.h - 1;
}
int i = 0;
int x, y;
const LockImageBits<RgbTraits> bits1(m_previousImage);
const LockImageBits<RgbTraits> bits2(m_currentImage);
const LockImageBits<RgbTraits> bits3(m_nextImage);
m_deltaImage.reset(Image::create(PixelFormat::IMAGE_RGB, m_spriteBounds.w, m_spriteBounds.h));
clear_image(m_deltaImage.get(), 0);
LockImageBits<RgbTraits> deltaBits(m_deltaImage.get());
typename LockImageBits<RgbTraits>::iterator deltaIt;
typename LockImageBits<RgbTraits>::const_iterator it1, it2, it3, end1, end2, end3, deltaEnd;
bool previousImageMatchsCurrent = true;
for (it1 = bits1.begin(), end1 = bits1.end(),
it2 = bits2.begin(), end2 = bits2.end(),
it3 = bits3.begin(), end2 = bits3.end(),
deltaIt = deltaBits.begin();
it1 != end1 && it2 != end2; ++it1, ++it2, ++it3, ++deltaIt, ++i) {
x = i % m_spriteBounds.w;
y = i / m_spriteBounds.w;
// While we are checking color differences,
// we enlarge the frameBounds where the color differences take place
if (*it1 != *it2 || *it3 == 0) {
previousImageMatchsCurrent = false;
*deltaIt = *it2;
if (x < x1) x1 = x;
if (x > x2) x2 = x;
if (y < y1) y1 = y;
if (y > y2) y2 = y;
}
// We need to change disposal mode DO_NOT_DISPOSE to RESTORE_BGCOLOR only
// if we found a "pixel clearing" in the next Image. RESTORE_BGCOLOR is
// our way to clear pixels.
if (*it2 != 0 && *it3 == 0) {
disposal = DisposalMethod::RESTORE_BGCOLOR;
}
}
if (previousImageMatchsCurrent)
frameBounds = gfx::Rect(m_lastFrameBounds);
else
frameBounds = gfx::Rect(x1, y1, x2-x1+1, y2-y1+1);
}
else
disposal = DisposalMethod::RESTORE_BGCOLOR;
// We need to conditionate the deltaImage to the next step: 'writeImage()'
// To do it, we need to crop deltaImage in frameBounds.
// If disposal method changed to RESTORE_BGCOLOR deltaImage we need to reproduce ALL the colors of m_currentImage
// contained in frameBounds (so, we will overwrite delta image with a cropped current image).
// In the other hand, if disposal is still DO_NOT_DISPOSAL, delta image will be a cropped image
// from itself in frameBounds.
if (disposal == DisposalMethod::RESTORE_BGCOLOR || m_lastDisposal == DisposalMethod::RESTORE_BGCOLOR) {
m_deltaImage.reset(crop_image(m_currentImage, frameBounds, 0));
}
else {
m_deltaImage.reset(crop_image(m_deltaImage.get(), frameBounds, 0));
disposal = DisposalMethod::DO_NOT_DISPOSE;
}
m_lastFrameBounds = frameBounds;
}
// TODO We could join both frames in a longer one (with more duration)
if (frameBounds.isEmpty())
frameBounds = gfx::Rect(0, 0, 1, 1);
m_lastDisposal = disposal;
}
doc::frame_t totalFrames() const {
return m_fop->roi().frames();
}
@ -1123,99 +1367,38 @@ private:
return frameBounds;
}
void calculateBestDisposalMethod(gifframe_t gifFrame, gfx::Rect& frameBounds,
DisposalMethod& disposal) {
if (m_hasBackground) {
disposal = DisposalMethod::DO_NOT_DISPOSE;
}
else {
disposal = DisposalMethod::RESTORE_BGCOLOR;
}
if (gifFrame == 0) {
frameBounds = m_spriteBounds;
}
else {
gfx::Rect prev, next;
if (gifFrame-1 >= 0)
prev = calculateFrameBounds(m_currentImage, m_previousImage);
if (!m_hasBackground &&
gifFrame+1 < totalFrames())
next = calculateFrameBounds(m_currentImage, m_nextImage);
frameBounds = prev.createUnion(next);
// Special case were it's better to restore the previous frame
// when we dispose the current one than clearing with the bg
// color.
if (m_hasBackground && !prev.isEmpty()) {
gfx::Rect prevNext = calculateFrameBounds(m_previousImage, m_nextImage);
if (!prevNext.isEmpty() &&
frameBounds.contains(prevNext) &&
prevNext.w*prevNext.h < frameBounds.w*frameBounds.h) {
disposal = DisposalMethod::RESTORE_PREVIOUS;
}
}
GIF_TRACE("GIF: frameBounds=%d %d %d %d prev=%d %d %d %d next=%d %d %d %d\n",
frameBounds.x, frameBounds.y, frameBounds.w, frameBounds.h,
prev.x, prev.y, prev.w, prev.h,
next.x, next.y, next.w, next.h);
}
}
void writeImage(const gifframe_t gifFrame,
const frame_t frame,
const gfx::Rect& frameBounds,
const DisposalMethod disposal,
const bool fixDuration) {
std::unique_ptr<Palette> framePaletteRef;
std::unique_ptr<RgbMap> rgbmapRef;
Palette* framePalette = m_sprite->palette(frame);
RgbMap* rgbmap = m_sprite->rgbMap(frame);
Palette framePalette;
if (m_globalColormap)
framePalette = m_globalColormapPalette;
else
framePalette = calculatePalette(frameBounds, disposal);
// Create optimized palette for RGB/Grayscale images
if (m_quantizeColormaps) {
framePaletteRef.reset(createOptimizedPalette(frameBounds));
framePalette = framePaletteRef.get();
rgbmapRef.reset(new RgbMap);
rgbmap = rgbmapRef.get();
rgbmap->regenerate(framePalette, m_transparentIndex);
}
// We will store the frameBounds pixels in frameImage, with the
// indexes that must be stored in the GIF file for this specific
// frame.
if (!m_frameImageBuf)
m_frameImageBuf.reset(new ImageBuffer);
RgbMapRGB5A3 rgbmap; // TODO RgbMapRGB5A3 configurable?
rgbmap.regenerateMap(&framePalette, m_transparentIndex);
ImageRef frameImage(Image::create(IMAGE_INDEXED,
frameBounds.w,
frameBounds.h,
m_frameImageBuf));
// Convert the frameBounds area of m_currentImage (RGB) to frameImage (Indexed)
// bool needsTransparent = false;
PalettePicks usedColors(framePalette->size());
// Every frame might use a small portion of the global palette,
// to optimize the gif file size, we will analize which colors
// will be used in each processed frame.
PalettePicks usedColors(framePalette.size());
// If the sprite needs a transparent color we mark it as used so
// the palette includes a spot for it. It doesn't matter if the
// image doesn't use the transparent index, if the sprite isn't
// opaque we need the transparent index anyway.
if (m_transparentIndex >= 0) {
int i = m_transparentIndex;
if (i >= usedColors.size())
usedColors.resize(i+1);
usedColors[i] = true;
}
int localTransparent = m_transparentIndex;
ColorMapObject* colormap = m_globalColormap;
Remap remap(256);
{
const LockImageBits<RgbTraits> srcBits(m_currentImage, frameBounds);
LockImageBits<IndexedTraits> dstBits(
frameImage.get(), gfx::Rect(0, 0, frameBounds.w, frameBounds.h));
if (!m_preservePaletteOrder) {
const LockImageBits<RgbTraits> srcBits(m_deltaImage.get());
LockImageBits<IndexedTraits> dstBits(frameImage.get());
auto srcIt = srcBits.begin();
auto dstIt = dstBits.begin();
@ -1229,20 +1412,16 @@ private:
int i;
if (rgba_geta(color) >= 128) {
i = framePalette->findExactMatch(
i = framePalette.findExactMatch(
rgba_getr(color),
rgba_getg(color),
rgba_getb(color),
255,
m_transparentIndex);
if (i < 0)
i = rgbmap->mapColor(rgba_getr(color),
rgba_getg(color),
rgba_getb(color),
255);
i = rgbmap.mapColor(color | rgba_a_mask); // alpha=255
}
else {
ASSERT(m_transparentIndex >= 0);
if (m_transparentIndex >= 0)
i = m_transparentIndex;
else
@ -1261,34 +1440,36 @@ private:
*dstIt = i;
}
}
}
int usedNColors = usedColors.picks();
int usedNColors = usedColors.picks();
Remap remap(256);
for (int i=0; i<remap.size(); ++i)
remap.map(i, i);
for (int i=0; i<remap.size(); ++i)
remap.map(i, i);
int localTransparent = m_transparentIndex;
ColorMapObject* colormap = m_globalColormap;
if (!colormap) {
Palette reducedPalette(0, usedNColors);
if (!colormap) {
Palette reducedPalette(0, usedNColors);
for (int i=0, j=0; i<framePalette->size(); ++i) {
if (usedColors[i]) {
reducedPalette.setEntry(j, framePalette->getEntry(i));
remap.map(i, j);
++j;
for (int i=0, j=0; i<framePalette.size(); ++i) {
if (usedColors[i]) {
reducedPalette.setEntry(j, framePalette.getEntry(i));
remap.map(i, j);
++j;
}
}
colormap = createColorMap(&reducedPalette);
if (localTransparent >= 0)
localTransparent = remap[localTransparent];
}
colormap = createColorMap(&reducedPalette);
if (localTransparent >= 0)
localTransparent = remap[localTransparent];
if (localTransparent >= 0 && m_transparentIndex != localTransparent)
remap.map(m_transparentIndex, localTransparent);
}
else {
frameImage.reset(Image::createCopy(m_deltaImage.get()));
for (int i=0; i<colormap->ColorCount; ++i)
remap.map(i, i);
}
if (localTransparent >= 0 && m_transparentIndex != localTransparent)
remap.map(m_transparentIndex, localTransparent);
// Write extension record.
writeExtension(gifFrame, frame, localTransparent,
@ -1338,20 +1519,173 @@ private:
GifFreeMapObject(colormap);
}
Palette* createOptimizedPalette(const gfx::Rect& frameBounds) {
Palette calculatePalette(const gfx::Rect& frameBounds,
const DisposalMethod disposal) {
// First, we must check the palette color count in m_deltaImage (our best shot
// to find the smaller palette color count)
Palette pal(createOptimizedPalette(m_deltaImage.get(), m_deltaImage->bounds(), 256));
if (pal.size() == 256) {
// Here the palette has 256 colors, there is no place to include
// the 0 color (createOptimizedPalette() doesn't create an entry
// for it).
//
// We have two paths:
// 1- Giving a try to palette generation on m_currentImage in frameBouns limits.
// 2- If the previous step is not possible (color count > 256), we will to start
// to approximate colors from m_deltaImage with some criterion. Final target:
// to approximate the palette to 255 colors + clear color (0)).
// 1- Giving a try to palette generation on m_currentImage in frameBouns limits.
// if disposal == RESTORE_BGCOLOR m_deltaImage already is a cropped copy of m_currentImage.
Palette auxPalette;
if (disposal == DisposalMethod::DO_NOT_DISPOSE)
auxPalette = createOptimizedPalette(m_currentImage, frameBounds, 257);
else
auxPalette = pal;
if (auxPalette.size() <= 256) {
// We are fine with color count in m_currentImage contained in
// frameBounds (we got 256 or less colors):
m_transparentIndex = -1;
pal = auxPalette;
if (disposal == DisposalMethod::DO_NOT_DISPOSE) {
ASSERT(frameBounds.w >= 1);
m_deltaImage.reset(crop_image(m_currentImage, frameBounds, 0));
}
}
else {
// 2- If the previous step fails, we will to start to approximate colors from m_deltaImage
// with some criterion:
// Final target: to approximate the palette to 255 colors + clear color (0)).
// CRITERION:
// Find a palette of 220 or less colors (in high precision) into the square border
// contained in m_deltaImage, then into the center square quantize the remaining colors
// to complete a palette of 255 colors, finally add the transparent color (0).
//
// m_currentImage__ __ m_deltaImage (same rectangle size as `frameBounds` variable)
// | |
// --------------*----|-----------
// | | |
// | --------------*- |
// | | | |
// | | ________ | |
// | | | | *--------------- square border (we will collect
// | | | | | | high precision colors from this area, less than 220)
// | | | | | |
// | | | *--------------------- center rectangle (we will to quantize
// | | | | | | colors contained in this area)
// | | |________| | |
// | | | |
// | |________________| |
// | |
// |_______________________________|
//
const gfx::Size deltaSize = m_deltaImage->size();
int thicknessTop = deltaSize.h / 4;
int thicknessLeft = deltaSize.w / 4;
int repeatCounter = 0;
while (repeatCounter < 10 && thicknessTop > 0 && thicknessLeft > 0) {
// ----------------
// |________________|
// | | | |
// | | | |
// | |________| |
// |________________|
render::PaletteOptimizer optimizer;
gfx::Rect auxRect(0, 0, deltaSize.w, thicknessTop);
optimizer.feedWithImage(m_deltaImage.get(), auxRect, false);
// ----------------
// | ________ |
// | | | |
// | | | |
// |___|________|___|
// |________________|
auxRect = gfx::Rect(0, deltaSize.h - thicknessTop - 1, deltaSize.w, thicknessTop);
optimizer.feedWithImage(m_deltaImage.get(), auxRect, false);
// ----------------
// |____________ |
// | | | |
// | | | |
// |___|________| |
// |________________|
auxRect = gfx::Rect(0, thicknessTop, thicknessLeft, deltaSize.h - 2 * thicknessTop);
optimizer.feedWithImage(m_deltaImage.get(), auxRect, false);
// ----------------
// | _____________|
// | | | |
// | | | |
// | |________|___|
// |________________|
auxRect = gfx::Rect(deltaSize.w - thicknessLeft - 1, thicknessTop, thicknessLeft, deltaSize.h - 2 * thicknessTop);
optimizer.feedWithImage(m_deltaImage.get(), auxRect, false);
int maxBorderColorCount = 220;
if (optimizer.isHighPrecision() && (optimizer.highPrecisionSize() < maxBorderColorCount)) {
pal.resize(optimizer.highPrecisionSize());
optimizer.calculate(&pal, -1);
break;
}
else if (thicknessTop <= 1 || thicknessLeft <= 1) {
pal.resize(0);
thicknessTop = 0;
thicknessLeft = 0;
break;
}
else {
thicknessTop -= thicknessTop / 2;
thicknessLeft -= thicknessLeft / 2;
}
repeatCounter++;
}
// Quantize the colors contained into center rectangle and add these in `pal`:
if (pal.size() < 255) {
gfx::Rect centerRect(thicknessLeft,
thicknessTop,
deltaSize.w - 2 * thicknessLeft,
deltaSize.h - 2 * thicknessTop);
Palette centerPalette(0, 255 - pal.size());
centerPalette = createOptimizedPalette(m_deltaImage.get(),
centerRect, 255 - pal.size());
for (int i=0; i < centerPalette.size(); i++)
pal.addEntry(centerPalette.getEntry(i));
}
// Finally add transparent color:
ASSERT(pal.size() <= 255);
pal.addEntry(0);
m_transparentIndex = pal.size() - 1;
}
}
// We are fine, we got 255 or less, there is room for the transparent color
else if (pal.size() <= 255) {
pal.addEntry(0);
m_transparentIndex = pal.size() - 1;
}
return pal;
}
static Palette createOptimizedPalette(const Image* image,
const gfx::Rect& bounds,
const int ncolors) {
render::PaletteOptimizer optimizer;
// Feed the palette optimizer with pixels inside frameBounds
for (const auto& color : LockImageBits<RgbTraits>(m_currentImage, frameBounds)) {
if (rgba_geta(color) >= 128)
// Feed the palette optimizer with pixels inside the given bounds
for (const auto& color : LockImageBits<RgbTraits>(image, bounds)) {
if (rgba_geta(color) >= 128) // Note: the mask color won't be part of the final palette
optimizer.feedWithRgbaColor(
rgba(rgba_getr(color),
rgba_getg(color),
rgba_getb(color), 255));
}
Palette* palette = new Palette(0, 256);
optimizer.calculate(palette, m_transparentIndex);
Palette palette(0, ncolors);
optimizer.calculate(&palette, -1);
return palette;
}
@ -1360,7 +1694,10 @@ private:
render.setNewBlend(m_fop->newBlend());
render.setBgType(render::BgType::NONE);
clear_image(dst, m_clearColor);
if (m_preservePaletteOrder)
clear_image(dst, m_bgIndex);
else
clear_image(dst, 0);
render.renderSprite(dst, m_sprite, frame);
}
@ -1398,18 +1735,23 @@ private:
gfx::Rect m_spriteBounds;
bool m_hasBackground;
int m_bgIndex;
color_t m_clearColor;
int m_transparentIndex;
int m_bitsPerPixel;
// Global palette to use on all frames, or nullptr in case that we
// have to quantize the palette on each frame.
ColorMapObject* m_globalColormap;
bool m_quantizeColormaps;
Palette m_globalColormapPalette;
bool m_interlaced;
int m_loop;
bool m_preservePaletteOrder;
gfx::Rect m_lastFrameBounds;
DisposalMethod m_lastDisposal;
ImageBufferPtr m_frameImageBuf;
ImageRef m_images[3];
Image* m_previousImage;
Image* m_currentImage;
Image* m_nextImage;
std::unique_ptr<Image> m_deltaImage;
};
bool GifFormat::onSave(FileOp* fop)
@ -1448,21 +1790,37 @@ FormatOptionsPtr GifFormat::onAskUserForFormatOptions(FileOp* fop)
opts->setInterlaced(pref.gif.interlaced());
if (pref.isSet(pref.gif.loop))
opts->setLoop(pref.gif.loop());
if (pref.isSet(pref.gif.preservePaletteOrder))
opts->setPreservePaletteOrder(pref.gif.preservePaletteOrder());
if (pref.gif.showAlert()) {
app::gen::GifOptions win;
win.interlaced()->setSelected(opts->interlaced());
win.loop()->setSelected(opts->loop());
win.preservePaletteOrder()->setSelected(opts->preservePaletteOrder());
if (fop->document()->sprite()->pixelFormat() == PixelFormat::IMAGE_INDEXED &&
!fop->document()->sprite()->isOpaque())
win.preservePaletteOrder()->setEnabled(true);
else {
win.preservePaletteOrder()->setEnabled(false);
if (fop->document()->sprite()->pixelFormat() == PixelFormat::IMAGE_INDEXED && fop->document()->sprite()->isOpaque())
win.preservePaletteOrder()->setSelected(true);
else
win.preservePaletteOrder()->setSelected(false);
}
win.openWindowInForeground();
if (win.closer() == win.ok()) {
pref.gif.interlaced(win.interlaced()->isSelected());
pref.gif.loop(win.loop()->isSelected());
pref.gif.preservePaletteOrder(win.preservePaletteOrder()->isSelected());
pref.gif.showAlert(!win.dontShow()->isSelected());
opts->setInterlaced(pref.gif.interlaced());
opts->setLoop(pref.gif.loop());
opts->setPreservePaletteOrder(pref.gif.preservePaletteOrder());
}
else {
opts.reset();

View File

@ -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
@ -18,20 +19,25 @@ namespace app {
public:
GifOptions(
bool interlaced = false,
bool loop = true)
bool loop = true,
bool preservePaletteOrder = true)
: m_interlaced(interlaced)
, m_loop(loop) {
, m_loop(loop)
, m_preservePaletteOrder(preservePaletteOrder) {
}
bool interlaced() const { return m_interlaced; }
bool loop() const { return m_loop; }
bool preservePaletteOrder() const { return m_preservePaletteOrder; }
void setInterlaced(bool interlaced) { m_interlaced = interlaced; }
void setLoop(bool loop) { m_loop = loop; }
void setPreservePaletteOrder(bool preservePaletteOrder) {m_preservePaletteOrder = preservePaletteOrder; }
private:
bool m_interlaced;
bool m_loop;
bool m_preservePaletteOrder;
};
} // namespace app

View File

@ -69,7 +69,7 @@ class JpegFormat : public FileFormat {
}
bool onLoad(FileOp* fop) override;
gfx::ColorSpacePtr loadColorSpace(FileOp* fop, jpeg_decompress_struct* dinfo);
gfx::ColorSpaceRef loadColorSpace(FileOp* fop, jpeg_decompress_struct* dinfo);
#ifdef ENABLE_SAVE
bool onSave(FileOp* fop) override;
void saveColorSpace(FileOp* fop, jpeg_compress_struct* cinfo,
@ -256,7 +256,7 @@ bool JpegFormat::onLoad(FileOp* fop)
}
// Read color space
gfx::ColorSpacePtr colorSpace = loadColorSpace(fop, &dinfo);
gfx::ColorSpaceRef colorSpace = loadColorSpace(fop, &dinfo);
if (colorSpace)
fop->setEmbeddedColorProfile();
else { // sRGB is the default JPG color space.
@ -282,7 +282,7 @@ bool JpegFormat::onLoad(FileOp* fop)
// in two steps:
// (1) Discover all ICC profile markers and verify that they are numbered properly.
// (2) Copy the data from each marker into a contiguous ICC profile.
gfx::ColorSpacePtr JpegFormat::loadColorSpace(FileOp* fop, jpeg_decompress_struct* dinfo)
gfx::ColorSpaceRef JpegFormat::loadColorSpace(FileOp* fop, jpeg_decompress_struct* dinfo)
{
// Note that 256 will be enough storage space since each markerIndex is stored in 8-bits.
jpeg_marker_struct* markerSequence[256];

View File

@ -59,7 +59,7 @@ class PngFormat : public FileFormat {
}
bool onLoad(FileOp* fop) override;
gfx::ColorSpacePtr loadColorSpace(png_structp png, png_infop info);
gfx::ColorSpaceRef loadColorSpace(png_structp png, png_infop info);
#ifdef ENABLE_SAVE
bool onSave(FileOp* fop) override;
void saveColorSpace(png_structp png, png_infop info, const gfx::ColorSpace* colorSpace);
@ -457,7 +457,7 @@ bool PngFormat::onLoad(FileOp* fop)
//
// Code to read color spaces from png files from Skia (SkPngCodec.cpp)
// by Google Inc.
gfx::ColorSpacePtr PngFormat::loadColorSpace(png_structp png_ptr, png_infop info_ptr)
gfx::ColorSpaceRef PngFormat::loadColorSpace(png_structp png_ptr, png_infop info_ptr)
{
// First check for an ICC profile
png_bytep profile;

View File

@ -20,7 +20,6 @@
#include "app/file/webp_options.h"
#include "app/ini_file.h"
#include "app/pref/preferences.h"
#include "base/bind.h"
#include "base/clamp.h"
#include "base/convert_to.h"
#include "base/file_handle.h"
@ -421,7 +420,7 @@ FormatOptionsPtr WebPFormat::onAskUserForFormatOptions(FileOp* fop)
win.imagePreset()->setSelectedItemIndex(opts->imagePreset());
updatePanels();
win.type()->Change.connect(base::Bind<void>(updatePanels));
win.type()->Change.connect(updatePanels);
win.openWindowInForeground();

View File

@ -1,4 +1,5 @@
// Aseprite
// Copyright (C) 2020 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@ -31,8 +32,8 @@ bool show_file_selector(
if (Preferences::instance().experimental.useNativeFileDialog() &&
os::instance()->nativeDialogs()) {
os::FileDialog* dlg =
os::instance()->nativeDialogs()->createFileDialog();
os::FileDialogRef dlg =
os::instance()->nativeDialogs()->makeFileDialog();
if (dlg) {
dlg->setTitle(title);
@ -65,7 +66,6 @@ bool show_file_selector(
else
output.push_back(dlg->fileName());
}
dlg->dispose();
return res;
}
}

View File

@ -56,7 +56,7 @@ namespace app {
namespace {
class FileItem;
typedef std::map<std::string, FileItem*> FileItemMap;
using FileItemMap = std::map<std::string, FileItem*>;
// the root of the file-system
FileItem* rootitem = nullptr;
@ -121,8 +121,8 @@ public:
m_thumbnailProgress = progress;
}
os::Surface* getThumbnail() override;
void setThumbnail(os::Surface* thumbnail) override;
os::SurfaceRef getThumbnail() override;
void setThumbnail(const os::SurfaceRef& thumbnail) override;
// Calls "delete this"
void deleteItem() {
@ -586,16 +586,21 @@ bool FileItem::hasExtension(const base::paths& extensions)
return base::has_file_extension(m_filename, extensions);
}
os::Surface* FileItem::getThumbnail()
os::SurfaceRef FileItem::getThumbnail()
{
return m_thumbnail;
os::SurfaceRef ref(m_thumbnail.load());
if (ref)
ref->ref(); // base::Ref(T*) doesn't add an extra reference
return ref;
}
void FileItem::setThumbnail(os::Surface* thumbnail)
void FileItem::setThumbnail(const os::SurfaceRef& newThumbnail)
{
auto old = m_thumbnail.exchange(thumbnail);
if (newThumbnail)
newThumbnail->ref();
auto old = m_thumbnail.exchange(newThumbnail.get());
if (old)
old->dispose();
old->unref();
}
FileItem::FileItem(FileItem* parent)
@ -621,8 +626,7 @@ FileItem::~FileItem()
{
FS_TRACE("FS: Destroying FileItem() with parent %p\n", m_parent);
if (auto ptr = m_thumbnail.load())
ptr->dispose();
m_thumbnail.exchange(nullptr);
#ifdef _WIN32
if (m_fullpidl && m_fullpidl != m_pidl) {

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2019 Igara Studio S.A.
// Copyright (C) 2019-2020 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@ -12,19 +12,15 @@
#include "base/mutex.h"
#include "base/paths.h"
#include "obs/signal.h"
#include "os/surface.h"
#include <string>
#include <vector>
namespace os {
class Surface;
}
namespace app {
class IFileItem;
typedef std::vector<IFileItem*> FileItemList;
using FileItemList = std::vector<IFileItem*>;
class FileSystemModule {
static FileSystemModule* m_instance;
@ -91,8 +87,8 @@ namespace app {
virtual double getThumbnailProgress() = 0;
virtual void setThumbnailProgress(double progress) = 0;
virtual os::Surface* getThumbnail() = 0;
virtual void setThumbnail(os::Surface* thumbnail) = 0;
virtual os::SurfaceRef getThumbnail() = 0;
virtual void setThumbnail(const os::SurfaceRef& thumbnail) = 0;
};
} // namespace app

View File

@ -96,9 +96,9 @@ protected:
void saveLayout(Widget* widget, const std::string& str) override;
};
static os::Display* main_display = NULL;
static CustomizedGuiManager* manager = NULL;
static Theme* gui_theme = NULL;
static os::DisplayRef main_display = nullptr;
static CustomizedGuiManager* manager = nullptr;
static Theme* gui_theme = nullptr;
static ui::Timer* defered_invalid_timer = nullptr;
static gfx::Region defered_invalid_region;
@ -124,7 +124,7 @@ static bool create_main_display(bool gpuAccel,
try {
if (w > 0 && h > 0) {
main_display = os::instance()->createDisplay(
main_display = os::instance()->makeDisplay(
w, h, (scale == 0 ? 2: base::clamp(scale, 1, 4)));
}
}
@ -136,7 +136,7 @@ static bool create_main_display(bool gpuAccel,
for (int c=0; try_resolutions[c].width; ++c) {
try {
main_display =
os::instance()->createDisplay(
os::instance()->makeDisplay(
try_resolutions[c].width,
try_resolutions[c].height,
(scale == 0 ? try_resolutions[c].scale: scale));
@ -193,7 +193,7 @@ int init_module_gui()
// Create the default-manager
manager = new CustomizedGuiManager();
manager->setDisplay(main_display);
manager->setDisplay(main_display.get());
// Setup the GUI theme for all widgets
gui_theme = new SkinTheme;
@ -221,7 +221,8 @@ void exit_module_gui()
ui::set_theme(nullptr, ui::guiscale());
delete gui_theme;
main_display->dispose();
// This should be the last unref() of the display to delete it.
main_display.reset();
}
void update_displays_color_profile_from_preferences()
@ -240,13 +241,13 @@ void update_displays_color_profile_from_preferences()
break;
case gen::WindowColorProfile::SRGB:
system->setDisplaysColorSpace(
system->createColorSpace(gfx::ColorSpace::MakeSRGB()));
system->makeColorSpace(gfx::ColorSpace::MakeSRGB()));
break;
case gen::WindowColorProfile::SPECIFIC: {
std::string name =
Preferences::instance().color.windowProfileName();
std::vector<os::ColorSpacePtr> colorSpaces;
std::vector<os::ColorSpaceRef> colorSpaces;
system->listColorSpaces(colorSpaces);
for (auto& cs : colorSpaces) {

View File

@ -54,6 +54,14 @@ Preferences::Preferences()
load();
// Create a connection with the default RgbMapAlgorithm preferences
// to change the default algorithm in the "doc" layer.
experimental.rgbmapAlgorithm.AfterChange.connect(
[](const doc::RgbMapAlgorithm& newValue){
doc::Sprite::SetDefaultRgbMapAlgorithm(newValue);
});
doc::Sprite::SetDefaultRgbMapAlgorithm(experimental.rgbmapAlgorithm());
// Create a connection with the default document preferences grid
// bounds to sync the default grid bounds for new sprites in the
// "doc" layer.

View File

@ -25,6 +25,7 @@
#include "doc/color_mode.h"
#include "doc/frame.h"
#include "doc/layer_list.h"
#include "doc/rgbmap_algorithm.h"
#include "doc/sprite.h"
#include "filters/hue_saturation_filter.h"
#include "filters/tiled_mode.h"

View File

@ -11,7 +11,6 @@
#include "app/res/http_loader.h"
#include "base/bind.h"
#include "base/fs.h"
#include "base/fstream_path.h"
#include "base/replace_string.h"
@ -29,7 +28,7 @@ HttpLoader::HttpLoader(const std::string& url)
: m_url(url)
, m_done(false)
, m_request(nullptr)
, m_thread(base::Bind<void>(&HttpLoader::threadHttpRequest, this))
, m_thread([this]{ threadHttpRequest(); })
{
}

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2019 Igara Studio S.A.
// Copyright (C) 2019-2020 Igara Studio S.A.
// Copyright (C) 2001-2017 David Capello
//
// This program is distributed under the terms of
@ -17,7 +17,6 @@
#include "app/file_system.h"
#include "app/res/palette_resource.h"
#include "app/resource_finder.h"
#include "base/bind.h"
#include "base/fs.h"
#include "base/scoped_value.h"
#include "doc/palette.h"

View File

@ -1,4 +1,5 @@
// Aseprite
// Copyright (C) 2020 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@ -14,7 +15,6 @@
#include "app/res/resource.h"
#include "app/res/resources_loader_delegate.h"
#include "app/resource_finder.h"
#include "base/bind.h"
#include "base/fs.h"
#include "base/scoped_value.h"
@ -24,9 +24,7 @@ ResourcesLoader::ResourcesLoader(ResourcesLoaderDelegate* delegate)
: m_delegate(delegate)
, m_done(false)
, m_cancel(false)
, m_thread(
new base::thread(
base::Bind<void>(&ResourcesLoader::threadLoadResources, this)))
, m_thread(new base::thread([this]{ threadLoadResources(); }))
{
}
@ -87,8 +85,7 @@ void ResourcesLoader::threadLoadResources()
base::thread* ResourcesLoader::createThread()
{
return new base::thread(
base::Bind<void>(&ResourcesLoader::threadLoadResources, this));
return new base::thread([this]{ threadLoadResources(); });
}
} // namespace app

View File

@ -19,7 +19,6 @@
#include "app/ui/color_shades.h"
#include "app/ui/expr_entry.h"
#include "app/ui/filename_field.h"
#include "base/bind.h"
#include "base/paths.h"
#include "ui/box.h"
#include "ui/button.h"

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (c) 2018-2019 Igara Studio S.A.
// Copyright (c) 2018-2020 Igara Studio S.A.
// Copyright (C) 2018 David Capello
//
// This program is distributed under the terms of
@ -115,7 +115,7 @@ int ImageSpec_set_colorSpace(lua_State* L)
{
auto spec = get_obj<doc::ImageSpec>(L, 1);
auto cs = get_obj<gfx::ColorSpace>(L, 2);
spec->setColorSpace(std::make_shared<gfx::ColorSpace>(*cs));
spec->setColorSpace(base::make_ref<gfx::ColorSpace>(*cs));
return 0;
}

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2018-2019 Igara Studio S.A.
// Copyright (C) 2018-2020 Igara Studio S.A.
// Copyright (C) 2015-2018 David Capello
//
// This program is distributed under the terms of
@ -294,7 +294,7 @@ int Sprite_assignColorSpace(lua_State* L)
auto cs = get_obj<gfx::ColorSpace>(L, 2);
Tx tx;
tx(new cmd::AssignColorProfile(
sprite, std::make_shared<gfx::ColorSpace>(*cs)));
sprite, base::make_ref<gfx::ColorSpace>(*cs)));
tx.commit();
return 1;
}
@ -305,7 +305,7 @@ int Sprite_convertColorSpace(lua_State* L)
auto cs = get_obj<gfx::ColorSpace>(L, 2);
Tx tx;
tx(new cmd::ConvertColorProfile(
sprite, std::make_shared<gfx::ColorSpace>(*cs)));
sprite, base::make_ref<gfx::ColorSpace>(*cs)));
tx.commit();
return 1;
}

View File

@ -184,6 +184,7 @@ FOR_ENUM(app::tools::RotationAlgorithm)
FOR_ENUM(doc::AniDir)
FOR_ENUM(doc::BrushPattern)
FOR_ENUM(doc::ColorMode)
FOR_ENUM(doc::RgbMapAlgorithm)
FOR_ENUM(filters::HueSaturationFilter::Mode)
FOR_ENUM(filters::TiledMode)
FOR_ENUM(render::OnionskinPosition)

View File

@ -16,7 +16,6 @@
#include "app/i18n/strings.h"
#include "app/resource_finder.h"
#include "app/task.h"
#include "base/bind.h"
#include "base/fs.h"
#include "base/launcher.h"
#include "fmt/format.h"
@ -139,7 +138,7 @@ void SendCrash::notificationClick()
if (isDev) {
dlg.official()->setVisible(false);
dlg.devFilename()->setText(m_dumpFilename);
dlg.devFilename()->Click.connect(base::Bind(&SendCrash::onClickDevFilename, this));
dlg.devFilename()->Click.connect([this]{ onClickDevFilename(); });
}
else
#endif // On other platforms the crash file might be useful even in
@ -148,7 +147,7 @@ void SendCrash::notificationClick()
{
dlg.dev()->setVisible(false);
dlg.filename()->setText(m_dumpFilename);
dlg.filename()->Click.connect(base::Bind(&SendCrash::onClickFilename, this));
dlg.filename()->Click.connect([this]{ onClickFilename(); });
}
dlg.openWindowInForeground();

View File

@ -17,7 +17,6 @@
#include "app/file/file.h"
#include "app/file_system.h"
#include "app/util/conversion_to_surface.h"
#include "base/bind.h"
#include "base/clamp.h"
#include "base/scoped_lock.h"
#include "base/thread.h"
@ -46,7 +45,7 @@ public:
: m_queue(queue)
, m_fop(nullptr)
, m_isDone(false)
, m_thread(base::Bind<void>(&Worker::loadBgThread, this)) {
, m_thread([this]{ loadBgThread(); }) {
}
~Worker() {
@ -159,13 +158,13 @@ private:
// Set the thumbnail of the file-item.
if (thumbnailImage) {
os::Surface* thumbnail =
os::instance()->createRgbaSurface(
os::SurfaceRef thumbnail =
os::instance()->makeRgbaSurface(
thumbnailImage->width(),
thumbnailImage->height());
convert_image_to_surface(
thumbnailImage.get(), palette.get(), thumbnail,
thumbnailImage.get(), palette.get(), thumbnail.get(),
0, 0, 0, 0, thumbnailImage->width(), thumbnailImage->height());
{
@ -226,7 +225,7 @@ ThumbnailGenerator* ThumbnailGenerator::instance()
static ThumbnailGenerator* singleton = nullptr;
if (singleton == NULL) {
singleton = new ThumbnailGenerator();
App::instance()->Exit.connect(base::Bind<void>(&delete_singleton, singleton));
App::instance()->Exit.connect([&]{ delete_singleton(singleton); });
}
return singleton;
}

View File

@ -22,8 +22,8 @@
namespace app {
namespace thumb {
os::Surface* get_cel_thumbnail(const doc::Cel* cel,
const gfx::Size& fitInSize)
os::SurfaceRef get_cel_thumbnail(const doc::Cel* cel,
const gfx::Size& fitInSize)
{
gfx::Size newSize;
@ -57,11 +57,11 @@ os::Surface* get_cel_thumbnail(const doc::Cel* cel,
gfx::Clip(gfx::Rect(gfx::Point(0, 0), newSize)),
255, doc::BlendMode::NORMAL);
if (os::Surface* thumbnail = os::instance()->createRgbaSurface(
if (os::SurfaceRef thumbnail = os::instance()->makeRgbaSurface(
thumbnailImage->width(),
thumbnailImage->height())) {
convert_image_to_surface(
thumbnailImage.get(), palette, thumbnail,
thumbnailImage.get(), palette, thumbnail.get(),
0, 0, 0, 0, thumbnailImage->width(), thumbnailImage->height());
return thumbnail;
}

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2019 Igara Studio S.A.
// Copyright (C) 2019-2020 Igara Studio S.A.
// Copyright (C) 2016 Carlo Caputo
//
// This program is distributed under the terms of
@ -10,6 +10,7 @@
#pragma once
#include "gfx/size.h"
#include "os/surface.h"
namespace doc {
class Cel;
@ -22,8 +23,8 @@ namespace os {
namespace app {
namespace thumb {
os::Surface* get_cel_thumbnail(const doc::Cel* cel,
const gfx::Size& fitInSize);
os::SurfaceRef get_cel_thumbnail(const doc::Cel* cel,
const gfx::Size& fitInSize);
} // thumb
} // app

View File

@ -292,10 +292,7 @@ public:
c = m_palette->getEntry(c);
c = rgba_blender_normal(c, m_color, m_opacity);
*m_dstAddress = m_rgbmap->mapColor(rgba_getr(c),
rgba_getg(c),
rgba_getb(c),
rgba_geta(c));
*m_dstAddress = m_rgbmap->mapColor(c);
}
private:
@ -365,10 +362,7 @@ public:
c = m_palette->getEntry(c);
c = rgba_blender_merge(c, m_color, m_opacity);
*m_dstAddress = m_rgbmap->mapColor(rgba_getr(c),
rgba_getg(c),
rgba_getb(c),
rgba_geta(c));
*m_dstAddress = m_rgbmap->mapColor(c);
}
private:
@ -520,8 +514,7 @@ public:
doc::rgba(m_area.r, m_area.g, m_area.b, m_area.a),
m_opacity);
*m_dstAddress = m_rgbmap->mapColor(
rgba_getr(c), rgba_getg(c), rgba_getb(c), rgba_geta(c));
*m_dstAddress = m_rgbmap->mapColor(c);
}
else {
*m_dstAddress = *m_srcAddress;
@ -633,8 +626,7 @@ public:
color_t c = rgba_blender_normal(
m_palette->getEntry(*m_srcAddress), m_color2, m_opacity);
*m_dstAddress = m_rgbmap->mapColor(
rgba_getr(c), rgba_getg(c), rgba_getb(c), rgba_geta(c));
*m_dstAddress = m_rgbmap->mapColor(c);
}
}
}
@ -721,10 +713,7 @@ void JumbleInkProcessing<IndexedTraits>::processPixel(int x, int y)
tc, m_opacity);
if (rgba_geta(c) >= 128)
*m_dstAddress = m_rgbmap->mapColor(rgba_getr(c),
rgba_getg(c),
rgba_getb(c),
rgba_geta(c));
*m_dstAddress = m_rgbmap->mapColor(c);
else
*m_dstAddress = 0;
}
@ -1060,10 +1049,7 @@ void GradientInkProcessing<IndexedTraits>::processPixel(int x, int y)
c0 = m_palette->getEntry(c0);
c = rgba_blender_normal(c0, c, m_opacity);
*m_dstAddress = m_rgbmap->mapColor(rgba_getr(c),
rgba_getg(c),
rgba_getb(c),
rgba_geta(c));
*m_dstAddress = m_rgbmap->mapColor(c);
++m_tmpAddress;
}
@ -1108,10 +1094,7 @@ public:
void processPixel(int x, int y) {
color_t c = rgba_blender_neg_bw(m_palette->getEntry(*m_srcAddress), m_color, 255);
*m_dstAddress = m_rgbmap->mapColor(rgba_getr(c),
rgba_getg(c),
rgba_getb(c),
rgba_geta(c));
*m_dstAddress = m_rgbmap->mapColor(c);
}
private:

View File

@ -20,7 +20,6 @@
#include "app/tools/stroke.h"
#include "app/tools/tool_group.h"
#include "app/tools/tool_loop.h"
#include "base/bind.h"
#include "base/exception.h"
#include "doc/algo.h"
#include "doc/algorithm/floodfill.h"

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2019 Igara Studio S.A.
// Copyright (C) 2019-2020 Igara Studio S.A.
// Copyright (C) 2001-2017 David Capello
//
// This program is distributed under the terms of
@ -25,8 +25,7 @@
#include "ui/widget.h"
#include <cstdarg>
#include <cstdio>
#include <cstring>
#include <string>
namespace app {
@ -47,18 +46,6 @@ AppMenuItem::AppMenuItem(const std::string& text,
{
}
AppMenuItem::~AppMenuItem()
{
if (m_native) {
// Do not call disposeNative(), the native handle will be disposed
// when the main menu (app menu) is disposed.
// TODO improve handling of these kind of pointer from laf-os library
delete m_native;
}
}
void AppMenuItem::setKey(const KeyPtr& key)
{
m_key = key;
@ -68,23 +55,14 @@ void AppMenuItem::setKey(const KeyPtr& key)
void AppMenuItem::setNative(const Native& native)
{
if (!m_native)
m_native = new Native(native);
else {
// Do not call disposeNative(), the native handle will be disposed
// when the main menu (app menu) is disposed.
m_native.reset(new Native(native));
else
*m_native = native;
}
}
void AppMenuItem::disposeNative()
{
#if 0 // TODO fix this and the whole handling of native menu items from laf-os
if (m_native->menuItem) {
m_native->menuItem->dispose();
m_native->menuItem = nullptr;
}
#endif
m_native.reset();
}
void AppMenuItem::syncNativeMenuItemKeyShortcut()

View File

@ -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
@ -10,12 +11,11 @@
#include "app/commands/params.h"
#include "app/ui/key.h"
#include "os/menus.h"
#include "os/shortcut.h"
#include "ui/menu.h"
namespace os {
class MenuItem;
}
#include <memory>
namespace app {
class Command;
@ -28,7 +28,7 @@ namespace app {
class AppMenuItem : public ui::MenuItem {
public:
struct Native {
os::MenuItem* menuItem = nullptr;
os::MenuItemRef menuItem = nullptr;
os::Shortcut shortcut;
app::KeyContext keyContext = app::KeyContext::Any;
};
@ -36,7 +36,6 @@ namespace app {
AppMenuItem(const std::string& text,
Command* command = nullptr,
const Params& params = Params());
~AppMenuItem();
KeyPtr key() { return m_key; }
void setKey(const KeyPtr& key);
@ -47,7 +46,7 @@ namespace app {
Command* getCommand() { return m_command; }
const Params& getParams() const { return m_params; }
Native* native() { return m_native; }
Native* native() const { return m_native.get(); }
void setNative(const Native& native);
void disposeNative();
void syncNativeMenuItemKeyShortcut();
@ -65,7 +64,7 @@ namespace app {
Command* m_command;
Params m_params;
bool m_isRecentFileItem;
Native* m_native;
std::unique_ptr<Native> m_native;
static Params s_contextParams;
};

View File

@ -1,4 +1,5 @@
// Aseprite
// Copyright (C) 2020 Igara Studio S.A.
// Copyright (C) 2001-2016 David Capello
//
// This program is distributed under the terms of
@ -11,7 +12,6 @@
#include "app/ui/backup_indicator.h"
#include "app/ui/status_bar.h"
#include "base/bind.h"
#include "ui/manager.h"
namespace app {
@ -21,7 +21,7 @@ BackupIndicator::BackupIndicator()
, m_small(false)
, m_running(false)
{
m_timer.Tick.connect(base::Bind<void>(&BackupIndicator::onTick, this));
m_timer.Tick.connect([this]{ onTick(); });
}
BackupIndicator::~BackupIndicator()

View File

@ -27,7 +27,6 @@
#include "app/ui/skin/skin_theme.h"
#include "app/ui_context.h"
#include "app/util/conversion_to_surface.h"
#include "base/bind.h"
#include "base/convert_to.h"
#include "doc/brush.h"
#include "doc/image.h"
@ -444,7 +443,7 @@ void BrushPopup::onBrushChanges()
}
// static
os::Surface* BrushPopup::createSurfaceForBrush(const BrushRef& origBrush)
os::SurfaceRef BrushPopup::createSurfaceForBrush(const BrushRef& origBrush)
{
Image* image = nullptr;
BrushRef brush = origBrush;
@ -456,7 +455,7 @@ os::Surface* BrushPopup::createSurfaceForBrush(const BrushRef& origBrush)
image = brush->image();
}
os::Surface* surface = os::instance()->createRgbaSurface(
os::SurfaceRef surface = os::instance()->makeRgbaSurface(
std::min(10, image ? image->width(): 4),
std::min(10, image ? image->height(): 4));
@ -469,7 +468,7 @@ os::Surface* BrushPopup::createSurfaceForBrush(const BrushRef& origBrush)
}
convert_image_to_surface(
image, palette, surface,
image, palette, surface.get(),
0, 0, 0, 0, image->width(), image->height());
if (image->pixelFormat() == IMAGE_BITMAP)

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2018 Igara Studio S.A.
// Copyright (C) 2018-2020 Igara Studio S.A.
// Copyright (C) 2001-2015 David Capello
//
// This program is distributed under the terms of
@ -23,7 +23,7 @@ namespace app {
void setBrush(doc::Brush* brush);
void regenerate(const gfx::Rect& box);
static os::Surface* createSurfaceForBrush(const doc::BrushRef& brush);
static os::SurfaceRef createSurfaceForBrush(const doc::BrushRef& brush);
private:
void onStandardBrush();

View File

@ -13,7 +13,6 @@
#include "app/modules/gui.h"
#include "app/ui/skin/skin_theme.h"
#include "base/bind.h"
#include "gfx/color.h"
#include "os/surface.h"
#include "ui/box.h"
@ -160,7 +159,7 @@ void ButtonSet::Item::onPaint(ui::PaintEvent& ev)
}
if (hasText()) {
g->setFont(font());
g->setFont(AddRef(font()));
g->drawUIText(text(), fg, gfx::ColorNone, textRc.origin(), 0);
}
}

View File

@ -54,7 +54,6 @@
#include "app/ui_context.h"
#include "app/util/cel_ops.h"
#include "app/util/clipboard.h"
#include "base/bind.h"
#include "base/clamp.h"
#include "base/scoped_value.h"
#include "doc/cel.h"
@ -257,18 +256,18 @@ ColorBar::ColorBar(int align, TooltipManager* tooltipManager)
m_fgColor.setExpansive(true);
m_bgColor.setExpansive(true);
m_remapPalButton.Click.connect(base::Bind<void>(&ColorBar::onRemapPalButtonClick, this));
m_remapTilesButton.Click.connect(base::Bind<void>(&ColorBar::onRemapTilesButtonClick, this));
m_remapPalButton.Click.connect([this]{ onRemapPalButtonClick(); });
m_remapTilesButton.Click.connect([this]{ onRemapTilesButtonClick(); });
m_fgColor.Change.connect(&ColorBar::onFgColorButtonChange, this);
m_fgColor.BeforeChange.connect(&ColorBar::onFgColorButtonBeforeChange, this);
m_bgColor.Change.connect(&ColorBar::onBgColorButtonChange, this);
m_fgWarningIcon->Click.connect(base::Bind<void>(&ColorBar::onFixWarningClick, this, &m_fgColor, m_fgWarningIcon));
m_bgWarningIcon->Click.connect(base::Bind<void>(&ColorBar::onFixWarningClick, this, &m_bgColor, m_bgWarningIcon));
m_redrawTimer.Tick.connect(base::Bind<void>(&ColorBar::onTimerTick, this));
m_editPal.ItemChange.connect(base::Bind<void>(&ColorBar::onSwitchPalEditMode, this));
m_buttons.ItemChange.connect(base::Bind<void>(&ColorBar::onPaletteButtonClick, this));
m_tilesButton.ItemChange.connect(base::Bind<void>(&ColorBar::onTilesButtonClick, this));
m_tilesetModeButtons.ItemChange.connect(base::Bind<void>(&ColorBar::onTilesetModeButtonClick, this));
m_fgWarningIcon->Click.connect([this]{ onFixWarningClick(&m_fgColor, m_fgWarningIcon); });
m_bgWarningIcon->Click.connect([this]{ onFixWarningClick(&m_bgColor, m_bgWarningIcon); });
m_redrawTimer.Tick.connect([this]{ onTimerTick(); });
m_editPal.ItemChange.connect([this]{ onSwitchPalEditMode(); });
m_buttons.ItemChange.connect([this]{ onPaletteButtonClick(); });
m_tilesButton.ItemChange.connect([this]{ onTilesButtonClick(); });
m_tilesetModeButtons.ItemChange.connect([this]{ onTilesetModeButtonClick(); });
InitTheme.connect(
[this, fgBox, bgBox]{
@ -334,14 +333,14 @@ ColorBar::ColorBar(int align, TooltipManager* tooltipManager)
UIContext::instance()->add_observer(this);
m_beforeCmdConn = UIContext::instance()->BeforeCommandExecution.connect(&ColorBar::onBeforeExecuteCommand, this);
m_afterCmdConn = UIContext::instance()->AfterCommandExecution.connect(&ColorBar::onAfterExecuteCommand, this);
m_fgConn = Preferences::instance().colorBar.fgColor.AfterChange.connect(base::Bind<void>(&ColorBar::onFgColorChangeFromPreferences, this));
m_bgConn = Preferences::instance().colorBar.bgColor.AfterChange.connect(base::Bind<void>(&ColorBar::onBgColorChangeFromPreferences, this));
m_sepConn = Preferences::instance().colorBar.entriesSeparator.AfterChange.connect(base::Bind<void>(&ColorBar::invalidate, this));
m_fgConn = Preferences::instance().colorBar.fgColor.AfterChange.connect([this]{ onFgColorChangeFromPreferences(); });
m_bgConn = Preferences::instance().colorBar.bgColor.AfterChange.connect([this]{ onBgColorChangeFromPreferences(); });
m_sepConn = Preferences::instance().colorBar.entriesSeparator.AfterChange.connect([this]{ invalidate(); });
m_paletteView.FocusOrClick.connect(&ColorBar::onFocusPaletteOrTilesView, this);
m_tilesView.FocusOrClick.connect(&ColorBar::onFocusPaletteOrTilesView, this);
m_appPalChangeConn = App::instance()->PaletteChange.connect(&ColorBar::onAppPaletteChange, this);
KeyboardShortcuts::instance()->UserChange.connect(
base::Bind<void>(&ColorBar::setupTooltips, this, tooltipManager));
[this, tooltipManager]{ setupTooltips(tooltipManager); });
setEditMode(false);
registerCommands();
@ -1703,18 +1702,18 @@ void ColorBar::showPaletteSortOptions()
if (m_ascending) asc.setSelected(true);
else des.setSelected(true);
rev.Click.connect(base::Bind<void>(&ColorBar::onReverseColors, this));
grd.Click.connect(base::Bind<void>(&ColorBar::onGradient, this));
hue.Click.connect(base::Bind<void>(&ColorBar::onSortBy, this, SortPaletteBy::HUE));
sat.Click.connect(base::Bind<void>(&ColorBar::onSortBy, this, SortPaletteBy::SATURATION));
bri.Click.connect(base::Bind<void>(&ColorBar::onSortBy, this, SortPaletteBy::VALUE));
lum.Click.connect(base::Bind<void>(&ColorBar::onSortBy, this, SortPaletteBy::LUMA));
red.Click.connect(base::Bind<void>(&ColorBar::onSortBy, this, SortPaletteBy::RED));
grn.Click.connect(base::Bind<void>(&ColorBar::onSortBy, this, SortPaletteBy::GREEN));
blu.Click.connect(base::Bind<void>(&ColorBar::onSortBy, this, SortPaletteBy::BLUE));
alp.Click.connect(base::Bind<void>(&ColorBar::onSortBy, this, SortPaletteBy::ALPHA));
asc.Click.connect(base::Bind<void>(&ColorBar::setAscending, this, true));
des.Click.connect(base::Bind<void>(&ColorBar::setAscending, this, false));
rev.Click.connect([this]{ onReverseColors(); });
grd.Click.connect([this]{ onGradient(); });
hue.Click.connect([this]{ onSortBy(SortPaletteBy::HUE); });
sat.Click.connect([this]{ onSortBy(SortPaletteBy::SATURATION); });
bri.Click.connect([this]{ onSortBy(SortPaletteBy::VALUE); });
lum.Click.connect([this]{ onSortBy(SortPaletteBy::LUMA); });
red.Click.connect([this]{ onSortBy(SortPaletteBy::RED); });
grn.Click.connect([this]{ onSortBy(SortPaletteBy::GREEN); });
blu.Click.connect([this]{ onSortBy(SortPaletteBy::BLUE); });
alp.Click.connect([this]{ onSortBy(SortPaletteBy::ALPHA); });
asc.Click.connect([this]{ setAscending(true); });
des.Click.connect([this]{ setAscending(false); });
menu.showPopup(gfx::Point(bounds.x, bounds.y+bounds.h));
}

View File

@ -28,7 +28,6 @@
#include "app/ui/palette_view.h"
#include "app/ui/skin/skin_theme.h"
#include "app/ui_context.h"
#include "base/bind.h"
#include "base/scoped_value.h"
#include "doc/image_impl.h"
#include "doc/palette.h"
@ -251,7 +250,7 @@ ColorPopup::ColorPopup(const ColorButtonOptions& options)
m_vbox.addChild(&m_maskLabel);
addChild(&m_vbox);
m_colorType.ItemChange.connect(base::Bind<void>(&ColorPopup::onColorTypeClick, this));
m_colorType.ItemChange.connect([this]{ onColorTypeClick(); });
m_sliders.ColorChange.connect(&ColorPopup::onColorSlidersChange, this);
m_hexColorEntry.ColorChange.connect(&ColorPopup::onColorHexEntryChange, this);

Some files were not shown because too many files have changed in this diff Show More