@shopify/react-native-skia 2.2.6 → 2.2.8

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 (93) hide show
  1. package/cpp/api/recorder/DrawingCtx.h +15 -0
  2. package/cpp/api/recorder/JsiRecorder.h +2 -1
  3. package/cpp/api/recorder/RNRecorder.h +2 -2
  4. package/cpp/api/recorder/Shaders.h +6 -5
  5. package/lib/commonjs/skia/types/Recorder.d.ts +1 -1
  6. package/lib/commonjs/skia/types/Recorder.js.map +1 -1
  7. package/lib/commonjs/sksg/Container.d.ts +3 -39
  8. package/lib/commonjs/sksg/Container.js +3 -195
  9. package/lib/commonjs/sksg/Container.js.map +1 -1
  10. package/lib/commonjs/sksg/Container.native.d.ts +14 -0
  11. package/lib/commonjs/sksg/Container.native.js +81 -0
  12. package/lib/commonjs/sksg/Container.native.js.map +1 -0
  13. package/lib/commonjs/sksg/Container.web.d.ts +14 -0
  14. package/lib/commonjs/sksg/Container.web.js +91 -0
  15. package/lib/commonjs/sksg/Container.web.js.map +1 -0
  16. package/lib/commonjs/sksg/HostConfig.d.ts +1 -1
  17. package/lib/commonjs/sksg/HostConfig.js.map +1 -1
  18. package/lib/commonjs/sksg/Reconciler.js.map +1 -1
  19. package/lib/commonjs/sksg/Recorder/ReanimatedRecorder.d.ts +1 -1
  20. package/lib/commonjs/sksg/Recorder/ReanimatedRecorder.js +2 -2
  21. package/lib/commonjs/sksg/Recorder/ReanimatedRecorder.js.map +1 -1
  22. package/lib/commonjs/sksg/Recorder/Recorder.d.ts +1 -1
  23. package/lib/commonjs/sksg/Recorder/Recorder.js +3 -2
  24. package/lib/commonjs/sksg/Recorder/Recorder.js.map +1 -1
  25. package/lib/commonjs/sksg/Recorder/Visitor.js +2 -2
  26. package/lib/commonjs/sksg/Recorder/Visitor.js.map +1 -1
  27. package/lib/commonjs/sksg/Recorder/commands/Shaders.js +3 -3
  28. package/lib/commonjs/sksg/Recorder/commands/Shaders.js.map +1 -1
  29. package/lib/commonjs/sksg/StaticContainer.d.ts +24 -0
  30. package/lib/commonjs/sksg/StaticContainer.js +70 -0
  31. package/lib/commonjs/sksg/StaticContainer.js.map +1 -0
  32. package/lib/module/skia/types/Recorder.d.ts +1 -1
  33. package/lib/module/skia/types/Recorder.js.map +1 -1
  34. package/lib/module/sksg/Container.d.ts +3 -39
  35. package/lib/module/sksg/Container.js +2 -192
  36. package/lib/module/sksg/Container.js.map +1 -1
  37. package/lib/module/sksg/Container.native.d.ts +14 -0
  38. package/lib/module/sksg/Container.native.js +73 -0
  39. package/lib/module/sksg/Container.native.js.map +1 -0
  40. package/lib/module/sksg/Container.web.d.ts +14 -0
  41. package/lib/module/sksg/Container.web.js +83 -0
  42. package/lib/module/sksg/Container.web.js.map +1 -0
  43. package/lib/module/sksg/HostConfig.d.ts +1 -1
  44. package/lib/module/sksg/HostConfig.js.map +1 -1
  45. package/lib/module/sksg/Reconciler.js.map +1 -1
  46. package/lib/module/sksg/Recorder/ReanimatedRecorder.d.ts +1 -1
  47. package/lib/module/sksg/Recorder/ReanimatedRecorder.js +2 -2
  48. package/lib/module/sksg/Recorder/ReanimatedRecorder.js.map +1 -1
  49. package/lib/module/sksg/Recorder/Recorder.d.ts +1 -1
  50. package/lib/module/sksg/Recorder/Recorder.js +3 -2
  51. package/lib/module/sksg/Recorder/Recorder.js.map +1 -1
  52. package/lib/module/sksg/Recorder/Visitor.js +2 -2
  53. package/lib/module/sksg/Recorder/Visitor.js.map +1 -1
  54. package/lib/module/sksg/Recorder/commands/Shaders.js +3 -3
  55. package/lib/module/sksg/Recorder/commands/Shaders.js.map +1 -1
  56. package/lib/module/sksg/StaticContainer.d.ts +24 -0
  57. package/lib/module/sksg/StaticContainer.js +62 -0
  58. package/lib/module/sksg/StaticContainer.js.map +1 -0
  59. package/lib/typescript/lib/commonjs/sksg/Container.d.ts +2 -42
  60. package/lib/typescript/lib/commonjs/sksg/Container.native.d.ts +11 -0
  61. package/lib/typescript/lib/commonjs/sksg/Container.web.d.ts +15 -0
  62. package/lib/typescript/lib/commonjs/sksg/Reconciler.d.ts +1 -45
  63. package/lib/typescript/lib/commonjs/sksg/Recorder/ReanimatedRecorder.d.ts +1 -1
  64. package/lib/typescript/lib/commonjs/sksg/Recorder/Recorder.d.ts +1 -1
  65. package/lib/typescript/lib/commonjs/sksg/StaticContainer.d.ts +23 -0
  66. package/lib/typescript/lib/module/sksg/Container.d.ts +2 -42
  67. package/lib/typescript/lib/module/sksg/Container.native.d.ts +11 -0
  68. package/lib/typescript/lib/module/sksg/Container.web.d.ts +15 -0
  69. package/lib/typescript/lib/module/sksg/Reconciler.d.ts +1 -45
  70. package/lib/typescript/lib/module/sksg/Recorder/ReanimatedRecorder.d.ts +1 -1
  71. package/lib/typescript/lib/module/sksg/Recorder/Recorder.d.ts +1 -1
  72. package/lib/typescript/lib/module/sksg/StaticContainer.d.ts +22 -0
  73. package/lib/typescript/src/skia/types/Recorder.d.ts +1 -1
  74. package/lib/typescript/src/sksg/Container.d.ts +3 -39
  75. package/lib/typescript/src/sksg/Container.native.d.ts +14 -0
  76. package/lib/typescript/src/sksg/Container.web.d.ts +14 -0
  77. package/lib/typescript/src/sksg/HostConfig.d.ts +1 -1
  78. package/lib/typescript/src/sksg/Recorder/ReanimatedRecorder.d.ts +1 -1
  79. package/lib/typescript/src/sksg/Recorder/Recorder.d.ts +1 -1
  80. package/lib/typescript/src/sksg/StaticContainer.d.ts +24 -0
  81. package/package.json +1 -1
  82. package/src/renderer/__tests__/e2e/Shader.spec.tsx +93 -0
  83. package/src/skia/types/Recorder.ts +5 -1
  84. package/src/sksg/Container.native.ts +86 -0
  85. package/src/sksg/Container.ts +3 -223
  86. package/src/sksg/Container.web.ts +95 -0
  87. package/src/sksg/HostConfig.ts +1 -1
  88. package/src/sksg/Reconciler.ts +1 -1
  89. package/src/sksg/Recorder/ReanimatedRecorder.ts +6 -2
  90. package/src/sksg/Recorder/Recorder.ts +6 -2
  91. package/src/sksg/Recorder/Visitor.ts +2 -2
  92. package/src/sksg/Recorder/commands/Shaders.ts +8 -3
  93. package/src/sksg/StaticContainer.ts +83 -0
