@shopify/react-native-skia 1.10.2 → 1.11.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (105) hide show
  1. package/cpp/api/JsiSkApi.h +3 -0
  2. package/cpp/api/recorder/ColorFilters.h +133 -0
  3. package/cpp/api/recorder/Command.h +58 -0
  4. package/cpp/api/recorder/Convertor.h +1212 -0
  5. package/cpp/api/recorder/DataTypes.h +234 -0
  6. package/cpp/api/recorder/DrawingCtx.h +187 -0
  7. package/cpp/api/recorder/Drawings.h +949 -0
  8. package/cpp/api/recorder/Image.h +108 -0
  9. package/cpp/api/recorder/ImageFilters.h +292 -0
  10. package/cpp/api/recorder/JsiRecorder.h +314 -0
  11. package/cpp/api/recorder/Paint.h +191 -0
  12. package/cpp/api/recorder/PathEffects.h +194 -0
  13. package/cpp/api/recorder/RNRecorder.h +635 -0
  14. package/cpp/api/recorder/Shaders.h +406 -0
  15. package/cpp/rnskia/dom/nodes/JsiAtlasNode.h +3 -2
  16. package/cpp/rnskia/dom/nodes/JsiImageNode.h +3 -2
  17. package/ios/RNSkia-iOS/SkiaCVPixelBufferUtils.mm +4 -8
  18. package/jestSetup.js +8 -0
  19. package/jestSetup.mjs +8 -0
  20. package/lib/commonjs/renderer/components/image/Image.d.ts +1 -1
  21. package/lib/commonjs/renderer/components/image/Image.js +8 -2
  22. package/lib/commonjs/renderer/components/image/Image.js.map +1 -1
  23. package/lib/commonjs/skia/types/Recorder.d.ts +52 -0
  24. package/lib/commonjs/skia/types/Recorder.js +6 -0
  25. package/lib/commonjs/skia/types/Recorder.js.map +1 -0
  26. package/lib/commonjs/skia/types/Skia.d.ts +2 -0
  27. package/lib/commonjs/skia/types/Skia.js.map +1 -1
  28. package/lib/commonjs/skia/types/index.d.ts +1 -0
  29. package/lib/commonjs/skia/types/index.js +11 -0
  30. package/lib/commonjs/skia/types/index.js.map +1 -1
  31. package/lib/commonjs/skia/web/JsiSkia.js +3 -0
  32. package/lib/commonjs/skia/web/JsiSkia.js.map +1 -1
  33. package/lib/commonjs/sksg/Container.d.ts +6 -1
  34. package/lib/commonjs/sksg/Container.js +59 -2
  35. package/lib/commonjs/sksg/Container.js.map +1 -1
  36. package/lib/commonjs/sksg/Recorder/DrawingContext.js +1 -0
  37. package/lib/commonjs/sksg/Recorder/DrawingContext.js.map +1 -1
  38. package/lib/commonjs/sksg/Recorder/ReanimatedRecorder.d.ts +53 -0
  39. package/lib/commonjs/sksg/Recorder/ReanimatedRecorder.js +189 -0
  40. package/lib/commonjs/sksg/Recorder/ReanimatedRecorder.js.map +1 -0
  41. package/lib/commonjs/sksg/Recorder/Recorder.d.ts +2 -2
  42. package/lib/commonjs/sksg/Recorder/Recorder.js.map +1 -1
  43. package/lib/commonjs/sksg/Recorder/Visitor.d.ts +2 -2
  44. package/lib/commonjs/sksg/Recorder/Visitor.js +2 -2
  45. package/lib/commonjs/sksg/Recorder/Visitor.js.map +1 -1
  46. package/lib/module/renderer/components/image/Image.d.ts +1 -1
  47. package/lib/module/renderer/components/image/Image.js +8 -2
  48. package/lib/module/renderer/components/image/Image.js.map +1 -1
  49. package/lib/module/skia/types/Recorder.d.ts +52 -0
  50. package/lib/module/skia/types/Recorder.js +2 -0
  51. package/lib/module/skia/types/Recorder.js.map +1 -0
  52. package/lib/module/skia/types/Skia.d.ts +2 -0
  53. package/lib/module/skia/types/Skia.js.map +1 -1
  54. package/lib/module/skia/types/index.d.ts +1 -0
  55. package/lib/module/skia/types/index.js +1 -0
  56. package/lib/module/skia/types/index.js.map +1 -1
  57. package/lib/module/skia/web/JsiSkia.js +3 -0
  58. package/lib/module/skia/web/JsiSkia.js.map +1 -1
  59. package/lib/module/sksg/Container.d.ts +6 -1
  60. package/lib/module/sksg/Container.js +59 -2
  61. package/lib/module/sksg/Container.js.map +1 -1
  62. package/lib/module/sksg/Recorder/DrawingContext.js +1 -0
  63. package/lib/module/sksg/Recorder/DrawingContext.js.map +1 -1
  64. package/lib/module/sksg/Recorder/ReanimatedRecorder.d.ts +53 -0
  65. package/lib/module/sksg/Recorder/ReanimatedRecorder.js +182 -0
  66. package/lib/module/sksg/Recorder/ReanimatedRecorder.js.map +1 -0
  67. package/lib/module/sksg/Recorder/Recorder.d.ts +2 -2
  68. package/lib/module/sksg/Recorder/Recorder.js.map +1 -1
  69. package/lib/module/sksg/Recorder/Visitor.d.ts +2 -2
  70. package/lib/module/sksg/Recorder/Visitor.js +2 -2
  71. package/lib/module/sksg/Recorder/Visitor.js.map +1 -1
  72. package/lib/typescript/lib/commonjs/renderer/components/image/Image.d.ts +4 -1
  73. package/lib/typescript/lib/commonjs/skia/types/Recorder.d.ts +1 -0
  74. package/lib/typescript/lib/commonjs/skia/web/JsiSkia.d.ts +1 -0
  75. package/lib/typescript/lib/commonjs/sksg/Container.d.ts +5 -1
  76. package/lib/typescript/lib/commonjs/sksg/Reconciler.d.ts +6 -0
  77. package/lib/typescript/lib/commonjs/sksg/Recorder/ReanimatedRecorder.d.ts +47 -0
  78. package/lib/typescript/lib/module/mock/index.d.ts +4 -1
  79. package/lib/typescript/lib/module/renderer/components/image/Image.d.ts +4 -1
  80. package/lib/typescript/lib/module/skia/Skia.web.d.ts +1 -0
  81. package/lib/typescript/lib/module/skia/types/Recorder.d.ts +1 -0
  82. package/lib/typescript/lib/module/skia/types/index.d.ts +1 -0
  83. package/lib/typescript/lib/module/skia/web/JsiSkia.d.ts +1 -0
  84. package/lib/typescript/lib/module/sksg/Container.d.ts +5 -1
  85. package/lib/typescript/lib/module/sksg/Reconciler.d.ts +6 -0
  86. package/lib/typescript/lib/module/sksg/Recorder/ReanimatedRecorder.d.ts +46 -0
  87. package/lib/typescript/src/renderer/components/image/Image.d.ts +1 -1
  88. package/lib/typescript/src/skia/types/Recorder.d.ts +52 -0
  89. package/lib/typescript/src/skia/types/Skia.d.ts +2 -0
  90. package/lib/typescript/src/skia/types/index.d.ts +1 -0
  91. package/lib/typescript/src/sksg/Container.d.ts +6 -1
  92. package/lib/typescript/src/sksg/Recorder/ReanimatedRecorder.d.ts +53 -0
  93. package/lib/typescript/src/sksg/Recorder/Recorder.d.ts +2 -2
  94. package/lib/typescript/src/sksg/Recorder/Visitor.d.ts +2 -2
  95. package/package.json +3 -2
  96. package/src/renderer/components/image/Image.tsx +2 -2
  97. package/src/skia/types/Recorder.ts +91 -0
  98. package/src/skia/types/Skia.ts +2 -0
  99. package/src/skia/types/index.ts +1 -0
  100. package/src/skia/web/JsiSkia.ts +3 -0
  101. package/src/sksg/Container.ts +63 -4
  102. package/src/sksg/Recorder/DrawingContext.ts +1 -0
  103. package/src/sksg/Recorder/ReanimatedRecorder.ts +271 -0
  104. package/src/sksg/Recorder/Recorder.ts +2 -2
  105. 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