Merge branch 'main' into beta

This commit is contained in:
David Capello 2024-03-05 19:22:23 -03:00
commit 4cfb3cfa3f
69 changed files with 569 additions and 247 deletions

View File

@ -1,4 +1,65 @@
#
# Disabled or configured checks:
#
# bugprone-easily-swappable-parameters: We have a lot of functions
# with (int, int) or (string, string) so it does't make sense to enable
# this option.
#
# readability-braces-around-statements: We use a lot of:
# if (cond)
# stmt;
# else
# stmt;
# and there is no way to allow this with this check.
#
# readability-function-cognitive-complexity: We have this disabled
# temporarily, but it'd be nice to enable this with a high threshold
# in the future.
#
# readability-identifier-length: We use a lot of short names like x,
# y, w, h so we prefer to remove this.
#
# readability-magic-numbers: We use a lot of magic numbers like 8, 16,
# 24 for masks like 0xFF00, etc.
#
# readability-isolate-declaration: We use multiple declarations
# several times (e.g. int x, y, etc.)
#
# readability-uppercase-literal-suffix: We use a lot of 0.0f, but in a
# future we might enable this.
#
# readability-named-parameter: We prefer misc-unused-parameters to
# remove a parameter name that is not used.
#
# misc-use-anonymous-namespace: We use anonymous namespaces or static
# functions indifferently.
#
# misc-non-private-member-variables-in-classes: We use structs with
# all public members in some cases.
#
---
Checks: 'clang-analyzer-*'
Checks: >
-*,
bugprone-*,
clang-analyzer-*,
concurrency-*,
misc-*,
performance-*,
portability-*,
readability-*,
-bugprone-easily-swappable-parameters,
-misc-use-anonymous-namespace,
-readability-braces-around-statements,
-readability-function-cognitive-complexity,
-readability-identifier-length,
-readability-isolate-declaration,
-readability-magic-numbers,
-readability-named-parameter,
-readability-uppercase-literal-suffix
WarningsAsErrors: ''
CheckOptions:
- key: readability-implicit-bool-conversion.AllowPointerConditions
value: true
- key: misc-non-private-member-variables-in-classes.IgnoreClassesWithAllMemberVariablesBeingPublic
value: true
...

32
.github/workflows/clang_tidy.yml vendored Normal file
View File

@ -0,0 +1,32 @@
name: Clang Tidy Diff
on:
pull_request:
paths:
- '**.cpp'
- '**.h'
- '.github/workflows/clang_tidy.yml'
permissions:
contents: read
jobs:
review:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: 'recursive'
- uses: ZedThree/clang-tidy-review@v0.14.0
id: review
with:
build_dir: build
config_file: .clang-tidy
split_workflow: true
apt_packages: |
libc++-dev, libc++abi-dev, libpixman-1-dev,
libfreetype6-dev, libharfbuzz-dev, zlib1g-dev, libx11-dev,
libxcursor-dev, libxi-dev, libgl1-mesa-dev
cmake_command: |
cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug -DLAF_BACKEND=none -DCMAKE_EXPORT_COMPILE_COMMANDS=on
- uses: ZedThree/clang-tidy-review/upload@v0.14.0
id: upload-review

18
.github/workflows/clang_tidy_post.yml vendored Normal file
View File

@ -0,0 +1,18 @@
name: Post Clang Tidy Comments
on:
workflow_run:
workflows: ["Clang Tidy Diff"]
types:
- completed
permissions:
checks: write
pull-requests: write
jobs:
post-comments:
runs-on: ubuntu-latest
steps:
- uses: ZedThree/clang-tidy-review/post@v0.14.0
with:
token: ${{ secrets.CLANG_TIDY_TOKEN }}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

After

Width:  |  Height:  |  Size: 57 KiB

BIN
data/icons/ase20.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 207 B

BIN
data/icons/ase28.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 263 B

View File

@ -126,9 +126,12 @@ at least.
### Old palette chunk (0x0004)
Ignore this chunk if you find the new palette chunk (0x2019) Aseprite
v1.1 saves both chunks 0x0004 and 0x2019 just for backward
compatibility.
Ignore this chunk if you find the new palette chunk (0x2019). Aseprite
v1.1 saves both chunks (0x0004 and 0x2019) just for backward
compatibility. Aseprite v1.3.5 writes this chunk if the palette
doesn't have alpha channel and contains 256 colors or less (because
this chunk is smaller), in other case the new palette chunk (0x2019)
will be used (and the old one is not saved anymore).
WORD Number of packets
+ For each packet

View File

@ -776,7 +776,10 @@ if(ENABLE_UPDATER)
endif()
if(ENABLE_STEAM)
add_definitions(-DENABLE_STEAM)
# 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
# marked as PUBLIC).
target_compile_definitions(app-lib PUBLIC -DENABLE_STEAM)
target_link_libraries(app-lib steam-lib)
endif()

View File