@@ -48,7 +48,11 @@ export interface BaseRecorder {
48
48
  colorFilterType: NodeType,
49
49
  props: AnimatedProps<unknown>
50
50
  ): void;
51
- pushShader(shaderType: NodeType, props: AnimatedProps<unknown>): void;
51
+ pushShader(
52
+ shaderType: NodeType,
53
+ props: AnimatedProps<unknown>,
54
+ children: number
55
+ ): void;
52
56
  pushBlurMaskFilter(props: AnimatedProps<BlurMaskFilterProps>): void;
53
57
  composePathEffect(): void;
54
58
  composeColorFilter(): void;
@@ -0,0 +1,86 @@
1
+ import type { SharedValue } from "react-native-reanimated";
2
+
3
+ import Rea from "../external/reanimated/ReanimatedProxy";
4
+ import type { Skia, SkSize } from "../skia/types";
5
+ import { HAS_REANIMATED_3 } from "../external/reanimated/renderHelpers";
6
+ import type { JsiRecorder } from "../skia/types/Recorder";
7
+
8
+ import { ReanimatedRecorder } from "./Recorder/ReanimatedRecorder";
9
+ import { Container, StaticContainer } from "./StaticContainer";
10
+ import { visit } from "./Recorder/Visitor";
11
+
12
+ import "../skia/NativeSetup";
13
+ import "../views/api";
14
+
15
+ const nativeDrawOnscreen = (
16
+ nativeId: number,
17
+ recorder: JsiRecorder,
18
+ onSize?: SharedValue<SkSize>
19
+ ) => {
20
+ "worklet";
21
+
22
+ //const start = performance.now();
23
+ if (onSize) {
24
+ const size = SkiaViewApi.size(nativeId);
25
+ if (
26
+ size.width !== onSize.value.width ||
27
+ size.height !== onSize.value.height
28
+ ) {
29
+ onSize.value = size;
30
+ }
31
+ }
32
+ const picture = recorder.play();
33
+ //const end = performance.now();
34
+ //console.log("Recording time: ", end - start);
35
+ SkiaViewApi.setJsiProperty(nativeId, "picture", picture);
36
+ };
37
+
38
+ class NativeReanimatedContainer extends Container {
39
+ private mapperId: number | null = null;
40
+
41
+ constructor(
42
+ Skia: Skia,
43
+ private nativeId: number,
44
+ private onSize?: SharedValue<SkSize>
45
+ ) {
46
+ super(Skia);
47
+ }
48
+
49
+ redraw() {
50
+ if (this.mapperId !== null) {
51
+ Rea.stopMapper(this.mapperId);
52
+ }
53
+ if (this.unmounted) {
54
+ return;
55
+ }
56
+ const { nativeId, Skia } = this;
57
+ const recorder = new ReanimatedRecorder(Skia);
58
+ visit(recorder, this.root);
59
+ const sharedValues = recorder.getSharedValues();
60
+ const sharedRecorder = recorder.getRecorder();
61
+ Rea.runOnUI((onSize?: SharedValue<SkSize>) => {
62
+ "worklet";
63
+ nativeDrawOnscreen(nativeId, sharedRecorder, onSize);
64
+ })(this.onSize);
65
+ if (sharedValues.length > 0) {
66
+ const { onSize } = this;
67
+ this.mapperId = Rea.startMapper(() => {
68
+ "worklet";
69
+ sharedRecorder.applyUpdates(sharedValues);
70
+ nativeDrawOnscreen(nativeId, sharedRecorder, onSize);
71
+ }, sharedValues);
72
+ }
73
+ }
74
+ }
75
+
76
+ export const createContainer = (
77
+ Skia: Skia,
78
+ nativeId: number,
79
+ onSize?: SharedValue<SkSize>
80
+ ) => {
81
+ if (HAS_REANIMATED_3 && nativeId !== -1) {
82
+ return new NativeReanimatedContainer(Skia, nativeId, onSize);
83
+ } else {
84
+ return new StaticContainer(Skia, nativeId);
85
+ }
86
+ };
@@ -1,233 +1,13 @@
1
1
  import type { SharedValue } from "react-native-reanimated";
