blob: c57425e7da6b16e97ff7b9fe251abc9e0c568307 [file] [log] [blame]
/*
* 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"
#include "include/core/SkImage.h"
#include "include/core/SkPath.h"
#include "include/private/SkTPin.h"
#include "include/utils/SkParsePath.h"
#include "include/utils/SkTextUtils.h"
#include "modules/particles/include/SkReflected.h"
#include "modules/skresources/include/SkResources.h"
#include "src/core/SkMatrixProvider.h"
#include "src/core/SkVM.h"
#include "src/shaders/SkShaderBase.h"
#include "src/sksl/SkSLCompiler.h"
void SkParticleBinding::visitFields(SkFieldVisitor* v) {
v->visit("Name", fName);
}
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}});
}
}
return lin;
}
// Exposes an SkPath as an external, callable function. p(x) returns a float4 { pos.xy, normal.xy }
class SkPathExternalFunction : public SkParticleExternalFunction {
public:
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) {}
int callParameterCount() const override { return 1; }
void getCallParameterTypes(const SkSL::Type** outTypes) const override {
outTypes[0] = fCompiler.context().fTypes.fFloat.get();
}
void call(skvm::Builder* builder,
skvm::F32* arguments,
skvm::F32* outResult,
skvm::I32 mask) const override {
if (fPath.empty()) {
return;
}
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);
}
private:
const LinearizedPath& fPath;
};
class SkPathBinding : public SkParticleBinding {
public:
SkPathBinding(const char* name = "", const char* pathPath = "", const char* pathName = "")
: SkParticleBinding(name)
, fPathPath(pathPath)
, fPathName(pathName) {}
REFLECTED(SkPathBinding, SkParticleBinding)
void visitFields(SkFieldVisitor* v) override {
SkParticleBinding::visitFields(v);
v->visit("PathPath", fPathPath);
v->visit("PathName", fPathName);
}
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);
}
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())) {
fData = linearize_path(path);
}
}
}
private:
SkString fPathPath;
SkString fPathName;
// Cached
LinearizedPath fData;
};
class SkTextBinding : public SkParticleBinding {
public:
SkTextBinding(const char* name = "", const char* text = "", SkScalar fontSize = 96)
: SkParticleBinding(name)
, fText(text)
, fFontSize(fontSize) {}
REFLECTED(SkTextBinding, SkParticleBinding)
void visitFields(SkFieldVisitor* v) override {
SkParticleBinding::visitFields(v);
v->visit("Text", fText);
v->visit("FontSize", fFontSize);
}
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);
}
void prepare(const skresources::ResourceProvider*) override {
if (fText.isEmpty()) {
return;
}
SkFont font(nullptr, fFontSize);
SkPath path;
SkTextUtils::GetPath(fText.c_str(), fText.size(), SkTextEncoding::kUTF8, 0, 0, font, &path);
fData = linearize_path(path);
}
private:
SkString fText;
SkScalar fFontSize;
// Cached
LinearizedPath fData;
};
// Exposes an SkShader as an external, callable function. p(xy) returns a float4
class SkShaderExternalFunction : public SkParticleExternalFunction {
public:
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)) {}
int callParameterCount() const override { return 1; }
void getCallParameterTypes(const SkSL::Type** outTypes) const override {
outTypes[0] = fCompiler.context().fTypes.fFloat2.get();
}
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},
matrixProvider, /*localM=*/nullptr, kLow_SkFilterQuality, colorInfo, fUniforms,
fAlloc);
SkASSERT(result);
outResult[0] = result.r;
outResult[1] = result.g;
outResult[2] = result.b;
outResult[3] = result.a;
}
private:
sk_sp<SkShader> fShader;
};
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);
}
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);
}
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)) {
SkMatrix normalize = SkMatrix::Scale(1.0f / image->width(), 1.0f / image->height());
fShader = image->makeShader(SkSamplingOptions(SkFilterMode::kLinear), &normalize);
return;
}
}
fShader = SkShaders::Color(SK_ColorWHITE);
}
private:
SkString fImagePath;
SkString fImageName;
// Cached
sk_sp<SkShader> fShader;
};
sk_sp<SkParticleBinding> SkParticleBinding::MakeImage(const char* name, const char* imagePath,
const char* imageName) {
return sk_sp<SkParticleBinding>(new SkImageBinding(name, imagePath, imageName));
}
sk_sp<SkParticleBinding> SkParticleBinding::MakePath(const char* name, const char* pathPath,
const char* pathName) {
return sk_sp<SkParticleBinding>(new SkPathBinding(name, pathPath, pathName));
}
void SkParticleBinding::RegisterBindingTypes() {
REGISTER_REFLECTED(SkParticleBinding);
REGISTER_REFLECTED(SkImageBinding);
REGISTER_REFLECTED(SkPathBinding);
REGISTER_REFLECTED(SkTextBinding);
}