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,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

View File

@ -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

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
@ -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());
}
}

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,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);
}
}
}

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
@ -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());
}
}

View File

@ -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());

View File

@ -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();
});

View File

@ -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();

View File

@ -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();

View File

@ -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.

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) 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();

View File

@ -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;

View File

@ -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;
}

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
// 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

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