Merge branch 'main' into beta

This commit is contained in:
David Capello 2024-03-11 18:03:17 -03:00
commit 56357e68ce
17 changed files with 262 additions and 142 deletions

2
laf

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

View File

@ -157,6 +157,8 @@ set(DATA_OUTPUT_DIR ${CMAKE_BINARY_DIR}/bin/data)
include(FetchContent) include(FetchContent)
find_package(Git)
if(GIT_FOUND)
FetchContent_Declare( FetchContent_Declare(
clone_strings clone_strings
GIT_REPOSITORY https://github.com/aseprite/strings.git GIT_REPOSITORY https://github.com/aseprite/strings.git
@ -168,6 +170,9 @@ FetchContent_Declare(
TEST_COMMAND "") TEST_COMMAND "")
FetchContent_MakeAvailable(clone_strings) FetchContent_MakeAvailable(clone_strings)
add_custom_target(clone_strings DEPENDS clone_strings) add_custom_target(clone_strings DEPENDS clone_strings)
else()
add_custom_target(clone_strings)
endif()
###################################################################### ######################################################################
# Copy data/ directory target into bin/data/ # Copy data/ directory target into bin/data/
@ -182,6 +187,7 @@ foreach(fn ${src_data_files})
list(APPEND out_data_files ${DATA_OUTPUT_DIR}/${fn}) list(APPEND out_data_files ${DATA_OUTPUT_DIR}/${fn})
endforeach() endforeach()
if(GIT_FOUND)
# Copy original en.ini to strings.git/en.ini to keep it updated. We # Copy original en.ini to strings.git/en.ini to keep it updated. We
# have to manually sync the "en.ini" file in the "strings" repo from # have to manually sync the "en.ini" file in the "strings" repo from
# the "aseprite" repo. # the "aseprite" repo.
@ -190,6 +196,7 @@ add_custom_command(
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${SOURCE_DATA_DIR}/strings/en.ini ${DATA_OUTPUT_DIR}/strings.git/en.ini COMMAND ${CMAKE_COMMAND} -E copy_if_different ${SOURCE_DATA_DIR}/strings/en.ini ${DATA_OUTPUT_DIR}/strings.git/en.ini
MAIN_DEPENDENCY ${SOURCE_DATA_DIR}/strings/en.ini) MAIN_DEPENDENCY ${SOURCE_DATA_DIR}/strings/en.ini)
list(APPEND out_data_files ${DATA_OUTPUT_DIR}/strings.git/en.ini) list(APPEND out_data_files ${DATA_OUTPUT_DIR}/strings.git/en.ini)
endif()
add_custom_command( add_custom_command(
OUTPUT ${DATA_OUTPUT_DIR}/README.md OUTPUT ${DATA_OUTPUT_DIR}/README.md

View File

@ -699,6 +699,7 @@ add_library(app-lib
util/range_utils.cpp util/range_utils.cpp
util/readable_time.cpp util/readable_time.cpp
util/resize_image.cpp util/resize_image.cpp
util/shader_helpers.cpp
util/tile_flags_utils.cpp util/tile_flags_utils.cpp
util/tileset_utils.cpp util/tileset_utils.cpp
util/wrap_point.cpp util/wrap_point.cpp

View File

@ -1,5 +1,5 @@
// Aseprite // Aseprite
// 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 program is distributed under the terms of // This program is distributed under the terms of
@ -11,6 +11,7 @@
#include "app/app.h" #include "app/app.h"
#include "app/commands/cmd_export_sprite_sheet.h" #include "app/commands/cmd_export_sprite_sheet.h"
#include "app/console.h"
#include "app/context.h" #include "app/context.h"
#include "app/context_access.h" #include "app/context_access.h"
#include "app/doc.h" #include "app/doc.h"
@ -141,6 +142,17 @@ ConstraintType constraint_type_from_params(const ExportSpriteSheetParams& params
#endif // ENABLE_UI #endif // ENABLE_UI
void destroy_doc(Context* ctx, Doc* doc)
{
try {
DocDestroyer destroyer(ctx, doc, 500);
destroyer.destroyDocument();
}
catch (const LockedDocException& ex) {
Console::showException(ex);
}
}
Doc* generate_sprite_sheet_from_params( Doc* generate_sprite_sheet_from_params(
DocExporter& exporter, DocExporter& exporter,
Context* ctx, Context* ctx,
@ -500,8 +512,7 @@ public:
auto ctx = UIContext::instance(); auto ctx = UIContext::instance();
ctx->setActiveDocument(m_site.document()); ctx->setActiveDocument(m_site.document());
DocDestroyer destroyer(ctx, m_spriteSheet.release(), 100); destroy_doc(ctx, m_spriteSheet.release());
destroyer.destroyDocument();
} }
} }
@ -1014,8 +1025,7 @@ private:
auto ctx = UIContext::instance(); auto ctx = UIContext::instance();
ctx->setActiveDocument(m_site.document()); ctx->setActiveDocument(m_site.document());
DocDestroyer destroyer(ctx, m_spriteSheet.release(), 100); destroy_doc(ctx, m_spriteSheet.release());
destroyer.destroyDocument();
m_editor = nullptr; m_editor = nullptr;
} }
return; return;
@ -1066,8 +1076,7 @@ private:
return; return;
if (token.canceled()) { if (token.canceled()) {
DocDestroyer destroyer(&tmpCtx, newDocument, 100); destroy_doc(&tmpCtx, newDocument);
destroyer.destroyDocument();
return; return;
} }
@ -1090,8 +1099,7 @@ private:
// old one. IN this case the newDocument contains a back // old one. IN this case the newDocument contains a back
// buffer (ImageBufferPtr) that will be discarded. // buffer (ImageBufferPtr) that will be discarded.
m_executionID != executionID) { m_executionID != executionID) {
DocDestroyer destroyer(context, newDocument, 100); destroy_doc(context, newDocument);
destroyer.destroyDocument();
return; return;
} }
@ -1137,8 +1145,7 @@ private:
m_spriteSheet->notifyGeneralUpdate(); m_spriteSheet->notifyGeneralUpdate();
DocDestroyer destroyer(context, newDocument, 100); destroy_doc(context, newDocument);
destroyer.destroyDocument();
} }
waitGenTaskAndDelete(); waitGenTaskAndDelete();
@ -1407,8 +1414,7 @@ void ExportSpriteSheetCommand::onExecute(Context* context)
newDocument.release(); newDocument.release();
} }
else { else {
DocDestroyer destroyer(context, newDocument.release(), 100); destroy_doc(context, newDocument.release());
destroyer.destroyDocument();
} }
} }

