blob: e654e0597acf15d4a60f0b8b5b2c8d51f27d1d80 [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/SkBitmap.h"
#include "include/core/SkContourMeasure.h"
#include "include/core/SkImage.h"
#include "include/core/SkPath.h"
#include "include/utils/SkParsePath.h"
#include "include/utils/SkRandom.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<SkParticleEffectParams> params)
: SkParticleExternalValue(name, compiler, *compiler.context().fVoid_Type)
, fParams(std::move(params)) {}
bool canCall() const override { return true; }
int callParameterCount() const override { return 1; }
void getCallParameterTypes(const SkSL::Type** outTypes) const override {
outTypes[0] = fCompiler.context().fBool_Type.get();
}
void call(int index, float* arguments, float* outReturn) override {
bool loop = ((int*)arguments)[0] != 0;
fEffect->addSpawnRequest(index, loop, fParams);
}
private:
sk_sp<SkParticleEffectParams> fParams;
};
class SkEffectBinding : public SkParticleBinding {
public:
SkEffectBinding(const char* name = "", sk_sp<SkParticleEffectParams> 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<SkParticleExternalValue> toValue(SkSL::Compiler& compiler) override {
return std::unique_ptr<SkParticleExternalValue>(
new SkEffectExternalValue(fName.c_str(), compiler, fParams));
}
void prepare(const skresources::ResourceProvider* resourceProvider) override {
fParams->prepare(resourceProvider);
}
private:
sk_sp<SkParticleEffectParams> fParams;
};
struct SkPathContours {
SkScalar fTotalLength;
SkTArray<sk_sp<SkContourMeasure>> 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().fFloat4_Type)
, fPath(path) { }
bool canCall() const override { return true; }
int callParameterCount() const override { return 1; }
void getCallParameterTypes(const SkSL::Type** outTypes) const override {
outTypes[0] = fCompiler.context().fFloat_Type.get();
}
void call(int index, float* arguments, float* outReturn) 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* path = "")
: SkParticleBinding(name)
, fPath(path) {}
REFLECTED(SkPathBinding, SkParticleBinding)
void visitFields(SkFieldVisitor* v) override {
SkParticleBinding::visitFields(v);
v->visit("Path", fPath);
}
std::unique_ptr<SkParticleExternalValue> toValue(SkSL::Compiler& compiler) override {
return std::unique_ptr<SkParticleExternalValue>(
new SkPathExternalValue(fName.c_str(), compiler, &fContours));
}
void prepare(const skresources::ResourceProvider*) override {
SkPath path;
if (SkParsePath::FromSVGString(fPath.c_str(), &path)) {
fContours.rebuild(path);
}
}
private:
SkString fPath;
// 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<SkParticleExternalValue> toValue(SkSL::Compiler& compiler) override {
return std::unique_ptr<SkParticleExternalValue>(
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().fFloat4_Type)
, fBitmap(bitmap) {
SkASSERT(bitmap.colorType() == kRGBA_F32_SkColorType);
}
bool canCall() const override { return true; }
int callParameterCount() const override { return 1; }
void getCallParameterTypes(const SkSL::Type** outTypes) const override {
outTypes[0] = fCompiler.context().fFloat2_Type.get();
}
void call(int index, float* arguments, float* outReturn) override {
int x = SkTPin(static_cast<int>(arguments[0] * fBitmap.width()), 0, fBitmap.width() - 1);
int y = SkTPin(static_cast<int>(arguments[1] * fBitmap.height()), 0, fBitmap.height() - 1);
float* p = static_cast<float*>(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<SkParticleExternalValue> toValue(SkSL::Compiler& compiler) override {
return std::unique_ptr<SkParticleExternalValue>(
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(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> SkParticleBinding::MakeEffectBinding(
const char* name, sk_sp<SkParticleEffectParams> params) {
return sk_sp<SkParticleBinding>(new SkEffectBinding(name, params));
}
sk_sp<SkParticleBinding> SkParticleBinding::MakePathBinding(const char* name, const char* path) {
return sk_sp<SkParticleBinding>(new SkPathBinding(name, path));
}
void SkParticleBinding::RegisterBindingTypes() {
REGISTER_REFLECTED(SkParticleBinding);
REGISTER_REFLECTED(SkEffectBinding);
REGISTER_REFLECTED(SkImageBinding);
REGISTER_REFLECTED(SkPathBinding);
REGISTER_REFLECTED(SkTextBinding);
}