/* * 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; } // 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 toFunction(SkSL::Compiler& compiler, skvm::Uniforms* uniforms, SkArenaAlloc* alloc) override { return std::make_unique(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 toFunction(SkSL::Compiler& compiler, skvm::Uniforms* uniforms, SkArenaAlloc* alloc) override { return std::make_unique(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 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, colorInfo, fUniforms, fAlloc); SkASSERT(result); outResult[0] = result.r; outResult[1] = result.g; outResult[2] = result.b; outResult[3] = result.a; } private: sk_sp 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 toFunction(SkSL::Compiler& compiler, skvm::Uniforms* uniforms, SkArenaAlloc* alloc) override { return std::make_unique(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 fShader; }; sk_sp SkParticleBinding::MakeImage(const char* name, const char* imagePath, const char* imageName) { return sk_sp(new SkImageBinding(name, imagePath, imageName)); } sk_sp SkParticleBinding::MakePath(const char* name, const char* pathPath, const char* pathName) { return sk_sp(new SkPathBinding(name, pathPath, pathName)); } void SkParticleBinding::RegisterBindingTypes() { REGISTER_REFLECTED(SkParticleBinding); REGISTER_REFLECTED(SkImageBinding); REGISTER_REFLECTED(SkPathBinding); REGISTER_REFLECTED(SkTextBinding); }