mirror of
https://github.com/aseprite/aseprite.git
synced 2025-01-29 12:32:52 +00:00
Merge branch 'main' into beta
This commit is contained in:
commit
56357e68ce
2
laf
2
laf
@ -1 +1 @@
|
||||
Subproject commit 1b5834cd52340f14408112b746c8c46a581c3488
|
||||
Subproject commit 946fdf956b8a41a33337c9e1f0046bf4a4471b48
|
@ -157,17 +157,22 @@ set(DATA_OUTPUT_DIR ${CMAKE_BINARY_DIR}/bin/data)
|
||||
|
||||
include(FetchContent)
|
||||
|
||||
FetchContent_Declare(
|
||||
clone_strings
|
||||
GIT_REPOSITORY https://github.com/aseprite/strings.git
|
||||
GIT_TAG origin/main
|
||||
SOURCE_DIR ${DATA_OUTPUT_DIR}/strings.git
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
INSTALL_COMMAND ""
|
||||
TEST_COMMAND "")
|
||||
FetchContent_MakeAvailable(clone_strings)
|
||||
add_custom_target(clone_strings DEPENDS clone_strings)
|
||||
find_package(Git)
|
||||
if(GIT_FOUND)
|
||||
FetchContent_Declare(
|
||||
clone_strings
|
||||
GIT_REPOSITORY https://github.com/aseprite/strings.git
|
||||
GIT_TAG origin/main
|
||||
SOURCE_DIR ${DATA_OUTPUT_DIR}/strings.git
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
INSTALL_COMMAND ""
|
||||
TEST_COMMAND "")
|
||||
FetchContent_MakeAvailable(clone_strings)
|
||||
add_custom_target(clone_strings DEPENDS clone_strings)
|
||||
else()
|
||||
add_custom_target(clone_strings)
|
||||
endif()
|
||||
|
||||
######################################################################
|
||||
# Copy data/ directory target into bin/data/
|
||||
@ -182,14 +187,16 @@ foreach(fn ${src_data_files})
|
||||
list(APPEND out_data_files ${DATA_OUTPUT_DIR}/${fn})
|
||||
endforeach()
|
||||
|
||||
# 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
|
||||
# the "aseprite" repo.
|
||||
add_custom_command(
|
||||
OUTPUT ${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)
|
||||
list(APPEND out_data_files ${DATA_OUTPUT_DIR}/strings.git/en.ini)
|
||||
if(GIT_FOUND)
|
||||
# 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
|
||||
# the "aseprite" repo.
|
||||
add_custom_command(
|
||||
OUTPUT ${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)
|
||||
list(APPEND out_data_files ${DATA_OUTPUT_DIR}/strings.git/en.ini)
|
||||
endif()
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${DATA_OUTPUT_DIR}/README.md
|
||||
|
@ -699,6 +699,7 @@ add_library(app-lib
|
||||
util/range_utils.cpp
|
||||
util/readable_time.cpp
|
||||
util/resize_image.cpp
|
||||
util/shader_helpers.cpp
|
||||
util/tile_flags_utils.cpp
|
||||
util/tileset_utils.cpp
|
||||
util/wrap_point.cpp
|
||||
|
@ -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
|
||||
@ -11,6 +11,7 @@
|
||||
|
||||
#include "app/app.h"
|
||||
#include "app/commands/cmd_export_sprite_sheet.h"
|
||||
#include "app/console.h"
|
||||
#include "app/context.h"
|
||||
#include "app/context_access.h"
|
||||
#include "app/doc.h"
|
||||
@ -141,6 +142,17 @@ ConstraintType constraint_type_from_params(const ExportSpriteSheetParams& params
|
||||
|
||||
#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(
|
||||
DocExporter& exporter,
|
||||
Context* ctx,
|
||||
@ -500,8 +512,7 @@ public:
|
||||
auto ctx = UIContext::instance();
|
||||
ctx->setActiveDocument(m_site.document());
|
||||
|
||||
DocDestroyer destroyer(ctx, m_spriteSheet.release(), 100);
|
||||
destroyer.destroyDocument();
|
||||
destroy_doc(ctx, m_spriteSheet.release());
|
||||
}
|
||||
}
|
||||
|
||||
@ -1014,8 +1025,7 @@ private:
|
||||
auto ctx = UIContext::instance();
|
||||
ctx->setActiveDocument(m_site.document());
|
||||
|
||||
DocDestroyer destroyer(ctx, m_spriteSheet.release(), 100);
|
||||
destroyer.destroyDocument();
|
||||
destroy_doc(ctx, m_spriteSheet.release());
|
||||
m_editor = nullptr;
|
||||
}
|
||||
return;
|
||||
@ -1066,8 +1076,7 @@ private:
|
||||
return;
|
||||
|
||||
if (token.canceled()) {
|
||||
DocDestroyer destroyer(&tmpCtx, newDocument, 100);
|
||||
destroyer.destroyDocument();
|
||||
destroy_doc(&tmpCtx, newDocument);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1090,8 +1099,7 @@ private:
|
||||
// old one. IN this case the newDocument contains a back
|
||||
// buffer (ImageBufferPtr) that will be discarded.
|
||||
m_executionID != executionID) {
|
||||
DocDestroyer destroyer(context, newDocument, 100);
|
||||
destroyer.destroyDocument();
|
||||
destroy_doc(context, newDocument);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1137,8 +1145,7 @@ private:
|
||||
|
||||
m_spriteSheet->notifyGeneralUpdate();
|
||||
|
||||
DocDestroyer destroyer(context, newDocument, 100);
|
||||
destroyer.destroyDocument();
|
||||
destroy_doc(context, newDocument);
|
||||
}
|
||||
|
||||
waitGenTaskAndDelete();
|
||||
@ -1407,8 +1414,7 @@ void ExportSpriteSheetCommand::onExecute(Context* context)
|
||||
newDocument.release();
|
||||
}
|
||||
else {
|
||||
DocDestroyer destroyer(context, newDocument.release(), 100);
|
||||
destroyer.destroyDocument();
|
||||
destroy_doc(context, newDocument.release());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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,6 +13,7 @@
|
||||
#include "app/commands/command.h"
|
||||
#include "app/commands/commands.h"
|
||||
#include "app/commands/new_params.h"
|
||||
#include "app/console.h"
|
||||
#include "app/context.h"
|
||||
#include "app/context_access.h"
|
||||
#include "app/doc_access.h"
|
||||
@ -264,8 +265,13 @@ private:
|
||||
releaseEditor();
|
||||
|
||||
if (m_fileOpened) {
|
||||
DocDestroyer destroyer(m_context, oldDocument, 100);
|
||||
destroyer.destroyDocument();
|
||||
try {
|
||||
DocDestroyer destroyer(m_context, oldDocument, 500);
|
||||
destroyer.destroyDocument();
|
||||
}
|
||||
catch (const LockedDocException& ex) {
|
||||
Console::showException(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
@ -14,11 +14,12 @@
|
||||
#include "app/color.h"
|
||||
#include "app/color_utils.h"
|
||||
#include "app/commands/command.h"
|
||||
#include "app/console.h"
|
||||
#include "app/context.h"
|
||||
#include "app/context_access.h"
|
||||
#include "app/doc.h"
|
||||
#include "app/ini_file.h"
|
||||
#include "app/i18n/strings.h"
|
||||
#include "app/ini_file.h"
|
||||
#include "app/modules/gui.h"
|
||||
#include "app/tx.h"
|
||||
#include "app/ui/color_bar.h"
|
||||
@ -260,22 +261,21 @@ void MaskByColorCommand::maskPreview(const ContextReader& reader)
|
||||
reader.sprite(), image,
|
||||
xpos, ypos,
|
||||
m_selMode->selectionMode()));
|
||||
{
|
||||
ContextWriter writer(reader);
|
||||
|
||||
ContextWriter writer(reader);
|
||||
|
||||
#ifdef SHOW_BOUNDARIES_GEN_PERFORMANCE
|
||||
base::Chrono chrono;
|
||||
base::Chrono chrono;
|
||||
#endif
|
||||
|
||||
writer.document()->generateMaskBoundaries(mask.get());
|
||||
writer.document()->generateMaskBoundaries(mask.get());
|
||||
|
||||
#ifdef SHOW_BOUNDARIES_GEN_PERFORMANCE
|
||||
double time = chrono.elapsed();
|
||||
m_window->setText("Mask by Color (" + base::convert_to<std::string>(time) + ")");
|
||||
double time = chrono.elapsed();
|
||||
m_window->setText("Mask by Color (" + base::convert_to<std::string>(time) + ")");
|
||||
#endif
|
||||
|
||||
update_screen_for_document(writer.document());
|
||||
}
|
||||
update_screen_for_document(writer.document());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -784,10 +784,15 @@ public:
|
||||
m_context->activeDocument() &&
|
||||
m_context->activeDocument()->sprite() &&
|
||||
m_context->activeDocument()->sprite()->gridBounds() != gridBounds()) {
|
||||
ContextWriter writer(m_context);
|
||||
Tx tx(writer, Strings::commands_GridSettings(), ModifyDocument);
|
||||
tx(new cmd::SetGridBounds(writer.sprite(), gridBounds()));
|
||||
tx.commit();
|
||||
try {
|
||||
ContextWriter writer(m_context, 1000);
|
||||
Tx tx(writer, Strings::commands_GridSettings(), ModifyDocument);
|
||||
tx(new cmd::SetGridBounds(writer.sprite(), gridBounds()));
|
||||
tx.commit();
|
||||
}
|
||||
catch (const std::exception& ex) {
|
||||
Console::showException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
m_curPref->show.grid(gridVisible()->isSelected());
|
||||
|
@ -9,31 +9,32 @@
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "app/cmd/add_tileset.h"
|
||||
#include "app/cmd/assign_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/set_pixel_ratio.h"
|
||||
#include "app/cmd/set_user_data.h"
|
||||
#include "app/color.h"
|
||||
#include "app/commands/command.h"
|
||||
#include "app/console.h"
|
||||
#include "app/context_access.h"
|
||||
#include "app/doc_api.h"
|
||||
#include "app/i18n/strings.h"
|
||||
#include "app/modules/gui.h"
|
||||
#include "app/pref/preferences.h"
|
||||
#include "app/util/tileset_utils.h"
|
||||
#include "app/tx.h"
|
||||
#include "app/ui/color_button.h"
|
||||
#include "app/ui/user_data_view.h"
|
||||
#include "app/ui/skin/skin_theme.h"
|
||||
#include "app/ui/user_data_view.h"
|
||||
#include "app/util/pixel_ratio.h"
|
||||
#include "app/util/tileset_utils.h"
|
||||
#include "base/mem_utils.h"
|
||||
#include "doc/image.h"
|
||||
#include "doc/palette.h"
|
||||
#include "doc/sprite.h"
|
||||
#include "doc/user_data.h"
|
||||
#include "doc/tilesets.h"
|
||||
#include "doc/user_data.h"
|
||||
#include "fmt/format.h"
|
||||
#include "os/color_space.h"
|
||||
#include "os/system.h"
|
||||
@ -352,12 +353,17 @@ void SpritePropertiesCommand::onExecute(Context* context)
|
||||
[&](){
|
||||
selectedColorProfile = window.colorProfile()->getSelectedItemIndex();
|
||||
|
||||
ContextWriter writer(context);
|
||||
Sprite* sprite(writer.sprite());
|
||||
Tx tx(writer, Strings::sprite_properties_assign_color_profile());
|
||||
tx(new cmd::AssignColorProfile(
|
||||
sprite, colorSpaces[selectedColorProfile]->gfxColorSpace()));
|
||||
tx.commit();
|
||||
try {
|
||||
ContextWriter writer(context);
|
||||
Sprite* sprite(writer.sprite());
|
||||
Tx tx(writer, Strings::sprite_properties_assign_color_profile());
|
||||
tx(new cmd::AssignColorProfile(
|
||||
sprite, colorSpaces[selectedColorProfile]->gfxColorSpace()));
|
||||
tx.commit();
|
||||
}
|
||||
catch (const base::Exception& e) {
|
||||
Console::showException(e);
|
||||
}
|
||||
|
||||
updateButtons();
|
||||
});
|
||||
@ -365,12 +371,17 @@ void SpritePropertiesCommand::onExecute(Context* context)
|
||||
[&](){
|
||||
selectedColorProfile = window.colorProfile()->getSelectedItemIndex();
|
||||
|
||||
ContextWriter writer(context);
|
||||
Sprite* sprite(writer.sprite());
|
||||
Tx tx(writer, Strings::sprite_properties_convert_color_profile());
|
||||
tx(new cmd::ConvertColorProfile(
|
||||
sprite, colorSpaces[selectedColorProfile]->gfxColorSpace()));
|
||||
tx.commit();
|
||||
try {
|
||||
ContextWriter writer(context);
|
||||
Sprite* sprite(writer.sprite());
|
||||
Tx tx(writer, Strings::sprite_properties_convert_color_profile());
|
||||
tx(new cmd::ConvertColorProfile(
|
||||
sprite, colorSpaces[selectedColorProfile]->gfxColorSpace()));
|
||||
tx.commit();
|
||||
}
|
||||
catch (const base::Exception& e) {
|
||||
Console::showException(e);
|
||||
}
|
||||
|
||||
updateButtons();
|
||||
});
|
||||
|
@ -88,19 +88,9 @@ ShaderRenderer::ShaderRenderer()
|
||||
m_properties.renderBgOnScreen = true;
|
||||
m_properties.requiresRgbaBackbuffer = true;
|
||||
|
||||
auto makeShader = [](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());
|
||||
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;
|
||||
m_bgEffect = make_shader(kBgShaderCode);
|
||||
m_indexedEffect = make_shader(kIndexedShaderCode);
|
||||
m_grayscaleEffect = make_shader(kGrayscaleShaderCode);
|
||||
}
|
||||
|
||||
ShaderRenderer::~ShaderRenderer() = default;
|
||||
@ -417,21 +407,11 @@ void ShaderRenderer::drawImage(SkCanvas* canvas,
|
||||
const int opacity,
|
||||
const doc::BlendMode blendMode)
|
||||
{
|
||||
auto skData = SkData::MakeWithoutCopy(
|
||||
(const void*)srcImage->getPixelAddress(0, 0),
|
||||
srcImage->rowBytes() * srcImage->height());
|
||||
auto skImg = make_skimage_for_docimage(srcImage);
|
||||
|
||||
switch (srcImage->colorMode()) {
|
||||
|
||||
case doc::ColorMode::RGB: {
|
||||
auto skImg = SkImages::RasterFromData(
|
||||
SkImageInfo::Make(srcImage->width(),
|
||||
srcImage->height(),
|
||||
kRGBA_8888_SkColorType,
|
||||
kUnpremul_SkAlphaType),
|
||||
skData,
|
||||
srcImage->rowBytes());
|
||||
|
||||
SkPaint p;
|
||||
p.setAlpha(opacity);
|
||||
p.setBlendMode(to_skia(blendMode));
|
||||
@ -444,15 +424,6 @@ void ShaderRenderer::drawImage(SkCanvas* canvas,
|
||||
}
|
||||
|
||||
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);
|
||||
builder.child("iImg") = skImg->makeRawShader(SkSamplingOptions(SkFilterMode::kNearest));
|
||||
|
||||
@ -472,15 +443,6 @@ void ShaderRenderer::drawImage(SkCanvas* canvas,
|
||||
}
|
||||
|
||||
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
|
||||
// width=number of palette colors, and height=1
|
||||
const size_t palSize = sizeof(color_t) * m_palette.size();
|
||||
|
@ -655,35 +655,22 @@ bool ColorSelector::buildEffects()
|
||||
|
||||
if (!m_mainEffect) {
|
||||
if (const char* code = getMainAreaShader())
|
||||
m_mainEffect = buildEffect(code);
|
||||
m_mainEffect = make_shader(code);
|
||||
}
|
||||
|
||||
if (!m_bottomEffect) {
|
||||
if (const char* code = getBottomBarShader())
|
||||
m_bottomEffect = buildEffect(code);
|
||||
m_bottomEffect = make_shader(code);
|
||||
}
|
||||
|
||||
if (!m_alphaEffect) {
|
||||
if (const char* code = getAlphaBarShader())
|
||||
m_alphaEffect = buildEffect(code);
|
||||
m_alphaEffect = make_shader(code);
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
m_bottomEffect.reset();
|
||||
|
@ -121,7 +121,6 @@ namespace app {
|
||||
#if SK_ENABLE_SKSL
|
||||
static const char* getAlphaBarShader();
|
||||
bool buildEffects();
|
||||
sk_sp<SkRuntimeEffect> buildEffect(const char* code);
|
||||
#endif
|
||||
|
||||
// Internal flag used to lock the modification of m_color.
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2019-2022 Igara Studio S.A.
|
||||
// Copyright (C) 2019-2024 Igara Studio S.A.
|
||||
// Copyright (C) 2017-2018 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
@ -14,6 +14,7 @@
|
||||
#include "app/cmd/set_mask_position.h"
|
||||
#include "app/commands/command.h"
|
||||
#include "app/commands/commands.h"
|
||||
#include "app/console.h"
|
||||
#include "app/context_access.h"
|
||||
#include "app/tx.h"
|
||||
#include "app/ui/editor/editor.h"
|
||||
@ -80,12 +81,15 @@ EditorState::LeaveAction MovingSelectionState::onLeaveState(Editor* editor, Edit
|
||||
doc->generateMaskBoundaries();
|
||||
}
|
||||
else {
|
||||
{
|
||||
try {
|
||||
ContextWriter writer(UIContext::instance(), 1000);
|
||||
Tx tx(writer, "Move Selection Edges", DoesntModifyDocument);
|
||||
tx(new cmd::SetMaskPosition(doc, newOrigin));
|
||||
tx.commit();
|
||||
}
|
||||
catch (const base::Exception& e) {
|
||||
Console::showException(e);
|
||||
}
|
||||
doc->resetTransformation();
|
||||
}
|
||||
doc->notifyGeneralUpdate();
|
||||
|
@ -1,4 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2024 Igara Studio S.A.
|
||||
// Copyright (C) 2001-2018 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
@ -29,6 +30,8 @@ namespace app {
|
||||
virtual bool onCanPaste(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 onCopy(Context* ctx) = 0;
|
||||
virtual bool onPaste(Context* ctx) = 0;
|
||||
|
@ -1347,7 +1347,7 @@ bool Timeline::onProcessMessage(Message* msg)
|
||||
else if (mouseMsg->left()) {
|
||||
Command* command = Commands::instance()
|
||||
->byId(CommandId::FrameTagProperties());
|
||||
UIContext::instance()->executeCommand(command, params);
|
||||
m_context->executeCommand(command, params);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@ -1389,13 +1389,18 @@ bool Timeline::onProcessMessage(Message* msg)
|
||||
if (tag) {
|
||||
if ((m_state == STATE_RESIZING_TAG_LEFT && tag->fromFrame() != m_resizeTagData.from) ||
|
||||
(m_state == STATE_RESIZING_TAG_RIGHT && tag->toFrame() != m_resizeTagData.to)) {
|
||||
ContextWriter writer(UIContext::instance());
|
||||
Tx tx(writer, Strings::commands_FrameTagProperties());
|
||||
tx(new cmd::SetTagRange(
|
||||
tag,
|
||||
(m_state == STATE_RESIZING_TAG_LEFT ? m_resizeTagData.from: tag->fromFrame()),
|
||||
(m_state == STATE_RESIZING_TAG_RIGHT ? m_resizeTagData.to: tag->toFrame())));
|
||||
tx.commit();
|
||||
try {
|
||||
ContextWriter writer(m_context);
|
||||
Tx tx(writer, Strings::commands_FrameTagProperties());
|
||||
tx(new cmd::SetTagRange(
|
||||
tag,
|
||||
(m_state == STATE_RESIZING_TAG_LEFT ? m_resizeTagData.from: tag->fromFrame()),
|
||||
(m_state == STATE_RESIZING_TAG_RIGHT ? m_resizeTagData.to: tag->toFrame())));
|
||||
tx.commit();
|
||||
}
|
||||
catch (const base::Exception& e) {
|
||||
Console::showException(e);
|
||||
}
|
||||
|
||||
regenerateRows();
|
||||
}
|
||||
@ -1431,7 +1436,7 @@ bool Timeline::onProcessMessage(Message* msg)
|
||||
Command* command = Commands::instance()
|
||||
->byId(CommandId::LayerProperties());
|
||||
|
||||
UIContext::instance()->executeCommand(command);
|
||||
m_context->executeCommand(command);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1441,7 +1446,7 @@ bool Timeline::onProcessMessage(Message* msg)
|
||||
Params params;
|
||||
params.set("frame", "current");
|
||||
|
||||
UIContext::instance()->executeCommand(command, params);
|
||||
m_context->executeCommand(command, params);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1449,7 +1454,7 @@ bool Timeline::onProcessMessage(Message* msg)
|
||||
Command* command = Commands::instance()
|
||||
->byId(CommandId::CelProperties());
|
||||
|
||||
UIContext::instance()->executeCommand(command);
|
||||
m_context->executeCommand(command);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
101
src/app/util/shader_helpers.cpp
Normal file
101
src/app/util/shader_helpers.cpp
Normal 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
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2022 Igara Studio S.A.
|
||||
// Copyright (C) 2022-2024 Igara Studio S.A.
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
@ -8,15 +8,30 @@
|
||||
#define APP_UTIL_SHADER_HELPERS_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#if SK_ENABLE_SKSL
|
||||
#if LAF_SKIA
|
||||
|
||||
#include "app/color.h"
|
||||
#include "gfx/color.h"
|
||||
|
||||
#include "include/core/SkCanvas.h"
|
||||
#include "include/core/SkImage.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 {
|
||||
|
||||
#if SK_ENABLE_SKSL
|
||||
|
||||
// rgb_to_hsl() and hsv_to_hsl() functions by Sam Hocevar licensed
|
||||
// under WTFPL (https://en.wikipedia.org/wiki/WTFPL)
|
||||
// Source:
|
||||
@ -71,8 +86,16 @@ inline SkV4 appColorHsl_to_SkV4(const app::Color& color) {
|
||||
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
|
||||
|
||||
#endif
|
||||
#endif // LAF_SKIA
|
||||
|
||||
#endif
|
||||
|
2
src/clip
2
src/clip
@ -1 +1 @@
|
||||
Subproject commit 94693e2414a2c69a8ca16f065240c80a94cc6221
|
||||
Subproject commit 835cd0f7e7a964bb969482117856bc56a0ac12bf
|
Loading…
x
Reference in New Issue
Block a user