2
2
 
3
- import Rea from "../external/reanimated/ReanimatedProxy";
4
- import type { Skia, SkCanvas, SkSize } from "../skia/types";
5
- import { HAS_REANIMATED_3 } from "../external/reanimated/renderHelpers";
6
- import type { JsiRecorder } from "../skia/types/Recorder";
3
+ import type { Skia, SkSize } from "../skia/types";
7
4
 
8
- import type { Node } from "./Node";
9
- import type { Recording } from "./Recorder/Recorder";
10
- import { Recorder } from "./Recorder/Recorder";
11
- import { visit } from "./Recorder/Visitor";
12
- import { replay } from "./Recorder/Player";
13
- import { createDrawingContext } from "./Recorder/DrawingContext";
14
- import { ReanimatedRecorder } from "./Recorder/ReanimatedRecorder";
15
-
16
- import "../views/api";
17
-
18
- const drawOnscreen = (
19
- Skia: Skia,
20
- nativeId: number,
21
- recording: Recording,
22
- onSize?: SharedValue<SkSize>
23
- ) => {
24
- "worklet";
25
- if (onSize) {
26
- const size = SkiaViewApi.size(nativeId);
27
- if (
28
- size.width !== onSize.value.width ||
29
- size.height !== onSize.value.height
30
- ) {
31
- onSize.value = size;
32
- }
33
- }
34
- const rec = Skia.PictureRecorder();
35
- const canvas = rec.beginRecording();
36
- //const start = performance.now();
37
-
38
- const ctx = createDrawingContext(Skia, recording.paintPool, canvas);
39
- replay(ctx, recording.commands);
40
- const picture = rec.finishRecordingAsPicture();
41
- //const end = performance.now();
42
- //console.log("Recording time: ", end - start);
43
- SkiaViewApi.setJsiProperty(nativeId, "picture", picture);
44
- };
45
-
46
- const nativeDrawOnscreen = (
47
- nativeId: number,
48
- recorder: JsiRecorder,
49
- onSize?: SharedValue<SkSize>
50
- ) => {
51
- "worklet";
52
-
53
- //const start = performance.now();
54
- if (onSize) {
55
- const size = SkiaViewApi.size(nativeId);
56
- if (
57
- size.width !== onSize.value.width ||
58
- size.height !== onSize.value.height
59
- ) {
60
- onSize.value = size;
61
- }
62
- }
63
- const picture = recorder.play();
64
- //const end = performance.now();
65
- //console.log("Recording time: ", end - start);
66
- SkiaViewApi.setJsiProperty(nativeId, "picture", picture);
67
- };
68
-
69
- export abstract class Container {
70
- private _root: Node[] = [];
71
- protected recording: Recording | null = null;
72
- protected unmounted = false;
73
-
74
- constructor(protected Skia: Skia) {}
75
-
76
- get root() {
77
- return this._root;
78
- }
79
-
80
- set root(value: Node[]) {
81
- this._root = value;
82
- }
83
-
84
- mount() {
85
- this.unmounted = false;
86
- }
87
-
88
- unmount() {
89
- this.unmounted = true;
90
- }
91
-
92
- drawOnCanvas(canvas: SkCanvas) {
93
- if (!this.recording) {
94
- throw new Error("No recording to draw");
95
- }
96
- const ctx = createDrawingContext(
97
- this.Skia,
98
- this.recording.paintPool,
99
- canvas
100
- );
101
- replay(ctx, this.recording.commands);
102
- }
103
-
104
- abstract redraw(): void;
105
- }
106
-
107
- class StaticContainer extends Container {
108
- constructor(
109
- Skia: Skia,
110
- private nativeId: number,
111
- private onSize?: SharedValue<SkSize>
112
- ) {
113
- super(Skia);
114
- }
115
-
116
- redraw() {
117
- const recorder = new Recorder();
118
- visit(recorder, this.root);
119
- this.recording = recorder.getRecording();
120
- const isOnScreen = this.nativeId !== -1;
121
- if (isOnScreen) {
122
- if (this.onSize) {
123
- const size = SkiaViewApi.size(this.nativeId);
124
- if (
125
- size.width !== this.onSize.value.width ||
126
- size.height !== this.onSize.value.height
127
- ) {
128
- this.onSize.value = size;
129
- }
130
- }
131
- const rec = this.Skia.PictureRecorder();
132
- const canvas = rec.beginRecording();
133
- this.drawOnCanvas(canvas);
134
- const picture = rec.finishRecordingAsPicture();
135
- SkiaViewApi.setJsiProperty(this.nativeId, "picture", picture);
136
- }
137
- }
138
- }
139
-
140
- class ReanimatedContainer extends Container {
141
- private mapperId: number | null = null;
142
-
143
- constructor(
144
- Skia: Skia,
145
- private nativeId: number,
146
- private onSize?: SharedValue<SkSize>
147
- ) {
148
- super(Skia);
149
- }
150
-
151
- redraw() {
152
- if (this.mapperId !== null) {
153
- Rea.stopMapper(this.mapperId);
154
- }
155
- if (this.unmounted) {
156
- return;
157
- }
158
- const recorder = new Recorder();
159
- visit(recorder, this.root);
160
- const record = recorder.getRecording();
161
- const { animationValues } = record;
162
- this.recording = {
163
- commands: record.commands,
164
- paintPool: record.paintPool,
165
- };
166
- const { nativeId, Skia, recording } = this;
167
- if (animationValues.size > 0) {
168
- this.mapperId = Rea.startMapper(() => {
169
- "worklet";
170
- drawOnscreen(Skia, nativeId, recording!);
171
- }, Array.from(animationValues));
172
- }
173
- Rea.runOnUI((onSize?: SharedValue<SkSize>) => {
174
- "worklet";
175
- drawOnscreen(Skia, nativeId, recording!, onSize);
176
- })(this.onSize);
177
- }
178
- }
179
-
180
- class NativeReanimatedContainer extends Container {
181
- private mapperId: number | null = null;
182
-
183
- constructor(
184
- Skia: Skia,
185
- private nativeId: number,
186
- private onSize?: SharedValue<SkSize>
187
- ) {
188
- super(Skia);
189
- }
190
-
191
- redraw() {
192
- if (this.mapperId !== null) {
193
- Rea.stopMapper(this.mapperId);
194
- }
195
- if (this.unmounted) {
196
- return;
197
- }
198
- const { nativeId, Skia } = this;
199
- const recorder = new ReanimatedRecorder(Skia);
200
- visit(recorder, this.root);
201
- const sharedValues = recorder.getSharedValues();
202
- const sharedRecorder = recorder.getRecorder();
203
- Rea.runOnUI((onSize?: SharedValue<SkSize>) => {
204
- "worklet";
205
- nativeDrawOnscreen(nativeId, sharedRecorder, onSize);
206
- })(this.onSize);
207
- if (sharedValues.length > 0) {
208
- const { onSize } = this;
209
- this.mapperId = Rea.startMapper(() => {
210
- "worklet";
211
- sharedRecorder.applyUpdates(sharedValues);
212
- nativeDrawOnscreen(nativeId, sharedRecorder, onSize);
213
- }, sharedValues);
214
- }
215
- }
216
- }
5
+ import { StaticContainer } from "./StaticContainer";
217
6
 
