Add helper functions make_shader(), make_skimage/skcanvas_for_docimage()

These functions can be used to create a new Skia shader from SkSL
code, and a new SkImage/SkCanvas to modify a doc::Image*
This commit is contained in:
David Capello 2024-03-11 10:34:05 -03:00
parent 50d4f9d802
commit b8514ad1c6
4 changed files with 127 additions and 48 deletions

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) 2022-2023 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.
@ -87,19 +87,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;
@ -416,21 +406,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 = SkImage::MakeRasterData(
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));
@ -443,15 +423,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 = SkImage::MakeRasterData(
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));
@ -471,15 +442,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 = SkImage::MakeRasterData(
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

@ -0,0 +1,94 @@
// 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"
namespace app {
sk_sp<SkRuntimeEffect> make_shader(const char* code)
{
auto result = SkRuntimeEffect::MakeForShader(SkString(code));
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 SkImage::MakeRasterData(
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,18 +8,32 @@
#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"
// To include kRGB_to_HSL_sksl and kHSL_to_RGB_sksl #if SK_ENABLE_SKSL
#include "src/core/SkRuntimeEffectPriv.h" #include "include/effects/SkRuntimeEffect.h"
// To include kRGB_to_HSL_sksl and kHSL_to_RGB_sksl
#include "src/core/SkRuntimeEffectPriv.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:
@ -74,8 +88,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