@shopify/react-native-skia 1.2.2 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (91) hide show
  1. package/android/CMakeLists.txt +1 -0
  2. package/android/cpp/jni/JniPlatformContext.cpp +23 -0
  3. package/android/cpp/jni/include/JniPlatformContext.h +2 -0
  4. package/android/cpp/rnskia-android/RNSkAndroidPlatformContext.h +6 -0
  5. package/android/cpp/rnskia-android/RNSkAndroidVideo.cpp +92 -0
  6. package/android/cpp/rnskia-android/RNSkAndroidVideo.h +36 -0
  7. package/android/cpp/rnskia-android/SkiaOpenGLSurfaceFactory.cpp +34 -14
  8. package/android/cpp/rnskia-android/SkiaOpenGLSurfaceFactory.h +2 -1
  9. package/android/src/main/java/com/shopify/reactnative/skia/PlatformContext.java +5 -0
  10. package/android/src/main/java/com/shopify/reactnative/skia/RNSkVideo.java +185 -0
  11. package/cpp/api/JsiSkApi.h +2 -0
  12. package/cpp/api/JsiSkImageFactory.h +3 -3
  13. package/cpp/api/JsiVideo.h +87 -0
  14. package/cpp/rnskia/RNSkPlatformContext.h +4 -1
  15. package/cpp/rnskia/RNSkVideo.h +23 -0
  16. package/ios/RNSkia-iOS/RNSkiOSPlatformContext.h +2 -0
  17. package/ios/RNSkia-iOS/RNSkiOSPlatformContext.mm +7 -1
  18. package/ios/RNSkia-iOS/RNSkiOSVideo.h +40 -0
  19. package/ios/RNSkia-iOS/RNSkiOSVideo.mm +119 -0
  20. package/ios/RNSkia-iOS/RNSkiOSView.mm +37 -0
  21. package/ios/RNSkia-iOS/SkiaMetalSurfaceFactory.mm +2 -2
  22. package/lib/commonjs/external/reanimated/index.d.ts +1 -0
  23. package/lib/commonjs/external/reanimated/index.js +11 -0
  24. package/lib/commonjs/external/reanimated/index.js.map +1 -1
  25. package/lib/commonjs/external/reanimated/useVideo.d.ts +10 -0
  26. package/lib/commonjs/external/reanimated/useVideo.js +93 -0
  27. package/lib/commonjs/external/reanimated/useVideo.js.map +1 -0
  28. package/lib/commonjs/mock/index.js +2 -1
  29. package/lib/commonjs/mock/index.js.map +1 -1
  30. package/lib/commonjs/skia/types/Image/ImageFactory.d.ts +3 -3
  31. package/lib/commonjs/skia/types/Image/ImageFactory.js.map +1 -1
  32. package/lib/commonjs/skia/types/NativeBuffer/NativeBufferFactory.d.ts +2 -2
  33. package/lib/commonjs/skia/types/NativeBuffer/NativeBufferFactory.js.map +1 -1
  34. package/lib/commonjs/skia/types/Skia.d.ts +2 -0
  35. package/lib/commonjs/skia/types/Skia.js.map +1 -1
  36. package/lib/commonjs/skia/types/Video/Video.d.ts +8 -0
  37. package/lib/commonjs/skia/types/Video/Video.js +6 -0
  38. package/lib/commonjs/skia/types/Video/Video.js.map +1 -0
  39. package/lib/commonjs/skia/types/Video/index.d.ts +1 -0
  40. package/lib/commonjs/skia/types/Video/index.js +17 -0
  41. package/lib/commonjs/skia/types/Video/index.js.map +1 -0
  42. package/lib/commonjs/skia/web/JsiSkNativeBufferFactory.d.ts +1 -1
  43. package/lib/commonjs/skia/web/JsiSkNativeBufferFactory.js +1 -1
  44. package/lib/commonjs/skia/web/JsiSkNativeBufferFactory.js.map +1 -1
  45. package/lib/commonjs/skia/web/JsiSkia.js +4 -1
  46. package/lib/commonjs/skia/web/JsiSkia.js.map +1 -1
  47. package/lib/module/external/reanimated/index.d.ts +1 -0
  48. package/lib/module/external/reanimated/index.js +1 -0
  49. package/lib/module/external/reanimated/index.js.map +1 -1
  50. package/lib/module/external/reanimated/useVideo.d.ts +10 -0
  51. package/lib/module/external/reanimated/useVideo.js +85 -0
  52. package/lib/module/external/reanimated/useVideo.js.map +1 -0
  53. package/lib/module/mock/index.js +2 -1
  54. package/lib/module/mock/index.js.map +1 -1
  55. package/lib/module/skia/types/Image/ImageFactory.d.ts +3 -3
  56. package/lib/module/skia/types/Image/ImageFactory.js.map +1 -1
  57. package/lib/module/skia/types/NativeBuffer/NativeBufferFactory.d.ts +2 -2
  58. package/lib/module/skia/types/NativeBuffer/NativeBufferFactory.js.map +1 -1
  59. package/lib/module/skia/types/Skia.d.ts +2 -0
  60. package/lib/module/skia/types/Skia.js.map +1 -1
  61. package/lib/module/skia/types/Video/Video.d.ts +8 -0
  62. package/lib/module/skia/types/Video/Video.js +2 -0
  63. package/lib/module/skia/types/Video/Video.js.map +1 -0
  64. package/lib/module/skia/types/Video/index.d.ts +1 -0
  65. package/lib/module/skia/types/Video/index.js +2 -0
  66. package/lib/module/skia/types/Video/index.js.map +1 -0
  67. package/lib/module/skia/web/JsiSkNativeBufferFactory.d.ts +1 -1
  68. package/lib/module/skia/web/JsiSkNativeBufferFactory.js +1 -1
  69. package/lib/module/skia/web/JsiSkNativeBufferFactory.js.map +1 -1
  70. package/lib/module/skia/web/JsiSkia.js +4 -1
  71. package/lib/module/skia/web/JsiSkia.js.map +1 -1
  72. package/lib/typescript/src/external/reanimated/index.d.ts +1 -0
  73. package/lib/typescript/src/external/reanimated/useVideo.d.ts +10 -0
  74. package/lib/typescript/src/skia/types/Image/ImageFactory.d.ts +3 -3
  75. package/lib/typescript/src/skia/types/NativeBuffer/NativeBufferFactory.d.ts +2 -2
  76. package/lib/typescript/src/skia/types/Skia.d.ts +2 -0
  77. package/lib/typescript/src/skia/types/Video/Video.d.ts +8 -0
  78. package/lib/typescript/src/skia/types/Video/index.d.ts +1 -0
  79. package/lib/typescript/src/skia/web/JsiSkNativeBufferFactory.d.ts +1 -1
  80. package/package.json +1 -1
  81. package/scripts/setup-canvaskit.js +1 -1
  82. package/src/external/reanimated/index.ts +1 -0
  83. package/src/external/reanimated/useVideo.ts +115 -0
  84. package/src/mock/index.ts +1 -0
  85. package/src/skia/types/Image/ImageFactory.ts +3 -3
  86. package/src/skia/types/NativeBuffer/NativeBufferFactory.ts +2 -2
  87. package/src/skia/types/Skia.ts +2 -0
  88. package/src/skia/types/Video/Video.ts +9 -0
  89. package/src/skia/types/Video/index.ts +1 -0
  90. package/src/skia/web/JsiSkNativeBufferFactory.ts +1 -1
  91. package/src/skia/web/JsiSkia.ts +3 -0
