Merge branch 'main' into beta

This commit is contained in:
David Capello 2024-04-08 17:04:45 -03:00
commit a6d9e5243f
21 changed files with 197 additions and 134 deletions

3
.gitmodules vendored
View File

@ -24,9 +24,6 @@
[submodule "third_party/libpng"]
path = third_party/libpng
url = https://github.com/aseprite/libpng.git
[submodule "src/clip"]
path = src/clip
url = https://github.com/aseprite/clip.git
[submodule "src/observable"]
path = src/observable
url = https://github.com/aseprite/observable.git

2
laf

@ -1 +1 @@
Subproject commit 946fdf956b8a41a33337c9e1f0046bf4a4471b48
Subproject commit f32d3918ddf6774b643f68f6525463cdacfb52ba

View File

@ -95,12 +95,6 @@ set(OBSERVABLE_TESTS OFF CACHE BOOL "Compile observable tests")
add_subdirectory(observable)
include_directories(observable)
# Disable clip examples and tests
set(CLIP_EXAMPLES OFF CACHE BOOL "Compile clip examples")
set(CLIP_TESTS OFF CACHE BOOL "Compile clip tests")
set(CLIP_X11_PNG_LIBRARY "${PNG_LIBRARY}")
add_subdirectory(clip)
# Disable undo tests
set(UNDO_TESTS OFF CACHE BOOL "Compile undo tests")
add_subdirectory(undo)

View File