218
7
  export const createContainer = (
219
8
  Skia: Skia,
220
9
  nativeId: number,
221
10
  onSize?: SharedValue<SkSize>
222
11
  ) => {
223
- const web = global.SkiaViewApi && global.SkiaViewApi.web;
224
- if (HAS_REANIMATED_3 && nativeId !== -1) {
225
- if (!web) {
226
- return new NativeReanimatedContainer(Skia, nativeId, onSize);
227
- } else {
228
- return new ReanimatedContainer(Skia, nativeId, onSize);
229
- }
230
- } else {
231
- return new StaticContainer(Skia, nativeId);
232
- }
12
+ return new StaticContainer(Skia, nativeId, onSize);
233
13
  };
@@ -0,0 +1,95 @@
1
+ import type { SharedValue } from "react-native-reanimated";
2
+
3
+ import Rea from "../external/reanimated/ReanimatedProxy";
4
+ import type { Skia, SkSize } from "../skia/types";
5
+ import { HAS_REANIMATED_3 } from "../external/reanimated/renderHelpers";
6
+
7
+ import type { Recording } from "./Recorder/Recorder";
8
+ import { Recorder } from "./Recorder/Recorder";
9
+ import { visit } from "./Recorder/Visitor";
10
+ import { replay } from "./Recorder/Player";
11
+ import { createDrawingContext } from "./Recorder/DrawingContext";
12
+ import { Container, StaticContainer } from "./StaticContainer";
13
+
14
+ import "../skia/NativeSetup";
15
+ import "../views/api";
16
+
17
+ const drawOnscreen = (
18
+ Skia: Skia,
19
+ nativeId: number,
20
+ recording: Recording,
21
+ onSize?: SharedValue<SkSize>
22
+ ) => {
23
+ "worklet";
24
+ if (onSize) {
25
+ const size = SkiaViewApi.size(nativeId);
26
+ if (
27
+ size.width !== onSize.value.width ||
28
+ size.height !== onSize.value.height
29
+ ) {
30
+ onSize.value = size;
31
+ }
32
+ }
33
+ const rec = Skia.PictureRecorder();
34
+ const canvas = rec.beginRecording();
35
+ //const start = performance.now();
36
+
37
+ const ctx = createDrawingContext(Skia, recording.paintPool, canvas);
38
+ replay(ctx, recording.commands);
39
+ const picture = rec.finishRecordingAsPicture();
40
+ //const end = performance.now();
41
+ //console.log("Recording time: ", end - start);
42
+ SkiaViewApi.setJsiProperty(nativeId, "picture", picture);
43
+ };
44
+
45
+ class ReanimatedContainer extends Container {
46
+ private mapperId: number | null = null;
47
+
48
+ constructor(
49
+ Skia: Skia,
50
+ private nativeId: number,
51
+ private onSize?: SharedValue<SkSize>
52
+ ) {
53
+ super(Skia);
54
+ }
55
+
56
+ redraw() {
57
+ if (this.mapperId !== null) {
58
+ Rea.stopMapper(this.mapperId);
59
+ }
60
+ if (this.unmounted) {
61
+ return;
62
+ }
63
+ const recorder = new Recorder();
64
+ visit(recorder, this.root);
65
+ const record = recorder.getRecording();
66
+ const { animationValues } = record;
67
+ this.recording = {
68
+ commands: record.commands,
69
+ paintPool: record.paintPool,
70
+ };
71
+ const { nativeId, Skia, recording } = this;
72
+ if (animationValues.size > 0) {
73
+ this.mapperId = Rea.startMapper(() => {
74
+ "worklet";
75
+ drawOnscreen(Skia, nativeId, recording!);
76
+ }, Array.from(animationValues));
77
+ }
78
+ Rea.runOnUI((onSize?: SharedValue<SkSize>) => {
79
+ "worklet";
80
+ drawOnscreen(Skia, nativeId, recording!, onSize);
81
+ })(this.onSize);
82
+ }
83
+ }
84
+
85
+ export const createContainer = (
86
+ Skia: Skia,
87
+ nativeId: number,
88
+ onSize?: SharedValue<SkSize>
89
+ ) => {
90
+ if (HAS_REANIMATED_3 && nativeId !== -1) {
91
+ return new ReanimatedContainer(Skia, nativeId, onSize);
92
+ } else {
93
+ return new StaticContainer(Skia, nativeId);
94
+ }
95
+ };
@@ -7,7 +7,7 @@ import type { NodeType } from "../dom/types";
7
7
  import { shallowEq } from "../renderer/typeddash";
