/* * 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/SkBitmap.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/SkParticleEffect.h" #include "modules/particles/include/SkReflected.h" #include "modules/skresources/include/SkResources.h" #include "src/sksl/SkSLCompiler.h" void SkParticleBinding::visitFields(SkFieldVisitor* v) { v->visit("Name", fName); } class SkEffectExternalValue : public SkParticleExternalValue { public: SkEffectExternalValue(const char* name, SkSL::Compiler& compiler, sk_sp params) : SkParticleExternalValue(name, compiler, *compiler.context().fTypes.fVoid) , fParams(std::move(params)) {} int callParameterCount() const override { return 1; } void getCallParameterTypes(const SkSL::Type** outTypes) const override { outTypes[0] = fCompiler.context().fTypes.fBool.get(); } void call(int index, float* arguments, float* outReturn) const override { bool loop = ((int*)arguments)[0] != 0; fEffect->addSpawnRequest(index, loop, fParams); } private: sk_sp fParams; }; class SkEffectBinding : public SkParticleBinding { public: SkEffectBinding(const char* name = "", sk_sp params = nullptr) : SkParticleBinding(name) , fParams(std::move(params)) { if (!fParams) { fParams.reset(new SkParticleEffectParams()); } } REFLECTED(SkEffectBinding, SkParticleBinding) void visitFields(SkFieldVisitor* v) override { SkParticleBinding::visitFields(v); fParams->visitFields(v); } std::unique_ptr toValue(SkSL::Compiler& compiler) override { return std::unique_ptr( new SkEffectExternalValue(fName.c_str(), compiler, fParams)); } void prepare(const skresources::ResourceProvider* resourceProvider) override { fParams->prepare(resourceProvider); } private: sk_sp fParams; }; struct SkPathContours { SkScalar fTotalLength; SkTArray> fContours; void rebuild(const SkPath& path) { fTotalLength = 0; fContours.reset(); SkContourMeasureIter iter(path, false); while (auto contour = iter.next()) { fContours.push_back(contour); fTotalLength += contour->length(); } } }; // Exposes an SkPath as an external, callable value. p(x) returns a float4 { pos.xy, normal.xy } class SkPathExternalValue : public SkParticleExternalValue { public: SkPathExternalValue(const char* name, SkSL::Compiler& compiler, const SkPathContours* path) : SkParticleExternalValue(name, compiler, *compiler.context().fTypes.fFloat4) , 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(int index, float* arguments, float* outReturn) const override { SkScalar len = fPath->fTotalLength * arguments[0]; int idx = 0; while (idx < fPath->fContours.count() - 1 && len > fPath->fContours[idx]->length()) { len -= fPath->fContours[idx++]->length(); } SkVector localXAxis; if (idx >= fPath->fContours.count() || !fPath->fContours[idx]->getPosTan(len, (SkPoint*)outReturn, &localXAxis)) { outReturn[0] = outReturn[1] = 0.0f; localXAxis = { 1, 0 }; } outReturn[2] = localXAxis.fY; outReturn[3] = -localXAxis.fX; } private: const SkPathContours* 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 toValue(SkSL::Compiler& compiler) override { return std::unique_ptr( new SkPathExternalValue(fName.c_str(), compiler, &fContours)); } 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())) { fContours.rebuild(path); } } } private: SkString fPathPath; SkString fPathName; // Cached SkPathContours fContours; }; 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 toValue(SkSL::Compiler& compiler) override { return std::unique_ptr( new SkPathExternalValue(fName.c_str(), compiler, &fContours)); } 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); fContours.rebuild(path); } private: SkString fText; SkScalar fFontSize; // Cached SkPathContours fContours; }; // Exposes an SkBitmap as an external, callable value. p(xy) returns a float4 class SkBitmapExternalValue : public SkParticleExternalValue { public: SkBitmapExternalValue(const char* name, SkSL::Compiler& compiler, const SkBitmap& bitmap) : SkParticleExternalValue(name, compiler, *compiler.context().fTypes.fFloat4) , fBitmap(bitmap) { SkASSERT(bitmap.colorType() == kRGBA_F32_SkColorType); } int callParameterCount() const override { return 1; } void getCallParameterTypes(const SkSL::Type** outTypes) const override { outTypes[0] = fCompiler.context().fTypes.fFloat2.get(); } void call(int index, float* arguments, float* outReturn) const override { int x = SkTPin(static_cast(arguments[0] * fBitmap.width()), 0, fBitmap.width() - 1); int y = SkTPin(static_cast(arguments[1] * fBitmap.height()), 0, fBitmap.height() - 1); float* p = static_cast(fBitmap.getAddr(x, y)); memcpy(outReturn, p, 4 * sizeof(float)); } private: SkBitmap fBitmap; }; 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 toValue(SkSL::Compiler& compiler) override { return std::unique_ptr( new SkBitmapExternalValue(fName.c_str(), compiler, fBitmap)); } 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)) { fBitmap.allocPixels(image->imageInfo().makeColorType(kRGBA_F32_SkColorType)); image->readPixels(nullptr, fBitmap.pixmap(), 0, 0); return; } } fBitmap.allocPixels(SkImageInfo::Make(1, 1, kRGBA_F32_SkColorType, kPremul_SkAlphaType)); fBitmap.eraseColor(SK_ColorWHITE); } private: SkString fImagePath; SkString fImageName; // Cached SkBitmap fBitmap; }; sk_sp SkParticleBinding::MakeEffect(const char* name, sk_sp params) { return sk_sp(new SkEffectBinding(name, std::move(params))); } 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(SkEffectBinding); REGISTER_REFLECTED(SkImageBinding); REGISTER_REFLECTED(SkPathBinding); REGISTER_REFLECTED(SkTextBinding); }