@shopify/react-native-skia 1.10.1 → 1.11.0
Sign up to get free protection for your applications and to get access to all the features.
- package/android/src/main/java/com/shopify/reactnative/skia/SkiaBaseView.java +0 -13
- package/android/src/main/java/com/shopify/reactnative/skia/SkiaTextureView.java +1 -24
- package/cpp/api/JsiSkApi.h +3 -0
- package/cpp/api/recorder/ColorFilters.h +133 -0
- package/cpp/api/recorder/Command.h +58 -0
- package/cpp/api/recorder/Convertor.h +1212 -0
- package/cpp/api/recorder/DataTypes.h +234 -0
- package/cpp/api/recorder/DrawingCtx.h +187 -0
- package/cpp/api/recorder/Drawings.h +949 -0
- package/cpp/api/recorder/Image.h +108 -0
- package/cpp/api/recorder/ImageFilters.h +292 -0
- package/cpp/api/recorder/JsiRecorder.h +314 -0
- package/cpp/api/recorder/Paint.h +191 -0
- package/cpp/api/recorder/PathEffects.h +194 -0
- package/cpp/api/recorder/RNRecorder.h +635 -0
- package/cpp/api/recorder/Shaders.h +406 -0
- package/cpp/rnskia/dom/nodes/JsiAtlasNode.h +3 -2
- package/cpp/rnskia/dom/nodes/JsiImageNode.h +3 -2
- package/ios/RNSkia-iOS/SkiaCVPixelBufferUtils.mm +4 -8
- package/jestSetup.js +8 -0
- package/jestSetup.mjs +8 -0
- package/lib/commonjs/renderer/components/image/Image.d.ts +1 -1
- package/lib/commonjs/renderer/components/image/Image.js +8 -2
- package/lib/commonjs/renderer/components/image/Image.js.map +1 -1
- package/lib/commonjs/skia/types/Recorder.d.ts +52 -0
- package/lib/commonjs/skia/types/Recorder.js +6 -0
- package/lib/commonjs/skia/types/Recorder.js.map +1 -0
- package/lib/commonjs/skia/types/Skia.d.ts +2 -0
- package/lib/commonjs/skia/types/Skia.js.map +1 -1
- package/lib/commonjs/skia/types/index.d.ts +1 -0
- package/lib/commonjs/skia/types/index.js +11 -0
- package/lib/commonjs/skia/types/index.js.map +1 -1
- package/lib/commonjs/skia/web/JsiSkia.js +3 -0
- package/lib/commonjs/skia/web/JsiSkia.js.map +1 -1
- package/lib/commonjs/sksg/Container.d.ts +6 -1
- package/lib/commonjs/sksg/Container.js +59 -2
- package/lib/commonjs/sksg/Container.js.map +1 -1
- package/lib/commonjs/sksg/Recorder/DrawingContext.js +1 -0
- package/lib/commonjs/sksg/Recorder/DrawingContext.js.map +1 -1
- package/lib/commonjs/sksg/Recorder/ReanimatedRecorder.d.ts +53 -0
- package/lib/commonjs/sksg/Recorder/ReanimatedRecorder.js +189 -0
- package/lib/commonjs/sksg/Recorder/ReanimatedRecorder.js.map +1 -0
- package/lib/commonjs/sksg/Recorder/Recorder.d.ts +2 -2
- package/lib/commonjs/sksg/Recorder/Recorder.js.map +1 -1
- package/lib/commonjs/sksg/Recorder/Visitor.d.ts +2 -2
- package/lib/commonjs/sksg/Recorder/Visitor.js +2 -2
- package/lib/commonjs/sksg/Recorder/Visitor.js.map +1 -1
- package/lib/module/renderer/components/image/Image.d.ts +1 -1
- package/lib/module/renderer/components/image/Image.js +8 -2
- package/lib/module/renderer/components/image/Image.js.map +1 -1
- package/lib/module/skia/types/Recorder.d.ts +52 -0
- package/lib/module/skia/types/Recorder.js +2 -0
- package/lib/module/skia/types/Recorder.js.map +1 -0
- package/lib/module/skia/types/Skia.d.ts +2 -0
- package/lib/module/skia/types/Skia.js.map +1 -1
- package/lib/module/skia/types/index.d.ts +1 -0
- package/lib/module/skia/types/index.js +1 -0
- package/lib/module/skia/types/index.js.map +1 -1
- package/lib/module/skia/web/JsiSkia.js +3 -0
- package/lib/module/skia/web/JsiSkia.js.map +1 -1
- package/lib/module/sksg/Container.d.ts +6 -1
- package/lib/module/sksg/Container.js +59 -2
- package/lib/module/sksg/Container.js.map +1 -1
- package/lib/module/sksg/Recorder/DrawingContext.js +1 -0
- package/lib/module/sksg/Recorder/DrawingContext.js.map +1 -1
- package/lib/module/sksg/Recorder/ReanimatedRecorder.d.ts +53 -0
- package/lib/module/sksg/Recorder/ReanimatedRecorder.js +182 -0
- package/lib/module/sksg/Recorder/ReanimatedRecorder.js.map +1 -0
- package/lib/module/sksg/Recorder/Recorder.d.ts +2 -2
- package/lib/module/sksg/Recorder/Recorder.js.map +1 -1
- package/lib/module/sksg/Recorder/Visitor.d.ts +2 -2
- package/lib/module/sksg/Recorder/Visitor.js +2 -2
- package/lib/module/sksg/Recorder/Visitor.js.map +1 -1
- package/lib/typescript/lib/commonjs/renderer/components/image/Image.d.ts +4 -1
- package/lib/typescript/lib/commonjs/skia/types/Recorder.d.ts +1 -0
- package/lib/typescript/lib/commonjs/skia/web/JsiSkia.d.ts +1 -0
- package/lib/typescript/lib/commonjs/sksg/Container.d.ts +5 -1
- package/lib/typescript/lib/commonjs/sksg/Reconciler.d.ts +6 -0
- package/lib/typescript/lib/commonjs/sksg/Recorder/ReanimatedRecorder.d.ts +47 -0
- package/lib/typescript/lib/module/mock/index.d.ts +4 -1
- package/lib/typescript/lib/module/renderer/components/image/Image.d.ts +4 -1
- package/lib/typescript/lib/module/skia/Skia.web.d.ts +1 -0
- package/lib/typescript/lib/module/skia/types/Recorder.d.ts +1 -0
- package/lib/typescript/lib/module/skia/types/index.d.ts +1 -0
- package/lib/typescript/lib/module/skia/web/JsiSkia.d.ts +1 -0
- package/lib/typescript/lib/module/sksg/Container.d.ts +5 -1
- package/lib/typescript/lib/module/sksg/Reconciler.d.ts +6 -0
- package/lib/typescript/lib/module/sksg/Recorder/ReanimatedRecorder.d.ts +46 -0
- package/lib/typescript/src/renderer/components/image/Image.d.ts +1 -1
- package/lib/typescript/src/skia/types/Recorder.d.ts +52 -0
- package/lib/typescript/src/skia/types/Skia.d.ts +2 -0
- package/lib/typescript/src/skia/types/index.d.ts +1 -0
- package/lib/typescript/src/sksg/Container.d.ts +6 -1
- package/lib/typescript/src/sksg/Recorder/ReanimatedRecorder.d.ts +53 -0
- package/lib/typescript/src/sksg/Recorder/Recorder.d.ts +2 -2
- package/lib/typescript/src/sksg/Recorder/Visitor.d.ts +2 -2
- package/package.json +3 -2
- package/src/renderer/components/image/Image.tsx +2 -2
- package/src/skia/types/Recorder.ts +91 -0
- package/src/skia/types/Skia.ts +2 -0
- package/src/skia/types/index.ts +1 -0
- package/src/skia/web/JsiSkia.ts +3 -0
- package/src/sksg/Container.ts +63 -4
- package/src/sksg/Recorder/DrawingContext.ts +1 -0
- package/src/sksg/Recorder/ReanimatedRecorder.ts +271 -0
- package/src/sksg/Recorder/Recorder.ts +2 -2
- package/src/sksg/Recorder/Visitor.ts +17 -12
@@ -0,0 +1,234 @@
|
|
1
|
+
#pragma once
|
2
|
+
|
3
|
+
#include <jsi/jsi.h>
|
4
|
+
|
5
|
+
namespace RNSkia {
|
6
|
+
|
7
|
+
using Uniforms = std::map<std::string, std::vector<float>>;
|
8
|
+
|
9
|
+
std::vector<float> processArray(jsi::Runtime &runtime,
|
10
|
+
const jsi::Array &array) {
|
11
|
+
std::vector<float> result;
|
12
|
+
size_t length = array.length(runtime);
|
13
|
+
result.reserve(length);
|
14
|
+
|
15
|
+
for (size_t i = 0; i < length; i++) {
|
16
|
+
jsi::Value element = array.getValueAtIndex(runtime, i);
|
17
|
+
if (element.isNumber()) {
|
18
|
+
result.push_back(static_cast<float>(element.asNumber()));
|
19
|
+
} else if (element.isObject() &&
|
20
|
+
element.asObject(runtime).isArray(runtime)) {
|
21
|
+
auto subArray =
|
22
|
+
processArray(runtime, element.asObject(runtime).asArray(runtime));
|
23
|
+
result.insert(result.end(), subArray.begin(), subArray.end());
|
24
|
+
} else if (element.isObject()) {
|
25
|
+
auto indexableObj = element.asObject(runtime);
|
26
|
+
std::vector<float> values;
|
27
|
+
values.reserve(4);
|
28
|
+
for (int i = 0; i < 4; i++) {
|
29
|
+
if (indexableObj.hasProperty(runtime, std::to_string(i).c_str())) {
|
30
|
+
result.push_back(static_cast<float>(
|
31
|
+
indexableObj.getProperty(runtime, std::to_string(i).c_str())
|
32
|
+
.asNumber()));
|
33
|
+
}
|
34
|
+
}
|
35
|
+
}
|
36
|
+
}
|
37
|
+
return result;
|
38
|
+
}
|
39
|
+
|
40
|
+
bool isJSPoint(jsi::Runtime &runtime, const jsi::Value &value) {
|
41
|
+
return value.isObject() &&
|
42
|
+
value.asObject(runtime).hasProperty(runtime, "x") &&
|
43
|
+
value.asObject(runtime).hasProperty(runtime, "y");
|
44
|
+
}
|
45
|
+
|
46
|
+
bool isIndexable(jsi::Runtime &runtime, const jsi::Value &value) {
|
47
|
+
return value.isObject() && value.asObject(runtime).hasProperty(runtime, "0");
|
48
|
+
}
|
49
|
+
|
50
|
+
std::shared_ptr<SkRect> processRect(jsi::Runtime &runtime,
|
51
|
+
const jsi::Value &value) {
|
52
|
+
if (value.isObject()) {
|
53
|
+
auto object = value.asObject(runtime);
|
54
|
+
if (object.isHostObject(runtime)) {
|
55
|
+
auto ptr = std::dynamic_pointer_cast<JsiSkRect>(
|
56
|
+
value.asObject(runtime).asHostObject(runtime));
|
57
|
+
if (ptr != nullptr) {
|
58
|
+
return ptr->getObject();
|
59
|
+
}
|
60
|
+
} else if (object.hasProperty(runtime, "x") &&
|
61
|
+
object.hasProperty(runtime, "y") &&
|
62
|
+
object.hasProperty(runtime, "width") &&
|
63
|
+
object.hasProperty(runtime, "height")) {
|
64
|
+
auto x = object.getProperty(runtime, "x").getNumber();
|
65
|
+
auto y = object.getProperty(runtime, "y").getNumber();
|
66
|
+
auto width = object.getProperty(runtime, "width").getNumber();
|
67
|
+
auto height = object.getProperty(runtime, "height").getNumber();
|
68
|
+
return std::make_shared<SkRect>(SkRect::MakeXYWH(x, y, width, height));
|
69
|
+
}
|
70
|
+
}
|
71
|
+
return nullptr;
|
72
|
+
}
|
73
|
+
|
74
|
+
SkPoint processPoint(jsi::Runtime &runtime, const jsi::Value &value) {
|
75
|
+
if (value.isObject()) {
|
76
|
+
auto object = value.asObject(runtime);
|
77
|
+
if (object.hasProperty(runtime, "x") && object.hasProperty(runtime, "y")) {
|
78
|
+
auto x = static_cast<float>(object.getProperty(runtime, "x").getNumber());
|
79
|
+
auto y = static_cast<float>(object.getProperty(runtime, "y").getNumber());
|
80
|
+
return SkPoint::Make(x, y);
|
81
|
+
}
|
82
|
+
}
|
83
|
+
throw std::runtime_error("Couldn't read point value");
|
84
|
+
};
|
85
|
+
|
86
|
+
// TODO: return the SkRRect directly
|
87
|
+
std::shared_ptr<SkRRect> processRRect(jsi::Runtime &runtime,
|
88
|
+
const jsi::Value &value) {
|
89
|
+
if (value.isObject()) {
|
90
|
+
auto object = value.asObject(runtime);
|
91
|
+
if (object.isHostObject(runtime)) {
|
92
|
+
auto ptr = std::dynamic_pointer_cast<JsiSkRRect>(
|
93
|
+
value.asObject(runtime).asHostObject(runtime));
|
94
|
+
if (ptr != nullptr) {
|
95
|
+
return ptr->getObject();
|
96
|
+
}
|
97
|
+
} else if (object.hasProperty(runtime, "rect") &&
|
98
|
+
object.hasProperty(runtime, "rx") &&
|
99
|
+
object.hasProperty(runtime, "ry")) {
|
100
|
+
auto rect = processRect(runtime, object.getProperty(runtime, "rect"));
|
101
|
+
auto rx = object.getProperty(runtime, "rx").getNumber();
|
102
|
+
auto ry = object.getProperty(runtime, "ry").getNumber();
|
103
|
+
return std::make_shared<SkRRect>(SkRRect::MakeRectXY(*rect, rx, ry));
|
104
|
+
} else if (object.hasProperty(runtime, "rect") &&
|
105
|
+
object.hasProperty(runtime, "topLeft") &&
|
106
|
+
object.hasProperty(runtime, "topRight") &&
|
107
|
+
object.hasProperty(runtime, "bottomRight") &&
|
108
|
+
object.hasProperty(runtime, "bottomLeft")) {
|
109
|
+
auto rect = processRect(runtime, object.getProperty(runtime, "rect"));
|
110
|
+
auto topLeft =
|
111
|
+
processPoint(runtime, object.getProperty(runtime, "topLeft"));
|
112
|
+
auto topRight =
|
113
|
+
processPoint(runtime, object.getProperty(runtime, "topRight"));
|
114
|
+
auto bottomRight =
|
115
|
+
processPoint(runtime, object.getProperty(runtime, "bottomRight"));
|
116
|
+
auto bottomLeft =
|
117
|
+
processPoint(runtime, object.getProperty(runtime, "bottomLeft"));
|
118
|
+
auto result = std::make_shared<SkRRect>(SkRRect::MakeRectXY(*rect, 0, 0));
|
119
|
+
const SkVector corners[4] = {topLeft, topRight, bottomRight, bottomLeft};
|
120
|
+
result->setRectRadii(*rect, corners);
|
121
|
+
return result;
|
122
|
+
}
|
123
|
+
}
|
124
|
+
return nullptr;
|
125
|
+
}
|
126
|
+
|
127
|
+
// Return SkPath instead of shared_ptr<SkPath>
|
128
|
+
std::shared_ptr<SkPath> processPath(jsi::Runtime &runtime,
|
129
|
+
const jsi::Value &value) {
|
130
|
+
if (value.isString()) {
|
131
|
+
auto pathString = value.getString(runtime).utf8(runtime);
|
132
|
+
SkPath result;
|
133
|
+
|
134
|
+
if (SkParsePath::FromSVGString(pathString.c_str(), &result)) {
|
135
|
+
return std::make_shared<SkPath>(result);
|
136
|
+
} else {
|
137
|
+
throw std::runtime_error("Could not parse path from string.");
|
138
|
+
}
|
139
|
+
} else if (value.isObject() &&
|
140
|
+
value.asObject(runtime).isHostObject(runtime)) {
|
141
|
+
auto ptr = std::dynamic_pointer_cast<JsiSkPath>(
|
142
|
+
value.asObject(runtime).asHostObject(runtime));
|
143
|
+
if (ptr != nullptr) {
|
144
|
+
return ptr->getObject();
|
145
|
+
}
|
146
|
+
}
|
147
|
+
return nullptr;
|
148
|
+
}
|
149
|
+
|
150
|
+
// Function to process uniforms and return SkData for PushShaderCmd
|
151
|
+
inline sk_sp<SkData> processUniforms(
|
152
|
+
const sk_sp<SkRuntimeEffect>& effect,
|
153
|
+
const Uniforms& uniforms) {
|
154
|
+
|
155
|
+
size_t uniformSize = effect->uniformSize();
|
156
|
+
auto uniformsData = SkData::MakeUninitialized(uniformSize);
|
157
|
+
auto uniformDataPtr = static_cast<float*>(uniformsData->writable_data());
|
158
|
+
|
159
|
+
const auto& sourceUniforms = effect->uniforms();
|
160
|
+
for (const auto& uniform : sourceUniforms) {
|
161
|
+
auto it = uniforms.find(std::string(uniform.name));
|
162
|
+
if (it == uniforms.end()) {
|
163
|
+
throw std::runtime_error("Missing uniform value for: " +
|
164
|
+
std::string(uniform.name));
|
165
|
+
}
|
166
|
+
|
167
|
+
const auto& uniformValues = it->second;
|
168
|
+
RuntimeEffectUniform reu = JsiSkRuntimeEffect::fromUniform(uniform);
|
169
|
+
size_t expectedSize = reu.columns * reu.rows;
|
170
|
+
|
171
|
+
if (uniformValues.size() != expectedSize) {
|
172
|
+
throw std::runtime_error("Incorrect uniform size for: " +
|
173
|
+
std::string(uniform.name) + ". Expected " +
|
174
|
+
std::to_string(expectedSize) + " got " +
|
175
|
+
std::to_string(uniformValues.size()));
|
176
|
+
}
|
177
|
+
|
178
|
+
// Process each element in the uniform
|
179
|
+
for (std::size_t j = 0; j < expectedSize; ++j) {
|
180
|
+
const std::size_t offset = reu.slot + j;
|
181
|
+
float fValue = uniformValues[j];
|
182
|
+
|
183
|
+
if (reu.isInteger) {
|
184
|
+
int iValue = static_cast<int>(fValue);
|
185
|
+
uniformDataPtr[offset] = SkBits2Float(iValue);
|
186
|
+
} else {
|
187
|
+
uniformDataPtr[offset] = fValue;
|
188
|
+
}
|
189
|
+
}
|
190
|
+
}
|
191
|
+
|
192
|
+
return uniformsData;
|
193
|
+
}
|
194
|
+
|
195
|
+
inline void processUniforms(
|
196
|
+
SkRuntimeShaderBuilder& builder,
|
197
|
+
const sk_sp<SkRuntimeEffect>& effect,
|
198
|
+
const Uniforms& uniforms) {
|
199
|
+
|
200
|
+
const auto& sourceUniforms = effect->uniforms();
|
201
|
+
for (const auto& uniform : sourceUniforms) {
|
202
|
+
auto it = uniforms.find(std::string(uniform.name));
|
203
|
+
if (it == uniforms.end()) {
|
204
|
+
throw std::runtime_error("Missing uniform value for: " +
|
205
|
+
std::string(uniform.name));
|
206
|
+
}
|
207
|
+
|
208
|
+
const auto& uniformValues = it->second;
|
209
|
+
RuntimeEffectUniform reu = JsiSkRuntimeEffect::fromUniform(uniform);
|
210
|
+
size_t expectedSize = reu.columns * reu.rows;
|
211
|
+
|
212
|
+
if (uniformValues.size() != expectedSize) {
|
213
|
+
throw std::runtime_error("Incorrect uniform size for: " +
|
214
|
+
std::string(uniform.name) + ". Expected " +
|
215
|
+
std::to_string(expectedSize) + " got " +
|
216
|
+
std::to_string(uniformValues.size()));
|
217
|
+
}
|
218
|
+
|
219
|
+
auto builderUniform = builder.uniform(uniform.name);
|
220
|
+
|
221
|
+
if (reu.isInteger) {
|
222
|
+
std::vector<float> convertedValues(uniformValues.size());
|
223
|
+
for (size_t i = 0; i < uniformValues.size(); ++i) {
|
224
|
+
int iValue = static_cast<int>(uniformValues[i]);
|
225
|
+
convertedValues[i] = SkBits2Float(iValue);
|
226
|
+
}
|
227
|
+
builderUniform.set(convertedValues.data(), convertedValues.size());
|
228
|
+
} else {
|
229
|
+
builderUniform.set(uniformValues.data(), uniformValues.size());
|
230
|
+
}
|
231
|
+
}
|
232
|
+
}
|
233
|
+
|
234
|
+
} // namespace RNSkia
|
@@ -0,0 +1,187 @@
|
|
1
|
+
#pragma once
|
2
|
+
|
3
|
+
#include <memory>
|
4
|
+
#include <vector>
|
5
|
+
|
6
|
+
namespace RNSkia {
|
7
|
+
|
8
|
+
// Generic composer type using std::function
|
9
|
+
template <typename T>
|
10
|
+
using Composer = std::function<sk_sp<T>(sk_sp<T>, sk_sp<T>)>;
|
11
|
+
|
12
|
+
// Generic composition function
|
13
|
+
template <typename T>
|
14
|
+
using Composer = std::function<sk_sp<T>(sk_sp<T>, sk_sp<T>)>;
|
15
|
+
// Generic composition function
|
16
|
+
template <typename T>
|
17
|
+
sk_sp<T> composeEffects(const std::vector<sk_sp<T>> &effects,
|
18
|
+
const Composer<T> &composer) {
|
19
|
+
if (effects.empty()) {
|
20
|
+
return nullptr;
|
21
|
+
}
|
22
|
+
if (effects.size() == 1) {
|
23
|
+
return effects[0];
|
24
|
+
}
|
25
|
+
// Use std::accumulate with reverse iterators to mimic JavaScript's
|
26
|
+
// reduceRight
|
27
|
+
return std::accumulate(
|
28
|
+
std::next(effects.rbegin()), // Start from second-to-last
|
29
|
+
effects.rend(), // End at first
|
30
|
+
effects.back(), // Initial value is last element
|
31
|
+
[&composer](const sk_sp<T> &inner, const sk_sp<T> &outer) {
|
32
|
+
return inner ? composer(outer, inner) : outer;
|
33
|
+
});
|
34
|
+
}
|
35
|
+
|
36
|
+
struct Composers {
|
37
|
+
static sk_sp<SkColorFilter> colorFilter(const sk_sp<SkColorFilter> &outer,
|
38
|
+
const sk_sp<SkColorFilter> &inner) {
|
39
|
+
return SkColorFilters::Compose(outer, inner);
|
40
|
+
}
|
41
|
+
|
42
|
+
static sk_sp<SkImageFilter> imageFilter(const sk_sp<SkImageFilter> &outer,
|
43
|
+
const sk_sp<SkImageFilter> &inner) {
|
44
|
+
return SkImageFilters::Compose(outer, inner);
|
45
|
+
}
|
46
|
+
|
47
|
+
static sk_sp<SkPathEffect> pathEffect(const sk_sp<SkPathEffect> &outer,
|
48
|
+
const sk_sp<SkPathEffect> &inner) {
|
49
|
+
return SkPathEffect::MakeCompose(outer, inner);
|
50
|
+
}
|
51
|
+
};
|
52
|
+
|
53
|
+
class DrawingCtx {
|
54
|
+
public:
|
55
|
+
DrawingCtx(SkCanvas *canvas) : canvas(canvas) {
|
56
|
+
SkPaint paint;
|
57
|
+
paint.setAntiAlias(true);
|
58
|
+
paints.push_back(paint);
|
59
|
+
}
|
60
|
+
|
61
|
+
void pushPaint(SkPaint &paint) { paints.push_back(paint); }
|
62
|
+
|
63
|
+
void savePaint() { paints.push_back(SkPaint(getPaint())); }
|
64
|
+
|
65
|
+
void saveBackdropFilter() {
|
66
|
+
// Initialize image filter as nullptr
|
67
|
+
sk_sp<SkImageFilter> imageFilter = nullptr;
|
68
|
+
|
69
|
+
// Try to get image filter first
|
70
|
+
if (!imageFilters.empty()) {
|
71
|
+
imageFilter = imageFilters.back();
|
72
|
+
imageFilters.pop_back();
|
73
|
+
} else if (!colorFilters.empty()) {
|
74
|
+
// If no image filter, try to create one from color filter
|
75
|
+
auto colorFilter = colorFilters.back();
|
76
|
+
colorFilters.pop_back();
|
77
|
+
imageFilter = SkImageFilters::ColorFilter(colorFilter, nullptr);
|
78
|
+
}
|
79
|
+
|
80
|
+
// Save layer with the image filter
|
81
|
+
SkPaint layerPaint;
|
82
|
+
if (imageFilter) {
|
83
|
+
layerPaint.setImageFilter(imageFilter);
|
84
|
+
}
|
85
|
+
canvas->saveLayer(
|
86
|
+
SkCanvas::SaveLayerRec(nullptr, nullptr, imageFilter.get(), 0));
|
87
|
+
canvas->restore();
|
88
|
+
}
|
89
|
+
|
90
|
+
SkPaint &getPaint() { return paints.back(); }
|
91
|
+
const SkPaint &getPaint() const { return paints.back(); }
|
92
|
+
|
93
|
+
SkPaint restorePaint() {
|
94
|
+
if (paints.empty()) {
|
95
|
+
throw std::runtime_error("No paint available");
|
96
|
+
}
|
97
|
+
auto paint = paints.back();
|
98
|
+
paints.pop_back();
|
99
|
+
return paint;
|
100
|
+
}
|
101
|
+
|
102
|
+
std::vector<sk_sp<SkShader>> popAllShaders() {
|
103
|
+
auto result = std::move(shaders);
|
104
|
+
shaders.clear();
|
105
|
+
return result;
|
106
|
+
}
|
107
|
+
|
108
|
+
void composeImageFilter() {
|
109
|
+
if (imageFilters.size() >= 2) {
|
110
|
+
auto outer = imageFilters.back();
|
111
|
+
imageFilters.pop_back();
|
112
|
+
auto inner = imageFilters.back();
|
113
|
+
imageFilters.pop_back();
|
114
|
+
|
115
|
+
auto imgf = SkImageFilters::Compose(outer, inner);
|
116
|
+
imageFilters.push_back(imgf);
|
117
|
+
}
|
118
|
+
}
|
119
|
+
|
120
|
+
void composePathEffect() {
|
121
|
+
if (pathEffects.size() >= 2) {
|
122
|
+
auto outer = pathEffects.back();
|
123
|
+
pathEffects.pop_back();
|
124
|
+
auto inner = pathEffects.back();
|
125
|
+
pathEffects.pop_back();
|
126
|
+
|
127
|
+
auto pe = SkPathEffect::MakeCompose(outer, inner);
|
128
|
+
pathEffects.push_back(pe);
|
129
|
+
}
|
130
|
+
}
|
131
|
+
|
132
|
+
void composeColorFilter() {
|
133
|
+
if (colorFilters.size() >= 2) {
|
134
|
+
auto outer = colorFilters.back();
|
135
|
+
colorFilters.pop_back();
|
136
|
+
auto inner = colorFilters.back();
|
137
|
+
colorFilters.pop_back();
|
138
|
+
|
139
|
+
auto cf = SkColorFilters::Compose(outer, inner);
|
140
|
+
colorFilters.push_back(cf);
|
141
|
+
}
|
142
|
+
}
|
143
|
+
|
144
|
+
void materializePaint() {
|
145
|
+
if (!colorFilters.empty()) {
|
146
|
+
getPaint().setColorFilter(
|
147
|
+
composeEffects<SkColorFilter>(colorFilters, Composers::colorFilter));
|
148
|
+
}
|
149
|
+
|
150
|
+
if (!shaders.empty()) {
|
151
|
+
getPaint().setShader(shaders.back());
|
152
|
+
}
|
153
|
+
|
154
|
+
if (!maskFilters.empty()) {
|
155
|
+
getPaint().setMaskFilter(maskFilters.back());
|
156
|
+
}
|
157
|
+
|
158
|
+
if (!imageFilters.empty()) {
|
159
|
+
getPaint().setImageFilter(
|
160
|
+
composeEffects<SkImageFilter>(imageFilters, Composers::imageFilter));
|
161
|
+
}
|
162
|
+
|
163
|
+
// Handle path effects
|
164
|
+
if (!pathEffects.empty()) {
|
165
|
+
getPaint().setPathEffect(
|
166
|
+
composeEffects<SkPathEffect>(pathEffects, Composers::pathEffect));
|
167
|
+
}
|
168
|
+
|
169
|
+
// Clear all vectors
|
170
|
+
colorFilters.clear();
|
171
|
+
shaders.clear();
|
172
|
+
imageFilters.clear();
|
173
|
+
pathEffects.clear();
|
174
|
+
maskFilters.clear();
|
175
|
+
}
|
176
|
+
|
177
|
+
SkCanvas *canvas;
|
178
|
+
std::vector<SkPaint> paints;
|
179
|
+
std::vector<sk_sp<SkMaskFilter>> maskFilters;
|
180
|
+
std::vector<sk_sp<SkColorFilter>> colorFilters;
|
181
|
+
std::vector<sk_sp<SkShader>> shaders;
|
182
|
+
std::vector<sk_sp<SkImageFilter>> imageFilters;
|
183
|
+
std::vector<sk_sp<SkPathEffect>> pathEffects;
|
184
|
+
std::vector<SkPaint> paintDeclarations;
|
185
|
+
};
|
186
|
+
|
187
|
+
} // namespace RNSkia
|