@@ -0,0 +1,115 @@
1
+ import {
2
+ runOnUI,
3
+ useSharedValue,
4
+ type FrameInfo,
5
+ type SharedValue,
6
+ } from "react-native-reanimated";
7
+ import { useCallback, useEffect, useMemo } from "react";
8
+
9
+ import { Skia } from "../../skia/Skia";
10
+ import type { SkImage } from "../../skia/types";
11
+ import { Platform } from "../../Platform";
12
+
13
+ import Rea from "./ReanimatedProxy";
14
+
15
+ type Animated<T> = SharedValue<T> | T;
16
+
17
+ export interface PlaybackOptions {
18
+ playbackSpeed: Animated<number>;
19
+ looping: Animated<boolean>;
20
+ paused: Animated<boolean>;
21
+ }
22
+
23
+ const defaultOptions = {
24
+ playbackSpeed: 1,
25
+ looping: true,
26
+ paused: false,
27
+ };
28
+
29
+ const useOption = <T>(value: Animated<T>) => {
30
+ "worklet";
31
+ // TODO: only create defaultValue is needed (via makeMutable)
32
+ const defaultValue = useSharedValue(
33
+ Rea.isSharedValue(value) ? value.value : value
34
+ );
35
+ return Rea.isSharedValue(value) ? value : defaultValue;
36
+ };
37
+
38
+ export const useVideo = (
39
+ source: string | null,
40
+ userOptions?: Partial<PlaybackOptions>
41
+ ) => {
42
+ const video = useMemo(() => (source ? Skia.Video(source) : null), [source]);
43
+ const isPaused = useOption(userOptions?.paused ?? defaultOptions.paused);
44
+ const looping = useOption(userOptions?.looping ?? defaultOptions.looping);
45
+ const playbackSpeed = useOption(
46
+ userOptions?.playbackSpeed ?? defaultOptions.playbackSpeed
47
+ );
48
+ const currentFrame = Rea.useSharedValue<null | SkImage>(null);
49
+ const lastTimestamp = Rea.useSharedValue(-1);
50
+ const startTimestamp = Rea.useSharedValue(-1);
51
+
52
+ const framerate = useMemo(() => (video ? video.framerate() : -1), [video]);
53
+ const duration = useMemo(() => (video ? video.duration() : -1), [video]);
54
+ const frameDuration = useMemo(
55
+ () => (framerate > 0 ? 1000 / framerate : -1),
56
+ [framerate]
57
+ );
58
+ const disposeVideo = useCallback(() => {
59
+ "worklet";
60
+ video?.dispose();
61
+ }, [video]);
62
+
63
+ Rea.useFrameCallback((frameInfo: FrameInfo) => {
64
+ if (!video) {
65
+ return;
66
+ }
67
+ if (isPaused.value && lastTimestamp.value !== -1) {
68
+ return;
69
+ }
70
+ const { timestamp } = frameInfo;
71
+
72
+ // Initialize start timestamp
73
+ if (startTimestamp.value === -1) {
74
+ startTimestamp.value = timestamp;
75
+ }
76
+
77
+ // Calculate the current time in the video
78
+ const currentTimestamp = timestamp - startTimestamp.value;
79
+
80
+ // Handle looping
81
+ if (currentTimestamp > duration && looping.value) {
82
+ video.seek(0);
83
+ startTimestamp.value = timestamp;
84
+ }
85
+
86
+ // Update frame only if the elapsed time since last update is greater than the frame duration
87
+ const currentFrameDuration = frameDuration / playbackSpeed.value;
88
+ if (
89
+ lastTimestamp.value === -1 ||
90
+ timestamp - lastTimestamp.value >= currentFrameDuration
91
+ ) {
92
+ const img = video.nextImage();
93
+ if (img) {
94
+ if (currentFrame.value) {
95
+ currentFrame.value.dispose();
96
+ }
97
+ if (Platform.OS === "android") {
98
+ currentFrame.value = img.makeNonTextureImage();
99
+ } else {
100
+ currentFrame.value = img;
101
+ }
102
+ }
103
+ lastTimestamp.value = timestamp;
104
+ }
105
+ });
106
+
107
+ useEffect(() => {
108
+ return () => {
109
+ // TODO: should video simply be a shared value instead?
110
+ runOnUI(disposeVideo)();
111
+ };
112
+ }, [disposeVideo, video]);
113
+
114
+ return currentFrame;
115
+ };
package/src/mock/index.ts CHANGED
@@ -46,5 +46,6 @@ export const Mock = (CanvasKit: CanvasKit) => {
46
46
  useTypeface: () => null,
47
47
  useImage: () => null,
48
48
  useSVG: () => null,
49
+ useVideo: () => null,
49
50
  };
