2019-08-30 14:59:47 +00:00
|
|
|
/*
|
|
|
|
* Copyright 2019 Google LLC
|
|
|
|
*
|
|
|
|
* Use of this source code is governed by a BSD-style license that can be
|
|
|
|
* found in the LICENSE file.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "modules/particles/include/SkParticleBinding.h"
|
|
|
|
|
|
|
|
#include "include/core/SkContourMeasure.h"
|
2019-12-03 14:49:12 +00:00
|
|
|
#include "include/core/SkImage.h"
|
2019-08-30 14:59:47 +00:00
|
|
|
#include "include/core/SkPath.h"
|
2020-10-16 16:04:18 +00:00
|
|
|
#include "include/private/SkTPin.h"
|
2019-08-30 14:59:47 +00:00
|
|
|
#include "include/utils/SkParsePath.h"
|
|
|
|
#include "include/utils/SkTextUtils.h"
|
|
|
|
#include "modules/particles/include/SkReflected.h"
|
2019-12-02 21:52:51 +00:00
|
|
|
#include "modules/skresources/include/SkResources.h"
|
2021-01-20 19:01:30 +00:00
|
|
|
#include "src/core/SkMatrixProvider.h"
|
|
|
|
#include "src/core/SkVM.h"
|
|
|
|
#include "src/shaders/SkShaderBase.h"
|
2019-08-30 14:59:47 +00:00
|
|
|
#include "src/sksl/SkSLCompiler.h"
|
|
|
|
|
|
|
|
void SkParticleBinding::visitFields(SkFieldVisitor* v) {
|
|
|
|
v->visit("Name", fName);
|
|
|
|
}
|
|
|
|
|
2021-01-20 19:01:30 +00:00
|
|
|
namespace {
|
|
|
|
struct PosNrm { SkPoint pos; SkVector nrm; };
|
|
|
|
using LinearizedPath = std::vector<PosNrm>;
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
static LinearizedPath linearize_path(const SkPath& path) {
|
|
|
|
LinearizedPath lin;
|
|
|
|
SkContourMeasureIter iter(path, false);
|
|
|
|
while (auto contour = iter.next()) {
|
|
|
|
for (SkScalar x = 0; x < contour->length(); x++) {
|
|
|
|
SkPoint pos;
|
|
|
|
SkVector tan;
|
|
|
|
SkAssertResult(contour->getPosTan(x, &pos, &tan));
|
|
|
|
lin.push_back({pos, {tan.fY, -tan.fX}});
|
2019-08-30 14:59:47 +00:00
|
|
|
}
|
|
|
|
}
|
2021-01-20 19:01:30 +00:00
|
|
|
return lin;
|
|
|
|
}
|
2019-08-30 14:59:47 +00:00
|
|
|
|
2021-01-20 19:01:30 +00:00
|
|
|
// Exposes an SkPath as an external, callable function. p(x) returns a float4 { pos.xy, normal.xy }
|
|
|
|
class SkPathExternalFunction : public SkParticleExternalFunction {
|
2019-08-30 14:59:47 +00:00
|
|
|
public:
|
2021-01-20 19:01:30 +00:00
|
|
|
SkPathExternalFunction(const char* name,
|
|
|
|
SkSL::Compiler& compiler,
|
|
|
|
const LinearizedPath& path,
|
|
|
|
skvm::Uniforms* uniforms,
|
|
|
|
SkArenaAlloc* alloc)
|
|
|
|
: SkParticleExternalFunction(
|
|
|
|
name, compiler, *compiler.context().fTypes.fFloat4, uniforms, alloc)
|
|
|
|
, fPath(path) {}
|
2019-08-30 14:59:47 +00:00
|
|
|
|
|
|
|
int callParameterCount() const override { return 1; }
|
|
|
|
void getCallParameterTypes(const SkSL::Type** outTypes) const override {
|
2021-01-11 19:22:36 +00:00
|
|
|
outTypes[0] = fCompiler.context().fTypes.fFloat.get();
|
2019-08-30 14:59:47 +00:00
|
|
|
}
|
|
|
|
|
2021-01-20 19:01:30 +00:00
|
|
|
void call(skvm::Builder* builder,
|
|
|
|
skvm::F32* arguments,
|
|
|
|
skvm::F32* outResult,
|
|
|
|
skvm::I32 mask) const override {
|
|
|
|
if (fPath.empty()) {
|
|
|
|
return;
|
2019-08-30 14:59:47 +00:00
|
|
|
}
|
2021-01-20 19:01:30 +00:00
|
|
|
|
|
|
|
skvm::Uniform ptr = fUniforms->pushPtr(fPath.data());
|
|
|
|
skvm::I32 index = trunc(clamp(arguments[0] * fPath.size(), 0, fPath.size() - 1));
|
|
|
|
|
|
|
|
outResult[0] = builder->gatherF(ptr, (index<<2)+0);
|
|
|
|
outResult[1] = builder->gatherF(ptr, (index<<2)+1);
|
|
|
|
outResult[2] = builder->gatherF(ptr, (index<<2)+2);
|
|
|
|
outResult[3] = builder->gatherF(ptr, (index<<2)+3);
|
2019-08-30 14:59:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
2021-01-20 19:01:30 +00:00
|
|
|
const LinearizedPath& fPath;
|
2019-08-30 14:59:47 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
class SkPathBinding : public SkParticleBinding {
|
|
|
|
public:
|
2019-12-03 19:31:37 +00:00
|
|
|
SkPathBinding(const char* name = "", const char* pathPath = "", const char* pathName = "")
|
2019-08-30 14:59:47 +00:00
|
|
|
: SkParticleBinding(name)
|
2019-12-03 19:31:37 +00:00
|
|
|
, fPathPath(pathPath)
|
|
|
|
, fPathName(pathName) {}
|
2019-08-30 14:59:47 +00:00
|
|
|
|
|
|
|
REFLECTED(SkPathBinding, SkParticleBinding)
|
|
|
|
|
|
|
|
void visitFields(SkFieldVisitor* v) override {
|
|
|
|
SkParticleBinding::visitFields(v);
|
2019-12-03 19:31:37 +00:00
|
|
|
v->visit("PathPath", fPathPath);
|
|
|
|
v->visit("PathName", fPathName);
|
2019-08-30 14:59:47 +00:00
|
|
|
}
|
|
|
|
|
2021-01-20 19:01:30 +00:00
|
|
|
std::unique_ptr<SkParticleExternalFunction> toFunction(SkSL::Compiler& compiler,
|
|
|
|
skvm::Uniforms* uniforms,
|
|
|
|
SkArenaAlloc* alloc) override {
|
|
|
|
return std::make_unique<SkPathExternalFunction>(fName.c_str(), compiler, fData, uniforms,
|
|
|
|
alloc);
|
2019-08-30 14:59:47 +00:00
|
|
|
}
|
|
|
|
|
2019-12-03 19:31:37 +00:00
|
|
|
void prepare(const skresources::ResourceProvider* resourceProvider) override {
|
|
|
|
if (auto pathData = resourceProvider->load(fPathPath.c_str(), fPathName.c_str())) {
|
|
|
|
SkPath path;
|
|
|
|
if (0 != path.readFromMemory(pathData->data(), pathData->size())) {
|
2021-01-20 19:01:30 +00:00
|
|
|
fData = linearize_path(path);
|
2019-12-03 19:31:37 +00:00
|
|
|
}
|
2019-08-30 14:59:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-02 21:52:51 +00:00
|
|
|
private:
|
2019-12-03 19:31:37 +00:00
|
|
|
SkString fPathPath;
|
|
|
|
SkString fPathName;
|
2019-12-02 21:52:51 +00:00
|
|
|
|
2019-08-30 14:59:47 +00:00
|
|
|
// Cached
|
2021-01-20 19:01:30 +00:00
|
|
|
LinearizedPath fData;
|
2019-08-30 14:59:47 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
class SkTextBinding : public SkParticleBinding {
|
|
|
|
public:
|
|
|
|
SkTextBinding(const char* name = "", const char* text = "", SkScalar fontSize = 96)
|
|
|
|
: SkParticleBinding(name)
|
|
|
|
, fText(text)
|
2019-12-02 21:52:51 +00:00
|
|
|
, fFontSize(fontSize) {}
|
2019-08-30 14:59:47 +00:00
|
|
|
|
|
|
|
REFLECTED(SkTextBinding, SkParticleBinding)
|
|
|
|
|
|
|
|
void visitFields(SkFieldVisitor* v) override {
|
|
|
|
SkParticleBinding::visitFields(v);
|
|
|
|
v->visit("Text", fText);
|
|
|
|
v->visit("FontSize", fFontSize);
|
|
|
|
}
|
|
|
|
|
2021-01-20 19:01:30 +00:00
|
|
|
std::unique_ptr<SkParticleExternalFunction> toFunction(SkSL::Compiler& compiler,
|
|
|
|
skvm::Uniforms* uniforms,
|
|
|
|
SkArenaAlloc* alloc) override {
|
|
|
|
return std::make_unique<SkPathExternalFunction>(fName.c_str(), compiler, fData, uniforms,
|
|
|
|
alloc);
|
2019-08-30 14:59:47 +00:00
|
|
|
}
|
|
|
|
|
2019-12-02 21:52:51 +00:00
|
|
|
void prepare(const skresources::ResourceProvider*) override {
|
2019-08-30 14:59:47 +00:00
|
|
|
if (fText.isEmpty()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
SkFont font(nullptr, fFontSize);
|
|
|
|
SkPath path;
|
|
|
|
SkTextUtils::GetPath(fText.c_str(), fText.size(), SkTextEncoding::kUTF8, 0, 0, font, &path);
|
2021-01-20 19:01:30 +00:00
|
|
|
fData = linearize_path(path);
|
2019-08-30 14:59:47 +00:00
|
|
|
}
|
|
|
|
|
2019-12-02 21:52:51 +00:00
|
|
|
private:
|
|
|
|
SkString fText;
|
|
|
|
SkScalar fFontSize;
|
|
|
|
|
2019-08-30 14:59:47 +00:00
|
|
|
// Cached
|
2021-01-20 19:01:30 +00:00
|
|
|
LinearizedPath fData;
|
2019-08-30 14:59:47 +00:00
|
|
|
};
|
|
|
|
|
2021-01-20 19:01:30 +00:00
|
|
|
// Exposes an SkShader as an external, callable function. p(xy) returns a float4
|
|
|
|
class SkShaderExternalFunction : public SkParticleExternalFunction {
|
2019-12-03 14:49:12 +00:00
|
|
|
public:
|
2021-01-20 19:01:30 +00:00
|
|
|
SkShaderExternalFunction(const char* name,
|
|
|
|
SkSL::Compiler& compiler,
|
|
|
|
sk_sp<SkShader> shader,
|
|
|
|
skvm::Uniforms* uniforms,
|
|
|
|
SkArenaAlloc* alloc)
|
|
|
|
: SkParticleExternalFunction(
|
|
|
|
name, compiler, *compiler.context().fTypes.fFloat4, uniforms, alloc)
|
|
|
|
, fShader(std::move(shader)) {}
|
2019-12-03 14:49:12 +00:00
|
|
|
|
|
|
|
int callParameterCount() const override { return 1; }
|
|
|
|
void getCallParameterTypes(const SkSL::Type** outTypes) const override {
|
2021-01-11 19:22:36 +00:00
|
|
|
outTypes[0] = fCompiler.context().fTypes.fFloat2.get();
|
2019-12-03 14:49:12 +00:00
|
|
|
}
|
|
|
|
|
2021-01-20 19:01:30 +00:00
|
|
|
void call(skvm::Builder* builder,
|
|
|
|
skvm::F32* arguments,
|
|
|
|
skvm::F32* outResult,
|
|
|
|
skvm::I32 mask) const override {
|
|
|
|
skvm::Coord coord = {arguments[0], arguments[1]};
|
|
|
|
skvm::F32 zero = builder->splat(0.0f);
|
|
|
|
SkSimpleMatrixProvider matrixProvider(SkMatrix::I());
|
|
|
|
SkColorInfo colorInfo(kRGBA_8888_SkColorType, kPremul_SkAlphaType, /*cs=*/nullptr);
|
|
|
|
|
|
|
|
skvm::Color result = as_SB(fShader)->program(
|
|
|
|
builder, /*device=*/coord, /*local=*/coord, /*paint=*/{zero, zero, zero, zero},
|
2021-03-19 18:05:19 +00:00
|
|
|
matrixProvider, /*localM=*/nullptr, colorInfo, fUniforms,
|
2021-01-20 19:01:30 +00:00
|
|
|
fAlloc);
|
|
|
|
SkASSERT(result);
|
|
|
|
outResult[0] = result.r;
|
|
|
|
outResult[1] = result.g;
|
|
|
|
outResult[2] = result.b;
|
|
|
|
outResult[3] = result.a;
|
2019-12-03 14:49:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
2021-01-20 19:01:30 +00:00
|
|
|
sk_sp<SkShader> fShader;
|
2019-12-03 14:49:12 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
class SkImageBinding : public SkParticleBinding {
|
|
|
|
public:
|
|
|
|
SkImageBinding(const char* name = "", const char* imagePath = "", const char* imageName = "")
|
|
|
|
: SkParticleBinding(name)
|
|
|
|
, fImagePath(imagePath)
|
|
|
|
, fImageName(imageName) {}
|
|
|
|
|
|
|
|
REFLECTED(SkImageBinding, SkParticleBinding)
|
|
|
|
|
|
|
|
void visitFields(SkFieldVisitor* v) override {
|
|
|
|
SkParticleBinding::visitFields(v);
|
|
|
|
v->visit("ImagePath", fImagePath);
|
|
|
|
v->visit("ImageName", fImageName);
|
|
|
|
}
|
|
|
|
|
2021-01-20 19:01:30 +00:00
|
|
|
std::unique_ptr<SkParticleExternalFunction> toFunction(SkSL::Compiler& compiler,
|
|
|
|
skvm::Uniforms* uniforms,
|
|
|
|
SkArenaAlloc* alloc) override {
|
|
|
|
return std::make_unique<SkShaderExternalFunction>(fName.c_str(), compiler, fShader,
|
|
|
|
uniforms, alloc);
|
2019-12-03 14:49:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void prepare(const skresources::ResourceProvider* resourceProvider) override {
|
|
|
|
if (auto asset = resourceProvider->loadImageAsset(fImagePath.c_str(), fImageName.c_str(),
|
|
|
|
nullptr)) {
|
|
|
|
if (auto image = asset->getFrame(0)) {
|
2021-01-20 19:01:30 +00:00
|
|
|
SkMatrix normalize = SkMatrix::Scale(1.0f / image->width(), 1.0f / image->height());
|
|
|
|
fShader = image->makeShader(SkSamplingOptions(SkFilterMode::kLinear), &normalize);
|
2019-12-03 14:49:12 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-20 19:01:30 +00:00
|
|
|
fShader = SkShaders::Color(SK_ColorWHITE);
|
2019-12-03 14:49:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
SkString fImagePath;
|
|
|
|
SkString fImageName;
|
|
|
|
|
|
|
|
// Cached
|
2021-01-20 19:01:30 +00:00
|
|
|
sk_sp<SkShader> fShader;
|
2019-12-03 14:49:12 +00:00
|
|
|
};
|
|
|
|
|
2019-12-03 19:31:37 +00:00
|
|
|
sk_sp<SkParticleBinding> SkParticleBinding::MakeImage(const char* name, const char* imagePath,
|
|
|
|
const char* imageName) {
|
|
|
|
return sk_sp<SkParticleBinding>(new SkImageBinding(name, imagePath, imageName));
|
Particles: Sub-effect spawning and some slight refactoring
* Added a new binding type, SkEffectBinding. This stores another
entire effect params structure (so the JSON is just nested).
The name is a callable value that spawns a new instance of
that effect, inheriting the parameters of the spawning effect
or particle (depending on which kind of script made the call).
* Broke up the monolithic update function into some helpers,
got some code reuse with the script calling logic.
* Unlike particle capacity, there is no upper limit on child
effects (yet), so it's easy to trigger runaway memory and
CPU consumption. Be careful.
* Added death scripts to effects and particles, which are a
common place to want to spawn sub-effects. Like spawn,
these run on each loop, but for one-shots they play at the
end. Even with loops, this is helpful for timing sub-effects
(see fireworks2.json).
* Finally, added a much more comprehensive example effect,
raincloud.json. This includes a total of three effects, to
generate a cloud, raindrops, and splashes when those drops
hit "the ground".
Change-Id: I3d7b72bcbb684642cd9723518b67ab1c7d7a538a
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/242479
Reviewed-by: Michael Ludwig <michaelludwig@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
2019-09-19 14:06:36 +00:00
|
|
|
}
|
|
|
|
|
2019-12-03 19:31:37 +00:00
|
|
|
sk_sp<SkParticleBinding> SkParticleBinding::MakePath(const char* name, const char* pathPath,
|
|
|
|
const char* pathName) {
|
|
|
|
return sk_sp<SkParticleBinding>(new SkPathBinding(name, pathPath, pathName));
|
2019-08-30 14:59:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void SkParticleBinding::RegisterBindingTypes() {
|
|
|
|
REGISTER_REFLECTED(SkParticleBinding);
|
2019-12-03 14:49:12 +00:00
|
|
|
REGISTER_REFLECTED(SkImageBinding);
|
2019-08-30 14:59:47 +00:00
|
|
|
REGISTER_REFLECTED(SkPathBinding);
|
|
|
|
REGISTER_REFLECTED(SkTextBinding);
|
|
|
|
}
|