mirror of
https://github.com/aseprite/aseprite.git
synced 2025-04-09 18:44:46 +00:00
Merge branch 'main' into beta
This commit is contained in:
commit
5ddce57ab0
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
@ -22,7 +22,7 @@ jobs:
|
|||||||
if: ${{ runner.os == 'Linux' || runner.os == 'macOS' }}
|
if: ${{ runner.os == 'Linux' || runner.os == 'macOS' }}
|
||||||
with:
|
with:
|
||||||
key: ${{ matrix.os }}-${{ matrix.enable_ui }}-${{ matrix.build_type }}
|
key: ${{ matrix.os }}-${{ matrix.enable_ui }}-${{ matrix.build_type }}
|
||||||
- uses: turtlesec-no/get-ninja@main
|
- uses: aseprite/get-ninja@main
|
||||||
- uses: ilammy/msvc-dev-cmd@v1
|
- uses: ilammy/msvc-dev-cmd@v1
|
||||||
if: runner.os == 'Windows'
|
if: runner.os == 'Windows'
|
||||||
- name: Workaround for windows-2022 and cmake 3.25.0
|
- name: Workaround for windows-2022 and cmake 3.25.0
|
||||||
|
@ -284,7 +284,7 @@ if(USE_SHARED_CMARK)
|
|||||||
find_path(CMARK_INCLUDE_DIRS NAMES cmark.h)
|
find_path(CMARK_INCLUDE_DIRS NAMES cmark.h)
|
||||||
else()
|
else()
|
||||||
add_definitions(-DCMARK_STATIC_DEFINE)
|
add_definitions(-DCMARK_STATIC_DEFINE)
|
||||||
set(CMARK_LIBRARIES cmark_static)
|
set(CMARK_LIBRARIES cmark)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(REQUIRE_CURL)
|
if(REQUIRE_CURL)
|
||||||
|
2
laf
2
laf
@ -1 +1 @@
|
|||||||
Subproject commit d590eec74a0358678f25b6d72c4fc97eb542069d
|
Subproject commit 0d8396ea41cf4432f07b31d9002c80ef52e0565a
|
@ -117,6 +117,10 @@ if(REQUIRE_CURL)
|
|||||||
add_subdirectory(net)
|
add_subdirectory(net)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
# We need the updater library to check for updates (when
|
||||||
|
# ENABLE_UPDATER) or for the app.os object (ENABLE_SCRIPTING).
|
||||||
|
add_subdirectory(updater)
|
||||||
|
|
||||||
if(GEN_EXE)
|
if(GEN_EXE)
|
||||||
add_executable(gen IMPORTED)
|
add_executable(gen IMPORTED)
|
||||||
set_target_properties(gen PROPERTIES IMPORTED_LOCATION ${GEN_EXE})
|
set_target_properties(gen PROPERTIES IMPORTED_LOCATION ${GEN_EXE})
|
||||||
@ -127,10 +131,6 @@ else()
|
|||||||
set(GEN_DEP gen)
|
set(GEN_DEP gen)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(ENABLE_UPDATER)
|
|
||||||
add_subdirectory(updater)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(ENABLE_STEAM)
|
if(ENABLE_STEAM)
|
||||||
add_subdirectory(steam)
|
add_subdirectory(steam)
|
||||||
endif()
|
endif()
|
||||||
|
@ -161,6 +161,7 @@ if(ENABLE_SCRIPTING)
|
|||||||
commands/cmd_run_script.cpp
|
commands/cmd_run_script.cpp
|
||||||
script/app_command_object.cpp
|
script/app_command_object.cpp
|
||||||
script/app_fs_object.cpp
|
script/app_fs_object.cpp
|
||||||
|
script/app_os_object.cpp
|
||||||
script/app_object.cpp
|
script/app_object.cpp
|
||||||
script/app_theme_object.cpp
|
script/app_theme_object.cpp
|
||||||
script/brush_class.cpp
|
script/brush_class.cpp
|
||||||
@ -717,6 +718,7 @@ target_link_libraries(app-lib
|
|||||||
laf-text
|
laf-text
|
||||||
ui-lib
|
ui-lib
|
||||||
ver-lib
|
ver-lib
|
||||||
|
updater-lib
|
||||||
undo
|
undo
|
||||||
${CMARK_LIBRARIES}
|
${CMARK_LIBRARIES}
|
||||||
${TINYXML_LIBRARY}
|
${TINYXML_LIBRARY}
|
||||||
@ -756,10 +758,6 @@ if(ENABLE_SCRIPTING)
|
|||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(ENABLE_UPDATER)
|
|
||||||
target_link_libraries(app-lib updater-lib)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(ENABLE_STEAM)
|
if(ENABLE_STEAM)
|
||||||
# We need the ENABLE_STEAM flag in main module too so AppOptions are
|
# We need the ENABLE_STEAM flag in main module too so AppOptions are
|
||||||
# equal in both modules, app-lib and main (that's why this flag is
|
# equal in both modules, app-lib and main (that's why this flag is
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
// Aseprite
|
// Aseprite
|
||||||
// Copyright (c) 2023 Igara Studio S.A.
|
// Copyright (c) 2023-2024 Igara Studio S.A.
|
||||||
// Copyright (C) 2001-2017 David Capello
|
// Copyright (C) 2001-2017 David Capello
|
||||||
//
|
//
|
||||||
// This program is distributed under the terms of
|
// This program is distributed under the terms of
|
||||||
@ -156,9 +156,7 @@ void ChangeBrushCommand::onExecute(Context* context)
|
|||||||
|
|
||||||
// Create a copy of the brush (to avoid modifying the original
|
// Create a copy of the brush (to avoid modifying the original
|
||||||
// brush from the AppBrushes stock)
|
// brush from the AppBrushes stock)
|
||||||
BrushRef newBrush = std::make_shared<Brush>(*brush);
|
BrushRef newBrush = brush->cloneWithExistingImages(newImg, newMsk);
|
||||||
newBrush->setImage(newImg.get(),
|
|
||||||
newMsk.get());
|
|
||||||
contextBar->setActiveBrush(newBrush);
|
contextBar->setActiveBrush(newBrush);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -210,9 +208,7 @@ void ChangeBrushCommand::onExecute(Context* context)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
BrushRef newBrush = std::make_shared<Brush>(*brush);
|
BrushRef newBrush = brush->cloneWithExistingImages(newImg, newMsk);
|
||||||
newBrush->setImage(newImg.get(),
|
|
||||||
newMsk.get());
|
|
||||||
contextBar->setActiveBrush(newBrush);
|
contextBar->setActiveBrush(newBrush);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -297,10 +293,7 @@ void ChangeBrushCommand::onExecute(Context* context)
|
|||||||
|
|
||||||
ImageRef newImg2(crop_image(newImg.get(), cropBounds, bg));
|
ImageRef newImg2(crop_image(newImg.get(), cropBounds, bg));
|
||||||
ImageRef newMsk2(crop_image(newMsk.get(), cropBounds, bg));
|
ImageRef newMsk2(crop_image(newMsk.get(), cropBounds, bg));
|
||||||
|
BrushRef newBrush = brush->cloneWithExistingImages(newImg2, newMsk2);
|
||||||
BrushRef newBrush = std::make_shared<Brush>(*brush);
|
|
||||||
newBrush->setImage(newImg.get(),
|
|
||||||
newMsk.get());
|
|
||||||
contextBar->setActiveBrush(newBrush);
|
contextBar->setActiveBrush(newBrush);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -484,7 +484,8 @@ protected:
|
|||||||
std::string onGetFriendlyName() const override;
|
std::string onGetFriendlyName() const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool m_useUI;
|
bool m_showDlg;
|
||||||
|
bool m_showProgress;
|
||||||
doc::PixelFormat m_format;
|
doc::PixelFormat m_format;
|
||||||
render::Dithering m_dithering;
|
render::Dithering m_dithering;
|
||||||
doc::RgbMapAlgorithm m_rgbmap;
|
doc::RgbMapAlgorithm m_rgbmap;
|
||||||
@ -494,7 +495,8 @@ private:
|
|||||||
ChangePixelFormatCommand::ChangePixelFormatCommand()
|
ChangePixelFormatCommand::ChangePixelFormatCommand()
|
||||||
: Command(CommandId::ChangePixelFormat(), CmdUIOnlyFlag)
|
: Command(CommandId::ChangePixelFormat(), CmdUIOnlyFlag)
|
||||||
{
|
{
|
||||||
m_useUI = true;
|
m_showDlg = true;
|
||||||
|
m_showProgress = true;
|
||||||
m_format = IMAGE_RGB;
|
m_format = IMAGE_RGB;
|
||||||
m_dithering = render::Dithering();
|
m_dithering = render::Dithering();
|
||||||
m_rgbmap = doc::RgbMapAlgorithm::DEFAULT;
|
m_rgbmap = doc::RgbMapAlgorithm::DEFAULT;
|
||||||
@ -503,15 +505,20 @@ ChangePixelFormatCommand::ChangePixelFormatCommand()
|
|||||||
|
|
||||||
void ChangePixelFormatCommand::onLoadParams(const Params& params)
|
void ChangePixelFormatCommand::onLoadParams(const Params& params)
|
||||||
{
|
{
|
||||||
m_useUI = false;
|
m_showDlg = false;
|
||||||
|
m_showProgress = true;
|
||||||
|
|
||||||
std::string format = params.get("format");
|
std::string format = params.get("format");
|
||||||
if (format == "rgb") m_format = IMAGE_RGB;
|
if (format == "rgb") m_format = IMAGE_RGB;
|
||||||
else if (format == "grayscale" ||
|
else if (format == "grayscale" ||
|
||||||
format == "gray") m_format = IMAGE_GRAYSCALE;
|
format == "gray") m_format = IMAGE_GRAYSCALE;
|
||||||
else if (format == "indexed") m_format = IMAGE_INDEXED;
|
else if (format == "indexed") m_format = IMAGE_INDEXED;
|
||||||
else
|
else {
|
||||||
m_useUI = true;
|
m_showDlg = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (params.has_param("ui"))
|
||||||
|
m_showDlg = m_showProgress = params.get_as<bool>("ui");
|
||||||
|
|
||||||
std::string dithering = params.get("dithering");
|
std::string dithering = params.get("dithering");
|
||||||
if (dithering == "ordered")
|
if (dithering == "ordered")
|
||||||
@ -587,7 +594,7 @@ bool ChangePixelFormatCommand::onEnabled(Context* context)
|
|||||||
if (!sprite)
|
if (!sprite)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (m_useUI)
|
if (m_showDlg)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (sprite->pixelFormat() == IMAGE_INDEXED &&
|
if (sprite->pixelFormat() == IMAGE_INDEXED &&
|
||||||
@ -600,7 +607,7 @@ bool ChangePixelFormatCommand::onEnabled(Context* context)
|
|||||||
|
|
||||||
bool ChangePixelFormatCommand::onChecked(Context* context)
|
bool ChangePixelFormatCommand::onChecked(Context* context)
|
||||||
{
|
{
|
||||||
if (m_useUI)
|
if (m_showDlg)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
const ContextReader reader(context);
|
const ContextReader reader(context);
|
||||||
@ -622,7 +629,7 @@ void ChangePixelFormatCommand::onExecute(Context* context)
|
|||||||
bool flatten = false;
|
bool flatten = false;
|
||||||
|
|
||||||
#ifdef ENABLE_UI
|
#ifdef ENABLE_UI
|
||||||
if (m_useUI) {
|
if (context->isUIAvailable() && m_showDlg) {
|
||||||
ColorModeWindow window(Editor::activeEditor());
|
ColorModeWindow window(Editor::activeEditor());
|
||||||
|
|
||||||
window.remapWindow();
|
window.remapWindow();
|
||||||
@ -651,7 +658,7 @@ void ChangePixelFormatCommand::onExecute(Context* context)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
{
|
{
|
||||||
SpriteJob job(context, doc, Strings::color_mode_title());
|
SpriteJob job(context, doc, Strings::color_mode_title(), m_showProgress);
|
||||||
Sprite* sprite(job.sprite());
|
Sprite* sprite(job.sprite());
|
||||||
|
|
||||||
// TODO this was moved in the main UI thread because
|
// TODO this was moved in the main UI thread because
|
||||||
@ -691,7 +698,7 @@ std::string ChangePixelFormatCommand::onGetFriendlyName() const
|
|||||||
{
|
{
|
||||||
std::string conversion;
|
std::string conversion;
|
||||||
|
|
||||||
if (!m_useUI) {
|
if (!m_showDlg) {
|
||||||
switch (m_format) {
|
switch (m_format) {
|
||||||
case IMAGE_RGB:
|
case IMAGE_RGB:
|
||||||
conversion = Strings::commands_ChangePixelFormat_RGB();
|
conversion = Strings::commands_ChangePixelFormat_RGB();
|
||||||
|
@ -93,10 +93,7 @@ bool ColorQuantizationCommand::onEnabled(Context* ctx)
|
|||||||
|
|
||||||
void ColorQuantizationCommand::onExecute(Context* ctx)
|
void ColorQuantizationCommand::onExecute(Context* ctx)
|
||||||
{
|
{
|
||||||
#ifdef ENABLE_UI
|
|
||||||
const bool ui = (params().ui() && ctx->isUIAvailable());
|
const bool ui = (params().ui() && ctx->isUIAvailable());
|
||||||
#endif
|
|
||||||
|
|
||||||
auto& pref = Preferences::instance();
|
auto& pref = Preferences::instance();
|
||||||
bool withAlpha = params().withAlpha();
|
bool withAlpha = params().withAlpha();
|
||||||
int maxColors = params().maxColors();
|
int maxColors = params().maxColors();
|
||||||
@ -183,7 +180,7 @@ void ColorQuantizationCommand::onExecute(Context* ctx)
|
|||||||
const Palette* curPalette = site.sprite()->palette(frame);
|
const Palette* curPalette = site.sprite()->palette(frame);
|
||||||
Palette tmpPalette(frame, entries.picks());
|
Palette tmpPalette(frame, entries.picks());
|
||||||
|
|
||||||
SpriteJob job(ctx, doc, "Color Quantization");
|
SpriteJob job(ctx, doc, "Color Quantization", ui);
|
||||||
const bool newBlend = pref.experimental.newBlend();
|
const bool newBlend = pref.experimental.newBlend();
|
||||||
job.startJobWithCallback(
|
job.startJobWithCallback(
|
||||||
[sprite, withAlpha, curPalette, &tmpPalette, &job, &entries,
|
[sprite, withAlpha, curPalette, &tmpPalette, &job, &entries,
|
||||||
|
@ -1193,8 +1193,9 @@ public:
|
|||||||
ExportSpriteSheetJob(
|
ExportSpriteSheetJob(
|
||||||
DocExporter& exporter,
|
DocExporter& exporter,
|
||||||
const Site& site,
|
const Site& site,
|
||||||
const ExportSpriteSheetParams& params)
|
const ExportSpriteSheetParams& params,
|
||||||
: Job(Strings::export_sprite_sheet_generating().c_str())
|
const bool showProgress)
|
||||||
|
: Job(Strings::export_sprite_sheet_generating(), showProgress)
|
||||||
, m_exporter(exporter)
|
, m_exporter(exporter)
|
||||||
, m_site(site)
|
, m_site(site)
|
||||||
, m_params(params) { }
|
, m_params(params) { }
|
||||||
@ -1373,7 +1374,9 @@ void ExportSpriteSheetCommand::onExecute(Context* context)
|
|||||||
std::unique_ptr<Doc> newDocument;
|
std::unique_ptr<Doc> newDocument;
|
||||||
#ifdef ENABLE_UI
|
#ifdef ENABLE_UI
|
||||||
if (context->isUIAvailable()) {
|
if (context->isUIAvailable()) {
|
||||||
ExportSpriteSheetJob job(exporter, site, params);
|
ExportSpriteSheetJob job(exporter, site, params,
|
||||||
|
// Progress bar can be disabled with ui=false
|
||||||
|
params.ui());
|
||||||
job.startJob();
|
job.startJob();
|
||||||
job.waitJob();
|
job.waitJob();
|
||||||
|
|
||||||
@ -1386,8 +1389,10 @@ void ExportSpriteSheetCommand::onExecute(Context* context)
|
|||||||
statusbar->showTip(1000, Strings::export_sprite_sheet_generated());
|
statusbar->showTip(1000, Strings::export_sprite_sheet_generated());
|
||||||
|
|
||||||
// Save the exported sprite sheet as a recent file
|
// Save the exported sprite sheet as a recent file
|
||||||
if (newDocument->isAssociatedToFile())
|
if (newDocument->isAssociatedToFile() &&
|
||||||
|
should_add_file_to_recents(context, params)) {
|
||||||
App::instance()->recentFiles()->addRecentFile(newDocument->filename());
|
App::instance()->recentFiles()->addRecentFile(newDocument->filename());
|
||||||
|
}
|
||||||
|
|
||||||
// Copy background and grid preferences
|
// Copy background and grid preferences
|
||||||
DocumentPreferences& newDocPref(
|
DocumentPreferences& newDocPref(
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
// Aseprite
|
// Aseprite
|
||||||
// Copyright (C) 2022 Igara Studio S.A.
|
// Copyright (C) 2022-2024 Igara Studio S.A.
|
||||||
//
|
//
|
||||||
// This program is distributed under the terms of
|
// This program is distributed under the terms of
|
||||||
// the End-User License Agreement for Aseprite.
|
// the End-User License Agreement for Aseprite.
|
||||||
@ -19,6 +19,7 @@ namespace app {
|
|||||||
|
|
||||||
struct ExportSpriteSheetParams : public NewParams {
|
struct ExportSpriteSheetParams : public NewParams {
|
||||||
Param<bool> ui { this, true, "ui" };
|
Param<bool> ui { this, true, "ui" };
|
||||||
|
Param<bool> recent { this, true, "recent" };
|
||||||
Param<bool> askOverwrite { this, true, { "askOverwrite", "ask-overwrite" } };
|
Param<bool> askOverwrite { this, true, { "askOverwrite", "ask-overwrite" } };
|
||||||
Param<app::SpriteSheetType> type { this, app::SpriteSheetType::None, "type" };
|
Param<app::SpriteSheetType> type { this, app::SpriteSheetType::None, "type" };
|
||||||
Param<int> columns { this, 0, "columns" };
|
Param<int> columns { this, 0, "columns" };
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
// Aseprite
|
// Aseprite
|
||||||
// Copyright (C) 2019-2021 Igara Studio S.A.
|
// Copyright (C) 2019-2024 Igara Studio S.A.
|
||||||
// Copyright (C) 2001-2018 David Capello
|
// Copyright (C) 2001-2018 David Capello
|
||||||
//
|
//
|
||||||
// This program is distributed under the terms of
|
// This program is distributed under the terms of
|
||||||
@ -36,8 +36,8 @@ namespace app {
|
|||||||
|
|
||||||
class OpenFileJob : public Job, public IFileOpProgress {
|
class OpenFileJob : public Job, public IFileOpProgress {
|
||||||
public:
|
public:
|
||||||
OpenFileJob(FileOp* fop)
|
OpenFileJob(FileOp* fop, const bool showProgress)
|
||||||
: Job(Strings::open_file_loading().c_str())
|
: Job(Strings::open_file_loading(), showProgress)
|
||||||
, m_fop(fop)
|
, m_fop(fop)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@ -76,6 +76,7 @@ private:
|
|||||||
|
|
||||||
OpenFileCommand::OpenFileCommand()
|
OpenFileCommand::OpenFileCommand()
|
||||||
: Command(CommandId::OpenFile(), CmdRecordableFlag)
|
: Command(CommandId::OpenFile(), CmdRecordableFlag)
|
||||||
|
, m_ui(true)
|
||||||
, m_repeatCheckbox(false)
|
, m_repeatCheckbox(false)
|
||||||
, m_oneFrame(false)
|
, m_oneFrame(false)
|
||||||
, m_seqDecision(gen::SequenceDecision::ASK)
|
, m_seqDecision(gen::SequenceDecision::ASK)
|
||||||
@ -86,6 +87,12 @@ void OpenFileCommand::onLoadParams(const Params& params)
|
|||||||
{
|
{
|
||||||
m_filename = params.get("filename");
|
m_filename = params.get("filename");
|
||||||
m_folder = params.get("folder"); // Initial folder
|
m_folder = params.get("folder"); // Initial folder
|
||||||
|
|
||||||
|
if (params.has_param("ui"))
|
||||||
|
m_ui = params.get_as<bool>("ui");
|
||||||
|
else
|
||||||
|
m_ui = true;
|
||||||
|
|
||||||
m_repeatCheckbox = params.get_as<bool>("repeat_checkbox");
|
m_repeatCheckbox = params.get_as<bool>("repeat_checkbox");
|
||||||
m_oneFrame = params.get_as<bool>("oneframe");
|
m_oneFrame = params.get_as<bool>("oneframe");
|
||||||
|
|
||||||
@ -220,7 +227,7 @@ void OpenFileCommand::onExecute(Context* context)
|
|||||||
m_usedFiles.push_back(fn);
|
m_usedFiles.push_back(fn);
|
||||||
}
|
}
|
||||||
|
|
||||||
OpenFileJob task(fop.get());
|
OpenFileJob task(fop.get(), m_ui);
|
||||||
task.showProgressWindow();
|
task.showProgressWindow();
|
||||||
|
|
||||||
// Post-load processing, it is called from the GUI because may require user intervention.
|
// Post-load processing, it is called from the GUI because may require user intervention.
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
// Aseprite
|
// Aseprite
|
||||||
// Copyright (C) 2020-2021 Igara Studio S.A.
|
// Copyright (C) 2020-2024 Igara Studio S.A.
|
||||||
// Copyright (C) 2016-2018 David Capello
|
// Copyright (C) 2016-2018 David Capello
|
||||||
//
|
//
|
||||||
// This program is distributed under the terms of
|
// This program is distributed under the terms of
|
||||||
@ -36,6 +36,7 @@ namespace app {
|
|||||||
private:
|
private:
|
||||||
std::string m_filename;
|
std::string m_filename;
|
||||||
std::string m_folder;
|
std::string m_folder;
|
||||||
|
bool m_ui;
|
||||||
bool m_repeatCheckbox;
|
bool m_repeatCheckbox;
|
||||||
bool m_oneFrame;
|
bool m_oneFrame;
|
||||||
base::paths m_usedFiles;
|
base::paths m_usedFiles;
|
||||||
|
@ -46,8 +46,10 @@ public:
|
|||||||
|
|
||||||
RotateJob(Context* ctx, Doc* doc,
|
RotateJob(Context* ctx, Doc* doc,
|
||||||
const std::string& jobName,
|
const std::string& jobName,
|
||||||
int angle, const CelList& cels, bool rotateSprite)
|
int angle, const CelList& cels,
|
||||||
: SpriteJob(ctx, doc, jobName)
|
const bool rotateSprite,
|
||||||
|
const bool showProgress)
|
||||||
|
: SpriteJob(ctx, doc, jobName, showProgress)
|
||||||
, m_cels(cels)
|
, m_cels(cels)
|
||||||
, m_rotateSprite(rotateSprite) {
|
, m_rotateSprite(rotateSprite) {
|
||||||
m_angle = angle;
|
m_angle = angle;
|
||||||
@ -167,12 +169,18 @@ protected:
|
|||||||
RotateCommand::RotateCommand()
|
RotateCommand::RotateCommand()
|
||||||
: Command(CommandId::Rotate(), CmdRecordableFlag)
|
: Command(CommandId::Rotate(), CmdRecordableFlag)
|
||||||
{
|
{
|
||||||
|
m_ui = true;
|
||||||
m_flipMask = false;
|
m_flipMask = false;
|
||||||
m_angle = 0;
|
m_angle = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RotateCommand::onLoadParams(const Params& params)
|
void RotateCommand::onLoadParams(const Params& params)
|
||||||
{
|
{
|
||||||
|
if (params.has_param("ui"))
|
||||||
|
m_ui = params.get_as<bool>("ui");
|
||||||
|
else
|
||||||
|
m_ui = true;
|
||||||
|
|
||||||
std::string target = params.get("target");
|
std::string target = params.get("target");
|
||||||
m_flipMask = (target == "mask");
|
m_flipMask = (target == "mask");
|
||||||
|
|
||||||
@ -238,7 +246,7 @@ void RotateCommand::onExecute(Context* context)
|
|||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
RotateJob job(context, doc, friendlyName(), m_angle, cels, rotateSprite);
|
RotateJob job(context, doc, friendlyName(), m_angle, cels, rotateSprite, m_ui);
|
||||||
job.startJob();
|
job.startJob();
|
||||||
job.waitJob();
|
job.waitJob();
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
// Aseprite
|
// Aseprite
|
||||||
|
// Copyright (C) 2024 Igara Studio S.A.
|
||||||
// Copyright (C) 2001-2017 David Capello
|
// Copyright (C) 2001-2017 David Capello
|
||||||
//
|
//
|
||||||
// This program is distributed under the terms of
|
// This program is distributed under the terms of
|
||||||
@ -27,6 +28,7 @@ namespace app {
|
|||||||
std::string onGetFriendlyName() const override;
|
std::string onGetFriendlyName() const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
bool m_ui;
|
||||||
bool m_flipMask;
|
bool m_flipMask;
|
||||||
int m_angle;
|
int m_angle;
|
||||||
};
|
};
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
// Aseprite
|
// Aseprite
|
||||||
// Copyright (C) 2019-2023 Igara Studio S.A.
|
// Copyright (C) 2019-2024 Igara Studio S.A.
|
||||||
// Copyright (C) 2001-2018 David Capello
|
// Copyright (C) 2001-2018 David Capello
|
||||||
//
|
//
|
||||||
// This program is distributed under the terms of
|
// This program is distributed under the terms of
|
||||||
@ -49,8 +49,8 @@ namespace app {
|
|||||||
|
|
||||||
class SaveFileJob : public Job, public IFileOpProgress {
|
class SaveFileJob : public Job, public IFileOpProgress {
|
||||||
public:
|
public:
|
||||||
SaveFileJob(FileOp* fop)
|
SaveFileJob(FileOp* fop, const bool showProgressBar)
|
||||||
: Job(Strings::save_file_saving().c_str())
|
: Job(Strings::save_file_saving(), showProgressBar)
|
||||||
, m_fop(fop)
|
, m_fop(fop)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@ -239,7 +239,7 @@ void SaveFileBaseCommand::saveDocumentInBackground(
|
|||||||
if (resizeOnTheFly == ResizeOnTheFly::On)
|
if (resizeOnTheFly == ResizeOnTheFly::On)
|
||||||
fop->setOnTheFlyScale(scale);
|
fop->setOnTheFlyScale(scale);
|
||||||
|
|
||||||
SaveFileJob job(fop.get());
|
SaveFileJob job(fop.get(), params().ui());
|
||||||
job.showProgressWindow();
|
job.showProgressWindow();
|
||||||
|
|
||||||
if (fop->hasError()) {
|
if (fop->hasError()) {
|
||||||
@ -257,7 +257,7 @@ void SaveFileBaseCommand::saveDocumentInBackground(
|
|||||||
document->impossibleToBackToSavedState();
|
document->impossibleToBackToSavedState();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (context->isUIAvailable() && params().ui())
|
if (should_add_file_to_recents(context, params()))
|
||||||
App::instance()->recentFiles()->addRecentFile(filename);
|
App::instance()->recentFiles()->addRecentFile(filename);
|
||||||
|
|
||||||
if (markAsSaved == MarkAsSaved::On) {
|
if (markAsSaved == MarkAsSaved::On) {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
// Aseprite
|
// Aseprite
|
||||||
// Copyright (C) 2021-2022 Igara Studio S.A.
|
// Copyright (C) 2021-2024 Igara Studio S.A.
|
||||||
// Copyright (C) 2001-2018 David Capello
|
// Copyright (C) 2001-2018 David Capello
|
||||||
//
|
//
|
||||||
// This program is distributed under the terms of
|
// This program is distributed under the terms of
|
||||||
@ -23,6 +23,7 @@ namespace app {
|
|||||||
|
|
||||||
struct SaveFileParams : public NewParams {
|
struct SaveFileParams : public NewParams {
|
||||||
Param<bool> ui { this, true, { "ui", "useUI" } };
|
Param<bool> ui { this, true, { "ui", "useUI" } };
|
||||||
|
Param<bool> recent { this, true, "recent" };
|
||||||
Param<std::string> filename { this, std::string(), "filename" };
|
Param<std::string> filename { this, std::string(), "filename" };
|
||||||
Param<std::string> filenameFormat { this, std::string(), { "filenameFormat", "filename-format" } };
|
Param<std::string> filenameFormat { this, std::string(), { "filenameFormat", "filename-format" } };
|
||||||
Param<std::string> tag { this, std::string(), { "tag", "frame-tag" } };
|
Param<std::string> tag { this, std::string(), { "tag", "frame-tag" } };
|
||||||
|
@ -83,8 +83,9 @@ public:
|
|||||||
SpriteSizeJob(Context* ctx, Doc* doc,
|
SpriteSizeJob(Context* ctx, Doc* doc,
|
||||||
const int new_width,
|
const int new_width,
|
||||||
const int new_height,
|
const int new_height,
|
||||||
const ResizeMethod resize_method)
|
const ResizeMethod resize_method,
|
||||||
: SpriteJob(ctx, doc, Strings::sprite_size_title()) {
|
const bool showProgress)
|
||||||
|
: SpriteJob(ctx, doc, Strings::sprite_size_title(), showProgress) {
|
||||||
m_new_width = new_width;
|
m_new_width = new_width;
|
||||||
m_new_height = new_height;
|
m_new_height = new_height;
|
||||||
m_resize_method = resize_method;
|
m_resize_method = resize_method;
|
||||||
@ -373,9 +374,7 @@ bool SpriteSizeCommand::onEnabled(Context* context)
|
|||||||
|
|
||||||
void SpriteSizeCommand::onExecute(Context* context)
|
void SpriteSizeCommand::onExecute(Context* context)
|
||||||
{
|
{
|
||||||
#ifdef ENABLE_UI
|
|
||||||
const bool ui = (params().ui() && context->isUIAvailable());
|
const bool ui = (params().ui() && context->isUIAvailable());
|
||||||
#endif
|
|
||||||
const Site site = context->activeSite();
|
const Site site = context->activeSite();
|
||||||
Doc* doc = site.document();
|
Doc* doc = site.document();
|
||||||
Sprite* sprite = site.sprite();
|
Sprite* sprite = site.sprite();
|
||||||
@ -465,7 +464,7 @@ void SpriteSizeCommand::onExecute(Context* context)
|
|||||||
new_height = std::clamp(new_height, 1, DOC_SPRITE_MAX_HEIGHT);
|
new_height = std::clamp(new_height, 1, DOC_SPRITE_MAX_HEIGHT);
|
||||||
|
|
||||||
{
|
{
|
||||||
SpriteSizeJob job(context, doc, new_width, new_height, resize_method);
|
SpriteSizeJob job(context, doc, new_width, new_height, resize_method, ui);
|
||||||
job.startJob();
|
job.startJob();
|
||||||
job.waitJob();
|
job.waitJob();
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
// Aseprite
|
// Aseprite
|
||||||
// Copyright (C) 2019-2022 Igara Studio S.A.
|
// Copyright (C) 2019-2024 Igara Studio S.A.
|
||||||
//
|
//
|
||||||
// This program is distributed under the terms of
|
// This program is distributed under the terms of
|
||||||
// the End-User License Agreement for Aseprite.
|
// the End-User License Agreement for Aseprite.
|
||||||
@ -9,6 +9,7 @@
|
|||||||
|
|
||||||
#include "app/commands/command.h"
|
#include "app/commands/command.h"
|
||||||
#include "app/commands/params.h"
|
#include "app/commands/params.h"
|
||||||
|
#include "app/context.h"
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <string>
|
#include <string>
|
||||||
@ -152,6 +153,20 @@ namespace app {
|
|||||||
T m_params;
|
T m_params;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Common logic to know if we should add a file to recent files. We
|
||||||
|
// offer two params: "ui" and "recent", if "recent" is specified, we
|
||||||
|
// do what it says. In other case "ui" is like the default value of
|
||||||
|
// "recent", i.e. if there is ui=true, we add to recent, if there is
|
||||||
|
// ui=false, we don't add it.
|
||||||
|
template<typename T>
|
||||||
|
inline bool should_add_file_to_recents(const Context* ctx,
|
||||||
|
const T& params) {
|
||||||
|
ASSERT(ctx);
|
||||||
|
return (ctx->isUIAvailable()
|
||||||
|
&& ((params.recent.isSet() && params.recent()) ||
|
||||||
|
(!params.recent.isSet() && params.ui())));
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace app
|
} // namespace app
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
// Aseprite
|
// Aseprite
|
||||||
|
// Copyright (C) 2024 Igara Studio S.A.
|
||||||
// Copyright (C) 2001-2015 David Capello
|
// Copyright (C) 2001-2015 David Capello
|
||||||
//
|
//
|
||||||
// This program is distributed under the terms of
|
// This program is distributed under the terms of
|
||||||
@ -43,10 +44,10 @@ namespace crash {
|
|||||||
|
|
||||||
// Adds a version (we don't know if the version if the latest one)
|
// Adds a version (we don't know if the version if the latest one)
|
||||||
void add(doc::ObjectVersion ver) {
|
void add(doc::ObjectVersion ver) {
|
||||||
auto minver = std::min_element(m_vers, m_vers+2);
|
auto* minver = std::min_element(m_vers, m_vers+size());
|
||||||
if (*minver < ver) {
|
if (*minver < ver) {
|
||||||
*minver = ver;
|
*minver = ver;
|
||||||
std::sort(m_vers, m_vers+2, std::greater<doc::ObjectVersion>());
|
std::sort(m_vers, m_vers+size(), std::greater<doc::ObjectVersion>());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
// Aseprite
|
// Aseprite
|
||||||
// Copyright (C) 2018-2023 Igara Studio S.A.
|
// Copyright (C) 2018-2024 Igara Studio S.A.
|
||||||
// Copyright (C) 2001-2018 David Capello
|
// Copyright (C) 2001-2018 David Capello
|
||||||
//
|
//
|
||||||
// This program is distributed under the terms of
|
// This program is distributed under the terms of
|
||||||
@ -58,6 +58,14 @@ using namespace doc;
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
// Returns true if the file was saved correctly (has the "FINE" magic
|
||||||
|
// number), so we can ignore broken versions of objects directly.
|
||||||
|
bool check_magic_number(const std::string& fn)
|
||||||
|
{
|
||||||
|
std::ifstream s(FSTREAM_PATH(fn), std::ifstream::binary);
|
||||||
|
return (read32(s) == MAGIC_NUMBER);
|
||||||
|
}
|
||||||
|
|
||||||
class Reader : public SubObjectsIO {
|
class Reader : public SubObjectsIO {
|
||||||
public:
|
public:
|
||||||
Reader(const std::string& dir,
|
Reader(const std::string& dir,
|
||||||
@ -83,6 +91,11 @@ public:
|
|||||||
if (!id || !ver)
|
if (!id || !ver)
|
||||||
continue; // Error converting strings to ID/ver
|
continue; // Error converting strings to ID/ver
|
||||||
|
|
||||||
|
if (!check_magic_number(base::join_path(m_dir, fn))) {
|
||||||
|
RECO_TRACE("RECO: Ignoring invalid file %s (no magic number)\n", fn.c_str());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
ObjVersions& versions = m_objVersions[id];
|
ObjVersions& versions = m_objVersions[id];
|
||||||
versions.add(ver);
|
versions.add(ver);
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
// Aseprite
|
// Aseprite
|
||||||
// Copyright (C) 2019-2023 Igara Studio S.A.
|
// Copyright (C) 2019-2024 Igara Studio S.A.
|
||||||
// Copyright (C) 2018 David Capello
|
// Copyright (C) 2018 David Capello
|
||||||
//
|
//
|
||||||
// This program is distributed under the terms of
|
// This program is distributed under the terms of
|
||||||
@ -191,8 +191,8 @@ DocDiff compare_docs(const Doc* a,
|
|||||||
if (aLay->type() != bLay->type() ||
|
if (aLay->type() != bLay->type() ||
|
||||||
aLay->name() != bLay->name() ||
|
aLay->name() != bLay->name() ||
|
||||||
aLay->userData() != bLay->userData() ||
|
aLay->userData() != bLay->userData() ||
|
||||||
((int(aLay->flags()) & int(LayerFlags::PersistentFlagsMask)) !=
|
((int(aLay->flags()) & int(LayerFlags::StructuralFlagsMask)) !=
|
||||||
(int(bLay->flags()) & int(LayerFlags::PersistentFlagsMask))) ||
|
(int(bLay->flags()) & int(LayerFlags::StructuralFlagsMask))) ||
|
||||||
(aLay->isImage() && bLay->isImage() &&
|
(aLay->isImage() && bLay->isImage() &&
|
||||||
(((const LayerImage*)aLay)->opacity() != ((const LayerImage*)bLay)->opacity())) ||
|
(((const LayerImage*)aLay)->opacity() != ((const LayerImage*)bLay)->opacity())) ||
|
||||||
(aLay->isTilemap() && bLay->isTilemap() &&
|
(aLay->isTilemap() && bLay->isTilemap() &&
|
||||||
|
@ -33,18 +33,19 @@ int Job::runningJobs()
|
|||||||
return g_runningJobs;
|
return g_runningJobs;
|
||||||
}
|
}
|
||||||
|
|
||||||
Job::Job(const std::string& jobName)
|
Job::Job(const std::string& jobName,
|
||||||
|
const bool showProgress)
|
||||||
{
|
{
|
||||||
m_last_progress = 0.0;
|
m_last_progress = 0.0;
|
||||||
m_done_flag = false;
|
m_done_flag = false;
|
||||||
m_canceled_flag = false;
|
m_canceled_flag = false;
|
||||||
|
|
||||||
if (App::instance()->isGui()) {
|
if (showProgress && App::instance()->isGui()) {
|
||||||
m_alert_window = ui::Alert::create(
|
m_alert_window = ui::Alert::create(
|
||||||
fmt::format(Strings::alerts_job_working(), jobName));
|
fmt::format(Strings::alerts_job_working(), jobName));
|
||||||
m_alert_window->addProgress();
|
m_alert_window->addProgress();
|
||||||
|
|
||||||
m_timer.reset(new ui::Timer(kMonitoringPeriod, m_alert_window.get()));
|
m_timer = std::make_unique<ui::Timer>(kMonitoringPeriod, m_alert_window.get());
|
||||||
m_timer->Tick.connect(&Job::onMonitoringTick, this);
|
m_timer->Tick.connect(&Job::onMonitoringTick, this);
|
||||||
m_timer->start();
|
m_timer->start();
|
||||||
}
|
}
|
||||||
@ -53,7 +54,7 @@ Job::Job(const std::string& jobName)
|
|||||||
Job::~Job()
|
Job::~Job()
|
||||||
{
|
{
|
||||||
if (App::instance()->isGui()) {
|
if (App::instance()->isGui()) {
|
||||||
ASSERT(!m_timer->isRunning());
|
ASSERT(!m_timer || !m_timer->isRunning());
|
||||||
|
|
||||||
if (m_alert_window)
|
if (m_alert_window)
|
||||||
m_alert_window->closeWindow(NULL);
|
m_alert_window->closeWindow(NULL);
|
||||||
|
@ -24,7 +24,10 @@ namespace app {
|
|||||||
public:
|
public:
|
||||||
static int runningJobs();
|
static int runningJobs();
|
||||||
|
|
||||||
Job(const std::string& jobName);
|
Job(const std::string& jobName, bool showProgress);
|
||||||
|
Job() = delete;
|
||||||
|
Job(const Job&) = delete;
|
||||||
|
Job& operator==(const Job&) = delete;
|
||||||
virtual ~Job();
|
virtual ~Job();
|
||||||
|
|
||||||
// Starts the job calling onJob() event in another thread and
|
// Starts the job calling onJob() event in another thread and
|
||||||
@ -68,12 +71,6 @@ namespace app {
|
|||||||
bool m_done_flag;
|
bool m_done_flag;
|
||||||
bool m_canceled_flag;
|
bool m_canceled_flag;
|
||||||
std::exception_ptr m_error;
|
std::exception_ptr m_error;
|
||||||
|
|
||||||
// these methods are privated and not defined
|
|
||||||
Job();
|
|
||||||
Job(const Job&);
|
|
||||||
Job& operator==(const Job&);
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace app
|
} // namespace app
|
||||||
|
@ -10,6 +10,6 @@
|
|||||||
|
|
||||||
// Increment this value if the scripting API is modified between two
|
// Increment this value if the scripting API is modified between two
|
||||||
// released Aseprite versions.
|
// released Aseprite versions.
|
||||||
#define API_VERSION 27
|
#define API_VERSION 28
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
121
src/app/script/app_os_object.cpp
Normal file
121
src/app/script/app_os_object.cpp
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
// Aseprite
|
||||||
|
// Copyright (C) 2024 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 "app/script/engine.h"
|
||||||
|
#include "app/script/luacpp.h"
|
||||||
|
#include "base/config.h"
|
||||||
|
#include "base/platform.h"
|
||||||
|
#include "updater/user_agent.h"
|
||||||
|
|
||||||
|
namespace app {
|
||||||
|
namespace script {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
struct AppOS { };
|
||||||
|
|
||||||
|
int AppOS_get_name(lua_State* L)
|
||||||
|
{
|
||||||
|
#if LAF_WINDOWS
|
||||||
|
lua_pushstring(L, "Windows");
|
||||||
|
#elif LAF_MACOS
|
||||||
|
lua_pushstring(L, "macOS");
|
||||||
|
#elif LAF_LINUX
|
||||||
|
lua_pushstring(L, "Linux");
|
||||||
|
#else
|
||||||
|
lua_pushnil(L);
|
||||||
|
#endif
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int AppOS_get_version(lua_State* L)
|
||||||
|
{
|
||||||
|
base::Platform p = base::get_platform();
|
||||||
|
push_version(L, p.osVer);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int AppOS_get_fullName(lua_State* L)
|
||||||
|
{
|
||||||
|
lua_pushstring(L, updater::getFullOSString().c_str());
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int AppOS_get_windows(lua_State* L)
|
||||||
|
{
|
||||||
|
lua_pushboolean(L, base::Platform::os == base::Platform::OS::Windows);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int AppOS_get_macos(lua_State* L)
|
||||||
|
{
|
||||||
|
lua_pushboolean(L, base::Platform::os == base::Platform::OS::macOS);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int AppOS_get_linux(lua_State* L)
|
||||||
|
{
|
||||||
|
lua_pushboolean(L, base::Platform::os == base::Platform::OS::Linux);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int AppOS_get_x64(lua_State* L)
|
||||||
|
{
|
||||||
|
lua_pushboolean(L, base::Platform::arch == base::Platform::Arch::x64);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int AppOS_get_x86(lua_State* L)
|
||||||
|
{
|
||||||
|
lua_pushboolean(L, base::Platform::arch == base::Platform::Arch::x86);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int AppOS_get_arm64(lua_State* L)
|
||||||
|
{
|
||||||
|
lua_pushboolean(L, base::Platform::arch == base::Platform::Arch::arm64);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Property AppOS_properties[] = {
|
||||||
|
{ "name", AppOS_get_name, nullptr },
|
||||||
|
{ "version", AppOS_get_version, nullptr },
|
||||||
|
{ "fullName", AppOS_get_fullName, nullptr },
|
||||||
|
{ "windows", AppOS_get_windows, nullptr },
|
||||||
|
{ "macos", AppOS_get_macos, nullptr },
|
||||||
|
{ "linux", AppOS_get_linux, nullptr },
|
||||||
|
{ "x64", AppOS_get_x64, nullptr },
|
||||||
|
{ "x86", AppOS_get_x86, nullptr },
|
||||||
|
{ "arm64", AppOS_get_arm64, nullptr },
|
||||||
|
{ nullptr, nullptr, nullptr }
|
||||||
|
};
|
||||||
|
|
||||||
|
const luaL_Reg AppOS_methods[] = {
|
||||||
|
{ nullptr, nullptr }
|
||||||
|
};
|
||||||
|
|
||||||
|
} // anonymous namespace
|
||||||
|
|
||||||
|
DEF_MTNAME(AppOS);
|
||||||
|
|
||||||
|
void register_app_os_object(lua_State* L)
|
||||||
|
{
|
||||||
|
REG_CLASS(L, AppOS);
|
||||||
|
REG_CLASS_PROPERTIES(L, AppOS);
|
||||||
|
|
||||||
|
lua_getglobal(L, "app");
|
||||||
|
lua_pushstring(L, "os");
|
||||||
|
push_new<AppOS>(L);
|
||||||
|
lua_rawset(L, -3);
|
||||||
|
lua_pop(L, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace script
|
||||||
|
} // namespace app
|
@ -1,5 +1,5 @@
|
|||||||
// Aseprite
|
// Aseprite
|
||||||
// Copyright (C) 2019-2020 Igara Studio S.A.
|
// Copyright (C) 2019-2024 Igara Studio S.A.
|
||||||
//
|
//
|
||||||
// This program is distributed under the terms of
|
// This program is distributed under the terms of
|
||||||
// the End-User License Agreement for Aseprite.
|
// the End-User License Agreement for Aseprite.
|
||||||
@ -36,7 +36,7 @@ BrushRef Brush_new(lua_State* L, int index)
|
|||||||
if (auto brush2 = may_get_obj<BrushObj>(L, index)) {
|
if (auto brush2 = may_get_obj<BrushObj>(L, index)) {
|
||||||
ASSERT(brush2->brush);
|
ASSERT(brush2->brush);
|
||||||
if (brush2->brush)
|
if (brush2->brush)
|
||||||
brush.reset(new Brush(*brush2->brush));
|
brush = brush2->brush->cloneWithNewImages();
|
||||||
}
|
}
|
||||||
else if (auto image = may_get_image_from_arg(L, index)) {
|
else if (auto image = may_get_image_from_arg(L, index)) {
|
||||||
if (image) {
|
if (image) {
|
||||||
|
@ -165,6 +165,7 @@ int os_clock(lua_State* L)
|
|||||||
void register_app_object(lua_State* L);
|
void register_app_object(lua_State* L);
|
||||||
void register_app_pixel_color_object(lua_State* L);
|
void register_app_pixel_color_object(lua_State* L);
|
||||||
void register_app_fs_object(lua_State* L);
|
void register_app_fs_object(lua_State* L);
|
||||||
|
void register_app_os_object(lua_State* L);
|
||||||
void register_app_command_object(lua_State* L);
|
void register_app_command_object(lua_State* L);
|
||||||
void register_app_preferences_object(lua_State* L);
|
void register_app_preferences_object(lua_State* L);
|
||||||
void register_json_object(lua_State* L);
|
void register_json_object(lua_State* L);
|
||||||
@ -259,6 +260,7 @@ Engine::Engine()
|
|||||||
register_app_object(L);
|
register_app_object(L);
|
||||||
register_app_pixel_color_object(L);
|
register_app_pixel_color_object(L);
|
||||||
register_app_fs_object(L);
|
register_app_fs_object(L);
|
||||||
|
register_app_os_object(L);
|
||||||
register_app_command_object(L);
|
register_app_command_object(L);
|
||||||
register_app_preferences_object(L);
|
register_app_preferences_object(L);
|
||||||
register_json_object(L);
|
register_json_object(L);
|
||||||
|
@ -16,8 +16,9 @@
|
|||||||
namespace app {
|
namespace app {
|
||||||
|
|
||||||
SpriteJob::SpriteJob(Context* ctx, Doc* doc,
|
SpriteJob::SpriteJob(Context* ctx, Doc* doc,
|
||||||
const std::string& jobName)
|
const std::string& jobName,
|
||||||
: Job(jobName)
|
const bool showProgress)
|
||||||
|
: Job(jobName, showProgress)
|
||||||
, m_doc(doc)
|
, m_doc(doc)
|
||||||
, m_sprite(doc->sprite())
|
, m_sprite(doc->sprite())
|
||||||
, m_tx(Tx::DontLockDoc, ctx, doc, jobName, ModifyDocument)
|
, m_tx(Tx::DontLockDoc, ctx, doc, jobName, ModifyDocument)
|
||||||
|
@ -37,7 +37,8 @@ class SpriteJob : public Job,
|
|||||||
public render::TaskDelegate {
|
public render::TaskDelegate {
|
||||||
public:
|
public:
|
||||||
SpriteJob(Context* ctx, Doc* doc,
|
SpriteJob(Context* ctx, Doc* doc,
|
||||||
const std::string& jobName);
|
const std::string& jobName,
|
||||||
|
const bool showProgress);
|
||||||
~SpriteJob();
|
~SpriteJob();
|
||||||
|
|
||||||
Doc* document() const { return m_doc; }
|
Doc* document() const { return m_doc; }
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
// Aseprite
|
// Aseprite
|
||||||
// Copyright (C) 2018-2022 Igara Studio S.A.
|
// Copyright (C) 2018-2024 Igara Studio S.A.
|
||||||
// Copyright (C) 2001-2018 David Capello
|
// Copyright (C) 2001-2018 David Capello
|
||||||
//
|
//
|
||||||
// This program is distributed under the terms of
|
// This program is distributed under the terms of
|
||||||
@ -564,7 +564,7 @@ public:
|
|||||||
// point so we can restore it when erasing a point because of
|
// point so we can restore it when erasing a point because of
|
||||||
// pixel-perfect. So we set the following flag to indicate this, and
|
// pixel-perfect. So we set the following flag to indicate this, and
|
||||||
// use it in doTransformPoint.
|
// use it in doTransformPoint.
|
||||||
m_saveStrokeArea = (c == m_pts.size() - 1 && !m_retainedTracePolicyLast);
|
m_saveStrokeArea = (c == m_pts.size() - 1);
|
||||||
if (m_saveStrokeArea) {
|
if (m_saveStrokeArea) {
|
||||||
clearPointshapeStrokePtAreas();
|
clearPointshapeStrokePtAreas();
|
||||||
setLastPtIndex(c);
|
setLastPtIndex(c);
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
// Aseprite
|
// Aseprite
|
||||||
// Copyright (C) 2019-2023 Igara Studio S.A.
|
// Copyright (C) 2019-2024 Igara Studio S.A.
|
||||||
// Copyright (C) 2001-2018 David Capello
|
// Copyright (C) 2001-2018 David Capello
|
||||||
//
|
//
|
||||||
// This program is distributed under the terms of
|
// This program is distributed under the terms of
|
||||||
@ -44,7 +44,8 @@ using namespace filters;
|
|||||||
ToolLoopManager::ToolLoopManager(ToolLoop* toolLoop)
|
ToolLoopManager::ToolLoopManager(ToolLoop* toolLoop)
|
||||||
: m_toolLoop(toolLoop)
|
: m_toolLoop(toolLoop)
|
||||||
, m_canceled(false)
|
, m_canceled(false)
|
||||||
, m_brush0(*toolLoop->getBrush())
|
, m_brushSize0(toolLoop->getBrush()->size())
|
||||||
|
, m_brushAngle0(toolLoop->getBrush()->angle())
|
||||||
, m_dynamics(toolLoop->getDynamics())
|
, m_dynamics(toolLoop->getDynamics())
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@ -202,6 +203,12 @@ void ToolLoopManager::movement(Pointer pointer)
|
|||||||
doLoopStep(false);
|
doLoopStep(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ToolLoopManager::disableMouseStabilizer()
|
||||||
|
{
|
||||||
|
// Disable mouse stabilizer for the current ToolLoopManager
|
||||||
|
m_dynamics.stabilizer = false;
|
||||||
|
}
|
||||||
|
|
||||||
void ToolLoopManager::doLoopStep(bool lastStep)
|
void ToolLoopManager::doLoopStep(bool lastStep)
|
||||||
{
|
{
|
||||||
// Original set of points to interwine (original user stroke,
|
// Original set of points to interwine (original user stroke,
|
||||||
@ -358,8 +365,8 @@ Stroke::Pt ToolLoopManager::getSpriteStrokePt(const Pointer& pointer)
|
|||||||
{
|
{
|
||||||
// Convert the screen point to a sprite point
|
// Convert the screen point to a sprite point
|
||||||
Stroke::Pt spritePoint = pointer.point();
|
Stroke::Pt spritePoint = pointer.point();
|
||||||
spritePoint.size = m_brush0.size();
|
spritePoint.size = m_brushSize0;
|
||||||
spritePoint.angle = m_brush0.angle();
|
spritePoint.angle = m_brushAngle0;
|
||||||
|
|
||||||
// Center the input to some grid point if needed
|
// Center the input to some grid point if needed
|
||||||
snapToGrid(spritePoint);
|
snapToGrid(spritePoint);
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
// Aseprite
|
// Aseprite
|
||||||
// Copyright (C) 2019-2021 Igara Studio S.A.
|
// Copyright (C) 2019-2024 Igara Studio S.A.
|
||||||
// Copyright (C) 2001-2017 David Capello
|
// Copyright (C) 2001-2017 David Capello
|
||||||
//
|
//
|
||||||
// This program is distributed under the terms of
|
// This program is distributed under the terms of
|
||||||
@ -78,6 +78,10 @@ public:
|
|||||||
// Should be called each time the user moves the mouse inside the editor.
|
// Should be called each time the user moves the mouse inside the editor.
|
||||||
void movement(Pointer pointer);
|
void movement(Pointer pointer);
|
||||||
|
|
||||||
|
// Should be called when Shift+brush tool is used to disable stabilizer
|
||||||
|
// on the line preview
|
||||||
|
void disableMouseStabilizer();
|
||||||
|
|
||||||
const Pointer& lastPointer() const { return m_lastPointer; }
|
const Pointer& lastPointer() const { return m_lastPointer; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -95,7 +99,8 @@ private:
|
|||||||
Pointer m_lastPointer;
|
Pointer m_lastPointer;
|
||||||
gfx::Region m_dirtyArea;
|
gfx::Region m_dirtyArea;
|
||||||
gfx::Region m_nextDirtyArea;
|
gfx::Region m_nextDirtyArea;
|
||||||
doc::Brush m_brush0;
|
const int m_brushSize0;
|
||||||
|
const int m_brushAngle0;
|
||||||
DynamicsOptions m_dynamics;
|
DynamicsOptions m_dynamics;
|
||||||
gfx::PointF m_stabilizerCenter;
|
gfx::PointF m_stabilizerCenter;
|
||||||
};
|
};
|
||||||
|
@ -361,9 +361,10 @@ BrushPopup::BrushPopup()
|
|||||||
m_box.addChild(new Separator("", HORIZONTAL));
|
m_box.addChild(new Separator("", HORIZONTAL));
|
||||||
|
|
||||||
for (const auto& brush : brushes.getStandardBrushes()) {
|
for (const auto& brush : brushes.getStandardBrushes()) {
|
||||||
|
auto* theme = SkinTheme::get(this);
|
||||||
m_standardBrushes.addItem(
|
m_standardBrushes.addItem(
|
||||||
new SelectBrushItem(
|
new SelectBrushItem(
|
||||||
BrushSlot(BrushSlot::Flags::BrushType, brush)), "standard_brush");
|
BrushSlot(BrushSlot::Flags::BrushType, brush)), theme->styles.standardBrush());
|
||||||
}
|
}
|
||||||
m_standardBrushes.setTransparent(true);
|
m_standardBrushes.setTransparent(true);
|
||||||
|
|
||||||
@ -398,6 +399,7 @@ void BrushPopup::setBrush(Brush* brush)
|
|||||||
void BrushPopup::regenerate(ui::Display* display,
|
void BrushPopup::regenerate(ui::Display* display,
|
||||||
const gfx::Point& pos)
|
const gfx::Point& pos)
|
||||||
{
|
{
|
||||||
|
auto* theme = SkinTheme::get(this);
|
||||||
auto& brushSlots = App::instance()->brushes().getBrushSlots();
|
auto& brushSlots = App::instance()->brushes().getBrushSlots();
|
||||||
|
|
||||||
if (m_customBrushes) {
|
if (m_customBrushes) {
|
||||||
@ -428,11 +430,11 @@ void BrushPopup::regenerate(ui::Display* display,
|
|||||||
}
|
}
|
||||||
m_customBrushes->addItem(new SelectBrushItem(brush, slot));
|
m_customBrushes->addItem(new SelectBrushItem(brush, slot));
|
||||||
m_customBrushes->addItem(new BrushShortcutItem(shortcut, slot));
|
m_customBrushes->addItem(new BrushShortcutItem(shortcut, slot));
|
||||||
m_customBrushes->addItem(new BrushOptionsItem(this, slot), "buttonset_item_icon_mono");
|
m_customBrushes->addItem(new BrushOptionsItem(this, slot), theme->styles.buttonsetItemIconMono());
|
||||||
}
|
}
|
||||||
|
|
||||||
m_customBrushes->addItem(new NewCustomBrushItem, 2, 1);
|
m_customBrushes->addItem(new NewCustomBrushItem, 2, 1);
|
||||||
m_customBrushes->addItem(new NewBrushOptionsItem, "buttonset_item_icon_mono");
|
m_customBrushes->addItem(new NewBrushOptionsItem, theme->styles.buttonsetItemIconMono());
|
||||||
m_customBrushes->setExpansive(true);
|
m_customBrushes->setExpansive(true);
|
||||||
m_customBrushes->initTheme();
|
m_customBrushes->initTheme();
|
||||||
m_box.addChild(m_customBrushes);
|
m_box.addChild(m_customBrushes);
|
||||||
@ -467,7 +469,9 @@ os::SurfaceRef BrushPopup::createSurfaceForBrush(const BrushRef& origBrush,
|
|||||||
BrushRef brush = origBrush;
|
BrushRef brush = origBrush;
|
||||||
if (brush) {
|
if (brush) {
|
||||||
if (brush->type() != kImageBrushType && brush->size() > kMaxSize) {
|
if (brush->type() != kImageBrushType && brush->size() > kMaxSize) {
|
||||||
brush.reset(new Brush(*brush));
|
// Clone with shared images, as setSize() will re-create the
|
||||||
|
// images and the brush is no kImageBrushType anyway.
|
||||||
|
brush = brush->cloneWithSharedImages();
|
||||||
brush->setSize(kMaxSize);
|
brush->setSize(kMaxSize);
|
||||||
}
|
}
|
||||||
// Show the original image in the popup (without the image colors
|
// Show the original image in the popup (without the image colors
|
||||||
|
@ -208,60 +208,50 @@ ButtonSet::ButtonSet(int columns)
|
|||||||
initTheme();
|
initTheme();
|
||||||
}
|
}
|
||||||
|
|
||||||
ButtonSet::Item* ButtonSet::addItem(const std::string& text, const char* styleId)
|
ButtonSet::Item* ButtonSet::addItem(const std::string& text, ui::Style* style)
|
||||||
{
|
{
|
||||||
return addItem(text, 1, 1, styleId);
|
return addItem(text, 1, 1, style);
|
||||||
}
|
}
|
||||||
|
|
||||||
ButtonSet::Item* ButtonSet::addItem(const std::string& text, int hspan, int vspan, const char* styleId)
|
ButtonSet::Item* ButtonSet::addItem(const std::string& text, int hspan, int vspan, ui::Style* style)
|
||||||
{
|
{
|
||||||
Item* item = new Item();
|
Item* item = new Item();
|
||||||
item->setText(text);
|
item->setText(text);
|
||||||
addItem(item, hspan, vspan, styleId);
|
addItem(item, hspan, vspan, style);
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
|
||||||
ButtonSet::Item* ButtonSet::addItem(const skin::SkinPartPtr& icon, const char* styleId)
|
ButtonSet::Item* ButtonSet::addItem(const skin::SkinPartPtr& icon, ui::Style* style)
|
||||||
{
|
{
|
||||||
return addItem(icon, 1, 1, styleId);
|
return addItem(icon, 1, 1, style);
|
||||||
}
|
}
|
||||||
|
|
||||||
ButtonSet::Item* ButtonSet::addItem(const skin::SkinPartPtr& icon, int hspan, int vspan, const char* styleId)
|
ButtonSet::Item* ButtonSet::addItem(const skin::SkinPartPtr& icon, int hspan, int vspan, ui::Style* style)
|
||||||
{
|
{
|
||||||
Item* item = new Item();
|
Item* item = new Item();
|
||||||
item->setIcon(icon);
|
item->setIcon(icon);
|
||||||
addItem(item, hspan, vspan, styleId);
|
addItem(item, hspan, vspan, style);
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
ButtonSet::Item* ButtonSet::addItem(Item* item, const char* styleId)
|
ButtonSet::Item* ButtonSet::addItem(Item* item, ui::Style* style)
|
||||||
{
|
{
|
||||||
return addItem(item, 1, 1, styleId);
|
return addItem(item, 1, 1, style);
|
||||||
}
|
}
|
||||||
|
|
||||||
ButtonSet::Item* ButtonSet::addItem(Item* item, int hspan, int vspan, const char* styleIdStr)
|
ButtonSet::Item* ButtonSet::addItem(Item* item, int hspan, int vspan, ui::Style* style)
|
||||||
{
|
{
|
||||||
std::string styleId;
|
|
||||||
if (styleIdStr)
|
|
||||||
styleId = styleIdStr;
|
|
||||||
|
|
||||||
item->InitTheme.connect(
|
item->InitTheme.connect(
|
||||||
[item, styleId] {
|
[item, style] {
|
||||||
auto theme = SkinTheme::get(item);
|
ui::Style* s = style;
|
||||||
ui::Style* style;
|
if (!s) {
|
||||||
if (!styleId.empty()) {
|
auto* theme = SkinTheme::get(item);
|
||||||
style = theme->getStyleById(styleId);
|
s = theme->styles.buttonsetItemIcon();
|
||||||
if (!style)
|
|
||||||
throw base::Exception(fmt::format("Style {} not found", styleId));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
style = theme->styles.buttonsetItemIcon();
|
|
||||||
if (!item->text().empty()) {
|
if (!item->text().empty()) {
|
||||||
style = (item->icon() ? theme->styles.buttonsetItemTextTopIconBottom() :
|
s = (item->icon() ? theme->styles.buttonsetItemTextTopIconBottom() :
|
||||||
theme->styles.buttonsetItemText());
|
theme->styles.buttonsetItemText());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
item->setStyle(s);
|
||||||
item->setStyle(style);
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
addChildInCell(item, hspan, vspan, HORIZONTAL | VERTICAL);
|
addChildInCell(item, hspan, vspan, HORIZONTAL | VERTICAL);
|
||||||
|
@ -44,12 +44,12 @@ namespace app {
|
|||||||
|
|
||||||
ButtonSet(int columns);
|
ButtonSet(int columns);
|
||||||
|
|
||||||
Item* addItem(const std::string& text, const char* styleId);
|
Item* addItem(const std::string& text, ui::Style* style);
|
||||||
Item* addItem(const std::string& text, int hspan = 1, int vspan = 1, const char* styleId = nullptr);
|
Item* addItem(const std::string& text, int hspan = 1, int vspan = 1, ui::Style* style = nullptr);
|
||||||
Item* addItem(const skin::SkinPartPtr& icon, const char* styleId);
|
Item* addItem(const skin::SkinPartPtr& icon, ui::Style* style);
|
||||||
Item* addItem(const skin::SkinPartPtr& icon, int hspan = 1, int vspan = 1, const char* styleId = nullptr);
|
Item* addItem(const skin::SkinPartPtr& icon, int hspan = 1, int vspan = 1, ui::Style* style = nullptr);
|
||||||
Item* addItem(Item* item, const char* styleId);
|
Item* addItem(Item* item, ui::Style* style);
|
||||||
Item* addItem(Item* item, int hspan = 1, int vspan = 1, const char* styleId = nullptr);
|
Item* addItem(Item* item, int hspan = 1, int vspan = 1, ui::Style* style = nullptr);
|
||||||
Item* getItem(int index);
|
Item* getItem(int index);
|
||||||
int getItemIndex(const Item* item) const;
|
int getItemIndex(const Item* item) const;
|
||||||
|
|
||||||
|
@ -185,7 +185,7 @@ ColorBar::ColorBar(int align, TooltipManager* tooltipManager)
|
|||||||
m_instance = this;
|
m_instance = this;
|
||||||
|
|
||||||
auto& pref = Preferences::instance();
|
auto& pref = Preferences::instance();
|
||||||
auto theme = SkinTheme::get(this);
|
auto* theme = SkinTheme::get(this);
|
||||||
|
|
||||||
auto item = m_editPal.addItem("");
|
auto item = m_editPal.addItem("");
|
||||||
item->InitTheme.connect(
|
item->InitTheme.connect(
|
||||||
@ -195,9 +195,9 @@ ColorBar::ColorBar(int align, TooltipManager* tooltipManager)
|
|||||||
SkinTheme::instance()->styles.palEditLock());
|
SkinTheme::instance()->styles.palEditLock());
|
||||||
item->setStyle(style);
|
item->setStyle(style);
|
||||||
});
|
});
|
||||||
m_buttons.addItem(theme->parts.palSort(), "pal_button");
|
m_buttons.addItem(theme->parts.palSort(), theme->styles.palButton());
|
||||||
m_buttons.addItem(theme->parts.palPresets(), "pal_button");
|
m_buttons.addItem(theme->parts.palPresets(), theme->styles.palButton());
|
||||||
m_buttons.addItem(theme->parts.palOptions(), "pal_button");
|
m_buttons.addItem(theme->parts.palOptions(), theme->styles.palButton());
|
||||||
item = m_tilesButton.addItem(theme->parts.tiles());
|
item = m_tilesButton.addItem(theme->parts.tiles());
|
||||||
item->InitTheme.connect(
|
item->InitTheme.connect(
|
||||||
[this, item]() {
|
[this, item]() {
|
||||||
@ -213,9 +213,9 @@ ColorBar::ColorBar(int align, TooltipManager* tooltipManager)
|
|||||||
1 == int(TilesetMode::Auto) &&
|
1 == int(TilesetMode::Auto) &&
|
||||||
2 == int(TilesetMode::Stack), "Tileset mode buttons doesn't match TilesetMode enum values");
|
2 == int(TilesetMode::Stack), "Tileset mode buttons doesn't match TilesetMode enum values");
|
||||||
|
|
||||||
m_tilesetModeButtons.addItem(theme->parts.tilesManual(), "pal_button");
|
m_tilesetModeButtons.addItem(theme->parts.tilesManual(), theme->styles.palButton());
|
||||||
m_tilesetModeButtons.addItem(theme->parts.tilesAuto(), "pal_button");
|
m_tilesetModeButtons.addItem(theme->parts.tilesAuto(), theme->styles.palButton());
|
||||||
m_tilesetModeButtons.addItem(theme->parts.tilesStack(), "pal_button");
|
m_tilesetModeButtons.addItem(theme->parts.tilesStack(), theme->styles.palButton());
|
||||||
|
|
||||||
m_tilesetMode = pref.colorBar.defaultTilesetMode();
|
m_tilesetMode = pref.colorBar.defaultTilesetMode();
|
||||||
setTilesetMode(m_tilesetMode);
|
setTilesetMode(m_tilesetMode);
|
||||||
|
@ -164,7 +164,8 @@ public:
|
|||||||
, m_brushes(App::instance()->brushes()) {
|
, m_brushes(App::instance()->brushes()) {
|
||||||
SkinPartPtr part(new SkinPart);
|
SkinPartPtr part(new SkinPart);
|
||||||
part->setBitmap(0, BrushPopup::createSurfaceForBrush(BrushRef(nullptr)));
|
part->setBitmap(0, BrushPopup::createSurfaceForBrush(BrushRef(nullptr)));
|
||||||
addItem(part, "brush_type");
|
auto* theme = SkinTheme::get(this);
|
||||||
|
addItem(part, theme->styles.brushType());
|
||||||
|
|
||||||
m_popupWindow.Open.connect(
|
m_popupWindow.Open.connect(
|
||||||
[this]{
|
[this]{
|
||||||
@ -385,8 +386,8 @@ protected:
|
|||||||
class ContextBar::PaintBucketSettingsField : public ButtonSet {
|
class ContextBar::PaintBucketSettingsField : public ButtonSet {
|
||||||
public:
|
public:
|
||||||
PaintBucketSettingsField() : ButtonSet(1) {
|
PaintBucketSettingsField() : ButtonSet(1) {
|
||||||
auto theme = SkinTheme::get(this);
|
auto* theme = SkinTheme::get(this);
|
||||||
addItem(theme->parts.timelineGear(), "context_bar_button");
|
addItem(theme->parts.timelineGear(), theme->styles.contextBarButton());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
@ -472,8 +473,8 @@ class ContextBar::InkTypeField : public ButtonSet {
|
|||||||
public:
|
public:
|
||||||
InkTypeField(ContextBar* owner) : ButtonSet(1)
|
InkTypeField(ContextBar* owner) : ButtonSet(1)
|
||||||
, m_owner(owner) {
|
, m_owner(owner) {
|
||||||
auto theme = SkinTheme::get(this);
|
auto* theme = SkinTheme::get(this);
|
||||||
addItem(theme->parts.inkSimple(), "ink_type");
|
addItem(theme->parts.inkSimple(), theme->styles.inkType());
|
||||||
}
|
}
|
||||||
|
|
||||||
void setInkType(InkType inkType) {
|
void setInkType(InkType inkType) {
|
||||||
@ -872,7 +873,8 @@ class ContextBar::PivotField : public ButtonSet {
|
|||||||
public:
|
public:
|
||||||
PivotField()
|
PivotField()
|
||||||
: ButtonSet(1) {
|
: ButtonSet(1) {
|
||||||
addItem(SkinTheme::get(this)->parts.pivotCenter(), "pivot_field");
|
auto* theme = SkinTheme::get(this);
|
||||||
|
addItem(SkinTheme::get(this)->parts.pivotCenter(), theme->styles.pivotField());
|
||||||
|
|
||||||
m_pivotConn = Preferences::instance().selection.pivotPosition.AfterChange.connect(
|
m_pivotConn = Preferences::instance().selection.pivotPosition.AfterChange.connect(
|
||||||
[this]{ onPivotChange(); });
|
[this]{ onPivotChange(); });
|
||||||
@ -885,22 +887,22 @@ private:
|
|||||||
void onItemChange(Item* item) override {
|
void onItemChange(Item* item) override {
|
||||||
ButtonSet::onItemChange(item);
|
ButtonSet::onItemChange(item);
|
||||||
|
|
||||||
auto theme = SkinTheme::get(this);
|
auto* theme = SkinTheme::get(this);
|
||||||
gfx::Rect bounds = this->bounds();
|
gfx::Rect bounds = this->bounds();
|
||||||
|
|
||||||
Menu menu;
|
Menu menu;
|
||||||
CheckBox visible(Strings::context_bar_default_display_pivot());
|
CheckBox visible(Strings::context_bar_default_display_pivot());
|
||||||
HBox box;
|
HBox box;
|
||||||
ButtonSet buttonset(3);
|
ButtonSet buttonset(3);
|
||||||
buttonset.addItem(theme->parts.pivotNorthwest(), "pivot_dir");
|
buttonset.addItem(theme->parts.pivotNorthwest(), theme->styles.pivotDir());
|
||||||
buttonset.addItem(theme->parts.pivotNorth(), "pivot_dir");
|
buttonset.addItem(theme->parts.pivotNorth(), theme->styles.pivotDir());
|
||||||
buttonset.addItem(theme->parts.pivotNortheast(), "pivot_dir");
|
buttonset.addItem(theme->parts.pivotNortheast(), theme->styles.pivotDir());
|
||||||
buttonset.addItem(theme->parts.pivotWest(), "pivot_dir");
|
buttonset.addItem(theme->parts.pivotWest(), theme->styles.pivotDir());
|
||||||
buttonset.addItem(theme->parts.pivotCenter(), "pivot_dir");
|
buttonset.addItem(theme->parts.pivotCenter(), theme->styles.pivotDir());
|
||||||
buttonset.addItem(theme->parts.pivotEast(), "pivot_dir");
|
buttonset.addItem(theme->parts.pivotEast(), theme->styles.pivotDir());
|
||||||
buttonset.addItem(theme->parts.pivotSouthwest(), "pivot_dir");
|
buttonset.addItem(theme->parts.pivotSouthwest(), theme->styles.pivotDir());
|
||||||
buttonset.addItem(theme->parts.pivotSouth(), "pivot_dir");
|
buttonset.addItem(theme->parts.pivotSouth(), theme->styles.pivotDir());
|
||||||
buttonset.addItem(theme->parts.pivotSoutheast(), "pivot_dir");
|
buttonset.addItem(theme->parts.pivotSoutheast(), theme->styles.pivotDir());
|
||||||
box.addChild(&buttonset);
|
box.addChild(&buttonset);
|
||||||
|
|
||||||
menu.addChild(&visible);
|
menu.addChild(&visible);
|
||||||
@ -1154,7 +1156,8 @@ public:
|
|||||||
DynamicsField(ContextBar* ctxBar)
|
DynamicsField(ContextBar* ctxBar)
|
||||||
: ButtonSet(1)
|
: ButtonSet(1)
|
||||||
, m_ctxBar(ctxBar) {
|
, m_ctxBar(ctxBar) {
|
||||||
addItem(SkinTheme::get(this)->parts.dynamics(), "dynamics_field");
|
auto* theme = SkinTheme::get(this);
|
||||||
|
addItem(theme->parts.dynamics(), theme->styles.dynamicsField());
|
||||||
|
|
||||||
loadDynamicsPref();
|
loadDynamicsPref();
|
||||||
initTheme();
|
initTheme();
|
||||||
@ -1381,10 +1384,10 @@ protected:
|
|||||||
class ContextBar::GradientTypeField : public ButtonSet {
|
class ContextBar::GradientTypeField : public ButtonSet {
|
||||||
public:
|
public:
|
||||||
GradientTypeField() : ButtonSet(2) {
|
GradientTypeField() : ButtonSet(2) {
|
||||||
auto theme = SkinTheme::get(this);
|
auto* theme = SkinTheme::get(this);
|
||||||
|
|
||||||
addItem(theme->parts.linearGradient(), "context_bar_button");
|
addItem(theme->parts.linearGradient(), theme->styles.contextBarButton());
|
||||||
addItem(theme->parts.radialGradient(), "context_bar_button");
|
addItem(theme->parts.radialGradient(), theme->styles.contextBarButton());
|
||||||
|
|
||||||
setSelectedItem(0);
|
setSelectedItem(0);
|
||||||
}
|
}
|
||||||
@ -1404,10 +1407,10 @@ public:
|
|||||||
class ContextBar::DropPixelsField : public ButtonSet {
|
class ContextBar::DropPixelsField : public ButtonSet {
|
||||||
public:
|
public:
|
||||||
DropPixelsField() : ButtonSet(2) {
|
DropPixelsField() : ButtonSet(2) {
|
||||||
auto theme = SkinTheme::get(this);
|
auto* theme = SkinTheme::get(this);
|
||||||
|
|
||||||
addItem(theme->parts.dropPixelsOk(), "context_bar_button");
|
addItem(theme->parts.dropPixelsOk(), theme->styles.contextBarButton());
|
||||||
addItem(theme->parts.dropPixelsCancel(), "context_bar_button");
|
addItem(theme->parts.dropPixelsCancel(), theme->styles.contextBarButton());
|
||||||
setOfferCapture(false);
|
setOfferCapture(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1529,10 +1532,10 @@ class ContextBar::SymmetryField : public ButtonSet {
|
|||||||
public:
|
public:
|
||||||
SymmetryField() : ButtonSet(3) {
|
SymmetryField() : ButtonSet(3) {
|
||||||
setMultiMode(MultiMode::Set);
|
setMultiMode(MultiMode::Set);
|
||||||
auto theme = SkinTheme::get(this);
|
auto* theme = SkinTheme::get(this);
|
||||||
addItem(theme->parts.horizontalSymmetry(), "symmetry_field");
|
addItem(theme->parts.horizontalSymmetry(), theme->styles.symmetryField());
|
||||||
addItem(theme->parts.verticalSymmetry(), "symmetry_field");
|
addItem(theme->parts.verticalSymmetry(), theme->styles.symmetryField());
|
||||||
addItem("...", "symmetry_options");
|
addItem("...", theme->styles.symmetryOptions());
|
||||||
}
|
}
|
||||||
|
|
||||||
void setupTooltips(TooltipManager* tooltipManager) {
|
void setupTooltips(TooltipManager* tooltipManager) {
|
||||||
@ -1654,7 +1657,7 @@ public:
|
|||||||
, m_combobox(this)
|
, m_combobox(this)
|
||||||
, m_action(2)
|
, m_action(2)
|
||||||
{
|
{
|
||||||
auto theme = SkinTheme::get(this);
|
auto* theme = SkinTheme::get(this);
|
||||||
|
|
||||||
m_sel.addItem(Strings::context_bar_all());
|
m_sel.addItem(Strings::context_bar_all());
|
||||||
m_sel.addItem(Strings::context_bar_none());
|
m_sel.addItem(Strings::context_bar_none());
|
||||||
@ -1667,8 +1670,8 @@ public:
|
|||||||
m_combobox.setExpansive(true);
|
m_combobox.setExpansive(true);
|
||||||
m_combobox.setMinSize(gfx::Size(256*guiscale(), 0));
|
m_combobox.setMinSize(gfx::Size(256*guiscale(), 0));
|
||||||
|
|
||||||
m_action.addItem(theme->parts.iconUserData(), "buttonset_item_icon_mono");
|
m_action.addItem(theme->parts.iconUserData(), theme->styles.buttonsetItemIconMono());
|
||||||
m_action.addItem(theme->parts.iconClose(), "buttonset_item_icon_mono");
|
m_action.addItem(theme->parts.iconClose(), theme->styles.buttonsetItemIconMono());
|
||||||
m_action.ItemChange.connect(
|
m_action.ItemChange.connect(
|
||||||
[this](ButtonSet::Item* item){
|
[this](ButtonSet::Item* item){
|
||||||
onAction(m_action.selectedItem());
|
onAction(m_action.selectedItem());
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
// Aseprite
|
// Aseprite
|
||||||
// Copyright (C) 2020-2023 Igara Studio S.A.
|
// Copyright (C) 2020-2024 Igara Studio S.A.
|
||||||
//
|
//
|
||||||
// This program is distributed under the terms of
|
// This program is distributed under the terms of
|
||||||
// the End-User License Agreement for Aseprite.
|
// the End-User License Agreement for Aseprite.
|
||||||
@ -165,7 +165,9 @@ private:
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
auto mouseMsg = static_cast<MouseMessage*>(msg);
|
auto mouseMsg = static_cast<MouseMessage*>(msg);
|
||||||
const gfx::Rect rc = bounds();
|
gfx::Rect rc = bounds();
|
||||||
|
rc.shrink(border());
|
||||||
|
rc.shrink(gfx::Border(3, 0, 3, 1) * guiscale());
|
||||||
float u = (mouseMsg->position().x - rc.x) / float(rc.w);
|
float u = (mouseMsg->position().x - rc.x) / float(rc.w);
|
||||||
u = std::clamp(u, 0.0f, 1.0f);
|
u = std::clamp(u, 0.0f, 1.0f);
|
||||||
switch (capture) {
|
switch (capture) {
|
||||||
|
@ -129,6 +129,12 @@ void DrawingState::initToolLoop(Editor* editor,
|
|||||||
editor->captureMouse();
|
editor->captureMouse();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DrawingState::disableMouseStabilizer()
|
||||||
|
{
|
||||||
|
ASSERT(m_toolLoopManager);
|
||||||
|
m_toolLoopManager->disableMouseStabilizer();
|
||||||
|
}
|
||||||
|
|
||||||
void DrawingState::sendMovementToToolLoop(const tools::Pointer& pointer)
|
void DrawingState::sendMovementToToolLoop(const tools::Pointer& pointer)
|
||||||
{
|
{
|
||||||
ASSERT(m_toolLoopManager);
|
ASSERT(m_toolLoopManager);
|
||||||
|
@ -60,6 +60,10 @@ namespace app {
|
|||||||
const ui::MouseMessage* msg,
|
const ui::MouseMessage* msg,
|
||||||
const tools::Pointer& pointer);
|
const tools::Pointer& pointer);
|
||||||
|
|
||||||
|
// Used to disable the current ToolLoopManager's stabilizer
|
||||||
|
// when Shift+brush tool is used to paint a line
|
||||||
|
void disableMouseStabilizer();
|
||||||
|
|
||||||
// Used to send a movement() to the ToolLoopManager when
|
// Used to send a movement() to the ToolLoopManager when
|
||||||
// Shift+brush tool is used to paint a line.
|
// Shift+brush tool is used to paint a line.
|
||||||
void sendMovementToToolLoop(const tools::Pointer& pointer);
|
void sendMovementToToolLoop(const tools::Pointer& pointer);
|
||||||
|
@ -696,6 +696,9 @@ bool StandbyState::checkStartDrawingStraightLine(Editor* editor,
|
|||||||
pointer ? pointer->type(): PointerType::Unknown,
|
pointer ? pointer->type(): PointerType::Unknown,
|
||||||
pointer ? pointer->pressure(): 0.0f));
|
pointer ? pointer->pressure(): 0.0f));
|
||||||
if (drawingState) {
|
if (drawingState) {
|
||||||
|
// Disable stabilizer so that it does not affect the line preview
|
||||||
|
drawingState->disableMouseStabilizer();
|
||||||
|
|
||||||
drawingState->sendMovementToToolLoop(
|
drawingState->sendMovementToToolLoop(
|
||||||
tools::Pointer(
|
tools::Pointer(
|
||||||
pointer ? pointer->point(): editor->screenToEditor(editor->mousePosInDisplay()),
|
pointer ? pointer->point(): editor->screenToEditor(editor->mousePosInDisplay()),
|
||||||
|
@ -45,6 +45,7 @@
|
|||||||
#include "app/ui_context.h"
|
#include "app/ui_context.h"
|
||||||
#include "app/util/expand_cel_canvas.h"
|
#include "app/util/expand_cel_canvas.h"
|
||||||
#include "app/util/layer_utils.h"
|
#include "app/util/layer_utils.h"
|
||||||
|
#include "doc/brush.h"
|
||||||
#include "doc/cel.h"
|
#include "doc/cel.h"
|
||||||
#include "doc/image.h"
|
#include "doc/image.h"
|
||||||
#include "doc/layer.h"
|
#include "doc/layer.h"
|
||||||
@ -189,6 +190,33 @@ public:
|
|||||||
ASSERT(m_ink);
|
ASSERT(m_ink);
|
||||||
ASSERT(m_controller);
|
ASSERT(m_controller);
|
||||||
|
|
||||||
|
// If the user right-clicks with a custom/image brush we change
|
||||||
|
// the image's colors of the brush to the background color.
|
||||||
|
//
|
||||||
|
// This is different from SwitchColors that makes a new brush
|
||||||
|
// switching fg <-> bg colors, so here we have some extra
|
||||||
|
// functionality with custom brushes (quickly convert the custom
|
||||||
|
// brush with a plain color, or in other words, replace the custom
|
||||||
|
// brush area with the background color).
|
||||||
|
if (m_brush->type() == kImageBrushType && m_button == Right) {
|
||||||
|
// We've to recalculate the background color to use for the
|
||||||
|
// brush using the specific brush image pixel format/color mode,
|
||||||
|
// as we cannot use m_primaryColor or m_bgColor here because
|
||||||
|
// those are in the sprite pixel format/color mode.
|
||||||
|
const color_t brushColor =
|
||||||
|
color_utils::color_for_target_mask(
|
||||||
|
Preferences::instance().colorBar.bgColor(),
|
||||||
|
ColorTarget(ColorTarget::TransparentLayer,
|
||||||
|
m_brush->image()->pixelFormat(),
|
||||||
|
-1));
|
||||||
|
|
||||||
|
// Clone the brush with new images to avoid modifying the
|
||||||
|
// current brush used in left-click / brush preview.
|
||||||
|
BrushRef newBrush = m_brush->cloneWithNewImages();
|
||||||
|
newBrush->setImageColor(Brush::ImageColor::BothColors, brushColor);
|
||||||
|
m_brush = newBrush;
|
||||||
|
}
|
||||||
|
|
||||||
if (m_tilesMode) {
|
if (m_tilesMode) {
|
||||||
// Use FloodFillPointShape or TilePointShape in tiles mode
|
// Use FloodFillPointShape or TilePointShape in tiles mode
|
||||||
if (!m_pointShape->isFloodFill()) {
|
if (!m_pointShape->isFloodFill()) {
|
||||||
|
@ -23,12 +23,12 @@ using namespace ui;
|
|||||||
SelectionModeField::SelectionModeField()
|
SelectionModeField::SelectionModeField()
|
||||||
: ButtonSet(4)
|
: ButtonSet(4)
|
||||||
{
|
{
|
||||||
auto theme = SkinTheme::get(this);
|
auto* theme = SkinTheme::get(this);
|
||||||
|
|
||||||
addItem(theme->parts.selectionReplace(), "selection_mode");
|
addItem(theme->parts.selectionReplace(), theme->styles.selectionMode());
|
||||||
addItem(theme->parts.selectionAdd(), "selection_mode");
|
addItem(theme->parts.selectionAdd(), theme->styles.selectionMode());
|
||||||
addItem(theme->parts.selectionSubtract(), "selection_mode");
|
addItem(theme->parts.selectionSubtract(), theme->styles.selectionMode());
|
||||||
addItem(theme->parts.selectionIntersect(), "selection_mode");
|
addItem(theme->parts.selectionIntersect(), theme->styles.selectionMode());
|
||||||
|
|
||||||
setSelectedItem((int)Preferences::instance().selection.mode());
|
setSelectedItem((int)Preferences::instance().selection.mode());
|
||||||
initTheme();
|
initTheme();
|
||||||
|
@ -42,13 +42,13 @@ enum AniAction {
|
|||||||
AniControls::AniControls(TooltipManager* tooltipManager)
|
AniControls::AniControls(TooltipManager* tooltipManager)
|
||||||
: ButtonSet(5)
|
: ButtonSet(5)
|
||||||
{
|
{
|
||||||
auto theme = SkinTheme::get(this);
|
auto* theme = SkinTheme::get(this);
|
||||||
|
|
||||||
addItem(theme->parts.aniFirst(), "ani_button");
|
addItem(theme->parts.aniFirst(), theme->styles.aniButton());
|
||||||
addItem(theme->parts.aniPrevious(), "ani_button");
|
addItem(theme->parts.aniPrevious(), theme->styles.aniButton());
|
||||||
addItem(theme->parts.aniPlay(), "ani_button");
|
addItem(theme->parts.aniPlay(), theme->styles.aniButton());
|
||||||
addItem(theme->parts.aniNext(), "ani_button");
|
addItem(theme->parts.aniNext(), theme->styles.aniButton());
|
||||||
addItem(theme->parts.aniLast(), "ani_button");
|
addItem(theme->parts.aniLast(), theme->styles.aniButton());
|
||||||
ItemChange.connect([this]{ onClickButton(); });
|
ItemChange.connect([this]{ onClickButton(); });
|
||||||
|
|
||||||
setTriggerOnMouseUp(true);
|
setTriggerOnMouseUp(true);
|
||||||
|
@ -24,21 +24,19 @@
|
|||||||
|
|
||||||
#include <climits>
|
#include <climits>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
#include <limits>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace doc {
|
namespace doc {
|
||||||
namespace algorithm {
|
namespace algorithm {
|
||||||
|
|
||||||
struct FLOODED_LINE { // store segments which have been flooded
|
struct FLOODED_LINE { // store segments which have been flooded
|
||||||
short flags; // status of the segment
|
char flags; // status of the segment
|
||||||
short lpos, rpos; // left and right ends of segment
|
int lpos, rpos; // left and right ends of segment
|
||||||
short y; // y coordinate of the segment
|
int y; // y coordinate of the segment
|
||||||
int next; // linked list if several per line
|
int next; // linked list if several per line
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Note: a 'short' is not sufficient for 'next' above in some corner cases. */
|
|
||||||
|
|
||||||
|
|
||||||
static std::vector<FLOODED_LINE> flood_buf;
|
static std::vector<FLOODED_LINE> flood_buf;
|
||||||
static int flood_count; /* number of flooded segments */
|
static int flood_count; /* number of flooded segments */
|
||||||
|
|
||||||
@ -415,8 +413,8 @@ void floodfill(const Image* image,
|
|||||||
FLOODED_LINE* p = (FLOODED_LINE*)&flood_buf[0];
|
FLOODED_LINE* p = (FLOODED_LINE*)&flood_buf[0];
|
||||||
for (int c=0; c<flood_count; c++) {
|
for (int c=0; c<flood_count; c++) {
|
||||||
p[c].flags = 0;
|
p[c].flags = 0;
|
||||||
p[c].lpos = SHRT_MAX;
|
p[c].lpos = std::numeric_limits<int>::max();
|
||||||
p[c].rpos = SHRT_MIN;
|
p[c].rpos = std::numeric_limits<int>::min();
|
||||||
p[c].y = y;
|
p[c].y = y;
|
||||||
p[c].next = 0;
|
p[c].next = 0;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
// Aseprite Document Library
|
// Aseprite Document Library
|
||||||
// Copyright (C) 2019-2022 Igara Studio S.A.
|
// Copyright (C) 2019-2024 Igara Studio S.A.
|
||||||
// Copyright (C) 2001-2016 David Capello
|
// Copyright (C) 2001-2016 David Capello
|
||||||
//
|
//
|
||||||
// This file is released under the terms of the MIT license.
|
// This file is released under the terms of the MIT license.
|
||||||
@ -48,32 +48,43 @@ Brush::Brush(BrushType type, int size, int angle)
|
|||||||
regenerate();
|
regenerate();
|
||||||
}
|
}
|
||||||
|
|
||||||
Brush::Brush(const Brush& brush)
|
|
||||||
{
|
|
||||||
m_type = brush.m_type;
|
|
||||||
m_size = brush.m_size;
|
|
||||||
m_angle = brush.m_angle;
|
|
||||||
m_image = brush.m_image;
|
|
||||||
m_maskBitmap = brush.m_maskBitmap;
|
|
||||||
m_pattern = brush.m_pattern;
|
|
||||||
m_patternOrigin = brush.m_patternOrigin;
|
|
||||||
m_gen = 0;
|
|
||||||
|
|
||||||
regenerate();
|
|
||||||
}
|
|
||||||
|
|
||||||
Brush::~Brush()
|
Brush::~Brush()
|
||||||
{
|
{
|
||||||
clean();
|
clean();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Brush::setType(BrushType type)
|
BrushRef Brush::cloneWithSharedImages() const
|
||||||
{
|
{
|
||||||
m_type = type;
|
BrushRef newBrush = std::make_shared<Brush>();
|
||||||
if (m_type != kImageBrushType)
|
newBrush->copyFieldsFromBrush(*this);
|
||||||
regenerate();
|
return newBrush;
|
||||||
|
}
|
||||||
|
|
||||||
|
BrushRef Brush::cloneWithNewImages() const
|
||||||
|
{
|
||||||
|
BrushRef newBrush = std::make_shared<Brush>();
|
||||||
|
newBrush->copyFieldsFromBrush(*this);
|
||||||
|
if (newBrush->m_image)
|
||||||
|
newBrush->m_image.reset(Image::createCopy(newBrush->m_image.get()));
|
||||||
|
if (newBrush->m_maskBitmap)
|
||||||
|
newBrush->m_maskBitmap.reset(Image::createCopy(newBrush->m_maskBitmap.get()));
|
||||||
|
return newBrush;
|
||||||
|
}
|
||||||
|
|
||||||
|
BrushRef Brush::cloneWithExistingImages(const ImageRef& image,
|
||||||
|
const ImageRef& maskBitmap) const
|
||||||
|
{
|
||||||
|
BrushRef newBrush = std::make_shared<Brush>();
|
||||||
|
newBrush->copyFieldsFromBrush(*this);
|
||||||
|
|
||||||
|
newBrush->m_image = image;
|
||||||
|
if (maskBitmap)
|
||||||
|
newBrush->m_maskBitmap = maskBitmap;
|
||||||
else
|
else
|
||||||
clean();
|
newBrush->regenerateMaskBitmap();
|
||||||
|
|
||||||
|
newBrush->resetBounds();
|
||||||
|
return newBrush;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Brush::setSize(int size)
|
void Brush::setSize(int size)
|
||||||
@ -95,16 +106,8 @@ void Brush::setImage(const Image* image,
|
|||||||
m_image.reset(Image::createCopy(image));
|
m_image.reset(Image::createCopy(image));
|
||||||
if (maskBitmap)
|
if (maskBitmap)
|
||||||
m_maskBitmap.reset(Image::createCopy(maskBitmap));
|
m_maskBitmap.reset(Image::createCopy(maskBitmap));
|
||||||
else {
|
else
|
||||||
int w = image->width();
|
regenerateMaskBitmap();
|
||||||
int h = image->height();
|
|
||||||
m_maskBitmap.reset(Image::create(IMAGE_BITMAP, w, h));
|
|
||||||
LockImageBits<BitmapTraits> bits(m_maskBitmap.get());
|
|
||||||
auto pos = bits.begin();
|
|
||||||
for (int v=0; v<h; ++v)
|
|
||||||
for (int u=0; u<w; ++u, ++pos)
|
|
||||||
*pos = (get_pixel(image, u, v) != image->maskColor());
|
|
||||||
}
|
|
||||||
|
|
||||||
m_backupImage.reset();
|
m_backupImage.reset();
|
||||||
m_mainColor.reset();
|
m_mainColor.reset();
|
||||||
@ -234,7 +237,8 @@ static void replace_image_colors_indexed(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Brush::setImageColor(ImageColor imageColor, color_t color)
|
void Brush::setImageColor(const ImageColor imageColor,
|
||||||
|
const color_t color)
|
||||||
{
|
{
|
||||||
ASSERT(m_image);
|
ASSERT(m_image);
|
||||||
if (!m_image)
|
if (!m_image)
|
||||||
@ -249,10 +253,13 @@ void Brush::setImageColor(ImageColor imageColor, color_t color)
|
|||||||
|
|
||||||
switch (imageColor) {
|
switch (imageColor) {
|
||||||
case ImageColor::MainColor:
|
case ImageColor::MainColor:
|
||||||
m_mainColor = color_t(color);
|
m_mainColor = color;
|
||||||
break;
|
break;
|
||||||
case ImageColor::BackgroundColor:
|
case ImageColor::BackgroundColor:
|
||||||
m_bgColor = color_t(color);
|
m_bgColor = color;
|
||||||
|
break;
|
||||||
|
case ImageColor::BothColors:
|
||||||
|
m_mainColor = m_bgColor = color;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -283,8 +290,11 @@ void Brush::setImageColor(ImageColor imageColor, color_t color)
|
|||||||
|
|
||||||
void Brush::resetImageColors()
|
void Brush::resetImageColors()
|
||||||
{
|
{
|
||||||
if (m_backupImage)
|
if (m_backupImage) {
|
||||||
m_image.reset(Image::createCopy(m_backupImage.get()));
|
m_image.reset(Image::createCopy(m_backupImage.get()));
|
||||||
|
m_mainColor.reset();
|
||||||
|
m_bgColor.reset();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Brush::setCenter(const gfx::Point& center)
|
void Brush::setCenter(const gfx::Point& center)
|
||||||
@ -376,6 +386,22 @@ void Brush::regenerate()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Brush::regenerateMaskBitmap()
|
||||||
|
{
|
||||||
|
ASSERT(m_image);
|
||||||
|
if (!m_image)
|
||||||
|
return;
|
||||||
|
|
||||||
|
int w = m_image->width();
|
||||||
|
int h = m_image->height();
|
||||||
|
m_maskBitmap.reset(Image::create(IMAGE_BITMAP, w, h));
|
||||||
|
LockImageBits<BitmapTraits> bits(m_maskBitmap.get());
|
||||||
|
auto pos = bits.begin();
|
||||||
|
for (int v=0; v<h; ++v)
|
||||||
|
for (int u=0; u<w; ++u, ++pos)
|
||||||
|
*pos = (get_pixel(m_image.get(), u, v) != m_image->maskColor());
|
||||||
|
}
|
||||||
|
|
||||||
void Brush::resetBounds()
|
void Brush::resetBounds()
|
||||||
{
|
{
|
||||||
m_center = gfx::Point(std::max(0, m_image->width()/2),
|
m_center = gfx::Point(std::max(0, m_image->width()/2),
|
||||||
@ -385,4 +411,19 @@ void Brush::resetBounds()
|
|||||||
m_image->height()));
|
m_image->height()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Brush::copyFieldsFromBrush(const Brush& brush)
|
||||||
|
{
|
||||||
|
m_type = brush.m_type;
|
||||||
|
m_size = brush.m_size;
|
||||||
|
m_angle = brush.m_angle;
|
||||||
|
m_image = brush.m_image;
|
||||||
|
m_maskBitmap = brush.m_maskBitmap;
|
||||||
|
m_bounds = brush.m_bounds;
|
||||||
|
m_center = brush.m_center;
|
||||||
|
m_pattern = brush.m_pattern;
|
||||||
|
m_patternOrigin = brush.m_patternOrigin;
|
||||||
|
m_patternImage = brush.m_patternImage;
|
||||||
|
m_gen = 0;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace doc
|
} // namespace doc
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
// Aseprite Document Library
|
// Aseprite Document Library
|
||||||
// Copyright (C) 2019-2022 Igara Studio S.A.
|
// Copyright (C) 2019-2024 Igara Studio S.A.
|
||||||
// Copyright (C) 2001-2018 David Capello
|
// Copyright (C) 2001-2018 David Capello
|
||||||
//
|
//
|
||||||
// This file is released under the terms of the MIT license.
|
// This file is released under the terms of the MIT license.
|
||||||
@ -22,18 +22,34 @@
|
|||||||
|
|
||||||
namespace doc {
|
namespace doc {
|
||||||
|
|
||||||
|
class Brush;
|
||||||
|
using BrushRef = std::shared_ptr<Brush>;
|
||||||
|
|
||||||
class Brush {
|
class Brush {
|
||||||
public:
|
public:
|
||||||
static const int kMinBrushSize = 1;
|
static const int kMinBrushSize = 1;
|
||||||
static const int kMaxBrushSize = 64;
|
static const int kMaxBrushSize = 64;
|
||||||
|
|
||||||
enum class ImageColor { MainColor, BackgroundColor };
|
enum class ImageColor { MainColor, BackgroundColor, BothColors };
|
||||||
|
|
||||||
Brush();
|
Brush();
|
||||||
Brush(BrushType type, int size, int angle);
|
Brush(BrushType type, int size, int angle);
|
||||||
Brush(const Brush& brush);
|
|
||||||
~Brush();
|
~Brush();
|
||||||
|
|
||||||
|
// Don't offer copy constructor/operator, use clone*() functions
|
||||||
|
// instead.
|
||||||
|
Brush(const Brush&) = delete;
|
||||||
|
Brush& operator=(const Brush&) = delete;
|
||||||
|
|
||||||
|
// Cloned brushes can share the same image until
|
||||||
|
// setSize()/Angle()/etc. (regenerate()) is called for the new
|
||||||
|
// brush. In that case the original brush and the cloned one will
|
||||||
|
// have a different image after all.
|
||||||
|
BrushRef cloneWithSharedImages() const;
|
||||||
|
BrushRef cloneWithNewImages() const;
|
||||||
|
BrushRef cloneWithExistingImages(const ImageRef& image,
|
||||||
|
const ImageRef& maskBitmap) const;
|
||||||
|
|
||||||
BrushType type() const { return m_type; }
|
BrushType type() const { return m_type; }
|
||||||
int size() const { return m_size; }
|
int size() const { return m_size; }
|
||||||
int angle() const { return m_angle; }
|
int angle() const { return m_angle; }
|
||||||
@ -48,7 +64,6 @@ namespace doc {
|
|||||||
const gfx::Rect& bounds() const { return m_bounds; }
|
const gfx::Rect& bounds() const { return m_bounds; }
|
||||||
const gfx::Point& center() const { return m_center; }
|
const gfx::Point& center() const { return m_center; }
|
||||||
|
|
||||||
void setType(BrushType type);
|
|
||||||
void setSize(int size);
|
void setSize(int size);
|
||||||
void setAngle(int angle);
|
void setAngle(int angle);
|
||||||
void setImage(const Image* image,
|
void setImage(const Image* image,
|
||||||
@ -81,7 +96,9 @@ namespace doc {
|
|||||||
private:
|
private:
|
||||||
void clean();
|
void clean();
|
||||||
void regenerate();
|
void regenerate();
|
||||||
|
void regenerateMaskBitmap();
|
||||||
void resetBounds();
|
void resetBounds();
|
||||||
|
void copyFieldsFromBrush(const Brush& brush);
|
||||||
|
|
||||||
BrushType m_type; // Type of brush
|
BrushType m_type; // Type of brush
|
||||||
int m_size; // Size (diameter)
|
int m_size; // Size (diameter)
|
||||||
@ -101,8 +118,6 @@ namespace doc {
|
|||||||
std::optional<color_t> m_bgColor; // Background color
|
std::optional<color_t> m_bgColor; // Background color
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef std::shared_ptr<Brush> BrushRef;
|
|
||||||
|
|
||||||
} // namespace doc
|
} // namespace doc
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
// Aseprite Document Library
|
// Aseprite Document Library
|
||||||
// Copyright (C) 2019-2021 Igara Studio S.A.
|
// Copyright (C) 2019-2024 Igara Studio S.A.
|
||||||
// Copyright (C) 2001-2018 David Capello
|
// Copyright (C) 2001-2018 David Capello
|
||||||
//
|
//
|
||||||
// This file is released under the terms of the MIT license.
|
// This file is released under the terms of the MIT license.
|
||||||
@ -42,10 +42,13 @@ namespace doc {
|
|||||||
Reference = 64, // Is a reference layer
|
Reference = 64, // Is a reference layer
|
||||||
|
|
||||||
PersistentFlagsMask = 0xffff,
|
PersistentFlagsMask = 0xffff,
|
||||||
|
|
||||||
Internal_WasVisible = 0x10000, // Was visible in the alternative state (Alt+click)
|
Internal_WasVisible = 0x10000, // Was visible in the alternative state (Alt+click)
|
||||||
|
|
||||||
BackgroundLayerFlags = LockMove | Background,
|
BackgroundLayerFlags = LockMove | Background,
|
||||||
|
|
||||||
|
// Flags that change the modified flag of the document
|
||||||
|
// (e.g. created by undoable actions).
|
||||||
|
StructuralFlagsMask = Background | Reference,
|
||||||
};
|
};
|
||||||
|
|
||||||
class Layer : public WithUserData {
|
class Layer : public WithUserData {
|
||||||
|
@ -311,7 +311,7 @@ LayerImage* Sprite::backgroundLayer() const
|
|||||||
Layer* Sprite::firstLayer() const
|
Layer* Sprite::firstLayer() const
|
||||||
{
|
{
|
||||||
Layer* layer = root()->firstLayer();
|
Layer* layer = root()->firstLayer();
|
||||||
while (layer->isGroup())
|
while (layer && layer->isGroup())
|
||||||
layer = static_cast<LayerGroup*>(layer)->firstLayer();
|
layer = static_cast<LayerGroup*>(layer)->firstLayer();
|
||||||
return layer;
|
return layer;
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
# ASEPRITE
|
# ASEPRITE
|
||||||
# Copyright (C) 2020-2021 Igara Studio S.A.
|
# Copyright (C) 2020-2024 Igara Studio S.A.
|
||||||
# Copyright (C) 2001-2017 David Capello
|
# Copyright (C) 2001-2017 David Capello
|
||||||
|
|
||||||
set(UPDATER_LIB_SOURCES
|
# By default the updater-lib will contain only the functions related
|
||||||
check_update.cpp
|
# the user agent string.
|
||||||
user_agent.cpp)
|
add_library(updater-lib user_agent.cpp)
|
||||||
|
target_link_libraries(updater-lib laf-base ver-lib)
|
||||||
|
|
||||||
add_library(updater-lib ${UPDATER_LIB_SOURCES})
|
# Only when ENABLE_UPDATER is ON we'll enable the "check for update"
|
||||||
|
# portion of the library.
|
||||||
target_link_libraries(updater-lib
|
if(ENABLE_UPDATER)
|
||||||
laf-base
|
target_sources(updater-lib PRIVATE check_update.cpp)
|
||||||
net-lib
|
target_link_libraries(updater-lib net-lib ${TINYXML_LIBRARY})
|
||||||
ver-lib
|
endif()
|
||||||
${TINYXML_LIBRARY})
|
|
||||||
|
@ -19,45 +19,42 @@
|
|||||||
|
|
||||||
namespace updater {
|
namespace updater {
|
||||||
|
|
||||||
std::string getUserAgent()
|
std::string getFullOSString()
|
||||||
{
|
{
|
||||||
base::Platform p = base::get_platform();
|
base::Platform p = base::get_platform();
|
||||||
std::stringstream userAgent;
|
std::stringstream os;
|
||||||
|
|
||||||
// App name and version
|
|
||||||
userAgent << get_app_name() << "/" << get_app_version() << " (";
|
|
||||||
|
|
||||||
#if LAF_WINDOWS
|
#if LAF_WINDOWS
|
||||||
|
|
||||||
// ----------------------------------------------------------------------
|
// ----------------------------------------------------------------------
|
||||||
// Windows
|
// Windows
|
||||||
|
|
||||||
userAgent << "Windows";
|
os << "Windows";
|
||||||
switch (p.windowsType) {
|
switch (p.windowsType) {
|
||||||
case base::Platform::WindowsType::Server:
|
case base::Platform::WindowsType::Server:
|
||||||
userAgent << " Server";
|
os << " Server";
|
||||||
break;
|
break;
|
||||||
case base::Platform::WindowsType::NT:
|
case base::Platform::WindowsType::NT:
|
||||||
userAgent << " NT";
|
os << " NT";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
userAgent << " " << p.osVer.str();
|
os << " " << p.osVer.str();
|
||||||
|
|
||||||
if (p.servicePack.major() > 0)
|
if (p.servicePack.major() > 0)
|
||||||
userAgent << " SP" << p.servicePack.major();
|
os << " SP" << p.servicePack.major();
|
||||||
|
|
||||||
if (p.isWow64)
|
if (p.isWow64)
|
||||||
userAgent << "; WOW64";
|
os << "; WOW64";
|
||||||
|
|
||||||
if (p.wineVer)
|
if (p.wineVer)
|
||||||
userAgent << "; Wine " << p.wineVer;
|
os << "; Wine " << p.wineVer;
|
||||||
|
|
||||||
#elif LAF_MACOS
|
#elif LAF_MACOS
|
||||||
|
|
||||||
userAgent << "macOS "
|
os << "macOS "
|
||||||
<< p.osVer.major() << "."
|
<< p.osVer.major() << "."
|
||||||
<< p.osVer.minor() << "."
|
<< p.osVer.minor() << "."
|
||||||
<< p.osVer.patch();
|
<< p.osVer.patch();
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
@ -65,14 +62,23 @@ std::string getUserAgent()
|
|||||||
// Unix like
|
// Unix like
|
||||||
|
|
||||||
if (!p.distroName.empty()) {
|
if (!p.distroName.empty()) {
|
||||||
userAgent << p.distroName;
|
os << p.distroName;
|
||||||
if (!p.distroVer.empty())
|
if (!p.distroVer.empty())
|
||||||
userAgent << " " << p.distroVer;
|
os << " " << p.distroVer;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
userAgent << ")";
|
return os.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string getUserAgent()
|
||||||
|
{
|
||||||
|
std::stringstream userAgent;
|
||||||
|
|
||||||
|
// App name and version
|
||||||
|
userAgent << get_app_name() << "/" << get_app_version()
|
||||||
|
<< " (" << getFullOSString() << ")";
|
||||||
return userAgent.str();
|
return userAgent.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
// Aseprite
|
// Aseprite
|
||||||
|
// Copyright (C) 2024 Igara Studio S.A.
|
||||||
// Copyright (C) 2001-2015 David Capello
|
// Copyright (C) 2001-2015 David Capello
|
||||||
//
|
//
|
||||||
// This program is distributed under the terms of
|
// This program is distributed under the terms of
|
||||||
@ -12,6 +13,7 @@
|
|||||||
|
|
||||||
namespace updater {
|
namespace updater {
|
||||||
|
|
||||||
|
std::string getFullOSString();
|
||||||
std::string getUserAgent();
|
std::string getUserAgent();
|
||||||
|
|
||||||
} // namespace updater
|
} // namespace updater
|
||||||
|
@ -8,21 +8,29 @@ local sep = fs.pathSeparator
|
|||||||
|
|
||||||
assert('' == fs.filePath('first.png'))
|
assert('' == fs.filePath('first.png'))
|
||||||
assert('path' == fs.filePath('path/second.png'))
|
assert('path' == fs.filePath('path/second.png'))
|
||||||
assert('C:\\path' == fs.filePath('C:\\path\\third.png'))
|
if app.os.windows then
|
||||||
|
assert('C:\\path' == fs.filePath('C:\\path\\third.png'))
|
||||||
|
end
|
||||||
|
|
||||||
assert('first.png' == fs.fileName('first.png'))
|
assert('first.png' == fs.fileName('first.png'))
|
||||||
assert('second.png' == fs.fileName('path/second.png'))
|
assert('second.png' == fs.fileName('path/second.png'))
|
||||||
assert('third.png' == fs.fileName('C:\\path\\third.png'))
|
if app.os.windows then
|
||||||
|
assert('third.png' == fs.fileName('C:\\path\\third.png'))
|
||||||
|
end
|
||||||
|
|
||||||
assert('png' == fs.fileExtension('path/file.png'))
|
assert('png' == fs.fileExtension('path/file.png'))
|
||||||
|
|
||||||
assert('first' == fs.fileTitle('first.png'))
|
assert('first' == fs.fileTitle('first.png'))
|
||||||
assert('second' == fs.fileTitle('path/second.png'))
|
assert('second' == fs.fileTitle('path/second.png'))
|
||||||
assert('third' == fs.fileTitle('C:\\path\\third.png'))
|
if app.os.windows then
|
||||||
|
assert('third' == fs.fileTitle('C:\\path\\third.png'))
|
||||||
|
end
|
||||||
|
|
||||||
assert('first' == fs.filePathAndTitle('first.png'))
|
assert('first' == fs.filePathAndTitle('first.png'))
|
||||||
assert('path/second' == fs.filePathAndTitle('path/second.png'))
|
assert('path/second' == fs.filePathAndTitle('path/second.png'))
|
||||||
assert('C:\\path\\third' == fs.filePathAndTitle('C:\\path\\third.png'))
|
if app.os.windows then
|
||||||
|
assert('C:\\path\\third' == fs.filePathAndTitle('C:\\path\\third.png'))
|
||||||
|
end
|
||||||
|
|
||||||
assert('hi/bye' == fs.joinPath('hi/', 'bye'))
|
assert('hi/bye' == fs.joinPath('hi/', 'bye'))
|
||||||
assert('hi/bye' .. sep .. 'smth.png' == fs.joinPath('hi/', 'bye', 'smth.png'))
|
assert('hi/bye' .. sep .. 'smth.png' == fs.joinPath('hi/', 'bye', 'smth.png'))
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
-- Copyright (C) 2019 Igara Studio S.A.
|
-- Copyright (C) 2019-2024 Igara Studio S.A.
|
||||||
--
|
--
|
||||||
-- This file is released under the terms of the MIT license.
|
-- This file is released under the terms of the MIT license.
|
||||||
-- Read LICENSE.txt for more information.
|
-- Read LICENSE.txt for more information.
|
||||||
@ -51,6 +51,7 @@ do
|
|||||||
assert(b.patternOrigin.y == 0)
|
assert(b.patternOrigin.y == 0)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Image brush
|
||||||
do
|
do
|
||||||
local rgba = app.pixelColor.rgba
|
local rgba = app.pixelColor.rgba
|
||||||
local r = rgba(255, 0, 0)
|
local r = rgba(255, 0, 0)
|
||||||
@ -71,4 +72,175 @@ do
|
|||||||
|
|
||||||
brush:setBgColor(b)
|
brush:setBgColor(b)
|
||||||
expect_img(brush.image, { b, g, g, b })
|
expect_img(brush.image, { b, g, g, b })
|
||||||
|
|
||||||
|
-- Test copy image brushes
|
||||||
|
local brush2 = Brush(brush)
|
||||||
|
expect_img(brush2.image, { b, g, g, b })
|
||||||
|
brush2:setFgColor(r)
|
||||||
|
expect_img(brush2.image, { b, r, r, b })
|
||||||
|
brush2:setBgColor(r)
|
||||||
|
expect_img(brush2.image, { r, r, r, r })
|
||||||
|
expect_img(brush.image, { b, g, g, b }) -- First brush wasn't modified
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Tests with Image Brushes
|
||||||
|
-- Brush in a certain pixel format used on different sprites of
|
||||||
|
-- all available pixel formats.
|
||||||
|
do
|
||||||
|
-- RGB sprite
|
||||||
|
local sprRGB = Sprite(2, 2, ColorMode.RGB)
|
||||||
|
local cel = sprRGB.cels[1]
|
||||||
|
expect_img(cel.image, { 0, 0,
|
||||||
|
0, 0})
|
||||||
|
local pal = Palette(4)
|
||||||
|
pal:setColor(1, Color{ r=255, g=0, b=0, a=128 })
|
||||||
|
pal:setColor(2, Color{ r=0, g=255, b=0, a=128 })
|
||||||
|
pal:setColor(3, Color{ r=0, g=0, b=255, a=128 })
|
||||||
|
sprRGB:setPalette(pal)
|
||||||
|
|
||||||
|
-- Test Sprite RGB with RGB brush
|
||||||
|
local brushImg = Image(2, 2, ColorMode.RGB)
|
||||||
|
array_to_pixels({ pal:getColor(1), pal:getColor(2),
|
||||||
|
pal:getColor(3), pal:getColor(0) }, brushImg)
|
||||||
|
local bruRGB = Brush { image=brushImg }
|
||||||
|
|
||||||
|
app.useTool{ tool=pencil, brush=bruRGB, points={ Point(1, 1) } }
|
||||||
|
expect_img(cel.image,
|
||||||
|
{ pal:getColor(1).rgbaPixel, pal:getColor(2).rgbaPixel,
|
||||||
|
pal:getColor(3).rgbaPixel, pal:getColor(0).rgbaPixel })
|
||||||
|
app.undo()
|
||||||
|
|
||||||
|
-- Test Sprite RGB with INDEXED brush
|
||||||
|
local brushImg = Image(2, 2, ColorMode.INDEXED)
|
||||||
|
array_to_pixels({ 1, 2,
|
||||||
|
3, 0 }, brushImg)
|
||||||
|
local bruINDEXED = Brush { image=brushImg }
|
||||||
|
|
||||||
|
app.useTool{ tool=pencil, brush=bruINDEXED, points={ Point(1, 1) } }
|
||||||
|
expect_img(cel.image,
|
||||||
|
{ pal:getColor(1).rgbaPixel, pal:getColor(2).rgbaPixel,
|
||||||
|
pal:getColor(3).rgbaPixel, 0 })
|
||||||
|
app.undo()
|
||||||
|
|
||||||
|
-- Test Sprite RGB with GRAYSCALE brush
|
||||||
|
local brushImg = Image(2, 2, ColorMode.GRAYSCALE)
|
||||||
|
array_to_pixels({ Color{ gray=255, alpha=128 }, Color{ gray=128, alpha=128 },
|
||||||
|
Color{ gray=64, alpha=255 }, Color{ gray=0, alpha=255 } }, brushImg)
|
||||||
|
local bruGRAYSCALE = Brush { image=brushImg }
|
||||||
|
|
||||||
|
app.useTool{ tool=pencil, brush=bruGRAYSCALE, points={ Point(1, 1) } }
|
||||||
|
expect_img(cel.image,
|
||||||
|
{ Color{ gray=255, alpha=128 }.rgbaPixel, Color{ gray=128, alpha=128 }.rgbaPixel,
|
||||||
|
Color{ gray=64, alpha=255 }.rgbaPixel, Color{ gray=0, alpha=255 }.rgbaPixel })
|
||||||
|
|
||||||
|
-- -- -- -- -- -- --
|
||||||
|
-- INDEXED sprite
|
||||||
|
local sprINDEXED = Sprite(2, 2, ColorMode.INDEXED)
|
||||||
|
local cel = sprINDEXED.cels[1]
|
||||||
|
expect_img(cel.image, { 0, 0,
|
||||||
|
0, 0 })
|
||||||
|
local pal = Palette(4)
|
||||||
|
pal:setColor(1, Color{ r=255, g=0, b=0, a=128 })
|
||||||
|
pal:setColor(2, Color{ r=0, g=255, b=0, a=128 })
|
||||||
|
pal:setColor(3, Color{ r=0, g=0, b=255, a=128 })
|
||||||
|
sprINDEXED:setPalette(pal)
|
||||||
|
|
||||||
|
-- Test Sprite INDEXED with RGB brush
|
||||||
|
local brushImg = Image(2, 2, ColorMode.RGB)
|
||||||
|
array_to_pixels({ pal:getColor(1), pal:getColor(2),
|
||||||
|
pal:getColor(3), app.pixelColor.rgba(0, 0, 0, 0) }, brushImg)
|
||||||
|
local bruRGB = Brush { image=brushImg }
|
||||||
|
|
||||||
|
app.useTool{ tool=pencil, brush=bruRGB, points={ Point(1, 1) } }
|
||||||
|
expect_img(cel.image,
|
||||||
|
{ 1, 2,
|
||||||
|
3, 3 })
|
||||||
|
app.undo()
|
||||||
|
|
||||||
|
-- Test Sprite INDEXED with INDEXED brush
|
||||||
|
local brushImg = Image(2, 2, ColorMode.INDEXED)
|
||||||
|
array_to_pixels({ 1, 2,
|
||||||
|
3, 0 }, brushImg)
|
||||||
|
local bruINDEXED = Brush { image=brushImg }
|
||||||
|
|
||||||
|
app.useTool{ tool=pencil, brush=bruINDEXED, points={ Point(1, 1) } }
|
||||||
|
expect_img(cel.image,
|
||||||
|
{ 1, 2,
|
||||||
|
3, 0 })
|
||||||
|
app.undo()
|
||||||
|
|
||||||
|
-- Test Sprite INDEXED with INDEXED brush
|
||||||
|
-- (INDEXED brush with one out of bounds index)
|
||||||
|
local brushImg = Image(2, 2, ColorMode.INDEXED)
|
||||||
|
array_to_pixels({ 1, 5,
|
||||||
|
3, 0 }, brushImg)
|
||||||
|
local bruINDEXED = Brush { image=brushImg }
|
||||||
|
|
||||||
|
app.useTool{ tool=pencil, brush=bruINDEXED, points={ Point(1, 1) } }
|
||||||
|
expect_img(cel.image,
|
||||||
|
{ 1, 3,
|
||||||
|
3, 0 })
|
||||||
|
app.undo()
|
||||||
|
|
||||||
|
-- Test Sprite INDEXED with GRAYSCALE brush
|
||||||
|
local brushImg = Image(2, 2, ColorMode.GRAYSCALE)
|
||||||
|
array_to_pixels({ Color{ gray=255, alpha=128 }, Color{ gray=128, alpha=128 },
|
||||||
|
Color{ gray=64, alpha=255 }, Color{ gray=0, alpha=255 } }, brushImg)
|
||||||
|
local bruGRAYSCALE = Brush { image=brushImg }
|
||||||
|
|
||||||
|
app.useTool{ tool=pencil, brush=bruGRAYSCALE, points={ Point(1, 1) } }
|
||||||
|
expect_img(cel.image,
|
||||||
|
{ 2, 3,
|
||||||
|
3, 3 })
|
||||||
|
|
||||||
|
-- -- -- -- -- -- --
|
||||||
|
-- GRAYSCALE sprite
|
||||||
|
local sprGRAYSCALE = Sprite(2, 2, ColorMode.GRAYSCALE)
|
||||||
|
local cel = sprGRAYSCALE.cels[1]
|
||||||
|
expect_img(cel.image, { 0, 0,
|
||||||
|
0, 0 })
|
||||||
|
local pal = Palette(4)
|
||||||
|
pal:setColor(1, Color{ gray=128, alpha=128 }.grayPixel)
|
||||||
|
pal:setColor(2, Color{ gray=64, alpha=128 }.grayPixel)
|
||||||
|
pal:setColor(3, Color{ gray=32, alpha=255 }.grayPixel)
|
||||||
|
print(pal:getColor(1).grayPixel)
|
||||||
|
print(pal:getColor(2).grayPixel)
|
||||||
|
print(pal:getColor(3).grayPixel)
|
||||||
|
sprGRAYSCALE:setPalette(pal)
|
||||||
|
|
||||||
|
-- Test Sprite GRAYSCALE with RGB brush
|
||||||
|
local brushImg = Image(2, 2, ColorMode.RGB)
|
||||||
|
array_to_pixels({ Color{ r=255, g=0, b=0, a=128 }, Color{ r=0, g=255, b=0, a=128 },
|
||||||
|
Color{ r=0, g=0, b=255, a=128 }, app.pixelColor.rgba(0, 0, 0, 0) }, brushImg)
|
||||||
|
local bruRGB = Brush { image=brushImg }
|
||||||
|
|
||||||
|
app.useTool{ tool=pencil, brush=bruRGB, points={ Point(1, 1) } }
|
||||||
|
expect_img(cel.image,
|
||||||
|
{ Color{ gray=54, alpha=128 }.grayPixel, Color{ gray=182, alpha=128 }.grayPixel,
|
||||||
|
Color{ gray=18, alpha=128 }.grayPixel, 0 })
|
||||||
|
app.undo()
|
||||||
|
|
||||||
|
-- Test Sprite GRAYSCALE with INDEXED brush
|
||||||
|
-- (INDEXED brush with out of bound index)
|
||||||
|
local brushImg = Image(2, 2, ColorMode.INDEXED)
|
||||||
|
array_to_pixels({ 1, 5,
|
||||||
|
3, 0 }, brushImg)
|
||||||
|
local bruINDEXED = Brush { image=brushImg }
|
||||||
|
|
||||||
|
app.useTool{ tool=pencil, brush=bruINDEXED, points={ Point(1, 1) } }
|
||||||
|
expect_img(cel.image,
|
||||||
|
{ Color{ gray=128, alpha=128 }.grayPixel,
|
||||||
|
Color{ gray=32, alpha=255 }.grayPixel })
|
||||||
|
app.undo()
|
||||||
|
|
||||||
|
-- Test Sprite GRAYSCALE with GRAYSCALE brush
|
||||||
|
local brushImg = Image(2, 2, ColorMode.GRAYSCALE)
|
||||||
|
array_to_pixels({ Color{ gray=128, alpha=128 }, Color{ gray=222, alpha=222 },
|
||||||
|
Color{ gray=32, alpha=255 }, Color{ gray=0, alpha=255 } }, brushImg)
|
||||||
|
local bruGRAYSCALE = Brush { image=brushImg }
|
||||||
|
|
||||||
|
app.useTool{ tool=pencil, brush=bruGRAYSCALE, points={ Point(1, 1) } }
|
||||||
|
expect_img(cel.image,
|
||||||
|
{ pal:getColor(1).grayPixel, Color{ gray=222, alpha=222 }.grayPixel,
|
||||||
|
pal:getColor(3).grayPixel, Color{ gray=0, alpha=255 }.grayPixel })
|
||||||
end
|
end
|
||||||
|
1
third_party/CMakeLists.txt
vendored
1
third_party/CMakeLists.txt
vendored
@ -50,6 +50,7 @@ if(ENABLE_WEBP AND NOT LAF_BACKEND STREQUAL "skia")
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(NOT USE_SHARED_TINYXML)
|
if(NOT USE_SHARED_TINYXML)
|
||||||
|
set(tinyxml2_BUILD_TESTING OFF CACHE BOOL "Build tests for tinyxml2")
|
||||||
add_subdirectory(tinyxml2)
|
add_subdirectory(tinyxml2)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
2
third_party/cmark
vendored
2
third_party/cmark
vendored
@ -1 +1 @@
|
|||||||
Subproject commit 728c68465062223295076d8cb365ca911a55a218
|
Subproject commit 186592f7ff021cd20c5e758239934a3b7848d51f
|
Loading…
x
Reference in New Issue
Block a user