50
51
  };
@@ -54,7 +54,7 @@ export interface ImageFactory {
54
54
 
55
55
  /**
56
56
  * Return an Image backed by a given native buffer.
57
- * The platform buffer must be a valid owning reference.
57
+ * The native buffer must be a valid owning reference.
58
58
  *
59
59
  * For instance, this API is used by
60
60
  * [react-native-vision-camera](https://github.com/mrousavy/react-native-vision-camera)
@@ -62,9 +62,9 @@ export interface ImageFactory {
62
62
  *
63
63
  * - On Android; This is an `AHardwareBuffer*`
64
64
  * - On iOS, this is a `CVPixelBufferRef`
65
- * @param nativeBuffer A strong `uintptr_t` pointer to the native platform buffer
65
+ * @param nativeBuffer A strong `uintptr_t` pointer to the native buffer
66
66
  * @throws Throws an error if the Image could not be created, for example when the given
67
- * platform buffer is invalid.
67
+ * native buffer is invalid.
68
68
  */
69
69
  MakeImageFromNativeBuffer: (nativeBuffer: NativeBuffer) => SkImage;
70
70
 
@@ -32,7 +32,7 @@ export interface NativeBufferFactory {
32
32
  */
33
33
  MakeFromImage: (image: SkImage) => NativeBuffer;
34
34
  /**
35
- * Release a platform buffer that was created with `MakeFromImage`.
35
+ * Release a native buffer that was created with `MakeFromImage`.
36
36
  */
37
- Release: (platformBuffer: NativeBuffer) => void;
37
+ Release: (nativeBuffer: NativeBuffer) => void;
38
38
  }
@@ -30,6 +30,7 @@ import type { Color, SkColor } from "./Color";
30
30
  import type { TypefaceFontProviderFactory } from "./Paragraph/TypefaceFontProviderFactory";
31
31
  import type { AnimatedImageFactory } from "./AnimatedImage";
32
32
  import type { ParagraphBuilderFactory } from "./Paragraph/ParagraphBuilder";
33
+ import type { Video } from "./Video";
33
34
  import type { NativeBufferFactory } from "./NativeBuffer";
34
35
 
35
36
  /**
@@ -95,5 +96,6 @@ export interface Skia {
95
96
  TextBlob: TextBlobFactory;
96
97
  Surface: SurfaceFactory;
97
98
  ParagraphBuilder: ParagraphBuilderFactory;
99
+ Video: (url: string) => Video;
98
100
  NativeBuffer: NativeBufferFactory;
99
101
  }
@@ -0,0 +1,9 @@
1
+ import type { SkImage } from "../Image";
2
+ import type { SkJSIInstance } from "../JsiInstance";
3
+
4
+ export interface Video extends SkJSIInstance<"Video"> {
5
+ duration(): number;
6
+ framerate(): number;
7
+ nextImage(): SkImage | null;
8
+ seek(time: number): void;
9
+ }
@@ -0,0 +1 @@
1
+ export * from "./Video";
@@ -29,7 +29,7 @@ export class JsiSkNativeBufferFactory
29
29
  return canvas;
30
30
  }
31
31
 
32
- Release(_platformBuffer: NativeBuffer) {
32
+ Release(_nativeBuffer: NativeBuffer) {
33
33
  // it's a noop on Web
34
34
  }
35
35
  }
@@ -127,4 +127,7 @@ export const JsiSkApi = (CanvasKit: CanvasKit): Skia => ({
127
127
  FontMgr: new JsiSkFontMgrFactory(CanvasKit),
128
128
  ParagraphBuilder: new JsiSkParagraphBuilderFactory(CanvasKit),
129
129
  NativeBuffer: new JsiSkNativeBufferFactory(CanvasKit),
130
+ Video: (_localUri: string) => {
131
+ throw new Error("Not implemented on React Native Web");
132
+ },
130
133
  });