8
8
 
9
9
  import type { Node } from "./Node";
10
- import type { Container } from "./Container";
10
+ import type { Container } from "./StaticContainer";
11
11
 
12
12
  type EventPriority = number;
13
13
  const NoEventPriority = 0;
@@ -7,7 +7,7 @@ import type { SkCanvas, Skia, SkSize } from "../skia/types";
7
7
  import { NodeType } from "../dom/types";
8
8
 
9
9
  import { debug, sksgHostConfig } from "./HostConfig";
10
- import type { Container } from "./Container";
10
+ import type { Container } from "./StaticContainer";
11
11
  import { createContainer } from "./Container";
12
12
 
13
13
  import "./Elements";
@@ -111,9 +111,13 @@ export class ReanimatedRecorder implements BaseRecorder {
111
111
  this.recorder.pushColorFilter(colorFilterType, props);
112
112
  }
113
113
 
114
- pushShader(shaderType: NodeType, props: AnimatedProps<unknown>): void {
114
+ pushShader(
115
+ shaderType: NodeType,
116
+ props: AnimatedProps<unknown>,
117
+ children: number
118
+ ): void {
115
119
  this.processAnimationValues(props);
116
- this.recorder.pushShader(shaderType, props);
120
+ this.recorder.pushShader(shaderType, props, children);
117
121
  }
118
122
 
119
123
  pushBlurMaskFilter(props: AnimatedProps<BlurMaskFilterProps>): void {
@@ -152,11 +152,15 @@ export class Recorder implements BaseRecorder {
152
152
  });
153
153
  }