View File

@ -1,5 +1,5 @@
// Aseprite // Aseprite
// 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 program is distributed under the terms of // This program is distributed under the terms of
@ -13,6 +13,7 @@
#include "app/commands/command.h" #include "app/commands/command.h"
#include "app/commands/commands.h" #include "app/commands/commands.h"
#include "app/commands/new_params.h" #include "app/commands/new_params.h"
#include "app/console.h"
#include "app/context.h" #include "app/context.h"
#include "app/context_access.h" #include "app/context_access.h"
#include "app/doc_access.h" #include "app/doc_access.h"
@ -264,9 +265,14 @@ private:
releaseEditor(); releaseEditor();
if (m_fileOpened) { if (m_fileOpened) {
DocDestroyer destroyer(m_context, oldDocument, 100); try {
DocDestroyer destroyer(m_context, oldDocument, 500);
destroyer.destroyDocument(); destroyer.destroyDocument();
} }
catch (const LockedDocException& ex) {
Console::showException(ex);
}
}
} }
captureEditor(); captureEditor();

View File

@ -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
@ -14,11 +14,12 @@
#include "app/color.h" #include "app/color.h"
#include "app/color_utils.h" #include "app/color_utils.h"
#include "app/commands/command.h" #include "app/commands/command.h"
#include "app/console.h"
#include "app/context.h" #include "app/context.h"
#include "app/context_access.h" #include "app/context_access.h"
#include "app/doc.h" #include "app/doc.h"
#include "app/ini_file.h"
#include "app/i18n/strings.h" #include "app/i18n/strings.h"
#include "app/ini_file.h"
#include "app/modules/gui.h" #include "app/modules/gui.h"
#include "app/tx.h" #include "app/tx.h"
#include "app/ui/color_bar.h" #include "app/ui/color_bar.h"
@ -260,7 +261,7 @@ void MaskByColorCommand::maskPreview(const ContextReader& reader)
reader.sprite(), image, reader.sprite(), image,
xpos, ypos, xpos, ypos,
m_selMode->selectionMode())); m_selMode->selectionMode()));
{
ContextWriter writer(reader); ContextWriter writer(reader);
#ifdef SHOW_BOUNDARIES_GEN_PERFORMANCE #ifdef SHOW_BOUNDARIES_GEN_PERFORMANCE
@ -277,7 +278,6 @@ void MaskByColorCommand::maskPreview(const ContextReader& reader)
update_screen_for_document(writer.document()); update_screen_for_document(writer.document());
} }
} }
}
Command* CommandFactory::createMaskByColorCommand() Command* CommandFactory::createMaskByColorCommand()
{ {

View File

@ -784,11 +784,16 @@ public:
m_context->activeDocument() && m_context->activeDocument() &&
m_context->activeDocument()->sprite() && m_context->activeDocument()->sprite() &&
m_context->activeDocument()->sprite()->gridBounds() != gridBounds()) { m_context->activeDocument()->sprite()->gridBounds() != gridBounds()) {
ContextWriter writer(m_context); try {
ContextWriter writer(m_context, 1000);
Tx tx(writer, Strings::commands_GridSettings(), ModifyDocument); Tx tx(writer, Strings::commands_GridSettings(), ModifyDocument);
tx(new cmd::SetGridBounds(writer.sprite(), gridBounds())); tx(new cmd::SetGridBounds(writer.sprite(), gridBounds()));
tx.commit(); tx.commit();
} }
catch (const std::exception& ex) {
Console::showException(ex);
}
}
m_curPref->show.grid(gridVisible()->isSelected()); m_curPref->show.grid(gridVisible()->isSelected());
m_curPref->grid.bounds(gridBounds()); m_curPref->grid.bounds(gridBounds());