@ -328,6 +328,11 @@ int App::initialize(const AppOptions& options)
app_configure_drm();
#endif
#ifdef ENABLE_STEAM
if (options.noInApp())
m_inAppSteam = false;
#endif
// Load modules
m_modules = std::make_unique<Modules>(createLogInDesktop, preferences());
m_legacy = std::make_unique<LegacyModules>(isGui() ? REQUIRE_INTERFACE: 0);
@ -511,9 +516,18 @@ void App::run()
// Initialize Steam API
#ifdef ENABLE_STEAM
steam::SteamAPI steam;
if (steam.initialized())
std::unique_ptr<steam::SteamAPI> steam;
if (m_inAppSteam) {
steam = std::make_unique<steam::SteamAPI>();
if (steam->isInitialized())
os::System::instance()->activateApp();
}
else {
// We tried to load the Steam SDK without calling
// SteamAPI_InitSafe() to check if we could run the program
// without "in game" indication but still capturing screenshots
// on Steam, and that wasn't the case.
}
#endif
#if defined(_DEBUG) || defined(ENABLE_DEVMODE)
@ -707,7 +721,6 @@ Workspace* App::workspace() const
{
if (m_mainWindow)
return m_mainWindow->getWorkspace();
else
return nullptr;
}
@ -715,7 +728,6 @@ ContextBar* App::contextBar() const
{
if (m_mainWindow)
return m_mainWindow->getContextBar();
else
return nullptr;
}
@ -723,7 +735,6 @@ Timeline* App::timeline() const
{
if (m_mainWindow)
return m_mainWindow->getTimeline();
else
return nullptr;
}
@ -853,7 +864,6 @@ PixelFormat app_get_current_pixel_format()
Doc* doc = ctx->activeDocument();
if (doc)
return doc->sprite()->pixelFormat();
else
return IMAGE_RGB;
}

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2018-2023 Igara Studio S.A.
// Copyright (C) 2018-2024 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@ -142,6 +142,9 @@ namespace app {
std::unique_ptr<LegacyModules> m_legacy;
bool m_isGui;
bool m_isShell;
#ifdef ENABLE_STEAM
bool m_inAppSteam = true;
#endif
std::unique_ptr<MainWindow> m_mainWindow;
base::paths m_files;
#ifdef ENABLE_UI

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2018-2022 Igara Studio S.A.
// Copyright (C) 2018-2024 Igara Studio S.A.
// Copyright (C) 2001-2017 David Capello
//
// This program is distributed under the terms of
@ -79,6 +79,9 @@ AppOptions::AppOptions(int argc, const char* argv[])
, m_exportTileset(m_po.add("export-tileset").description("Export only tilesets from visible tilemap layers"))
, m_verbose(m_po.add("verbose").mnemonic('v').description("Explain what is being done"))
, m_debug(m_po.add("debug").description("Extreme verbose mode and\ncopy log to desktop"))
#ifdef ENABLE_STEAM
, m_noInApp(m_po.add("noinapp").description("Disable \"in game\" visibility on Steam\nDoesn't count playtime"))
#endif
#ifdef _WIN32
, m_disableWintab(m_po.add("disable-wintab").description("Don't load wintab32.dll library"))
#endif
@ -121,6 +124,13 @@ bool AppOptions::hasExporterParams() const
m_po.enabled(m_sheet);
}
#ifdef ENABLE_STEAM
bool AppOptions::noInApp() const
{
return m_po.enabled(m_noInApp);
}
#endif
#ifdef _WIN32
bool AppOptions::disableWintab() const
{

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2018-2022 Igara Studio S.A.
// Copyright (C) 2018-2024 Igara Studio S.A.
// Copyright (C) 2001-2017 David Capello
//
// This program is distributed under the terms of
@ -95,6 +95,9 @@ public:
const Option& exportTileset() const { return m_exportTileset; }
bool hasExporterParams() const;
#ifdef ENABLE_STEAM
bool noInApp() const;
#endif
#ifdef _WIN32
bool disableWintab() const;
#endif
@ -166,6 +169,9 @@ private:
Option& m_verbose;
Option& m_debug;
#ifdef ENABLE_STEAM
Option& m_noInApp;
#endif
#ifdef _WIN32
Option& m_disableWintab;
#endif

View File

@ -1,4 +1,5 @@
// Aseprite
// Copyright (C) 2024 Igara Studio S.A.
// Copyright (C) 2001-2017 David Capello
//
// This program is distributed under the terms of
@ -8,7 +9,6 @@
#include "config.h"
#endif
#include "app/app.h"
#include "app/commands/command.h"
#include "app/context_access.h"
#include "app/modules/gui.h"
@ -49,7 +49,7 @@ bool LayerVisibilityCommand::onChecked(Context* context)
return false;
SelectedLayers selLayers;
auto range = App::instance()->timeline()->range();
DocRange range = context->activeSite().range();
if (range.enabled()) {
selLayers = range.selectedLayers();
}
@ -67,24 +67,25 @@ bool LayerVisibilityCommand::onChecked(Context* context)
void LayerVisibilityCommand::onExecute(Context* context)
{
ContextWriter writer(context);
Doc* doc = writer.document();
SelectedLayers selLayers;
auto range = App::instance()->timeline()->range();
DocRange range = context->activeSite().range();
if (range.enabled()) {
selLayers = range.selectedLayers();
}
else {
selLayers.insert(writer.layer());
}
bool anyVisible = false;
for (auto layer : selLayers) {
if (layer->isVisible())
anyVisible = true;
}
for (auto layer : selLayers) {
layer->setVisible(!anyVisible);
}
update_screen_for_document(writer.document());
const bool newState = !anyVisible;
for (auto layer : selLayers)
doc->setLayerVisibilityWithNotifications(layer, newState);
}
Command* CommandFactory::createLayerVisibilityCommand()

View File

@ -151,7 +151,7 @@ void FilterWorker::run()
}
{
std::lock_guard lock(m_mutex);
const std::lock_guard lock(m_mutex);
if (m_done && m_filterMgr->isTransaction())
m_filterMgr->commitTransaction();
else
@ -180,7 +180,7 @@ void FilterWorker::run()
//
void FilterWorker::reportProgress(float progress)
{
std::lock_guard lock(m_mutex);
const std::lock_guard lock(m_mutex);
m_pos = progress;
}
@ -192,7 +192,7 @@ bool FilterWorker::isCancelled()
{
bool cancelled;
std::lock_guard lock(m_mutex);
const std::lock_guard lock(m_mutex);
cancelled = (m_cancelled || m_abort);
return cancelled;
@ -209,7 +209,7 @@ void FilterWorker::applyFilterInBackground()
m_filterMgr->applyToTarget();
// Mark the work as 'done'.
std::lock_guard lock(m_mutex);
const std::lock_guard lock(m_mutex);
m_done = true;
}
catch (std::exception& e) {
@ -224,7 +224,7 @@ void FilterWorker::applyFilterInBackground()
// every 100 milliseconds).
void FilterWorker::onMonitoringTick()
{
std::lock_guard lock(m_mutex);
const std::lock_guard lock(m_mutex);
if (m_alert) {
m_alert->setProgress(m_pos);

View File

@ -48,7 +48,6 @@ namespace app {
auto it = m_params.find(name);
if (it != m_params.end())
return it->second;
else
return std::string();
}

View File

@ -193,6 +193,16 @@ color_t Doc::bgColor(Layer* layer) const
return layer->sprite()->transparentColor();
}
//////////////////////////////////////////////////////////////////////
// Modifications with notifications
void Doc::setLayerVisibilityWithNotifications(Layer* layer, const bool visible)
{
notifyBeforeLayerVisibilityChange(layer, visible);
layer->setVisible(visible);
notifyAfterLayerVisibilityChange(layer);
}
//////////////////////////////////////////////////////////////////////
// Notifications
@ -244,6 +254,20 @@ void Doc::notifyLayerMergedDown(Layer* srcLayer, Layer* targetLayer)
notify_observers<DocEvent&>(&DocObserver::onLayerMergedDown, ev);
}
void Doc::notifyBeforeLayerVisibilityChange(Layer* layer, bool newState)
{
DocEvent ev(this);
ev.layer(layer);
notify_observers<DocEvent&, bool>(&DocObserver::onBeforeLayerVisibilityChange, ev, newState);
}
void Doc::notifyAfterLayerVisibilityChange(Layer* layer)
{
DocEvent ev(this);
ev.layer(layer);
notify_observers<DocEvent&>(&DocObserver::onAfterLayerVisibilityChange, ev);
}
void Doc::notifyCelMoved(Layer* fromLayer, frame_t fromFrame, Layer* toLayer, frame_t toFrame)
{
DocEvent ev(this);

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2018-2023 Igara Studio S.A.
// Copyright (C) 2018-2024 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@ -107,6 +107,14 @@ namespace app {
os::ColorSpaceRef osColorSpace() const { return m_osColorSpace; }
//////////////////////////////////////////////////////////////////////
// Modifications with notifications
// Use this function to change the layer visibility and notify all
// DocObservers about this change (e.g. so the Editor can be
// invalidated/redrawn, MovingPixelsState can drop pixels, etc.)
void setLayerVisibilityWithNotifications(Layer* layer, const bool visible);
//////////////////////////////////////////////////////////////////////
// Notifications
@ -116,6 +124,8 @@ namespace app {
void notifySpritePixelsModified(Sprite* sprite, const gfx::Region& region, frame_t frame);
void notifyExposeSpritePixels(Sprite* sprite, const gfx::Region& region);
void notifyLayerMergedDown(Layer* srcLayer, Layer* targetLayer);
void notifyBeforeLayerVisibilityChange(Layer* layer, bool newState);
void notifyAfterLayerVisibilityChange(Layer* layer);
void notifyCelMoved(Layer* fromLayer, frame_t fromFrame, Layer* toLayer, frame_t toFrame);
void notifyCelCopied(Layer* fromLayer, frame_t fromFrame, Layer* toLayer, frame_t toFrame);
void notifySelectionChanged();

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2018-2023 Igara Studio S.A.
// Copyright (C) 2018-2024 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@ -97,6 +97,10 @@ namespace app {
// The collapsed/expanded flag of a specific layer changed.
virtual void onLayerCollapsedChanged(DocEvent& ev) { }
// The visibility flag of a specific layer is going to change/changed.
virtual void onBeforeLayerVisibilityChange(DocEvent& ev, bool newState) { }
virtual void onAfterLayerVisibilityChange(DocEvent& ev) { }
// The tileset was remapped (e.g. when tiles are re-ordered).
virtual void onRemapTileset(DocEvent& ev, const doc::Remap& remap) { }

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2018-2023 Igara Studio S.A.
// Copyright (C) 2018-2024 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@ -355,7 +355,7 @@ bool AseFormat::onSave(FileOp* fop)
bool require_new_palette_chunk = false;
for (Palette* pal : sprite->getPalettes()) {
if (pal->size() != 256 || pal->hasAlpha()) {
if (pal->size() > 256 || pal->hasAlpha()) {
require_new_palette_chunk = true;
break;
}
@ -393,10 +393,13 @@ bool AseFormat::onSave(FileOp* fop)
ase_file_write_palette_chunk(f, &frame_header,
pal, palFrom, palTo);
}
// Write color chunk for backward compatibility only
else {
// Use old color chunk only when the palette has 256 or less
// colors, and we don't need the alpha channel (as this chunk
// is smaller than the new palette chunk).
ase_file_write_color2_chunk(f, &frame_header, pal);
}
}
// Write extra chunks in the first frame
if (frame == fop->roi().fromFrame()) {
@ -676,6 +679,7 @@ static void ase_file_write_color2_chunk(FILE* f, dio::AsepriteFrameHeader* frame
// First packet
fputc(0, f); // skip 0 colors
ASSERT(pal->size() <= 256); // For >256 we use the palette chunk
fputc(pal->size() == 256 ? 0: pal->size(), f); // number of colors
for (c=0; c<pal->size(); c++) {
color = pal->getEntry(c);
@ -1692,8 +1696,12 @@ static void ase_ungroup_all(LayerGroup* group)
for (Layer* child : list) {
if (child->isGroup()) {
ase_ungroup_all(static_cast<LayerGroup*>(child));
group->removeLayer(child);
auto* childGroup = static_cast<LayerGroup*>(child);
ase_ungroup_all(childGroup);
group->removeLayer(childGroup);
ASSERT(childGroup->layersCount() == 0);
delete childGroup;
}
else if (group != root) {
// Create a new name adding all group layer names
@ -1711,11 +1719,6 @@ static void ase_ungroup_all(LayerGroup* group)
root->addLayer(child);
}
}
if (group != root) {
ASSERT(group->layersCount() == 0);
delete group;
}
}
} // namespace app

View File

@ -1122,13 +1122,13 @@ void FileOp::operate(IFileOpProgress* progress)
void FileOp::done()
{
// Finally done.
std::lock_guard lock(m_mutex);
const std::lock_guard lock(m_mutex);
m_done = true;
}
void FileOp::stop()
{
std::lock_guard lock(m_mutex);
const std::lock_guard lock(m_mutex);
if (!m_done)
m_stop = true;
}
@ -1443,7 +1443,7 @@ void FileOp::setError(const char *format, ...)
// Concatenate the new error
{
std::lock_guard lock(m_mutex);
const std::lock_guard lock(m_mutex);
// Add a newline char automatically if it's needed
if (!m_error.empty() && m_error.back() != '\n')
m_error.push_back('\n');
@ -1455,7 +1455,7 @@ void FileOp::setIncompatibilityError(const std::string& msg)
{
// Concatenate the new error
{
std::lock_guard lock(m_mutex);
const std::lock_guard lock(m_mutex);
// Add a newline char automatically if it's needed
if (!m_incompatibilityError.empty() && m_incompatibilityError.back() != '\n')
m_incompatibilityError.push_back('\n');
@ -1465,7 +1465,7 @@ void FileOp::setIncompatibilityError(const std::string& msg)
void FileOp::setProgress(double progress)
{
std::lock_guard lock(m_mutex);
const std::lock_guard lock(m_mutex);
if (isSequence()) {
m_progress =
@ -1494,7 +1494,7 @@ double FileOp::progress() const
{
double progress;
{
std::lock_guard lock(m_mutex);
const std::lock_guard lock(m_mutex);
progress = m_progress;
}
return progress;
@ -1506,7 +1506,7 @@ bool FileOp::isDone() const
{
bool done;
{
std::lock_guard lock(m_mutex);
const std::lock_guard lock(m_mutex);
done = m_done;
}
return done;

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2018-2023 Igara Studio S.A.
// Copyright (C) 2018-2024 Igara Studio S.A.
// Copyright (C) 2018 David Capello
//
// This program is distributed under the terms of
@ -329,7 +329,9 @@ int Layer_set_isEditable(lua_State* L)
int Layer_set_isVisible(lua_State* L)
{
auto layer = get_docobj<Layer>(L, 1);
layer->setVisible(lua_toboolean(L, 2));
const bool newState = lua_toboolean(L, 2);
Doc* doc = static_cast<Doc*>(layer->sprite()->document());
doc->setLayerVisibilityWithNotifications(layer, newState);
return 0;
}

View File

@ -29,7 +29,7 @@ Task::~Task()
void Task::run(base::task::func_t&& func)
{
std::lock_guard lock(m_token_mutex);
const std::lock_guard lock(m_token_mutex);
m_task.on_execute(std::move(func));
m_token = &m_task.start(tasks_pool);
}

View File

@ -34,29 +34,27 @@ namespace app {
}
bool canceled() const {
std::lock_guard lock(m_token_mutex);
const std::lock_guard lock(m_token_mutex);
if (m_token)
return m_token->canceled();
else
return false;
}
float progress() const {
std::lock_guard lock(m_token_mutex);
const std::lock_guard lock(m_token_mutex);
if (m_token)
return m_token->progress();
else
return 0.0f;
}
void cancel() {
std::lock_guard lock(m_token_mutex);
const std::lock_guard lock(m_token_mutex);
if (m_token)
m_token->cancel();
}
void set_progress(float progress) {
std::lock_guard lock(m_token_mutex);
const std::lock_guard lock(m_token_mutex);
if (m_token)
m_token->set_progress(progress);
}

View File

@ -49,7 +49,7 @@ public:
~Worker() {
{
std::lock_guard lock(m_mutex);
const std::lock_guard lock(m_mutex);
if (m_fop)
m_fop->stop();
}
@ -57,7 +57,7 @@ public:
}
void stop() const {
std::lock_guard lock(m_mutex);
const std::lock_guard lock(m_mutex);
if (m_fop)
m_fop->stop();
}
@ -67,7 +67,7 @@ public:
}
void updateProgress() {
std::lock_guard lock(m_mutex);
const std::lock_guard lock(m_mutex);
if (m_item.fileitem && m_item.fop) {
double progress = m_item.fop->progress();
if (progress > m_item.fileitem->getThumbnailProgress())
@ -80,7 +80,7 @@ private:
ASSERT(!m_fop);
try {
{
std::lock_guard lock(m_mutex);
const std::lock_guard lock(m_mutex);
m_fop = m_item.fop;
ASSERT(m_fop);
}
@ -167,7 +167,7 @@ private:
0, 0, 0, 0, thumbnailImage->width(), thumbnailImage->height());
{
std::lock_guard lock(m_mutex);
const std::lock_guard lock(m_mutex);
m_item.fileitem->setThumbnail(thumbnail);
}
}
@ -191,13 +191,13 @@ private:
// Reset the m_item (first the fileitem so this worker is not
// associated to this fileitem anymore, and then the FileOp).
{
std::lock_guard lock(m_mutex);
const std::lock_guard lock(m_mutex);
m_item.fileitem = nullptr;
}
m_fop->done();
{
std::lock_guard lock(m_mutex);
const std::lock_guard lock(m_mutex);
m_item.fop = nullptr;
delete m_fop;
m_fop = nullptr;
@ -212,7 +212,7 @@ private:
bool success = true;
while (success) {
{
std::lock_guard lock(m_mutex); // To access m_item
const std::lock_guard lock(m_mutex); // To access m_item
success = m_queue.try_pop(m_item);
}
if (success)
@ -253,7 +253,7 @@ ThumbnailGenerator::ThumbnailGenerator()
bool ThumbnailGenerator::checkWorkers()
{
std::lock_guard lock(m_workersAccess);
const std::lock_guard lock(m_workersAccess);
bool doingWork = (!m_workers.empty());
for (WorkerList::iterator
@ -339,14 +339,14 @@ void ThumbnailGenerator::stopAllWorkers()
}
}
std::lock_guard lock(m_workersAccess);
const std::lock_guard lock(m_workersAccess);
for (const auto& worker : m_workers)
worker->stop();
}
void ThumbnailGenerator::startWorker()
{
std::lock_guard lock(m_workersAccess);
const std::lock_guard lock(m_workersAccess);
if (m_workers.size() < m_maxWorkers) {
m_workers.push_back(std::make_unique<Worker>(m_remainingItems));
}

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2019-2023 Igara Studio S.A.
// Copyright (C) 2019-2024 Igara Studio S.A.
// Copyright (C) 2018 David Capello
//
// This program is distributed under the terms of
@ -23,7 +23,7 @@ namespace app {
// Wrapper to create a new transaction or get the current
// transaction in the context.
class Tx {
class [[nodiscard]] Tx {
public:
enum LockAction {
DocIsLocked, // The doc is locked to be written

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2018-2023 Igara Studio S.A.
// Copyright (C) 2018-2024 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@ -478,6 +478,15 @@ void DocView::onTotalFramesChanged(DocEvent& ev)
void DocView::onLayerRestacked(DocEvent& ev)
{
if (hasContentInActiveFrame(ev.layer()))
m_editor->invalidate();
}
void DocView::onAfterLayerVisibilityChange(DocEvent& ev)
{
// If there is no cel for this layer in the current frame, there is
// no need to redraw the editor
if (hasContentInActiveFrame(ev.layer()))
m_editor->invalidate();
}
@ -653,4 +662,19 @@ void DocView::onCancel(Context* ctx)
}
}
bool DocView::hasContentInActiveFrame(const doc::Layer* layer) const
{
if (!layer)
return false;
else if (layer->cel(m_editor->frame()))
return true;
else if (layer->isGroup()) {
for (const doc::Layer* child : static_cast<const doc::LayerGroup*>(layer)->layers()) {
if (hasContentInActiveFrame(child))
return true;
}
}
return false;
}
} // namespace app

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2019-2023 Igara Studio S.A.
// Copyright (C) 2019-2024 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@ -15,6 +15,10 @@
#include "app/ui/workspace_view.h"
#include "ui/box.h"
namespace doc {
class Layer;
}
namespace ui {
class View;
}
@ -86,6 +90,7 @@ namespace app {
void onAfterRemoveCel(DocEvent& ev) override;
void onTotalFramesChanged(DocEvent& ev) override;
void onLayerRestacked(DocEvent& ev) override;
void onAfterLayerVisibilityChange(DocEvent& ev) override;
void onTilesetChanged(DocEvent& ev) override;
// InputChainElement impl
@ -105,6 +110,8 @@ namespace app {
bool onProcessMessage(ui::Message* msg) override;
private:
bool hasContentInActiveFrame(const doc::Layer* layer) const;
Type m_type;
Doc* m_document;
ui::View* m_view;

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2018-2022 Igara Studio S.A.
// Copyright (C) 2018-2024 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@ -96,8 +96,9 @@ void DrawingState::initToolLoop(Editor* editor,
// For selection inks we don't use a "the selected layer" for
// preview purposes, because we want the selection feedback to be at
// the top of all layers.
Layer* previewLayer = (m_toolLoop->getInk()->isSelection() ? nullptr:
m_toolLoop->getLayer());
Layer* previewLayer = (m_toolLoop->getInk()->isSelection() ||
m_toolLoop->getInk()->isSlice() ?
nullptr : m_toolLoop->getLayer());
// Prepare preview image (the destination image will be our preview
// in the tool-loop time, so we can see what we are drawing)

View File

@ -2438,6 +2438,12 @@ void Editor::onRemoveSlice(DocEvent& ev)
}
}
void Editor::onBeforeLayerVisibilityChange(DocEvent& ev, bool newState)
{
if (m_state)
m_state->onBeforeLayerVisibilityChange(this, ev.layer(), newState);
}
void Editor::setCursor(const gfx::Point& mouseDisplayPos)
{
Rect vp = View::getView(this)->viewportBounds();

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2018-2023 Igara Studio S.A.
// Copyright (C) 2018-2024 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@ -351,6 +351,7 @@ namespace app {
void onAddTag(DocEvent& ev) override;
void onRemoveTag(DocEvent& ev) override;
void onRemoveSlice(DocEvent& ev) override;
void onBeforeLayerVisibilityChange(DocEvent& ev, bool newState) override;
// ActiveToolObserver impl
void onActiveToolChange(tools::Tool* tool) override;

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2019-2023 Igara Studio S.A.
// Copyright (C) 2019-2024 Igara Studio S.A.
// Copyright (C) 2001-2016 David Capello
//
// This program is distributed under the terms of
@ -26,6 +26,7 @@ namespace ui {
}
namespace doc {
class Layer;
class Tag;
}
@ -145,6 +146,11 @@ namespace app {
// collection.
virtual void onBeforeRemoveLayer(Editor* editor) { }
// Called when the visibility of a specific layer is changed.
virtual void onBeforeLayerVisibilityChange(Editor* editor,
doc::Layer* layer,
bool newState) { }
private:
DISABLE_COPYING(EditorState);
};

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2019-2023 Igara Studio S.A.
// Copyright (C) 2019-2024 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@ -578,6 +578,25 @@ bool MovingPixelsState::acceptQuickTool(tools::Tool* tool)
tool->getInk(0)->isZoom());
}
void MovingPixelsState::onBeforeLayerVisibilityChange(Editor* editor,
doc::Layer* layer,
bool newState)
{
if (!isActiveDocument())
return;
// If the layer visibility of any selected layer changes, we just
// drop the pixels (it's the easiest way to avoid modifying hidden
// pixels).
if (m_pixelsMovement) {
const Site& site = m_pixelsMovement->site();
if (site.layer() == layer ||
site.range().contains(layer)) {
dropPixels();
}
}
}
// Before executing any command, we drop the pixels (go back to standby).
void MovingPixelsState::onBeforeCommandExecution(CommandExecutionEvent& ev)
{

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2019-2022 Igara Studio S.A.
// Copyright (C) 2019-2024 Igara Studio S.A.
// Copyright (C) 2001-2017 David Capello
//
// This program is distributed under the terms of
@ -21,7 +21,7 @@
#include "ui/timer.h"
namespace doc {
class Image;
class Layer;
}
namespace app {
@ -54,35 +54,37 @@ namespace app {
void updateTransformation(const Transformation& t);
// EditorState
virtual void onEnterState(Editor* editor) override;
virtual void onEditorGotFocus(Editor* editor) override;
virtual LeaveAction onLeaveState(Editor* editor, EditorState* newState) override;
virtual void onActiveToolChange(Editor* editor, tools::Tool* tool) override;
virtual bool onMouseDown(Editor* editor, ui::MouseMessage* msg) override;
virtual bool onMouseUp(Editor* editor, ui::MouseMessage* msg) override;
virtual bool onMouseMove(Editor* editor, ui::MouseMessage* msg) override;
virtual bool onSetCursor(Editor* editor, const gfx::Point& mouseScreenPos) override;
virtual bool onKeyDown(Editor* editor, ui::KeyMessage* msg) override;
virtual bool onKeyUp(Editor* editor, ui::KeyMessage* msg) override;
virtual bool onUpdateStatusBar(Editor* editor) override;
virtual bool acceptQuickTool(tools::Tool* tool) override;
virtual bool requireBrushPreview() override { return false; }
void onEnterState(Editor* editor) override;
void onEditorGotFocus(Editor* editor) override;
LeaveAction onLeaveState(Editor* editor, EditorState* newState) override;
void onActiveToolChange(Editor* editor, tools::Tool* tool) override;
bool onMouseDown(Editor* editor, ui::MouseMessage* msg) override;
bool onMouseUp(Editor* editor, ui::MouseMessage* msg) override;
bool onMouseMove(Editor* editor, ui::MouseMessage* msg) override;
bool onSetCursor(Editor* editor, const gfx::Point& mouseScreenPos) override;
bool onKeyDown(Editor* editor, ui::KeyMessage* msg) override;
bool onKeyUp(Editor* editor, ui::KeyMessage* msg) override;
bool onUpdateStatusBar(Editor* editor) override;
bool acceptQuickTool(tools::Tool* tool) override;
bool requireBrushPreview() override { return false; }
void onBeforeLayerVisibilityChange(Editor* editor, doc::Layer* layer, bool newState) override;
// EditorObserver
virtual void onDestroyEditor(Editor* editor) override;
virtual void onBeforeFrameChanged(Editor* editor) override;
virtual void onBeforeLayerChanged(Editor* editor) override;
void onDestroyEditor(Editor* editor) override;
void onBeforeFrameChanged(Editor* editor) override;
void onBeforeLayerChanged(Editor* editor) override;
// TimelineObserver
virtual void onBeforeRangeChanged(Timeline* timeline) override;
void onBeforeRangeChanged(Timeline* timeline) override;
// ContextBarObserver
virtual void onDropPixels(ContextBarObserver::DropAction action) override;
void onDropPixels(ContextBarObserver::DropAction action) override;
// PixelsMovementDelegate
virtual void onPivotChange() override;
void onPivotChange() override;
virtual Transformation getTransformation(Editor* editor) override;
Transformation getTransformation(Editor* editor) override;
private:
// DelayedMouseMoveDelegate impl

View File

@ -787,9 +787,10 @@ void PixelsMovement::stampImage(bool finalStamp)
cels.push_back(currentCel);
}
if (currentCel && currentCel->layer() &&
if (currentCel &&
currentCel->layer() &&
currentCel->layer()->isImage() &&
!currentCel->layer()->isEditableHierarchy()) {
!currentCel->layer()->canEditPixels()) {
Transformation initialCelPos(gfx::Rect(m_initialMask0->bounds()), m_currentData.cornerThick());
redrawExtraImage(&initialCelPos);
stampExtraCelImage();

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2019-2021 Igara Studio S.A.
// Copyright (C) 2019-2024 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@ -75,6 +75,8 @@ namespace app {
const Mask* mask,
const char* operationName);
const Site& site() { return m_site; }
HandleType handle() const { return m_handle; }
bool canHandleFrameChange() const { return m_canHandleFrameChange; }

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2019-2023 Igara Studio S.A.
// Copyright (C) 2019-2024 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@ -452,7 +452,7 @@ protected:
//////////////////////////////////////////////////////////////////////
// For drawing
class ToolLoopImpl : public ToolLoopBase,
class ToolLoopImpl final : public ToolLoopBase,
public EditorObserver {
Context* m_context;
bool m_filled;
@ -517,6 +517,9 @@ public:
}
}
// 'isSelectionPreview = true' if the intention is to show a preview
// of Selection tools or Slice tool.
const bool isSelectionPreview = m_ink->isSelection() || m_ink->isSlice();
m_expandCelCanvas.reset(new ExpandCelCanvas(
site, m_layer,
m_docPref.tiled.mode(),
@ -530,9 +533,9 @@ public:
(m_layer->isTilemap() &&
site.tilemapMode() == TilemapMode::Pixels &&
site.tilesetMode() == TilesetMode::Manual &&
!m_ink->isSelection() ? ExpandCelCanvas::TilesetPreview:
!isSelectionPreview ? ExpandCelCanvas::TilesetPreview:
ExpandCelCanvas::None) |
(m_ink->isSelection() ? ExpandCelCanvas::SelectionPreview:
(isSelectionPreview ? ExpandCelCanvas::SelectionPreview:
ExpandCelCanvas::None))));
if (!m_floodfillSrcImage)
@ -555,7 +558,7 @@ public:
m_sprayWidth = m_toolPref.spray.width();
m_spraySpeed = m_toolPref.spray.speed();
if (m_ink->isSelection()) {
if (isSelectionPreview) {
m_useMask = false;
}
else {
@ -563,7 +566,7 @@ public:
}
// Start with an empty mask if the user is selecting with "default selection mode"
if (m_ink->isSelection() &&
if (isSelectionPreview &&
(!m_document->isMaskVisible() ||
(int(getModifiers()) & int(tools::ToolLoopModifiers::kReplaceSelection)))) {
Mask emptyMask;
@ -592,6 +595,9 @@ public:
m_editor->remove_observer(this);
#endif
// getSrcImage() is a virtual member function but ToolLoopImpl is
// marked as final to avoid not calling a derived version from
// this destructor.
if (m_floodfillSrcImage != getSrcImage())
delete m_floodfillSrcImage;
}
@ -954,7 +960,7 @@ tools::ToolLoop* create_tool_loop_for_script(
#ifdef ENABLE_UI
class PreviewToolLoopImpl : public ToolLoopBase {
class PreviewToolLoopImpl final : public ToolLoopBase {
Image* m_image;
public:

View File

@ -110,7 +110,7 @@ namespace app {
if (it != m_styles.end())
return it->second;
else
return nullptr;
return getDefaultStyle();
}
SkinPartPtr getPartById(const std::string& id) const {

View File

@ -696,7 +696,9 @@ bool Timeline::onProcessMessage(Message* msg)
bool newVisibleState = !allLayersVisible();
for (Layer* topLayer : m_sprite->root()->layers()) {
if (topLayer->isVisible() != newVisibleState) {
topLayer->setVisible(newVisibleState);
m_document->setLayerVisibilityWithNotifications(
topLayer, newVisibleState);
if (topLayer->isGroup())
regenRows = true;
}
@ -825,11 +827,11 @@ bool Timeline::onProcessMessage(Message* msg)
for (Row& row : m_rows) {
Layer* l = row.layer();
if (l->hasFlags(LayerFlags::Internal_WasVisible)) {
l->setVisible(true);
m_document->setLayerVisibilityWithNotifications(l, true);
l->switchFlags(LayerFlags::Internal_WasVisible, false);
}
else {
l->setVisible(false);
m_document->setLayerVisibilityWithNotifications(l, false);
}
}
}
@ -838,7 +840,7 @@ bool Timeline::onProcessMessage(Message* msg)
for (Row& row : m_rows) {
Layer* l = row.layer();
l->switchFlags(LayerFlags::Internal_WasVisible, l->isVisible());
l->setVisible(false);
m_document->setLayerVisibilityWithNotifications(l, false);
}
}
@ -2016,6 +2018,14 @@ void Timeline::onLayerCollapsedChanged(DocEvent& ev)
invalidate();
}
void Timeline::onAfterLayerVisibilityChange(DocEvent& ev)
{
layer_t layerIdx = getLayerIndex(ev.layer());
if (layerIdx >= 0)
invalidateRect(getPartBounds(Hit(PART_ROW_EYE_ICON, layerIdx))
.offset(origin()));
}
void Timeline::onStateChanged(Editor* editor)
{
m_aniControls.updateUsingEditor(editor);
@ -4475,12 +4485,10 @@ void Timeline::setLayerVisibleFlag(const layer_t l, const bool state)
if (!layer)
return;
bool redrawEditors = false;
bool regenRows = false;
if (layer->isVisible() != state) {
layer->setVisible(state);
redrawEditors = true;
m_document->setLayerVisibilityWithNotifications(layer, state);
// Regenerate rows because might change the flag of the children
// (the flag is propagated to the children in m_inheritedFlags).
@ -4493,9 +4501,8 @@ void Timeline::setLayerVisibleFlag(const layer_t l, const bool state)
layer = layer->parent();
while (layer) {
if (!layer->isVisible()) {
layer->setVisible(true);
m_document->setLayerVisibilityWithNotifications(layer, true);
regenRows = true;
redrawEditors = true;
}
layer = layer->parent();
}
@ -4506,9 +4513,6 @@ void Timeline::setLayerVisibleFlag(const layer_t l, const bool state)
regenerateRows();
invalidate();
}
if (redrawEditors)
m_document->notifyGeneralUpdate();
}
void Timeline::setLayerEditableFlag(const layer_t l, const bool state)

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2018-2023 Igara Studio S.A.
// Copyright (C) 2018-2024 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@ -163,6 +163,7 @@ namespace app {
void onTagChange(DocEvent& ev) override;
void onTagRename(DocEvent& ev) override;
void onLayerCollapsedChanged(DocEvent& ev) override;
void onAfterLayerVisibilityChange(DocEvent& ev) override;
// app::Context slots.
void onBeforeCommandExecution(CommandExecutionEvent& ev);

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2020 Igara Studio S.A.
// Copyright (C) 2020-2024 Igara Studio S.A.
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -45,14 +45,34 @@ Layer* candidate_if_layer_is_deleted(
bool layer_is_locked(Editor* editor)
{
Layer* layer = editor->layer();
if (layer && !layer->isEditableHierarchy()) {
if (!layer)
return false;
#ifdef ENABLE_UI
if (auto statusBar = StatusBar::instance())
auto statusBar = StatusBar::instance();
#endif
if (!layer->isVisibleHierarchy()) {
#ifdef ENABLE_UI
if (statusBar) {
statusBar->showTip(
1000, fmt::format(Strings::statusbar_tips_layer_locked(), layer->name()));
1000, fmt::format(Strings::statusbar_tips_layer_x_is_hidden(),
layer->name()));
}
#endif
return true;
}
if (!layer->isEditableHierarchy()) {
#ifdef ENABLE_UI
if (statusBar) {
statusBar->showTip(
1000, fmt::format(Strings::statusbar_tips_layer_locked(), layer->name()));
}
#endif
return true;
}
return false;
}

View File

@ -1,15 +1,15 @@
// Aseprite
// Copyright (C) 2018-2020 Igara Studio S.A.
// Copyright (C) 2018-2024 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
#ifdef __ASEPRITE_CONFIG_H
#ifdef ASEPRITE_CONFIG_H_INCLUDED
#error You cannot use config.h two times
#endif
#define __ASEPRITE_CONFIG_H
#define ASEPRITE_CONFIG_H_INCLUDED
// In MSVC
#ifdef _MSC_VER

View File

@ -75,7 +75,6 @@ namespace doc {
Image* originalImage() const {
if (m_backupImage)
return m_backupImage.get();
else
return m_image.get();
}

View File

@ -1,5 +1,5 @@
// Aseprite Document Library
// Copyright (c) 2018-2023 Igara Studio S.A.
// Copyright (c) 2018-2024 Igara Studio S.A.
// Copyright (c) 2001-2015 David Capello
//
// This file is released under the terms of the MIT license.
@ -53,12 +53,10 @@ namespace doc {
if (rgba_geta(a) == 0) {
if (rgba_geta(b) == 0)
return true;
else
return false;
}
else if (rgba_geta(b) == 0)
if (rgba_geta(b) == 0)
return false;
else
return a == b;
}
};
@ -98,12 +96,10 @@ namespace doc {
if (graya_geta(a) == 0) {
if (graya_geta(b) == 0)
return true;
else
return false;
}
else if (graya_geta(b) == 0)
if (graya_geta(b) == 0)
return false;
else
return a == b;
}
};

View File

@ -1,5 +1,5 @@
// Aseprite Document Library
// Copyright (c) 2022 Igara Studio S.A.
// Copyright (c) 2022-2024 Igara Studio S.A.
// Copyright (c) 2017 David Capello
//
// This file is released under the terms of the MIT license.
@ -72,13 +72,11 @@ namespace doc {
T* operator*() {
if (m_it != m_end)
return m_it->value();
else
return nullptr;
}
T* operator->() {
if (m_it != m_end)
return m_it->value();
else
return nullptr;
}
private:
@ -154,7 +152,6 @@ namespace doc {
it->value() &&
frame >= it->frame())
return it->value();
else
return nullptr;
}
@ -185,14 +182,12 @@ namespace doc {
frame_t fromFrame() const {
if (!m_keys.empty())
return m_keys.front().frame();
else
return -1;
}
frame_t toFrame() const {
if (!m_keys.empty())
return m_keys.back().frame();
else
return -1;
}

View File

@ -53,7 +53,7 @@ const ObjectId Object::id() const
// The first time the ID is request, we store the object in the
// "objects" hash table.
if (!m_id) {
std::lock_guard lock(g_mutex);
const std::lock_guard lock(g_mutex);
m_id = ++newId;
objects.insert(std::make_pair(m_id, const_cast<Object*>(this)));
}
@ -62,7 +62,7 @@ const ObjectId Object::id() const
void Object::setId(ObjectId id)
{
std::lock_guard lock(g_mutex);
const std::lock_guard lock(g_mutex);
if (m_id) {
auto it = objects.find(m_id);
@ -101,7 +101,7 @@ void Object::setVersion(ObjectVersion version)
Object* get_object(ObjectId id)
{
std::lock_guard lock(g_mutex);
const std::lock_guard lock(g_mutex);
auto it = objects.find(id);
if (it != objects.end())
return it->second;

View File

@ -73,7 +73,6 @@ namespace doc {
ASSERT(i >= 0);
if (i >= 0 && i < size())
return m_colors[i];
else
return 0;
}
color_t getEntry(int i) const {

View File

@ -435,7 +435,8 @@ bool Playback::decrementRepeat(const frame_t frameDelta)
PLAY_TRACE(" Repeat tag", tag->name(), " frame=", m_frame,
"repeat=", m_playing.back()->repeat,
"forward=", m_playing.back()->forward);
return true;
// Tag has only 1 frame, then don't move the playback cue.
return tag->frames() > 1;
}
else {
// Remove tag from played

View File

@ -282,6 +282,48 @@ TEST(Playback, SimplePingPong3)
EXPECT_FALSE(play.isStopped());
}
TEST(Playback, SimplePingPong4)
{
// A
// <>
// 0
Tag* a = make_tag("A", 0, 0, AniDir::PING_PONG, 1);
auto sprite = make_sprite(1, { a });
Playback play(sprite.get(), 0, Playback::Mode::PlayAll);
expect_frames(play, {0,0});
EXPECT_TRUE(play.isStopped());
}
TEST(Playback, SimplePingPong5)
{
// A
// <>
// 0
Tag* a = make_tag("A", 0, 0, AniDir::PING_PONG, 3);
auto sprite = make_sprite(1, { a });
Playback play(sprite.get(), 0, Playback::Mode::PlayAll);
expect_frames(play, {0,0,0,0});
EXPECT_TRUE(play.isStopped());
}
TEST(Playback, SimplePingPong6)
{
// A
// <>
// 0
Tag* a = make_tag("A", 0, 0, AniDir::PING_PONG, 0);
auto sprite = make_sprite(1, { a });
Playback play(sprite.get(), 0, Playback::Mode::PlayAll);
expect_frames(play, {0,0,0});
EXPECT_TRUE(play.isStopped());
}
TEST(Playback, SimplePingPong3Repeats)
{
// A
@ -297,6 +339,7 @@ TEST(Playback, SimplePingPong3Repeats)
EXPECT_FALSE(play.isStopped());
}
TEST(Playback, TagOneFrame)
{
// A

View File

@ -54,7 +54,6 @@ namespace doc {
//ASSERT(index >= 0 && index < size());
if (index >= 0 && index < size())
return m_map[index];
else
return index; // No remap
}

View File

@ -9,7 +9,6 @@
#define DOC_TAG_H_INCLUDED
#pragma once
#include "base/disable_copying.h"
#include "doc/anidir.h"
#include "doc/frame.h"
#include "doc/object.h"

View File

@ -84,7 +84,6 @@ namespace doc {
ImageRef get(const tile_index ti) const {
if (ti >= 0 && ti < size())
return m_tiles[ti].image;
else
return ImageRef(nullptr);
}
void set(const tile_index ti,
@ -93,7 +92,6 @@ namespace doc {
UserData& getTileData(const tile_index ti) const {
if (ti >= 0 && ti < size())
return const_cast<UserData&>(m_tiles[ti].data);
else
return kNoUserData;
}
void setTileData(const tile_index ti,

View File

@ -38,7 +38,6 @@ namespace doc {
Tileset* get(const tileset_index tsi) const {
if (tsi < size())
return m_tilesets[tsi];
else
return nullptr;
}

View File

@ -52,18 +52,14 @@ namespace fixmath {
errno = ERANGE;
return -0x7FFFFFFF;
}
else
return result;
}
else {
if ((x > 0) && (y > 0)) {
errno = ERANGE;
return 0x7FFFFFFF;
}
else
return result;
}
}
inline fixed fixsub(fixed x, fixed y) {
fixed result = x - y;
@ -73,18 +69,14 @@ namespace fixmath {
errno = ERANGE;
return -0x7FFFFFFF;
}
else
return result;
}
else {
if ((x > 0) && (y < 0)) {
errno = ERANGE;
return 0x7FFFFFFF;
}
else
return result;
}
}
inline fixed fixmul(fixed x, fixed y) {
return ftofix(fixtof(x) * fixtof(y));
@ -95,7 +87,6 @@ namespace fixmath {
errno = ERANGE;
return (x < 0) ? -0x7FFFFFFF : 0x7FFFFFFF;
}
else
return ftofix(fixtof(x) / fixtof(y));
}
@ -103,7 +94,6 @@ namespace fixmath {
/* (x >> 16) is not portable */
if (x >= 0)
return (x >> 16);
else
return ~((~x) >> 16);
}

View File

@ -21,12 +21,12 @@ BEGIN
BEGIN
VALUE "Comments", "https://www.aseprite.org/"
VALUE "CompanyName", "Igara Studio S.A."
VALUE "FileDescription", "Aseprite - Animated sprites editor & pixel art tool"
VALUE "FileDescription", "Aseprite"
VALUE "FileVersion", "1,3,0,0"
VALUE "InternalName", "aseprite"
VALUE "LegalCopyright", "Copyright (C) 2001-2024 Igara Studio S.A."
VALUE "OriginalFilename", "aseprite.exe"
VALUE "ProductName", "ASEPRITE"
VALUE "ProductName", "Aseprite"
VALUE "ProductVersion", "1,3,0,0"
END
END

View File

@ -72,7 +72,6 @@ namespace render {
if (n == 2)
return D2[i*2 + j];
else
return
+ 4*Dn(i%(n/2), j%(n/2), n/2)
+ Dn(i/(n/2), j/(n/2), 2);

View File

@ -76,7 +76,6 @@ namespace render {
inline int Zoom::remove(int x) const {
if (x < 0)
return (x * m_den / m_num) - 1;
else
return (x * m_den / m_num);
}
@ -84,7 +83,6 @@ namespace render {
int v = x * m_den;
if (x < 0)
return (v / m_num);
else
return (v / m_num) + (v % m_num != 0);
}

View File

@ -134,7 +134,7 @@ public:
unloadLib();
}
bool initialized() const {
bool isInitialized() const {
return m_initialized;
}
@ -239,7 +239,7 @@ SteamAPI* SteamAPI::instance()
}
SteamAPI::SteamAPI()
: m_impl(new Impl)
: m_impl(std::make_unique<Impl>())
{
ASSERT(g_instance == nullptr);
g_instance = this;
@ -247,15 +247,13 @@ SteamAPI::SteamAPI()
SteamAPI::~SteamAPI()
{
delete m_impl;
ASSERT(g_instance == this);
g_instance = nullptr;
}
bool SteamAPI::initialized() const
bool SteamAPI::isInitialized() const
{
return m_impl->initialized();
return m_impl->isInitialized();
}
void SteamAPI::runCallbacks()

View File

@ -1,5 +1,5 @@
// Aseprite Steam Wrapper
// Copyright (c) 2020 Igara Studio S.A.
// Copyright (c) 2020-2024 Igara Studio S.A.
// Copyright (c) 2016 David Capello
//
// This file is released under the terms of the MIT license.
@ -9,6 +9,8 @@
#define STEAM_STEAM_H_INCLUDED
#pragma once
#include <memory>
namespace steam {
class SteamAPI {
@ -18,7 +20,7 @@ public:
SteamAPI();
~SteamAPI();
bool initialized() const;
bool isInitialized() const;
void runCallbacks();
bool writeScreenshot(void* rgbBuffer,
@ -27,7 +29,7 @@ public:
private:
class Impl;
Impl* m_impl;
std::unique_ptr<Impl> m_impl;
};
} // namespace steam

View File

@ -1,5 +1,5 @@
// Aseprite UI Library
// Copyright (C) 2018-2023 Igara Studio S.A.
// Copyright (C) 2018-2024 Igara Studio S.A.
// Copyright (C) 2001-2017 David Capello
//
// This file is released under the terms of the MIT license.
@ -125,6 +125,9 @@ Grid::Info Grid::getChildInfo(Widget* child)
void Grid::setStyle(Style* style)
{
ASSERT(style);
if (!style)
style = Theme::getDefaultStyle();
Widget::setStyle(style);
setGap(style->gap());
}

View File

@ -8,7 +8,6 @@
#define UI_PAINT_H_INCLUDED
#pragma once
#include "base/disable_copying.h"
#include "gfx/color.h"
#include "os/paint.h"

View File

@ -142,6 +142,9 @@ Theme::~Theme()
set_theme(nullptr, guiscale());
}
// static
ui::Style Theme::m_defaultStyle(nullptr);
void Theme::regenerateTheme()
{
set_mouse_cursor(kNoCursor);

View File

@ -1,5 +1,5 @@
// Aseprite UI Library
// Copyright (C) 2020-2022 Igara Studio S.A.
// Copyright (C) 2020-2024 Igara Studio S.A.
// Copyright (C) 2001-2017 David Capello
//
// This file is released under the terms of the MIT license.
@ -141,6 +141,8 @@ namespace ui {
static void drawTextBox(Graphics* g, const Widget* textbox,
int* w, int* h, gfx::Color bg, gfx::Color fg);
static ui::Style* getDefaultStyle() { return &m_defaultStyle; }
protected:
virtual void onRegenerateTheme() = 0;
@ -165,6 +167,8 @@ namespace ui {
gfx::Size& sizeHint,
gfx::Border& borderHint,
gfx::Rect& textHint, int& textAlign);
static ui::Style m_defaultStyle;
};
} // namespace ui

View File

@ -199,7 +199,9 @@ void Widget::setTheme(Theme* theme)
void Widget::setStyle(Style* style)
{
assert_ui_thread();
ASSERT(style);
if (!style)
style = Theme::getDefaultStyle();
m_style = style;
m_border = m_theme->calcBorder(this, style);
m_bgColor = m_theme->calcBgColor(this, style);

View File

@ -142,7 +142,6 @@ namespace ui {
gfx::Color bgColor() const {
if (gfx::geta(m_bgColor) == 0 && m_parent)
return m_parent->bgColor();
else
return m_bgColor;
}

View File

@ -8,9 +8,7 @@
#define UPDATER_USER_AGENT_H_INCLUDED
#pragma once
#include "base/disable_copying.h"
#include <iosfwd>
#include <string>
namespace updater {

View File

@ -1,4 +1,4 @@
-- Copyright (C) 2023 Igara Studio S.A.
-- Copyright (C) 2023-2024 Igara Studio S.A.
--
-- This file is released under the terms of the MIT license.
-- Read LICENSE.txt for more information.
@ -8,7 +8,11 @@
--
dofile('./test_utils.lua')
do
-- You can use this code to fill file-tests-props.aseprite properties,
-- but we did this just one time in the past, now we want to check
-- that we can read the properties correctly from the file, e.g. to
-- check the correct endianness of the platform.
if false then
local spr = Sprite{ fromFile="sprites/file-tests-props.aseprite" }
-- Set sprite custom properties
@ -16,6 +20,7 @@ do
spr.properties.b = 1
spr.properties.c = "hi"
spr.properties.d = 2.3
spr.properties.e = 8.123456e-12
spr.properties("ext").a = {"one", "two", "three"}
-- Set layer custom properties
spr.layers[2].properties.a = "i'm the layer 3"
@ -24,11 +29,11 @@ do
spr.layers[1].tileset.properties.a = "i'm a tilemap"
spr.layers[1].tileset.properties.b = 11
spr.layers[1].tileset.properties("ext").a = "text from extension"
-- Create some tiles with properties
local tile = spr:newTile(spr.layers[1].tileset, 1)
-- Set tiles custom properties
local tile = spr.layers[1].tileset:tile(1)
tile.properties.a = 320
tile.properties.b = 330
tile = spr:newTile(spr.layers[1].tileset, 2)
tile = spr.layers[1].tileset:tile(2)
tile.properties.a = 640
tile.properties.b = 650
-- Set tags custom properties
@ -42,19 +47,25 @@ do
spr.slices[1].properties = {a=Point(3,4), b=Size(10,20)}
spr.slices[1].properties("ext", {a=Rectangle(10,20,30,40)})
spr:saveAs("sprites/file-tests-props.aseprite")
spr:close()
end
do
-- Test load/save file (and keep the properties intact)
local spr = Sprite{ fromFile="sprites/file-tests-props.aseprite" }
assert(#spr.properties == 5)
spr:saveAs("_test_userdata_codec_1.aseprite")
spr:close()
local origSpr = Sprite{ fromFile="sprites/file-tests-props.aseprite" }
spr = Sprite{ fromFile="_test_userdata_codec_1.aseprite" }
assert_sprites_eq(origSpr, spr)
origSpr:close()
assert(#spr.properties == 4)
local spr = Sprite{ fromFile="_test_userdata_codec_1.aseprite" }
assert(#spr.properties == 5)
assert(#spr.properties("ext") == 1)
assert(spr.properties.a == true)
assert(spr.properties.b == 1)
assert(spr.properties.c == "hi")
assert(spr.properties.d == 2.3)
assert(spr.properties.e == 8.123456e-12)
assert(spr.properties("ext").a[1] == "one")
assert(spr.properties("ext").a[2] == "two")
assert(spr.properties("ext").a[3] == "three")

View File

@ -28,10 +28,11 @@
* `4f-index-4x4.aseprite`: Indexed, 4 frames, 1 layer, mask color set
to index 0.
* `file-tests-props.aseprite`: Indexed, 64x64, 6 frames, 4 layers (one
of them is a tilemap), 13 cels, 1 tag.
of them is a tilemap), 13 cels, 1 tag, pre-defined user data
properties of all kinds in several sprite elements.
* `slices.aseprite`: Indexed, 4x4, background layer, 2 slices.
* `slices-moving.aseprite`: Indexed, 4x4, 1 linked cel in 4 frames,
background layer, 1 slice with 4 keyframes (each keyframe with a
different position/size).
* `2x2tilemap2x2tile.aseprite`: RGB, 6x6, 2x2 tilemap layer, 5 tiles tileset,
2x2 tile size, 1 frame.
2x2 tile size, 1 frame.

2
third_party/json11 vendored

@ -1 +1 @@
Subproject commit e5868fff1fc5128077ed7699bdaa20ae47c47eca
Subproject commit 92ba6ce0fa1f1c8fd8783b6930b52539b3861888