@ -101,23 +101,20 @@ add_definitions(-DLIBARCHIVE_STATIC)
######################################################################
# app-lib target
add_library(app-lib ${generated_files})
# These specific-platform files should be in an external library
# (e.g. "base" or "os").
set(app_platform_files)
if(WIN32)
set(app_platform_files
font_path_win.cpp)
target_sources(app-lib PRIVATE font_path_win.cpp)
elseif(APPLE)
set(app_platform_files
font_path_osx.mm)
target_sources(app-lib PRIVATE font_path_osx.mm)
else()
set(app_platform_files
font_path_unix.cpp)
target_sources(app-lib PRIVATE font_path_unix.cpp)
endif()
set(data_recovery_files)
if(ENABLE_DATA_RECOVERY)
set(data_recovery_files
target_sources(app-lib PRIVATE
crash/backup_observer.cpp
crash/data_recovery.cpp
crash/read_document.cpp
@ -126,7 +123,7 @@ if(ENABLE_DATA_RECOVERY)
ui/data_recovery_view.cpp)
endif()
set(file_formats
target_sources(app-lib PRIVATE
file/ase_format.cpp
file/bmp_format.cpp
file/css_format.cpp
@ -140,27 +137,27 @@ set(file_formats
file/svg_format.cpp
file/tga_format.cpp)
if(ENABLE_WEBP)
list(APPEND file_formats file/webp_format.cpp)
target_sources(app-lib PRIVATE
file/webp_format.cpp)
endif()
if(ENABLE_PSD)
list(APPEND file_formats file/psd_format.cpp)
target_sources(app-lib PRIVATE
file/psd_format.cpp)
endif()
set(scripting_files)
if(ENABLE_SCRIPTING)
set(scripting_files_ui)
if(ENABLE_UI)
set(scripting_files_ui
target_sources(app-lib PRIVATE
commands/cmd_developer_console.cpp
commands/cmd_open_script_folder.cpp
commands/debugger.cpp
ui/devconsole_view.cpp)
endif()
if(ENABLE_WEBSOCKET)
set(scripting_files_ws
target_sources(app-lib PRIVATE
script/websocket_class.cpp)
endif()
set(scripting_files
target_sources(app-lib PRIVATE
commands/cmd_run_script.cpp
script/app_command_object.cpp
script/app_fs_object.cpp
@ -219,14 +216,11 @@ if(ENABLE_SCRIPTING)
script/values.cpp
script/version_class.cpp
script/window_class.cpp
shell.cpp
${scripting_files_ws}
${scripting_files_ui})
shell.cpp)
endif()
set(ui_app_files)
if(ENABLE_UI)
set(ui_app_files
target_sources(app-lib PRIVATE
app_brushes.cpp
app_menus.cpp
closed_docs.cpp
@ -436,34 +430,30 @@ if(ENABLE_UI)
ui_context.cpp
widget_loader.cpp)
if(ENABLE_NEWS)
set(ui_app_files
target_sources(app-lib PRIVATE
res/http_loader.cpp
ui/news_listbox.cpp
${ui_app_files})
ui/news_listbox.cpp)
endif()
if(ENABLE_DRM)
set(ui_app_files
target_sources(app-lib PRIVATE
ui/enter_license.cpp
ui/aseprite_update.cpp
${ui_app_files})
ui/aseprite_update.cpp)
endif()
endif()
set(send_crash_files)
if(ENABLE_SENTRY)
set(send_crash_files sentry_wrapper.cpp)
target_sources(app-lib PRIVATE sentry_wrapper.cpp)
else()
set(send_crash_files send_crash.cpp)
target_sources(app-lib PRIVATE send_crash.cpp)
endif()
add_library(app-lib
target_sources(app-lib PRIVATE
active_site_handler.cpp
app.cpp
check_update.cpp
cli/app_options.cpp
cli/cli_open_file.cpp
cli/cli_processor.cpp
${file_formats}
cli/default_cli_delegate.cpp
cli/preview_cli_delegate.cpp
cmd.cpp
@ -704,13 +694,7 @@ add_library(app-lib
util/tileset_utils.cpp
util/wrap_point.cpp
xml_document.cpp
xml_exception.cpp
${send_crash_files}
${ui_app_files}
${app_platform_files}
${data_recovery_files}
${scripting_files}
${generated_files})
xml_exception.cpp)
if(TARGET generated_version)
add_dependencies(app-lib generated_version)

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
@ -646,12 +646,12 @@ void ChangePixelFormatCommand::onExecute(Context* context)
#endif // ENABLE_UI
// No conversion needed
if (context->activeDocument()->sprite()->pixelFormat() == m_format)
Doc* doc = context->activeDocument();
if (doc->sprite()->pixelFormat() == m_format)
return;
{
const ContextReader reader(context);
SpriteJob job(reader, Strings::color_mode_title().c_str());
SpriteJob job(context, doc, Strings::color_mode_title());
Sprite* sprite(job.sprite());
// TODO this was moved in the main UI thread because
@ -662,16 +662,17 @@ void ChangePixelFormatCommand::onExecute(Context* context)
// https://github.com/aseprite/aseprite/issues/509
// https://github.com/aseprite/aseprite/issues/378
if (flatten) {
Tx tx(Tx::LockDoc, context, doc);
const bool newBlend = Preferences::instance().experimental.newBlend();
SelectedLayers selLayers;
for (auto layer : sprite->root()->layers())
selLayers.insert(layer);
job.tx()(new cmd::FlattenLayers(sprite, selLayers, newBlend));
tx(new cmd::FlattenLayers(sprite, selLayers, newBlend));
}
job.startJobWithCallback(
[this, &job, sprite] {
job.tx()(
[this, &job, sprite](Tx& tx) {
tx(
new cmd::SetPixelFormat(
sprite, m_format,
m_dithering,

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
@ -177,45 +177,46 @@ void ColorQuantizationCommand::onExecute(Context* ctx)
return;
try {
ContextReader reader(ctx);
Doc* doc = site.document();
Sprite* sprite = site.sprite();
frame_t frame = site.frame();
const Palette* curPalette = site.sprite()->palette(frame);
Palette tmpPalette(frame, entries.picks());
SpriteJob job(reader, "Color Quantization");
SpriteJob job(ctx, doc, "Color Quantization");
const bool newBlend = pref.experimental.newBlend();
job.startJobWithCallback(
[sprite, withAlpha, &tmpPalette, &job, newBlend, algorithm]{
[sprite, withAlpha, curPalette, &tmpPalette, &job, &entries,
newBlend, algorithm, createPal, site, frame](Tx& tx) {
render::create_palette_from_sprite(
sprite, 0, sprite->lastFrame(),
withAlpha, &tmpPalette,
&job, // SpriteJob is a render::TaskDelegate
newBlend,
algorithm);
std::unique_ptr<Palette> newPalette(
new Palette(createPal ? tmpPalette:
*site.palette()));
if (createPal) {
entries = PalettePicks(newPalette->size());
entries.all();
}
int i = 0, j = 0;
for (bool state : entries) {
if (state)
newPalette->setEntry(i, tmpPalette.getEntry(j++));
++i;
}
if (*curPalette != *newPalette)
tx(new cmd::SetPalette(sprite, frame, newPalette.get()));
});
job.waitJob();
if (job.isCanceled())
return;
std::unique_ptr<Palette> newPalette(
new Palette(createPal ? tmpPalette:
*site.palette()));
if (createPal) {
entries = PalettePicks(newPalette->size());
entries.all();
}
int i = 0, j = 0;
for (bool state : entries) {
if (state)
newPalette->setEntry(i, tmpPalette.getEntry(j++));
++i;
}
if (*curPalette != *newPalette)
job.tx()(new cmd::SetPalette(sprite, frame, newPalette.get()));
}
catch (const base::Exception& e) {
Console::showException(e);

View File

@ -1316,9 +1316,20 @@ private:
if (language()->getItemCount() > 0)
return;
// Select current language by lang ID
// Check if the current language exists, in other case select English.
Strings* strings = Strings::instance();
std::string curLang = strings->currentLanguage();
bool found = false;
for (const LangInfo& lang : strings->availableLanguages()) {
if (lang.id == curLang) {
found = true;
break;
}
}
if (!found)
curLang = Strings::kDefLanguage;
// Select current language by lang ID
for (const LangInfo& lang : strings->availableLanguages()) {
int i = language()->addItem(new LangItem(lang));
if (lang.id == curLang)

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-2018 David Capello
//
// This program is distributed under the terms of
@ -13,7 +13,6 @@
#include "app/cmd/set_cel_bounds.h"
#include "app/commands/cmd_rotate.h"
#include "app/commands/params.h"
#include "app/context_access.h"
#include "app/doc_api.h"
#include "app/doc_range.h"
#include "app/i18n/strings.h"
@ -45,10 +44,10 @@ class RotateJob : public SpriteJob {
public:
RotateJob(const ContextReader& reader,
RotateJob(Context* ctx, Doc* doc,
const std::string& jobName,
int angle, const CelList& cels, bool rotateSprite)
: SpriteJob(reader, jobName.c_str())
: SpriteJob(ctx, doc, jobName)
, m_cels(cels)
, m_rotateSprite(rotateSprite) {
m_angle = angle;
@ -80,8 +79,8 @@ protected:
}
// [working thread]
void onJob() override {
DocApi api = document()->getApi(tx());
void onSpriteJob(Tx& tx) override {
DocApi api = document()->getApi(tx);
// 1) Rotate cel positions
for (Cel* cel : m_cels) {
@ -93,7 +92,7 @@ protected:
gfx::RectF bounds = cel->boundsF();
rotate_rect(bounds);
if (cel->boundsF() != bounds)
tx()(new cmd::SetCelBoundsF(cel, bounds));
tx(new cmd::SetCelBoundsF(cel, bounds));
}
else {
gfx::Rect bounds = cel->bounds();
@ -192,6 +191,7 @@ void RotateCommand::onExecute(Context* context)
{
{
Site site = context->activeSite();
Doc* doc = site.document();
CelList cels;
bool rotateSprite = false;
@ -203,7 +203,7 @@ void RotateCommand::onExecute(Context* context)
// If we want to rotate the visible mask, we can go to
// MovingPixelsState (even when the range is enabled, because
// now PixelsMovement support ranges).
if (site.document()->isMaskVisible()) {
if (doc->isMaskVisible()) {
// Select marquee tool
if (tools::Tool* tool = App::instance()->toolBox()
->getToolById(tools::WellKnownTools::RectangularMarquee)) {
@ -237,13 +237,12 @@ void RotateCommand::onExecute(Context* context)
rotateSprite = true;
}
ContextReader reader(context);
{
RotateJob job(reader, friendlyName(), m_angle, cels, rotateSprite);
RotateJob job(context, doc, friendlyName(), m_angle, cels, rotateSprite);
job.startJob();
job.waitJob();
}
update_screen_for_document(reader.document());
update_screen_for_document(doc);
}
}

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
@ -80,8 +80,11 @@ class SpriteSizeJob : public SpriteJob {
public:
SpriteSizeJob(const ContextReader& reader, int new_width, int new_height, ResizeMethod resize_method)
: SpriteJob(reader, Strings::sprite_size_title().c_str()) {
SpriteSizeJob(Context* ctx, Doc* doc,
const int new_width,
const int new_height,
const ResizeMethod resize_method)
: SpriteJob(ctx, doc, Strings::sprite_size_title()) {
m_new_width = new_width;
m_new_height = new_height;
m_resize_method = resize_method;
@ -90,8 +93,8 @@ public:
protected:
// [working thread]
void onJob() override {
DocApi api = writer().document()->getApi(tx());
void onSpriteJob(Tx& tx) override {
DocApi api = document()->getApi(tx);
Tilesets* tilesets = sprite()->tilesets();
int img_count = 0;
@ -147,7 +150,7 @@ protected:
++progress;
++idx;
}
tx()(new cmd::ReplaceTileset(sprite(), tsi, newTileset));
tx(new cmd::ReplaceTileset(sprite(), tsi, newTileset));
// Cancel all the operation?
if (isCanceled())
@ -170,11 +173,11 @@ protected:
cel->y()*scale.h,
canvasSize.w,
canvasSize.h);
tx()(new cmd::SetCelBoundsF(cel, newBounds));
tx(new cmd::SetCelBoundsF(cel, newBounds));
}
else {
resize_cel_image(
tx(), cel, scale,
tx, cel, scale,
m_resize_method,
cel->layer()->isReference() ?
-cel->boundsF().origin():
@ -239,7 +242,7 @@ protected:
newKey.setPivot(gfx::Point(scale_x(newKey.pivot().x),
scale_y(newKey.pivot().y)));
tx()(new cmd::SetSliceKey(slice, k.frame(), newKey));
tx(new cmd::SetSliceKey(slice, k.frame(), newKey));
}
}
@ -373,8 +376,9 @@ void SpriteSizeCommand::onExecute(Context* context)
#ifdef ENABLE_UI
const bool ui = (params().ui() && context->isUIAvailable());
#endif
const ContextReader reader(context);
const Sprite* sprite(reader.sprite());
const Site site = context->activeSite();
Doc* doc = site.document();
Sprite* sprite = site.sprite();
auto& params = this->params();
double ratio = sprite->width() / double(sprite->height());
@ -461,13 +465,13 @@ void SpriteSizeCommand::onExecute(Context* context)
new_height = std::clamp(new_height, 1, DOC_SPRITE_MAX_HEIGHT);
{
SpriteSizeJob job(reader, new_width, new_height, resize_method);
SpriteSizeJob job(context, doc, new_width, new_height, resize_method);
job.startJob();
job.waitJob();
}
#ifdef ENABLE_UI
update_screen_for_document(reader.document());
update_screen_for_document(doc);
#endif
}

View File

@ -25,7 +25,8 @@
namespace app {
static Strings* singleton = nullptr;
static const char* kDefLanguage = "en";
const char* Strings::kDefLanguage = "en";
// static
void Strings::createInstance(Preferences& pref,

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2023 Igara Studio S.A.
// Copyright (C) 2023-2024 Igara Studio S.A.
// Copyright (C) 2016-2018 David Capello
//
// This program is distributed under the terms of
@ -25,6 +25,8 @@ namespace app {
// Singleton class to load and access "strings/en.ini" file.
class Strings : public app::gen::Strings<app::Strings> {
public:
static const char* kDefLanguage;
static void createInstance(Preferences& pref,
Extensions& exts);
static Strings* instance();

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2021 Igara Studio S.A.
// Copyright (C) 2021-2024 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@ -33,7 +33,7 @@ int Job::runningJobs()
return g_runningJobs;
}
Job::Job(const char* jobName)
Job::Job(const std::string& jobName)
{
m_last_progress = 0.0;
m_done_flag = false;

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2021 Igara Studio S.A.
// Copyright (C) 2021-2024 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@ -15,6 +15,7 @@
#include <atomic>
#include <exception>
#include <mutex>
#include <string>
#include <thread>
namespace app {
@ -23,7 +24,7 @@ namespace app {
public:
static int runningJobs();
Job(const char* jobName);
Job(const std::string& jobName);
virtual ~Job();
// Starts the job calling onJob() event in another thread and

View File

@ -1,4 +1,5 @@
// Aseprite
// Copyright (C) 2024 Igara Studio S.A.
// Copyright (C) 2017 David Capello
//
// This program is distributed under the terms of
@ -10,26 +11,49 @@
#include "app/sprite_job.h"
#include "base/log.h"
namespace app {
SpriteJob::SpriteJob(const ContextReader& reader, const char* jobName)
SpriteJob::SpriteJob(Context* ctx, Doc* doc,
const std::string& jobName)
: Job(jobName)
, m_writer(reader, 500)
, m_document(m_writer.document())
, m_sprite(m_writer.sprite())
, m_tx(m_writer, jobName, ModifyDocument)
, m_doc(doc)
, m_sprite(doc->sprite())
, m_tx(Tx::DontLockDoc, ctx, doc, jobName, ModifyDocument)
, m_lockAction(Tx::LockDoc)
{
// Try to write-lock the document to see if we have to lock the
// document in the background thread.
auto lockResult = m_doc->writeLock(500);
if (lockResult != Doc::LockResult::Fail) {
if (lockResult == Doc::LockResult::Reentrant)
m_lockAction = Tx::DontLockDoc;
m_doc->unlock(lockResult);
}
}
SpriteJob::~SpriteJob()
{
if (!isCanceled())
m_tx.commit();
try {
if (!isCanceled())
m_tx.commit();
}
catch (const std::exception& ex) {
LOG(ERROR, "Error committing changes: %s\n", ex.what());
}
}
void SpriteJob::onSpriteJob(Tx& tx)
{
if (m_callback)
m_callback(tx);
}
void SpriteJob::onJob()
{
m_callback();
Tx subtx(m_lockAction, m_ctx, m_doc);
onSpriteJob(subtx);
}
bool SpriteJob::continueTask()

View File

@ -1,4 +1,5 @@
// Aseprite
// Copyright (C) 2024 Igara Studio S.A.
// Copyright (C) 2017-2018 David Capello
//
// This program is distributed under the terms of
@ -15,19 +16,32 @@
#include "render/task_delegate.h"
#include <functional>
#include <memory>
#include <string>
namespace app {
class Context;
// Creates a Job to run a task in a background thread. At the same
// time it creates a new Tx in the main thread (to group all sub-Txs)
// without locking the sprite. You have to lock the sprite with a sub
// Tx (the onSpriteJob(Tx) already has the sprite locked for write
// access).
//
// This class takes care to lock the sprite in the background thread
// for write access, or to avoid re-locking the sprite in case it's
// already locked from the main thread (where SpriteJob was created,
// generally true when we're running a script).
class SpriteJob : public Job,
public render::TaskDelegate {
public:
SpriteJob(const ContextReader& reader, const char* jobName);
SpriteJob(Context* ctx, Doc* doc,
const std::string& jobName);
~SpriteJob();
ContextWriter& writer() { return m_writer; }
Doc* document() const { return m_document; }
Doc* document() const { return m_doc; }
Sprite* sprite() const { return m_sprite; }
Tx& tx() { return m_tx; }
template<typename T>
void startJobWithCallback(T&& callback) {
@ -36,6 +50,8 @@ public:
}
private:
virtual void onSpriteJob(Tx& tx);
// Job impl
void onJob() override;
@ -44,15 +60,21 @@ private:
bool continueTask() override;
void notifyTaskProgress(double progress) override;
ContextWriter m_writer;
Doc* m_document;
Context* m_ctx;
Doc* m_doc;
Sprite* m_sprite;
Tx m_tx;
// What action to do with the sub-Tx inside the background thread.
// This is required to check if the sprite is already locked for
// write access in the main thread, in that case we don't need to
// lock it again from the background thread.
Tx::LockAction m_lockAction;
// Default implementation calls the given function in
// startJob(). Anyway you can just extended the SpriteJob and
// override onJob().
std::function<void()> m_callback;
std::function<void(Tx&)> m_callback;
};
} // namespace app

View File

@ -1770,7 +1770,9 @@ private:
}
void updateLayout() {
const bool visible = (m_doc && !m_doc->sprite()->slices().empty());
const bool visible = (m_doc &&
m_doc->sprite() &&
!m_doc->sprite()->slices().empty());
const bool relayout = (visible != m_combobox.isVisible() ||
visible != m_action.isVisible());

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) 2018 David Capello
//
// This program is distributed under the terms of
@ -56,7 +56,17 @@ ExportFileWindow::ExportFileWindow(const Doc* doc)
fill_layers_combobox(m_doc->sprite(), layers(), m_docPref.saveCopy.layer(), m_docPref.saveCopy.layerIndex());
fill_frames_combobox(m_doc->sprite(), frames(), m_docPref.saveCopy.frameTag());
fill_anidir_combobox(anidir(), m_docPref.saveCopy.aniDir());
pixelRatio()->setSelected(m_docPref.saveCopy.applyPixelRatio());
if (doc->sprite()->hasPixelRatio()) {
pixelRatio()->setSelected(m_docPref.saveCopy.applyPixelRatio());
}
else {
// Hide "Apply pixel ratio" checkbox when there is no pixel aspect
// ratio to apply.
pixelRatio()->setSelected(false);
pixelRatio()->setVisible(false);
}
forTwitter()->setSelected(m_docPref.saveCopy.forTwitter());
adjustResize()->setVisible(false);
playSubtags()->setSelected(m_docPref.saveCopy.playSubtags());
@ -218,7 +228,9 @@ void ExportFileWindow::updateAniDir()
void ExportFileWindow::updatePlaySubtags()
{
std::string framesValue = this->framesValue();
playSubtags()->setVisible(framesValue != kSelectedFrames);
playSubtags()->setVisible(framesValue != kSelectedFrames &&
// We hide the option if there is no tag
!m_doc->sprite()->tags().empty());
layout();
}

View File

@ -2093,7 +2093,10 @@ void Timeline::setCursor(ui::Message* msg, const Hit& hit)
ui::set_mouse_cursor(kSizeECursor);
}
else if (hit.part == PART_RANGE_OUTLINE) {
ui::set_mouse_cursor(kMoveCursor);
if (is_copy_key_pressed(msg))
ui::set_mouse_cursor(kArrowPlusCursor);
else
ui::set_mouse_cursor(kMoveCursor);
}
else if (hit.part == PART_SEPARATOR) {
ui::set_mouse_cursor(kSizeWECursor);

@ -1 +0,0 @@
Subproject commit 835cd0f7e7a964bb969482117856bc56a0ac12bf

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-2018 David Capello
//
// This file is released under the terms of the MIT license.
@ -198,6 +198,11 @@ Sprite* Sprite::MakeStdTilemapSpriteWithTileset(const ImageSpec& spec,
//////////////////////////////////////////////////////////////////////
// Main properties
bool Sprite::hasPixelRatio() const
{
return m_pixelRatio != PixelRatio(1, 1);
}
void Sprite::setPixelFormat(PixelFormat format)
{
m_spec.setColorMode((ColorMode)format);

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-2018 David Capello
//
// This file is released under the terms of the MIT license.
@ -93,6 +93,7 @@ namespace doc {
PixelFormat pixelFormat() const { return (PixelFormat)m_spec.colorMode(); }
ColorMode colorMode() const { return m_spec.colorMode(); }
const PixelRatio& pixelRatio() const { return m_pixelRatio; }
bool hasPixelRatio() const;
gfx::Size size() const { return m_spec.size(); }
gfx::Rect bounds() const { return m_spec.bounds(); }
int width() const { return m_spec.width(); }