@shopify/react-native-skia 1.10.2 → 1.11.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (104) 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 +1213 -0
  5. package/cpp/api/recorder/DataTypes.h +232 -0
  6. package/cpp/api/recorder/DrawingCtx.h +187 -0
  7. package/cpp/api/recorder/Drawings.h +950 -0
  8. package/cpp/api/recorder/ImageFilters.h +292 -0
  9. package/cpp/api/recorder/ImageFit.h +108 -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 +637 -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/jestSetup.js +8 -0
  18. package/jestSetup.mjs +8 -0
  19. package/lib/commonjs/renderer/components/image/Image.d.ts +1 -1
  20. package/lib/commonjs/renderer/components/image/Image.js +8 -2
  21. package/lib/commonjs/renderer/components/image/Image.js.map +1 -1
  22. package/lib/commonjs/skia/types/Recorder.d.ts +52 -0
  23. package/lib/commonjs/skia/types/Recorder.js +6 -0
  24. package/lib/commonjs/skia/types/Recorder.js.map +1 -0
  25. package/lib/commonjs/skia/types/Skia.d.ts +2 -0
  26. package/lib/commonjs/skia/types/Skia.js.map +1 -1
  27. package/lib/commonjs/skia/types/index.d.ts +1 -0
  28. package/lib/commonjs/skia/types/index.js +11 -0
  29. package/lib/commonjs/skia/types/index.js.map +1 -1
  30. package/lib/commonjs/skia/web/JsiSkia.js +3 -0
  31. package/lib/commonjs/skia/web/JsiSkia.js.map +1 -1
  32. package/lib/commonjs/sksg/Container.d.ts +6 -1
  33. package/lib/commonjs/sksg/Container.js +59 -2
  34. package/lib/commonjs/sksg/Container.js.map +1 -1
  35. package/lib/commonjs/sksg/Recorder/DrawingContext.js +1 -0
  36. package/lib/commonjs/sksg/Recorder/DrawingContext.js.map +1 -1
  37. package/lib/commonjs/sksg/Recorder/ReanimatedRecorder.d.ts +53 -0
  38. package/lib/commonjs/sksg/Recorder/ReanimatedRecorder.js +189 -0
  39. package/lib/commonjs/sksg/Recorder/ReanimatedRecorder.js.map +1 -0
  40. package/lib/commonjs/sksg/Recorder/Recorder.d.ts +2 -2
  41. package/lib/commonjs/sksg/Recorder/Recorder.js.map +1 -1
  42. package/lib/commonjs/sksg/Recorder/Visitor.d.ts +2 -2
  43. package/lib/commonjs/sksg/Recorder/Visitor.js +2 -2
  44. package/lib/commonjs/sksg/Recorder/Visitor.js.map +1 -1
  45. package/lib/module/renderer/components/image/Image.d.ts +1 -1
  46. package/lib/module/renderer/components/image/Image.js +8 -2
  47. package/lib/module/renderer/components/image/Image.js.map +1 -1
  48. package/lib/module/skia/types/Recorder.d.ts +52 -0
  49. package/lib/module/skia/types/Recorder.js +2 -0
  50. package/lib/module/skia/types/Recorder.js.map +1 -0
  51. package/lib/module/skia/types/Skia.d.ts +2 -0
  52. package/lib/module/skia/types/Skia.js.map +1 -1
  53. package/lib/module/skia/types/index.d.ts +1 -0
  54. package/lib/module/skia/types/index.js +1 -0
  55. package/lib/module/skia/types/index.js.map +1 -1
  56. package/lib/module/skia/web/JsiSkia.js +3 -0
  57. package/lib/module/skia/web/JsiSkia.js.map +1 -1
  58. package/lib/module/sksg/Container.d.ts +6 -1
  59. package/lib/module/sksg/Container.js +59 -2
  60. package/lib/module/sksg/Container.js.map +1 -1
  61. package/lib/module/sksg/Recorder/DrawingContext.js +1 -0
  62. package/lib/module/sksg/Recorder/DrawingContext.js.map +1 -1
  63. package/lib/module/sksg/Recorder/ReanimatedRecorder.d.ts +53 -0
  64. package/lib/module/sksg/Recorder/ReanimatedRecorder.js +182 -0
  65. package/lib/module/sksg/Recorder/ReanimatedRecorder.js.map +1 -0
  66. package/lib/module/sksg/Recorder/Recorder.d.ts +2 -2
  67. package/lib/module/sksg/Recorder/Recorder.js.map +1 -1
  68. package/lib/module/sksg/Recorder/Visitor.d.ts +2 -2
  69. package/lib/module/sksg/Recorder/Visitor.js +2 -2
  70. package/lib/module/sksg/Recorder/Visitor.js.map +1 -1
  71. package/lib/typescript/lib/commonjs/renderer/components/image/Image.d.ts +4 -1
  72. package/lib/typescript/lib/commonjs/skia/types/Recorder.d.ts +1 -0
  73. package/lib/typescript/lib/commonjs/skia/web/JsiSkia.d.ts +1 -0
  74. package/lib/typescript/lib/commonjs/sksg/Container.d.ts +5 -1
  75. package/lib/typescript/lib/commonjs/sksg/Reconciler.d.ts +6 -0
  76. package/lib/typescript/lib/commonjs/sksg/Recorder/ReanimatedRecorder.d.ts +47 -0
  77. package/lib/typescript/lib/module/mock/index.d.ts +4 -1
  78. package/lib/typescript/lib/module/renderer/components/image/Image.d.ts +4 -1
  79. package/lib/typescript/lib/module/skia/Skia.web.d.ts +1 -0
  80. package/lib/typescript/lib/module/skia/types/Recorder.d.ts +1 -0
  81. package/lib/typescript/lib/module/skia/types/index.d.ts +1 -0
  82. package/lib/typescript/lib/module/skia/web/JsiSkia.d.ts +1 -0
  83. package/lib/typescript/lib/module/sksg/Container.d.ts +5 -1
  84. package/lib/typescript/lib/module/sksg/Reconciler.d.ts +6 -0
  85. package/lib/typescript/lib/module/sksg/Recorder/ReanimatedRecorder.d.ts +46 -0
  86. package/lib/typescript/src/renderer/components/image/Image.d.ts +1 -1
  87. package/lib/typescript/src/skia/types/Recorder.d.ts +52 -0
  88. package/lib/typescript/src/skia/types/Skia.d.ts +2 -0
  89. package/lib/typescript/src/skia/types/index.d.ts +1 -0
  90. package/lib/typescript/src/sksg/Container.d.ts +6 -1
  91. package/lib/typescript/src/sksg/Recorder/ReanimatedRecorder.d.ts +53 -0
  92. package/lib/typescript/src/sksg/Recorder/Recorder.d.ts +2 -2
  93. package/lib/typescript/src/sksg/Recorder/Visitor.d.ts +2 -2
  94. package/package.json +3 -2
  95. package/src/renderer/components/image/Image.tsx +2 -2
  96. package/src/skia/types/Recorder.ts +91 -0
  97. package/src/skia/types/Skia.ts +2 -0
  98. package/src/skia/types/index.ts +1 -0
  99. package/src/skia/web/JsiSkia.ts +3 -0
  100. package/src/sksg/Container.ts +63 -4
  101. package/src/sksg/Recorder/DrawingContext.ts +1 -0
  102. package/src/sksg/Recorder/ReanimatedRecorder.ts +271 -0
  103. package/src/sksg/Recorder/Recorder.ts +2 -2
  104. package/src/sksg/Recorder/Visitor.ts +17 -12