154
154
 
155
- pushShader(shaderType: NodeType, props: AnimatedProps<unknown>) {
155
+ pushShader(
156
+ shaderType: NodeType,
157
+ props: AnimatedProps<unknown>,
158
+ children: number
159
+ ) {
156
160
  if (!isShader(shaderType) && !(shaderType === NodeType.Blend)) {
157
161
  throw new Error("Invalid color filter type: " + shaderType);
158
162
  }
159
- this.add({ type: CommandType.PushShader, shaderType, props });
163
+ this.add({ type: CommandType.PushShader, shaderType, props, children });
160
164
  }
161
165
 
162
166
  pushBlurMaskFilter(props: AnimatedProps<BlurMaskFilterProps>) {
@@ -160,7 +160,7 @@ const pushImageFilters = (
160
160
  if (isImageFilter(imageFilter.type)) {
161
161
  recorder.pushImageFilter(imageFilter.type, imageFilter.props);
162
162
  } else if (isShader(imageFilter.type)) {
163
- recorder.pushShader(imageFilter.type, imageFilter.props);
163
+ recorder.pushShader(imageFilter.type, imageFilter.props, 0);
164
164
  }
165
165
  const needsComposition =
166
166
  imageFilter.type !== NodeType.BlendImageFilter &&
@@ -176,7 +176,7 @@ const pushShaders = (recorder: BaseRecorder, shaders: Node<any>[]) => {
176
176
  if (shader.children.length > 0) {
177
177
  pushShaders(recorder, shader.children);
178
178
  }
179
- recorder.pushShader(shader.type, shader.props);
179
+ recorder.pushShader(shader.type, shader.props, shader.children.length);
180
180
  });
181
181
  };
182
182
 
@@ -34,14 +34,18 @@ import type { Command } from "../Core";
34
34
  import { CommandType } from "../Core";
35
35
  import type { DrawingContext } from "../DrawingContext";
36
36
 
37
- const declareShader = (ctx: DrawingContext, props: ShaderProps) => {
37
+ const declareShader = (
38
+ ctx: DrawingContext,
39
+ props: ShaderProps,
40
+ children: number
41
+ ) => {
38
42
  "worklet";
39
43
  const { source, uniforms, ...transform } = props;
40
44
  const m3 = ctx.Skia.Matrix();
41
45
  processTransformProps(m3, transform);
42
46
  const shader = source.makeShaderWithChildren(
43
47
  processUniforms(source, uniforms),
44
- ctx.shaders.splice(0, ctx.shaders.length),
48
+ ctx.shaders.splice(0, children),
45
49
  m3
46
50
  );
47
51
  ctx.shaders.push(shader);
@@ -257,6 +261,7 @@ interface PushShader<T extends keyof Props>
257
261
  extends Command<CommandType.PushShader> {
258
262
  shaderType: T;
259
263
  props: Props[T];
264
+ children: number;
260
265
  }
261
266
 
262
267
  const isShader = <T extends keyof Props>(
@@ -273,7 +278,7 @@ export const pushShader = (
273
278
  ) => {
274
279
  "worklet";
275
280
  if (isShader(command, NodeType.Shader)) {
276
- declareShader(ctx, command.props);
281
+ declareShader(ctx, command.props, command.children);
277
282
  } else if (isShader(command, NodeType.ImageShader)) {
278
283
  declareImageShader(ctx, command.props);
279
284
  } else if (isShader(command, NodeType.ColorShader)) {
@@ -0,0 +1,83 @@
1
+ import type { SharedValue } from "react-native-reanimated";
2
+
3
+ import type { Skia, SkCanvas, SkSize } from "../skia/types";
4
+
5
+ import type { Node } from "./Node";
6
+ import type { Recording } from "./Recorder/Recorder";
7
+ import { Recorder } from "./Recorder/Recorder";
8
+ import { visit } from "./Recorder/Visitor";
9
+ import { replay } from "./Recorder/Player";
10
+ import { createDrawingContext } from "./Recorder/DrawingContext";
11
+
12
+ import "../views/api";
13
+
14
+ export abstract class Container {
15
+ private _root: Node[] = [];
16
+ protected recording: Recording | null = null;
17
+ protected unmounted = false;
18
+
19
+ constructor(protected Skia: Skia) {}
20
+
21
+ get root() {
22
+ return this._root;
23
+ }
24
+
25
+ set root(value: Node[]) {
26
+ this._root = value;
27
+ }
28
+
29
+ mount() {
30
+ this.unmounted = false;
31
+ }
32
+
33
+ unmount() {
34
+ this.unmounted = true;
35
+ }
36
+
37
+ drawOnCanvas(canvas: SkCanvas) {
38
+ if (!this.recording) {
39
+ throw new Error("No recording to draw");
40
+ }
41
+ const ctx = createDrawingContext(
42
+ this.Skia,
43
+ this.recording.paintPool,
44
+ canvas
45
+ );
46
+ replay(ctx, this.recording.commands);
47
+ }
48
+
49
+ abstract redraw(): void;
50
+ }
51
+
52
+ export class StaticContainer extends Container {
53
+ constructor(
54
+ Skia: Skia,
55
+ private nativeId: number,
56
+ private onSize?: SharedValue<SkSize>
57
+ ) {
58
+ super(Skia);
59
+ }
60
+
61
+ redraw() {
62
+ const recorder = new Recorder();
63
+ visit(recorder, this.root);
64
+ this.recording = recorder.getRecording();
65
+ const isOnScreen = this.nativeId !== -1;
66
+ if (isOnScreen) {
67
+ if (this.onSize) {
68
+ const size = SkiaViewApi.size(this.nativeId);
69
+ if (
70
+ size.width !== this.onSize.value.width ||
71
+ size.height !== this.onSize.value.height
72
+ ) {
73
+ this.onSize.value = size;
74
+ }
75
+ }
76
+ const rec = this.Skia.PictureRecorder();
77
+ const canvas = rec.beginRecording();
78
+ this.drawOnCanvas(canvas);
79
+ const picture = rec.finishRecordingAsPicture();
80
+ SkiaViewApi.setJsiProperty(this.nativeId, "picture", picture);
81
+ }
82
+ }
83
+ }