View File

@ -9,31 +9,32 @@
#include "config.h" #include "config.h"
#endif #endif
#include "app/cmd/add_tileset.h"
#include "app/cmd/assign_color_profile.h" #include "app/cmd/assign_color_profile.h"
#include "app/cmd/convert_color_profile.h" #include "app/cmd/convert_color_profile.h"
#include "app/cmd/add_tileset.h"
#include "app/cmd/remove_tileset.h" #include "app/cmd/remove_tileset.h"
#include "app/cmd/set_pixel_ratio.h" #include "app/cmd/set_pixel_ratio.h"
#include "app/cmd/set_user_data.h" #include "app/cmd/set_user_data.h"
#include "app/color.h" #include "app/color.h"
#include "app/commands/command.h" #include "app/commands/command.h"
#include "app/console.h"
#include "app/context_access.h" #include "app/context_access.h"
#include "app/doc_api.h" #include "app/doc_api.h"
#include "app/i18n/strings.h" #include "app/i18n/strings.h"
#include "app/modules/gui.h" #include "app/modules/gui.h"
#include "app/pref/preferences.h" #include "app/pref/preferences.h"
#include "app/util/tileset_utils.h"
#include "app/tx.h" #include "app/tx.h"
#include "app/ui/color_button.h" #include "app/ui/color_button.h"
#include "app/ui/user_data_view.h"
#include "app/ui/skin/skin_theme.h" #include "app/ui/skin/skin_theme.h"
#include "app/ui/user_data_view.h"
#include "app/util/pixel_ratio.h" #include "app/util/pixel_ratio.h"
#include "app/util/tileset_utils.h"
#include "base/mem_utils.h" #include "base/mem_utils.h"
#include "doc/image.h" #include "doc/image.h"
#include "doc/palette.h" #include "doc/palette.h"
#include "doc/sprite.h" #include "doc/sprite.h"
#include "doc/user_data.h"
#include "doc/tilesets.h" #include "doc/tilesets.h"
#include "doc/user_data.h"
#include "fmt/format.h" #include "fmt/format.h"
#include "os/color_space.h" #include "os/color_space.h"
#include "os/system.h" #include "os/system.h"
@ -352,12 +353,17 @@ void SpritePropertiesCommand::onExecute(Context* context)
[&](){ [&](){
selectedColorProfile = window.colorProfile()->getSelectedItemIndex(); selectedColorProfile = window.colorProfile()->getSelectedItemIndex();
try {
ContextWriter writer(context); ContextWriter writer(context);
Sprite* sprite(writer.sprite()); Sprite* sprite(writer.sprite());
Tx tx(writer, Strings::sprite_properties_assign_color_profile()); Tx tx(writer, Strings::sprite_properties_assign_color_profile());
tx(new cmd::AssignColorProfile( tx(new cmd::AssignColorProfile(
sprite, colorSpaces[selectedColorProfile]->gfxColorSpace())); sprite, colorSpaces[selectedColorProfile]->gfxColorSpace()));
tx.commit(); tx.commit();
}
catch (const base::Exception& e) {
Console::showException(e);
}
updateButtons(); updateButtons();
}); });
@ -365,12 +371,17 @@ void SpritePropertiesCommand::onExecute(Context* context)
[&](){ [&](){
selectedColorProfile = window.colorProfile()->getSelectedItemIndex(); selectedColorProfile = window.colorProfile()->getSelectedItemIndex();
try {
ContextWriter writer(context); ContextWriter writer(context);
Sprite* sprite(writer.sprite()); Sprite* sprite(writer.sprite());
Tx tx(writer, Strings::sprite_properties_convert_color_profile()); Tx tx(writer, Strings::sprite_properties_convert_color_profile());
tx(new cmd::ConvertColorProfile( tx(new cmd::ConvertColorProfile(
sprite, colorSpaces[selectedColorProfile]->gfxColorSpace())); sprite, colorSpaces[selectedColorProfile]->gfxColorSpace()));
tx.commit(); tx.commit();
}
catch (const base::Exception& e) {
Console::showException(e);
}
updateButtons(); updateButtons();
}); });