@@ -0,0 +1,232 @@
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(const sk_sp<SkRuntimeEffect> &effect,
152
+ const Uniforms &uniforms) {
153
+
154
+ size_t uniformSize = effect->uniformSize();
155
+ auto uniformsData = SkData::MakeUninitialized(uniformSize);
156
+ auto uniformDataPtr = static_cast<float *>(uniformsData->writable_data());
157
+
158
+ const auto &sourceUniforms = effect->uniforms();
159
+ for (const auto &uniform : sourceUniforms) {
160
+ auto it = uniforms.find(std::string(uniform.name));
161
+ if (it == uniforms.end()) {
162
+ throw std::runtime_error("Missing uniform value for: " +
163
+ std::string(uniform.name));
164
+ }
165
+
166
+ const auto &uniformValues = it->second;
167
+ RuntimeEffectUniform reu = JsiSkRuntimeEffect::fromUniform(uniform);
168
+ size_t expectedSize = reu.columns * reu.rows;
169
+
170
+ if (uniformValues.size() != expectedSize) {
171
+ throw std::runtime_error(
172
+ "Incorrect uniform size for: " + std::string(uniform.name) +
173
+ ". Expected " + std::to_string(expectedSize) + " got " +
174
+ std::to_string(uniformValues.size()));
175
+ }
176
+
177
+ // Process each element in the uniform
178
+ for (std::size_t j = 0; j < expectedSize; ++j) {
179
+ const std::size_t offset = reu.slot + j;
180
+ float fValue = uniformValues[j];
181
+
182
+ if (reu.isInteger) {
183
+ int iValue = static_cast<int>(fValue);
184
+ uniformDataPtr[offset] = SkBits2Float(iValue);
185
+ } else {
186
+ uniformDataPtr[offset] = fValue;
187
+ }
188
+ }
189
+ }
190
+
191
+ return uniformsData;
192
+ }
193
+
194
+ inline void processUniforms(SkRuntimeShaderBuilder &builder,
195
+ const sk_sp<SkRuntimeEffect> &effect,
196
+ const Uniforms &uniforms) {
197
+
198
+ const auto &sourceUniforms = effect->uniforms();
199
+ for (const auto &uniform : sourceUniforms) {
200
+ auto it = uniforms.find(std::string(uniform.name));
201
+ if (it == uniforms.end()) {
202
+ throw std::runtime_error("Missing uniform value for: " +
203
+ std::string(uniform.name));
204
+ }
205
+
206
+ const auto &uniformValues = it->second;
207
+ RuntimeEffectUniform reu = JsiSkRuntimeEffect::fromUniform(uniform);
208
+ size_t expectedSize = reu.columns * reu.rows;
209
+
210
+ if (uniformValues.size() != expectedSize) {
211
+ throw std::runtime_error(
212
+ "Incorrect uniform size for: " + std::string(uniform.name) +
213
+ ". Expected " + std::to_string(expectedSize) + " got " +
214
+ std::to_string(uniformValues.size()));
215
+ }
216
+
217
+ auto builderUniform = builder.uniform(uniform.name);
218
+
219
+ if (reu.isInteger) {
220
+ std::vector<float> convertedValues(uniformValues.size());
221
+ for (size_t i = 0; i < uniformValues.size(); ++i) {
222
+ int iValue = static_cast<int>(uniformValues[i]);
223
+ convertedValues[i] = SkBits2Float(iValue);
224
+ }
225
+ builderUniform.set(convertedValues.data(), convertedValues.size());
226
+ } else {
227
+ builderUniform.set(uniformValues.data(), uniformValues.size());
228
+ }
229
+ }
230
+ }
231
+
232
+ } // 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