View File

@ -88,19 +88,9 @@ ShaderRenderer::ShaderRenderer()
m_properties.renderBgOnScreen = true; m_properties.renderBgOnScreen = true;
m_properties.requiresRgbaBackbuffer = true; m_properties.requiresRgbaBackbuffer = true;
auto makeShader = [](const char* code) { m_bgEffect = make_shader(kBgShaderCode);
auto result = SkRuntimeEffect::MakeForShader(SkString(code)); m_indexedEffect = make_shader(kIndexedShaderCode);
if (!result.errorText.isEmpty()) { m_grayscaleEffect = make_shader(kGrayscaleShaderCode);
LOG(ERROR, "Shader error: %s\n", result.errorText.c_str());
std::printf("Shader error: %s\n", result.errorText.c_str());
throw std::runtime_error("Cannot compile shaders for ShaderRenderer");
}
return result;
};
m_bgEffect = makeShader(kBgShaderCode).effect;
m_indexedEffect = makeShader(kIndexedShaderCode).effect;
m_grayscaleEffect = makeShader(kGrayscaleShaderCode).effect;
} }
ShaderRenderer::~ShaderRenderer() = default; ShaderRenderer::~ShaderRenderer() = default;
@ -417,21 +407,11 @@ void ShaderRenderer::drawImage(SkCanvas* canvas,
const int opacity, const int opacity,
const doc::BlendMode blendMode) const doc::BlendMode blendMode)
{ {
auto skData = SkData::MakeWithoutCopy( auto skImg = make_skimage_for_docimage(srcImage);
(const void*)srcImage->getPixelAddress(0, 0),
srcImage->rowBytes() * srcImage->height());
switch (srcImage->colorMode()) { switch (srcImage->colorMode()) {
case doc::ColorMode::RGB: { case doc::ColorMode::RGB: {
auto skImg = SkImages::RasterFromData(
SkImageInfo::Make(srcImage->width(),
srcImage->height(),
kRGBA_8888_SkColorType,
kUnpremul_SkAlphaType),
skData,
srcImage->rowBytes());
SkPaint p; SkPaint p;
p.setAlpha(opacity); p.setAlpha(opacity);
p.setBlendMode(to_skia(blendMode)); p.setBlendMode(to_skia(blendMode));
@ -444,15 +424,6 @@ void ShaderRenderer::drawImage(SkCanvas* canvas,
} }
case doc::ColorMode::GRAYSCALE: { case doc::ColorMode::GRAYSCALE: {
// We use kR8G8_unorm_SkColorType to access gray and alpha
auto skImg = SkImages::RasterFromData(
SkImageInfo::Make(srcImage->width(),
srcImage->height(),
kR8G8_unorm_SkColorType,
kOpaque_SkAlphaType),
skData,
srcImage->rowBytes());
SkRuntimeShaderBuilder builder(m_grayscaleEffect); SkRuntimeShaderBuilder builder(m_grayscaleEffect);
builder.child("iImg") = skImg->makeRawShader(SkSamplingOptions(SkFilterMode::kNearest)); builder.child("iImg") = skImg->makeRawShader(SkSamplingOptions(SkFilterMode::kNearest));
@ -472,15 +443,6 @@ void ShaderRenderer::drawImage(SkCanvas* canvas,
} }
case doc::ColorMode::INDEXED: { case doc::ColorMode::INDEXED: {
// We use kAlpha_8_SkColorType to access to the index value through the alpha channel
auto skImg = SkImages::RasterFromData(
SkImageInfo::Make(srcImage->width(),
srcImage->height(),
kAlpha_8_SkColorType,
kUnpremul_SkAlphaType),
skData,
srcImage->rowBytes());
// Use the palette data as an "width x height" image where // Use the palette data as an "width x height" image where
// width=number of palette colors, and height=1 // width=number of palette colors, and height=1
const size_t palSize = sizeof(color_t) * m_palette.size(); const size_t palSize = sizeof(color_t) * m_palette.size();

View File

@ -655,35 +655,22 @@ bool ColorSelector::buildEffects()
if (!m_mainEffect) { if (!m_mainEffect) {
if (const char* code = getMainAreaShader()) if (const char* code = getMainAreaShader())
m_mainEffect = buildEffect(code); m_mainEffect = make_shader(code);
} }
if (!m_bottomEffect) { if (!m_bottomEffect) {
if (const char* code = getBottomBarShader()) if (const char* code = getBottomBarShader())
m_bottomEffect = buildEffect(code); m_bottomEffect = make_shader(code);
} }
if (!m_alphaEffect) { if (!m_alphaEffect) {
if (const char* code = getAlphaBarShader()) if (const char* code = getAlphaBarShader())
m_alphaEffect = buildEffect(code); m_alphaEffect = make_shader(code);
} }
return (m_mainEffect && m_bottomEffect && m_alphaEffect); return (m_mainEffect && m_bottomEffect && m_alphaEffect);
} }
sk_sp<SkRuntimeEffect> ColorSelector::buildEffect(const char* code)
{
auto result = SkRuntimeEffect::MakeForShader(SkString(code));
if (!result.errorText.isEmpty()) {
LOG(ERROR, "Shader error: %s\n", result.errorText.c_str());
std::printf("Shader error: %s\n", result.errorText.c_str());
return nullptr;
}
else {
return result.effect;
}
}
void ColorSelector::resetBottomEffect() void ColorSelector::resetBottomEffect()
{ {
m_bottomEffect.reset(); m_bottomEffect.reset();

View File

@ -121,7 +121,6 @@ namespace app {
#if SK_ENABLE_SKSL #if SK_ENABLE_SKSL
static const char* getAlphaBarShader(); static const char* getAlphaBarShader();
bool buildEffects(); bool buildEffects();
sk_sp<SkRuntimeEffect> buildEffect(const char* code);
#endif #endif
// Internal flag used to lock the modification of m_color. // Internal flag used to lock the modification of m_color.

View File

@ -1,5 +1,5 @@
// Aseprite // Aseprite
// Copyright (C) 2019-2022 Igara Studio S.A. // Copyright (C) 2019-2024 Igara Studio S.A.
// Copyright (C) 2017-2018 David Capello // Copyright (C) 2017-2018 David Capello
// //
// This program is distributed under the terms of // This program is distributed under the terms of
@ -14,6 +14,7 @@
#include "app/cmd/set_mask_position.h" #include "app/cmd/set_mask_position.h"
#include "app/commands/command.h" #include "app/commands/command.h"
#include "app/commands/commands.h" #include "app/commands/commands.h"
#include "app/console.h"
#include "app/context_access.h" #include "app/context_access.h"
#include "app/tx.h" #include "app/tx.h"
#include "app/ui/editor/editor.h" #include "app/ui/editor/editor.h"
@ -80,12 +81,15 @@ EditorState::LeaveAction MovingSelectionState::onLeaveState(Editor* editor, Edit
doc->generateMaskBoundaries(); doc->generateMaskBoundaries();
} }
else { else {
{ try {
ContextWriter writer(UIContext::instance(), 1000); ContextWriter writer(UIContext::instance(), 1000);
Tx tx(writer, "Move Selection Edges", DoesntModifyDocument); Tx tx(writer, "Move Selection Edges", DoesntModifyDocument);
tx(new cmd::SetMaskPosition(doc, newOrigin)); tx(new cmd::SetMaskPosition(doc, newOrigin));
tx.commit(); tx.commit();
} }
catch (const base::Exception& e) {
Console::showException(e);
}
doc->resetTransformation(); doc->resetTransformation();
} }
doc->notifyGeneralUpdate(); doc->notifyGeneralUpdate();

View File

@ -1,4 +1,5 @@
// Aseprite // Aseprite
// Copyright (C) 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
@ -29,6 +30,8 @@ namespace app {
virtual bool onCanPaste(Context* ctx) = 0; virtual bool onCanPaste(Context* ctx) = 0;
virtual bool onCanClear(Context* ctx) = 0; virtual bool onCanClear(Context* ctx) = 0;
// These commands are executed from Context::executeCommand()
// which catch any exception that is thrown.
virtual bool onCut(Context* ctx) = 0; virtual bool onCut(Context* ctx) = 0;
virtual bool onCopy(Context* ctx) = 0; virtual bool onCopy(Context* ctx) = 0;
virtual bool onPaste(Context* ctx) = 0; virtual bool onPaste(Context* ctx) = 0;

View File

@ -1347,7 +1347,7 @@ bool Timeline::onProcessMessage(Message* msg)
else if (mouseMsg->left()) { else if (mouseMsg->left()) {
Command* command = Commands::instance() Command* command = Commands::instance()
->byId(CommandId::FrameTagProperties()); ->byId(CommandId::FrameTagProperties());
UIContext::instance()->executeCommand(command, params); m_context->executeCommand(command, params);
} }
} }
break; break;
@ -1389,13 +1389,18 @@ bool Timeline::onProcessMessage(Message* msg)
if (tag) { if (tag) {
if ((m_state == STATE_RESIZING_TAG_LEFT && tag->fromFrame() != m_resizeTagData.from) || if ((m_state == STATE_RESIZING_TAG_LEFT && tag->fromFrame() != m_resizeTagData.from) ||
(m_state == STATE_RESIZING_TAG_RIGHT && tag->toFrame() != m_resizeTagData.to)) { (m_state == STATE_RESIZING_TAG_RIGHT && tag->toFrame() != m_resizeTagData.to)) {
ContextWriter writer(UIContext::instance()); try {
ContextWriter writer(m_context);
Tx tx(writer, Strings::commands_FrameTagProperties()); Tx tx(writer, Strings::commands_FrameTagProperties());
tx(new cmd::SetTagRange( tx(new cmd::SetTagRange(
tag, tag,
(m_state == STATE_RESIZING_TAG_LEFT ? m_resizeTagData.from: tag->fromFrame()), (m_state == STATE_RESIZING_TAG_LEFT ? m_resizeTagData.from: tag->fromFrame()),
(m_state == STATE_RESIZING_TAG_RIGHT ? m_resizeTagData.to: tag->toFrame()))); (m_state == STATE_RESIZING_TAG_RIGHT ? m_resizeTagData.to: tag->toFrame())));
tx.commit(); tx.commit();
}
catch (const base::Exception& e) {
Console::showException(e);
}
regenerateRows(); regenerateRows();
} }
@ -1431,7 +1436,7 @@ bool Timeline::onProcessMessage(Message* msg)
Command* command = Commands::instance() Command* command = Commands::instance()
->byId(CommandId::LayerProperties()); ->byId(CommandId::LayerProperties());
UIContext::instance()->executeCommand(command); m_context->executeCommand(command);
return true; return true;
} }
@ -1441,7 +1446,7 @@ bool Timeline::onProcessMessage(Message* msg)
Params params; Params params;
params.set("frame", "current"); params.set("frame", "current");
UIContext::instance()->executeCommand(command, params); m_context->executeCommand(command, params);
return true; return true;
} }
@ -1449,7 +1454,7 @@ bool Timeline::onProcessMessage(Message* msg)
Command* command = Commands::instance() Command* command = Commands::instance()
->byId(CommandId::CelProperties()); ->byId(CommandId::CelProperties());
UIContext::instance()->executeCommand(command); m_context->executeCommand(command);
return true; return true;
} }

View File

@ -0,0 +1,101 @@
// 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
#if SK_ENABLE_SKSL
#include "app/util/shader_helpers.h"
#include "base/exception.h"
#include "doc/image.h"
#include "fmt/format.h"
#include "include/effects/SkRuntimeEffect.h"
#include "src/core/SkRuntimeEffectPriv.h"
namespace app {
sk_sp<SkRuntimeEffect> make_shader(const char* code)
{
SkRuntimeEffect::Options options;
// Allow usage of private functions like $hsl_to_rgb without a SkSL
// compilation error at runtime.
SkRuntimeEffectPriv::AllowPrivateAccess(&options);
auto result = SkRuntimeEffect::MakeForShader(SkString(code), options);
if (!result.errorText.isEmpty()) {
std::string error = fmt::format("Error compiling shader.\nError: {}\n",
result.errorText.c_str());
LOG(ERROR, error.c_str());
std::printf("%s", error.c_str());
throw base::Exception(error);
}
return result.effect;
}
SkImageInfo get_skimageinfo_for_docimage(const doc::Image* img)
{
switch (img->colorMode()) {
case doc::ColorMode::RGB:
return SkImageInfo::Make(img->width(),
img->height(),
kRGBA_8888_SkColorType,
kUnpremul_SkAlphaType);
case doc::ColorMode::GRAYSCALE:
// We use kR8G8_unorm_SkColorType to access gray and alpha
return SkImageInfo::Make(img->width(),
img->height(),
kR8G8_unorm_SkColorType,
kOpaque_SkAlphaType);
case doc::ColorMode::INDEXED: {
// We use kAlpha_8_SkColorType to access to the index value through the alpha channel
return SkImageInfo::Make(img->width(),
img->height(),
kAlpha_8_SkColorType,
kUnpremul_SkAlphaType);
}
}
return SkImageInfo();
}
sk_sp<SkImage> make_skimage_for_docimage(const doc::Image* img)
{
switch (img->colorMode()) {
case doc::ColorMode::RGB:
case doc::ColorMode::GRAYSCALE:
case doc::ColorMode::INDEXED: {
auto skData = SkData::MakeWithoutCopy(
(const void*)img->getPixelAddress(0, 0),
img->rowBytes() * img->height());
return SkImages::RasterFromData(
get_skimageinfo_for_docimage(img),
skData,
img->rowBytes());
}
}
return nullptr;
}
std::unique_ptr<SkCanvas> make_skcanvas_for_docimage(const doc::Image* img)
{
return SkCanvas::MakeRasterDirect(
get_skimageinfo_for_docimage(img),
(void*)img->getPixelAddress(0, 0),
img->rowBytes());
}
} // namespace app
#endif // SK_ENABLE_SKSL

View File

@ -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.
@ -8,15 +8,30 @@
#define APP_UTIL_SHADER_HELPERS_H_INCLUDED #define APP_UTIL_SHADER_HELPERS_H_INCLUDED
#pragma once #pragma once
#if SK_ENABLE_SKSL #if LAF_SKIA
#include "app/color.h" #include "app/color.h"
#include "gfx/color.h" #include "gfx/color.h"
#include "include/core/SkCanvas.h"
#include "include/core/SkImage.h"
#include "include/core/SkM44.h" #include "include/core/SkM44.h"
#include "include/core/SkRefCnt.h"
#if SK_ENABLE_SKSL
#include "include/effects/SkRuntimeEffect.h"
#endif
#include <memory>
namespace doc {
class Image;
}
namespace app { namespace app {
#if SK_ENABLE_SKSL
// rgb_to_hsl() and hsv_to_hsl() functions by Sam Hocevar licensed // rgb_to_hsl() and hsv_to_hsl() functions by Sam Hocevar licensed
// under WTFPL (https://en.wikipedia.org/wiki/WTFPL) // under WTFPL (https://en.wikipedia.org/wiki/WTFPL)
// Source: // Source:
@ -71,8 +86,16 @@ inline SkV4 appColorHsl_to_SkV4(const app::Color& color) {
float(color.getAlpha() / 255.0)}; float(color.getAlpha() / 255.0)};
} }
sk_sp<SkRuntimeEffect> make_shader(const char* code);
#endif // SK_ENABLE_SKSL
SkImageInfo get_skimageinfo_for_docimage(const doc::Image* img);
sk_sp<SkImage> make_skimage_for_docimage(const doc::Image* img);
std::unique_ptr<SkCanvas> make_skcanvas_for_docimage(const doc::Image* img);
} // namespace app } // namespace app
#endif #endif // LAF_SKIA
#endif #endif

@ -1 +1 @@
Subproject commit 94693e2414a2c69a8ca16f065240c80a94cc6221 Subproject commit 835cd0f7e7a964bb969482117856bc56a0ac12bf