@shopify/react-native-skia 2.12.0-next.1 → 2.12.0

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 (202) hide show
  1. package/Package.swift +117 -0
  2. package/android/CMakeLists.txt +1 -2
  3. package/android/cpp/rnskia-android/RNSkAndroidPlatformContext.h +0 -10
  4. package/android/src/main/java/com/shopify/reactnative/skia/ViewScreenshotService.java +9 -12
  5. package/apple/RNSkApplePlatformContext.h +0 -3
  6. package/apple/RNSkApplePlatformContext.mm +0 -11
  7. package/cpp/api/JsiSkApi.h +0 -6
  8. package/cpp/api/JsiSkFont.h +5 -10
  9. package/cpp/api/JsiSkMatrix.h +4 -2
  10. package/cpp/api/JsiSkSVGFactory.h +4 -0
  11. package/cpp/api/recorder/Convertor.h +5 -2
  12. package/cpp/api/recorder/Drawings.h +13 -3
  13. package/cpp/api/recorder/ImageFilters.h +15 -1
  14. package/cpp/api/recorder/Paint.h +8 -1
  15. package/cpp/jsi/JSICache.cpp +99 -0
  16. package/cpp/jsi/JSICache.h +90 -0
  17. package/cpp/jsi/NativeObject.h +38 -87
  18. package/cpp/jsi/ViewProperty.h +0 -24
  19. package/cpp/rnskia/RNDawnContext.h +2 -1
  20. package/cpp/rnskia/RNDawnWindowContext.h +22 -0
  21. package/cpp/rnskia/RNSkManager.cpp +0 -6
  22. package/cpp/rnskia/RNSkPlatformContext.h +0 -3
  23. package/cpp/skia/include/core/RasterContext.h +25 -0
  24. package/cpp/skia/include/core/SkCanvas.h +0 -1
  25. package/cpp/skia/include/core/SkContext.h +62 -0
  26. package/cpp/skia/include/core/SkContextOptions.h +15 -0
  27. package/cpp/skia/include/core/SkMilestone.h +1 -1
  28. package/cpp/skia/include/core/SkPicture.h +36 -18
  29. package/cpp/skia/include/core/SkRecorder.h +8 -0
  30. package/cpp/skia/include/core/SkSerialProcs.h +2 -6
  31. package/cpp/skia/include/effects/SkImageFilters.h +15 -2
  32. package/cpp/skia/include/gpu/ganesh/GrDirectContext.h +73 -47
  33. package/cpp/skia/include/gpu/ganesh/SkSurfaceGanesh.h +7 -4
  34. package/cpp/skia/include/gpu/ganesh/d3d/GrD3DDirectContext.h +12 -0
  35. package/cpp/skia/include/gpu/ganesh/gl/GrGLDirectContext.h +11 -0
  36. package/cpp/skia/include/gpu/ganesh/mtl/GrMtlDirectContext.h +11 -0
  37. package/cpp/skia/include/gpu/ganesh/vk/GrVkDirectContext.h +13 -0
  38. package/cpp/skia/include/gpu/graphite/ContextOptions.h +6 -0
  39. package/cpp/skia/include/gpu/graphite/GraphiteTypes.h +5 -4
  40. package/cpp/skia/include/gpu/graphite/PrecompileContext.h +9 -1
  41. package/cpp/skia/include/gpu/graphite/Recorder.h +0 -2
  42. package/cpp/skia/include/gpu/graphite/Recording.h +3 -0
  43. package/cpp/skia/include/gpu/graphite/dawn/DawnBackendContext.h +8 -0
  44. package/cpp/skia/include/gpu/graphite/mtl/MtlBackendContext.h +11 -0
  45. package/cpp/skia/include/gpu/graphite/vk/VulkanGraphiteContext.h +11 -0
  46. package/cpp/skia/include/private/SkLog.h +1 -19
  47. package/cpp/skia/include/private/chromium/GrVkSecondaryCBDrawContext.h +2 -1
  48. package/cpp/skia/include/utils/SkLogHandler.h +53 -0
  49. package/cpp/skia/include/utils/SkNWayCanvas.h +1 -0
  50. package/cpp/skia/modules/skcms/src/Transform_inl.h +9 -10
  51. package/cpp/skia/modules/skcms/src/skcms_internals.h +8 -3
  52. package/cpp/skia/modules/skottie/include/SkottieProperty.h +2 -0
  53. package/cpp/skia/modules/skottie/include/TextShaper.h +1 -0
  54. package/cpp/skia/modules/skshaper/include/SkShaper.h +23 -1
  55. package/include/ReactNativeSkia.h +6 -0
  56. package/lib/commonjs/dom/nodes/datatypes/Rect.js +5 -1
  57. package/lib/commonjs/dom/nodes/datatypes/Rect.js.map +1 -1
  58. package/lib/commonjs/external/reanimated/useVideo.js +8 -2
  59. package/lib/commonjs/external/reanimated/useVideo.js.map +1 -1
  60. package/lib/commonjs/renderer/components/Mask.js +3 -9
  61. package/lib/commonjs/renderer/components/Mask.js.map +1 -1
  62. package/lib/commonjs/skia/types/JsiInstance.d.ts +1 -0
  63. package/lib/commonjs/skia/types/JsiInstance.js.map +1 -1
  64. package/lib/commonjs/skia/types/Matrix4.js +7 -2
  65. package/lib/commonjs/skia/types/Matrix4.js.map +1 -1
  66. package/lib/commonjs/skia/types/Skia.d.ts +1 -6
  67. package/lib/commonjs/skia/types/Skia.js.map +1 -1
  68. package/lib/commonjs/skia/web/JsiSkCanvas.js +4 -4
  69. package/lib/commonjs/skia/web/JsiSkCanvas.js.map +1 -1
  70. package/lib/commonjs/skia/web/JsiSkImage.js +6 -3
  71. package/lib/commonjs/skia/web/JsiSkImage.js.map +1 -1
  72. package/lib/commonjs/skia/web/JsiSkPathBuilder.d.ts +1 -1
  73. package/lib/commonjs/skia/web/JsiSkPathBuilder.js +2 -2
  74. package/lib/commonjs/skia/web/JsiSkPathBuilder.js.map +1 -1
  75. package/lib/commonjs/skia/web/JsiSkTextStyle.js +5 -5
  76. package/lib/commonjs/skia/web/JsiSkTextStyle.js.map +1 -1
  77. package/lib/commonjs/skia/web/JsiSkia.js +0 -3
  78. package/lib/commonjs/skia/web/JsiSkia.js.map +1 -1
  79. package/lib/commonjs/skia/web/JsiSkottieAnimation.js +8 -8
  80. package/lib/commonjs/skia/web/JsiSkottieAnimation.js.map +1 -1
  81. package/lib/commonjs/sksg/Recorder/DrawingContext.d.ts +1 -0
  82. package/lib/commonjs/sksg/Recorder/DrawingContext.js +15 -0
  83. package/lib/commonjs/sksg/Recorder/DrawingContext.js.map +1 -1
  84. package/lib/commonjs/sksg/Recorder/Player.js +1 -1
  85. package/lib/commonjs/sksg/Recorder/Player.js.map +1 -1
  86. package/lib/commonjs/sksg/Recorder/Visitor.js +6 -1
  87. package/lib/commonjs/sksg/Recorder/Visitor.js.map +1 -1
  88. package/lib/commonjs/sksg/Recorder/commands/Drawing.js +12 -9
  89. package/lib/commonjs/sksg/Recorder/commands/Drawing.js.map +1 -1
  90. package/lib/commonjs/sksg/Recorder/commands/ImageFilters.js +15 -1
  91. package/lib/commonjs/sksg/Recorder/commands/ImageFilters.js.map +1 -1
  92. package/lib/commonjs/sksg/Recorder/commands/Shaders.js +1 -1
  93. package/lib/commonjs/sksg/Recorder/commands/Shaders.js.map +1 -1
  94. package/lib/commonjs/views/SkiaPictureView.web.js +247 -57
  95. package/lib/commonjs/views/SkiaPictureView.web.js.map +1 -1
  96. package/lib/module/dom/nodes/datatypes/Rect.js +5 -1
  97. package/lib/module/dom/nodes/datatypes/Rect.js.map +1 -1
  98. package/lib/module/external/reanimated/useVideo.js +8 -2
  99. package/lib/module/external/reanimated/useVideo.js.map +1 -1
  100. package/lib/module/renderer/components/Mask.js +3 -9
  101. package/lib/module/renderer/components/Mask.js.map +1 -1
  102. package/lib/module/skia/types/JsiInstance.d.ts +1 -0
  103. package/lib/module/skia/types/JsiInstance.js.map +1 -1
  104. package/lib/module/skia/types/Matrix4.js +7 -2
  105. package/lib/module/skia/types/Matrix4.js.map +1 -1
  106. package/lib/module/skia/types/Skia.d.ts +1 -6
  107. package/lib/module/skia/types/Skia.js.map +1 -1
  108. package/lib/module/skia/web/JsiSkCanvas.js +4 -4
  109. package/lib/module/skia/web/JsiSkCanvas.js.map +1 -1
  110. package/lib/module/skia/web/JsiSkImage.js +6 -3
  111. package/lib/module/skia/web/JsiSkImage.js.map +1 -1
  112. package/lib/module/skia/web/JsiSkPathBuilder.d.ts +1 -1
  113. package/lib/module/skia/web/JsiSkPathBuilder.js +2 -2
  114. package/lib/module/skia/web/JsiSkPathBuilder.js.map +1 -1
  115. package/lib/module/skia/web/JsiSkTextStyle.js +5 -5
  116. package/lib/module/skia/web/JsiSkTextStyle.js.map +1 -1
  117. package/lib/module/skia/web/JsiSkia.js +0 -3
  118. package/lib/module/skia/web/JsiSkia.js.map +1 -1
  119. package/lib/module/skia/web/JsiSkottieAnimation.js +8 -8
  120. package/lib/module/skia/web/JsiSkottieAnimation.js.map +1 -1
  121. package/lib/module/sksg/Recorder/DrawingContext.d.ts +1 -0
  122. package/lib/module/sksg/Recorder/DrawingContext.js +15 -0
  123. package/lib/module/sksg/Recorder/DrawingContext.js.map +1 -1
  124. package/lib/module/sksg/Recorder/Player.js +1 -1
  125. package/lib/module/sksg/Recorder/Player.js.map +1 -1
  126. package/lib/module/sksg/Recorder/Visitor.js +6 -1
  127. package/lib/module/sksg/Recorder/Visitor.js.map +1 -1
  128. package/lib/module/sksg/Recorder/commands/Drawing.js +12 -9
  129. package/lib/module/sksg/Recorder/commands/Drawing.js.map +1 -1
  130. package/lib/module/sksg/Recorder/commands/ImageFilters.js +15 -1
  131. package/lib/module/sksg/Recorder/commands/ImageFilters.js.map +1 -1
  132. package/lib/module/sksg/Recorder/commands/Shaders.js +1 -1
  133. package/lib/module/sksg/Recorder/commands/Shaders.js.map +1 -1
  134. package/lib/module/views/SkiaPictureView.web.js +247 -57
  135. package/lib/module/views/SkiaPictureView.web.js.map +1 -1
  136. package/lib/typescript/lib/commonjs/skia/web/JsiSkPathBuilder.d.ts +1 -1
  137. package/lib/typescript/lib/commonjs/skia/web/JsiSkia.d.ts +0 -1
  138. package/lib/typescript/lib/commonjs/sksg/Recorder/DrawingContext.d.ts +1 -0
  139. package/lib/typescript/lib/module/skia/Skia.web.d.ts +0 -1
  140. package/lib/typescript/lib/module/skia/web/JsiSkPathBuilder.d.ts +1 -1
  141. package/lib/typescript/lib/module/skia/web/JsiSkia.d.ts +0 -1
  142. package/lib/typescript/lib/module/sksg/Recorder/DrawingContext.d.ts +1 -0
  143. package/lib/typescript/react-native.config.d.ts +3 -0
  144. package/lib/typescript/src/skia/types/JsiInstance.d.ts +1 -0
  145. package/lib/typescript/src/skia/types/Skia.d.ts +1 -6
  146. package/lib/typescript/src/skia/web/JsiSkPathBuilder.d.ts +1 -1
  147. package/lib/typescript/src/sksg/Recorder/DrawingContext.d.ts +1 -0
  148. package/package.json +13 -10
  149. package/react-native.config.js +5 -0
  150. package/src/dom/nodes/datatypes/Rect.ts +6 -1
  151. package/src/external/reanimated/useVideo.ts +7 -2
  152. package/src/renderer/components/Mask.tsx +10 -3
  153. package/src/skia/types/JsiInstance.ts +2 -0
  154. package/src/skia/types/Matrix4.ts +6 -2
  155. package/src/skia/types/Skia.ts +1 -7
  156. package/src/skia/web/JsiSkCanvas.ts +11 -6
  157. package/src/skia/web/JsiSkImage.ts +6 -3
  158. package/src/skia/web/JsiSkPathBuilder.ts +2 -2
  159. package/src/skia/web/JsiSkTextStyle.ts +20 -15
  160. package/src/skia/web/JsiSkia.ts +0 -4
  161. package/src/skia/web/JsiSkottieAnimation.ts +8 -8
  162. package/src/sksg/Recorder/DrawingContext.ts +15 -0
  163. package/src/sksg/Recorder/Player.ts +1 -1
  164. package/src/sksg/Recorder/Visitor.ts +9 -6
  165. package/src/sksg/Recorder/commands/Drawing.ts +12 -9
  166. package/src/sksg/Recorder/commands/ImageFilters.ts +20 -1
  167. package/src/sksg/Recorder/commands/Shaders.ts +1 -1
  168. package/src/views/SkiaPictureView.web.tsx +245 -57
  169. package/cpp/api/JsiSkiaContext.h +0 -94
  170. package/cpp/dawn/include/dawn/dawn_proc.h +0 -53
  171. package/cpp/dawn/include/dawn/dawn_proc_table.h +0 -330
  172. package/cpp/dawn/include/dawn/dawn_thread_dispatch_proc.h +0 -47
  173. package/cpp/dawn/include/dawn/dawn_version.h +0 -41
  174. package/cpp/dawn/include/dawn/native/D3D11Backend.h +0 -65
  175. package/cpp/dawn/include/dawn/native/D3D12Backend.h +0 -103
  176. package/cpp/dawn/include/dawn/native/D3DBackend.h +0 -56
  177. package/cpp/dawn/include/dawn/native/DawnNative.h +0 -368
  178. package/cpp/dawn/include/dawn/native/MetalBackend.h +0 -56
  179. package/cpp/dawn/include/dawn/native/NullBackend.h +0 -39
  180. package/cpp/dawn/include/dawn/native/OpenGLBackend.h +0 -89
  181. package/cpp/dawn/include/dawn/native/VulkanBackend.h +0 -183
  182. package/cpp/dawn/include/dawn/native/WebGPUBackend.h +0 -49
  183. package/cpp/dawn/include/dawn/native/dawn_native_export.h +0 -49
  184. package/cpp/dawn/include/dawn/platform/DawnPlatform.h +0 -207
  185. package/cpp/dawn/include/dawn/platform/dawn_platform_export.h +0 -49
  186. package/cpp/dawn/include/dawn/replay/Replay.h +0 -99
  187. package/cpp/dawn/include/dawn/replay/dawn_replay_export.h +0 -49
  188. package/cpp/dawn/include/dawn/webgpu_cpp_print.h +0 -2856
  189. package/cpp/dawn/include/tint/tint.h +0 -91
  190. package/cpp/dawn/include/webgpu/webgpu.h +0 -5029
  191. package/cpp/dawn/include/webgpu/webgpu_cpp.h +0 -10353
  192. package/cpp/dawn/include/webgpu/webgpu_cpp_chained_struct.h +0 -57
  193. package/cpp/dawn/include/webgpu/webgpu_enum_class_bitmasks.h +0 -162
  194. package/cpp/dawn/include/webgpu/webgpu_glfw.h +0 -88
  195. package/cpp/jsi/RuntimeAwareCache.cpp +0 -7
  196. package/cpp/jsi/RuntimeAwareCache.h +0 -117
  197. package/cpp/jsi/RuntimeLifecycleMonitor.cpp +0 -72
  198. package/cpp/jsi/RuntimeLifecycleMonitor.h +0 -32
  199. package/cpp/skia/src/gpu/graphite/ContextOptionsPriv.h +0 -45
  200. package/cpp/skia/src/gpu/graphite/ResourceTypes.h +0 -360
  201. package/cpp/skia/src/gpu/graphite/TextureProxyView.h +0 -105
  202. package/libs/.graphite +0 -1
package/Package.swift ADDED
@@ -0,0 +1,117 @@
1
+ // swift-tools-version: 6.0
2
+ //
3
+ // SwiftPM manifest for @shopify/react-native-skia. iOS, Ganesh.
4
+ // Additive: the CocoaPods podspec remains the supported default.
5
+ //
6
+ // React Native 0.87+ references a library that ships its own Package.swift
7
+ // through a symlink at <app>/ios/build/generated/autolinking/libs/<name>, and
8
+ // resolves the relative paths below against that symlink. They are the same
9
+ // for every standard app, because the symlink location is.
10
+
11
+ import Foundation
12
+ import PackageDescription
13
+
14
+ // Skia's xcframeworks are ~200MB and are not carried in this package. They come
15
+ // from the react-native-skia-apple-ios npm package, which @shopify/react-native-skia
16
+ // depends on, so a checkout that has installed its dependencies builds offline.
17
+ // Xcode passes the symlink as the package directory, hence resolvingSymlinks.
18
+ let packageRoot = URL(fileURLWithPath: Context.packageDirectory)
19
+ .resolvingSymlinksInPath().path
20
+
21
+ let binariesPath = [
22
+ "../../react-native-skia-apple-ios", // consumer: sibling in node_modules
23
+ "../../node_modules/react-native-skia-apple-ios", // this monorepo
24
+ ]
25
+ .map { "\(packageRoot)/\($0)" }
26
+ .first { FileManager.default.fileExists(atPath: "\($0)/Package.swift") }
27
+
28
+ guard let binariesPath else {
29
+ // Reached when the package was skipped at install time (`--omit=optional`,
30
+ // or a non-Apple `os` filter), which SwiftPM would otherwise report as an
31
+ // unresolvable dependency path.
32
+ fatalError(
33
+ """
34
+ react-native-skia-apple-ios was not found next to @shopify/react-native-skia. \
35
+ It ships the prebuilt Skia binaries this package links against. Reinstall \
36
+ dependencies without --omit=optional, on macOS.
37
+ """)
38
+ }
39
+
40
+ let package = Package(
41
+ name: "ReactNativeSkia",
42
+ platforms: [.iOS(.v15)],
43
+ products: [
44
+ // Autolinking looks this name up verbatim; react-native.config.js pins it.
45
+ .library(name: "ReactNativeSkia", targets: ["ReactNativeSkia"])
46
+ ],
47
+ dependencies: [
48
+ .package(name: "React-GeneratedCode", path: "../../../ios"),
49
+ .package(name: "ReactNative", path: "../../../../xcframeworks"),
50
+ .package(path: binariesPath),
51
+ ],
52
+ targets: [
53
+ .target(
54
+ name: "ReactNativeSkia",
55
+ dependencies: [
56
+ .product(name: "ReactHeaders", package: "ReactNative"),
57
+ .product(name: "ReactNativeHeaders", package: "ReactNative"),
58
+ .product(name: "ReactNativeDependenciesHeaders", package: "ReactNative"),
59
+ .product(name: "ReactAppHeaders", package: "React-GeneratedCode"),
60
+ .product(name: "react-native-skia-apple-ios", package: "react-native-skia-apple-ios"),
61
+ ],
62
+ // apple/ and cpp/ have no common ancestor below the package root, and
63
+ // .headerSearchPath cannot escape the target path.
64
+ path: ".",
65
+ exclude: [
66
+ "cpp/rnskia/RNDawnWindowContext.cpp", // Graphite only
67
+ "cpp/rnskia/RNDawnInterop.cpp", // Graphite only
68
+ ],
69
+ // Explicit, so SwiftPM never walks node_modules, lib, android, or the
70
+ // headers-only cpp/skia — which carries x86 AVX skcms sources that
71
+ // cannot build for arm64.
72
+ sources: [
73
+ "apple",
74
+ "cpp/api",
75
+ "cpp/jsi",
76
+ "cpp/rnskia",
77
+ "cpp/utils",
78
+ // libskottie.a needs skjson and Apple builds no libskjson archive.
79
+ // CocoaPods compiles this via its cpp/**/*.cpp glob.
80
+ "cpp/skia/modules/jsonreader/SkJSONReader.cpp",
81
+ ],
82
+ cxxSettings: [
83
+ // Replaces the podspec's recursive cpp/** glob, which expands to 90.
84
+ .headerSearchPath("cpp"), // "api/…", "jsi/…", "utils/…"
85
+ .headerSearchPath("cpp/skia"), // "include/core/…", "modules/…", "src/…"
86
+ .headerSearchPath("cpp/rnskia"), // apple/ uses bare "RNSkView.h"
87
+ .headerSearchPath("cpp/utils"), // apple/ uses bare "RNSkLog.h"
88
+
89
+ // CocoaPods forces both project-wide; the SwiftPM path defines neither.
90
+ // Skia's Apple sources still gate on them: without them RNSkiaModule's
91
+ // legacy branch fails to compile and -getTurboModule: is dropped, so
92
+ // the JSI bindings never install.
93
+ .define("RCT_NEW_ARCH_ENABLED", to: "1"),
94
+ .define("RCT_REMOVE_LEGACY_ARCH", to: "1"),
95
+
96
+ .define("SK_METAL", to: "1"),
97
+ .define("SK_GANESH", to: "1"),
98
+ .define("SK_IMAGE_READ_PIXELS_DISABLE_LEGACY_API", to: "1"),
99
+ .define("SK_DISABLE_LEGACY_SHAPER_FACTORY", to: "1"),
100
+
101
+ // React's prebuilt C++ ABI is NDEBUG-gated, and Skia derives
102
+ // SK_RELEASE from it. Omitting either breaks the Release link.
103
+ .define("DEBUG", .when(configuration: .debug)),
104
+ .define("NDEBUG", .when(configuration: .release)),
105
+ ],
106
+ linkerSettings: [
107
+ .linkedFramework("MetalKit"),
108
+ .linkedFramework("AVFoundation"),
109
+ .linkedFramework("AVKit"),
110
+ .linkedFramework("CoreMedia"),
111
+ ]
112
+ )
113
+ ],
114
+ // React Native's headers need C++20, and so does Skia m152: SkMathPriv.h
115
+ // calls std::countl_zero, std::countr_zero and std::popcount.
116
+ cxxLanguageStandard: .cxx20
117
+ )
@@ -100,8 +100,7 @@ add_library(
100
100
  "${PROJECT_SOURCE_DIR}/cpp/rnskia-android/AHardwareBufferUtils.cpp"
101
101
  "${PROJECT_SOURCE_DIR}/cpp/rnskia-android/RNSkAndroidVideo.cpp"
102
102
 
103
- "${PROJECT_SOURCE_DIR}/../cpp/jsi/RuntimeLifecycleMonitor.cpp"
104
- "${PROJECT_SOURCE_DIR}/../cpp/jsi/RuntimeAwareCache.cpp"
103
+ "${PROJECT_SOURCE_DIR}/../cpp/jsi/JSICache.cpp"
105
104
  "${PROJECT_SOURCE_DIR}/../cpp/jsi/JsiPromises.cpp"
106
105
  "${PROJECT_SOURCE_DIR}/../cpp/jsi/Promise.cpp"
107
106
 
@@ -63,16 +63,6 @@ public:
63
63
  #endif
64
64
  }
65
65
 
66
- std::shared_ptr<WindowContext>
67
- makeContextFromNativeSurface(void *surface, int width, int height) override {
68
- #if defined(SK_GRAPHITE)
69
- return DawnContext::getInstance().MakeWindow(surface, width, height);
70
- #else
71
- auto aWindow = reinterpret_cast<ANativeWindow *>(surface);
72
- return OpenGLContext::getInstance().MakeWindow(aWindow);
73
- #endif
74
- }
75
-
76
66
  sk_sp<SkImage> makeImageFromNativeBuffer(void *buffer) override {
77
67
  #if defined(SK_GRAPHITE)
78
68
  return DawnContext::getInstance().MakeImageFromBuffer(buffer);
@@ -19,10 +19,11 @@ import android.widget.ScrollView;
19
19
  import androidx.annotation.NonNull;
20
20
  import com.facebook.react.bridge.ReactContext;
21
21
  import com.facebook.react.bridge.UIManager;
22
+ import com.facebook.react.uimanager.BackgroundStyleApplicator;
22
23
  import com.facebook.react.uimanager.UIManagerHelper;
24
+ import com.facebook.react.uimanager.ViewProps;
23
25
  import com.facebook.react.views.view.ReactViewGroup;
24
26
 
25
- import java.lang.reflect.Method;
26
27
  import java.util.concurrent.CountDownLatch;
27
28
  import java.util.concurrent.TimeUnit;
28
29
 
@@ -118,17 +119,13 @@ public class ViewScreenshotService {
118
119
  }
119
120
 
120
121
  private static void drawChildren(Canvas canvas, ViewGroup group, Paint paint, float parentOpacity) {
121
- // Handle clipping for ReactViewGroup
122
- if (group instanceof ReactViewGroup) {
123
- try {
124
- Class[] cArg = new Class[1];
125
- cArg[0] = Canvas.class;
126
- Method method = ReactViewGroup.class.getDeclaredMethod("dispatchOverflowDraw", cArg);
127
- method.setAccessible(true);
128
- method.invoke(group, canvas);
129
- } catch (Exception e) {
130
- Log.e(TAG, "couldn't invoke dispatchOverflowDraw() on ReactViewGroup", e);
131
- }
122
+ // ReactViewGroup applies `overflow: hidden` / `overflow: scroll` clipping from its
123
+ // dispatchDraw() override. We never call dispatchDraw() here because children are
124
+ // walked manually (TextureView / SurfaceView need special handling), so the clip has
125
+ // to be replicated explicitly, exactly as ReactViewGroup.dispatchDraw() does it.
126
+ if (group instanceof ReactViewGroup
127
+ && !ViewProps.VISIBLE.equals(((ReactViewGroup) group).getOverflow())) {
128
+ BackgroundStyleApplicator.clipToPaddingBox(group, canvas);
132
129
  }
133
130
  for (int i = 0; i < group.getChildCount(); i++) {
134
131
  View child = group.getChildAt(i);
@@ -63,9 +63,6 @@ public:
63
63
 
64
64
  std::shared_ptr<RNSkVideo> createVideo(const std::string &url) override;
65
65
 
66
- std::shared_ptr<WindowContext>
67
- makeContextFromNativeSurface(void *surface, int width, int height) override;
68
-
69
66
  virtual void performStreamOperation(
70
67
  const std::string &sourceUri,
71
68
  const std::function<void(std::unique_ptr<SkStreamAsset>)> &op) override;
@@ -307,17 +307,6 @@ RNSkApplePlatformContext::createVideo(const std::string &url) {
307
307
  return std::make_shared<RNSkAppleVideo>(url, this);
308
308
  }
309
309
 
310
- std::shared_ptr<WindowContext>
311
- RNSkApplePlatformContext::makeContextFromNativeSurface(void *surface, int width,
312
- int height) {
313
- #if defined(SK_GRAPHITE)
314
- return DawnContext::getInstance().MakeWindow(surface, width, height);
315
- #else
316
- return MetalContext::getInstance().MakeWindow((__bridge CALayer *)surface,
317
- width, height);
318
- #endif
319
- }
320
-
321
310
  void RNSkApplePlatformContext::raiseError(const std::exception &err) {
322
311
  RCTFatal(RCTErrorWithMessage([NSString stringWithUTF8String:err.what()]));
323
312
  }
@@ -56,7 +56,6 @@
56
56
  #include "JsiSkTypefaceFactory.h"
57
57
  #include "JsiSkTypefaceFontProviderFactory.h"
58
58
  #include "JsiSkVertices.h"
59
- #include "JsiSkiaContext.h"
60
59
  #include "JsiSkottieFactory.h"
61
60
  #include "JsiVideo.h"
62
61
  #include "api/recorder/JsiRecorder.h"
@@ -116,10 +115,6 @@ public:
116
115
  return JsiVideo::createCtor(getContext())(runtime, thisValue, arguments,
117
116
  count);
118
117
  }
119
- JSI_HOST_FUNCTION(Context) {
120
- return JsiSkiaContext::createCtor(getContext())(runtime, thisValue,
121
- arguments, count);
122
- }
123
118
  JSI_HOST_FUNCTION(Font) {
124
119
  return JsiSkFont::createCtor(getContext())(runtime, thisValue, arguments,
125
120
  count);
@@ -256,7 +251,6 @@ public:
256
251
 
257
252
  static void definePrototype(jsi::Runtime &runtime, jsi::Object &prototype) {
258
253
  installHostMethod(runtime, prototype, "Video", &JsiSkApi::Video);
259
- installHostMethod(runtime, prototype, "Context", &JsiSkApi::Context);
260
254
  installHostMethod(runtime, prototype, "getNativeDevice",
261
255
  &JsiSkApi::getNativeDevice);
262
256
  installHostMethod(runtime, prototype, "Font", &JsiSkApi::Font);
@@ -135,8 +135,9 @@ public:
135
135
  bool isEmbolden() { return getObject()->isEmbolden(); }
136
136
 
137
137
  std::shared_ptr<JsiSkTypeface> getTypeface() {
138
- return std::make_shared<JsiSkTypeface>(
139
- getContext(), sk_sp<SkTypeface>(getObject()->getTypeface()));
138
+ // refTypeface(), not getTypeface(): the wrapper owns what it is handed.
139
+ return std::make_shared<JsiSkTypeface>(getContext(),
140
+ getObject()->refTypeface());
140
141
  }
141
142
 
142
143
  void setEdging(double edging) {
@@ -161,15 +162,9 @@ public:
161
162
 
162
163
  void setSize(double size) { getObject()->setSize(size); }
163
164
 
164
- // The JS API declares booleans here, but the previous implementation read
165
- // the arguments with asNumber() — keep accepting numbers.
166
- void setEmbolden(double embolden) {
167
- getObject()->setEmbolden(static_cast<bool>(embolden));
168
- }
165
+ void setEmbolden(bool embolden) { getObject()->setEmbolden(embolden); }
169
166
 
170
- void setSubpixel(double subpixel) {
171
- getObject()->setSubpixel(static_cast<bool>(subpixel));
172
- }
167
+ void setSubpixel(bool subpixel) { getObject()->setSubpixel(subpixel); }
173
168
 
174
169
  void setTypeface(std::variant<std::nullptr_t, sk_sp<SkTypeface>> typeface) {
175
170
  getObject()->setTypeface(
@@ -69,12 +69,14 @@ public:
69
69
 
70
70
  void postTranslate(double x, double y) { getObject()->postTranslate(x, y); }
71
71
 
72
+ // A missing y means a uniform scale, matching the CanvasKit backend and the
73
+ // SkMatrix type, where y is optional rather than defaulted to 1.
72
74
  void scale(double x, JsiOptional<double> y) {
73
- getObject()->preScale(x, y.has_value() ? *y : 1);
75
+ getObject()->preScale(x, y.has_value() ? *y : x);
74
76
  }
75
77
 
76
78
  void postScale(double x, JsiOptional<double> y) {
77
- getObject()->postScale(x, y.has_value() ? *y : 1);
79
+ getObject()->postScale(x, y.has_value() ? *y : x);
78
80
  }
79
81
 
80
82
  void skew(double x, double y) { getObject()->preSkew(x, y); }
@@ -116,6 +116,10 @@ private:
116
116
  builder.setResourceProvider(provider);
117
117
 
118
118
  auto svg_dom = builder.make(*stream);
119
+ if (!svg_dom) {
120
+ return jsi::Value::null();
121
+ }
122
+
119
123
  return makeJsiObject(
120
124
  runtime, std::make_shared<JsiSkSVG>(getContext(), std::move(svg_dom)));
121
125
  }
@@ -361,13 +361,16 @@ SkM44 getPropertyValue(jsi::Runtime &runtime, const jsi::Value &value) {
361
361
  m4.preScale(1, s);
362
362
  } else if (key == "skewX") {
363
363
  auto angle = value.getProperty(runtime, key.c_str()).asNumber();
364
- SkM44 skewX(1, 0, 0, 0, std::tan(angle), 1, 0, 0, 0, 0, 1, 0, 0, 0, 0,
364
+ // The SkM44 constructor takes its arguments in row-major reading
365
+ // order, so the shear factor of a horizontal skew belongs in row 0,
366
+ // where it scales y into x.
367
+ SkM44 skewX(1, std::tan(angle), 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0,
365
368
  1);
366
369
  m4.preConcat(skewX);
367
370
 
368
371
  } else if (key == "skewY") {
369
372
  auto angle = value.getProperty(runtime, key.c_str()).asNumber();
370
- SkM44 skewY(1, std::tan(angle), 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0,
373
+ SkM44 skewY(1, 0, 0, 0, std::tan(angle), 1, 0, 0, 0, 0, 1, 0, 0, 0, 0,
371
374
  1);
372
375
  m4.preConcat(skewY);
373
376
  } else if (key == "rotate" || key == "rotateZ") {
@@ -149,6 +149,9 @@ public:
149
149
  if (hasStrokeOptions) {
150
150
  const auto &stroke = props.stroke.value();
151
151
  SkPaint strokePaint;
152
+ // A default SkPaint is fill-style, and FillPathWithPaint only
153
+ // outlines a paint that strokes.
154
+ strokePaint.setStyle(SkPaint::kStroke_Style);
152
155
 
153
156
  if (stroke.cap.has_value()) {
154
157
  strokePaint.setStrokeCap(stroke.cap.value());
@@ -166,11 +169,18 @@ public:
166
169
  strokePaint.setStrokeMiter(stroke.miter_limit.value());
167
170
  }
168
171
 
172
+ float precision = stroke.precision.value_or(1.0f);
173
+
169
174
  SkPathBuilder resultBuilder;
170
- if (!skpathutils::FillPathWithPaint(*p, strokePaint, &resultBuilder)) {
171
- throw std::runtime_error("Failed to apply stroke to path");
175
+ auto ctm = SkMatrix::Scale(precision, precision);
176
+ if (skpathutils::FillPathWithPaint(*p, strokePaint, &resultBuilder,
177
+ nullptr, ctm)) {
178
+ pathToUse = std::make_shared<const SkPath>(resultBuilder.snapshot());
179
+ } else {
180
+ // The JS player keeps the unstroked path when Path.Stroke returns
181
+ // null (e.g. a hairline width of 0).
182
+ pathToUse = std::const_pointer_cast<const SkPath>(p);
172
183
  }
173
- pathToUse = std::make_shared<const SkPath>(resultBuilder.snapshot());
174
184
  } else {
175
185
  pathToUse = std::const_pointer_cast<const SkPath>(p);
176
186
  }
@@ -135,8 +135,22 @@ private:
135
135
  auto sourceAlpha = SkImageFilters::ColorFilter(
136
136
  SkColorFilters::Blend(SK_ColorBLACK, SkBlendMode::kSrcIn), nullptr);
137
137
 
138
+ // The shadow is generated outside the shape and then clipped back into it,
139
+ // so "outside" has to be the complement of the shape's silhouette. Taking
140
+ // SrcOut against the source graphic itself makes it the complement of the
141
+ // source's *alpha*: inside a translucent shape 1 - alpha is non-zero, so
142
+ // the shadow is generated across the whole interior and tints it, no matter
143
+ // how small the blur and the offset are (issue #2990). Saturating alpha
144
+ // first keeps the shadow tied to the shape's outline instead of to its
145
+ // opacity. 255 is the saturation point of an 8-bit alpha channel, so one
146
+ // unit of coverage is enough to reach 1.
147
+ static constexpr float kAlphaSaturate[20] = {
148
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0};
149
+ auto coverage = SkImageFilters::ColorFilter(
150
+ SkColorFilters::Matrix(kAlphaSaturate), nullptr);
151
+
138
152
  auto f1 = SkImageFilters::ColorFilter(
139
- SkColorFilters::Blend(color, SkBlendMode::kSrcOut), nullptr);
153
+ SkColorFilters::Blend(color, SkBlendMode::kSrcOut), coverage);
140
154
 
141
155
  auto f2 = SkImageFilters::Offset(dx, dy, f1);
142
156
  auto f3 = SkImageFilters::Blur(sigmaX, sigmaY, SkTileMode::kDecal, f2);
@@ -154,8 +154,15 @@ public:
154
154
  }
155
155
  ctx->savePaint();
156
156
  if (standalone) {
157
+ // Reset the paint that savePaint() just pushed instead of pushing a
158
+ // second one: the matching RestorePaintDeclaration pops a single frame,
159
+ // so an extra push would leave the enclosing group's opacity on the
160
+ // stack and leak it onto every sibling drawn afterwards. The fresh
161
+ // paint is anti-aliased to match the TS player, which resets with
162
+ // Skia.Paint().
157
163
  SkPaint freshPaint;
158
- ctx->pushPaint(freshPaint);
164
+ freshPaint.setAntiAlias(true);
165
+ ctx->getPaint() = freshPaint;
159
166
  }
160
167
  auto &paint = ctx->getPaint();
161
168
  if (props.opacity.has_value()) {
@@ -0,0 +1,99 @@
1
+ #include "JSICache.h"
2
+
3
+ #include <iterator>
4
+ #include <memory>
5
+ #include <stdexcept>
6
+ #include <string>
7
+ #include <unordered_map>
8
+ #include <utility>
9
+
10
+ namespace RNJsi {
11
+
12
+ namespace {
13
+ // Hidden global holding the per-runtime cache object. Named so it is
14
+ // obviously ours if it ever shows up in a debugger or heap snapshot.
15
+ constexpr const char *kGlobalName = "__rnskiaJsiCache";
16
+ } // namespace
17
+
18
+ std::mutex JSICache::_registryMutex;
19
+ std::unordered_map<jsi::Runtime *, std::weak_ptr<JSICache>> JSICache::_registry;
20
+
21
+ JSICache &JSICache::get(jsi::Runtime &runtime) {
22
+ {
23
+ std::lock_guard<std::mutex> lock(_registryMutex);
24
+ auto it = _registry.find(&runtime);
25
+ if (it != _registry.end()) {
26
+ if (auto cache = it->second.lock()) {
27
+ // The cache is kept alive by `runtime`'s global, so the raw
28
+ // reference stays valid for as long as the caller may legally use
29
+ // `runtime` on this thread.
30
+ return *cache;
31
+ }
32
+ // Expired: the runtime that owned this entry is gone and `&runtime` is
33
+ // a new runtime reusing the address. Fall through and recreate.
34
+ }
35
+ }
36
+ auto cache = getOrCreateOnGlobal(runtime);
37
+ {
38
+ std::lock_guard<std::mutex> lock(_registryMutex);
39
+ // Drop entries of runtimes that are gone so the map does not grow by one
40
+ // stale pointer per reload / worklet runtime.
41
+ for (auto it = _registry.begin(); it != _registry.end();) {
42
+ it = it->second.expired() ? _registry.erase(it) : std::next(it);
43
+ }
44
+ _registry[&runtime] = cache;
45
+ }
46
+ return *cache;
47
+ }
48
+
49
+ std::shared_ptr<JSICache> JSICache::getOrCreateOnGlobal(jsi::Runtime &runtime) {
50
+ auto global = runtime.global();
51
+ auto existing = global.getProperty(runtime, kGlobalName);
52
+ if (existing.isObject()) {
53
+ auto obj = existing.getObject(runtime);
54
+ if (obj.hasNativeState<JSICache>(runtime)) {
55
+ return obj.getNativeState<JSICache>(runtime);
56
+ }
57
+ }
58
+ if (!existing.isUndefined()) {
59
+ // Something else already claimed our global. The most likely cause is two
60
+ // copies of react-native-skia loaded in one process (each has its own
61
+ // JSICache type, so the NativeState check above fails). Say so instead of
62
+ // letting Object.defineProperty throw a bare TypeError on the
63
+ // non-configurable property.
64
+ throw std::runtime_error(
65
+ std::string("react-native-skia: global.") + kGlobalName +
66
+ " already exists but is not our JSICache. Is react-native-skia "
67
+ "linked twice, or is another module using this global?");
68
+ }
69
+ auto cache = std::make_shared<JSICache>();
70
+ jsi::Object holder(runtime);
71
+ holder.setNativeState(runtime, cache);
72
+ // Non-enumerable, non-writable, non-configurable: JS code must not be able
73
+ // to drop the holder (which would finalize the cache under our feet).
74
+ auto objectCtor = global.getPropertyAsObject(runtime, "Object");
75
+ auto defineProperty =
76
+ objectCtor.getPropertyAsFunction(runtime, "defineProperty");
77
+ jsi::Object descriptor(runtime);
78
+ descriptor.setProperty(runtime, "value", std::move(holder));
79
+ descriptor.setProperty(runtime, "writable", false);
80
+ descriptor.setProperty(runtime, "enumerable", false);
81
+ descriptor.setProperty(runtime, "configurable", false);
82
+ defineProperty.call(runtime, global,
83
+ jsi::String::createFromAscii(runtime, kGlobalName),
84
+ descriptor);
85
+ return cache;
86
+ }
87
+
88
+ jsi::Object *JSICache::getPrototype(PrototypeKey key) {
89
+ auto it = _prototypes.find(key);
90
+ return it == _prototypes.end() ? nullptr : &it->second;
91
+ }
92
+
93
+ jsi::Object &JSICache::setPrototype(PrototypeKey key, jsi::Object prototype) {
94
+ auto [it, inserted] = _prototypes.insert_or_assign(key, std::move(prototype));
95
+ (void)inserted;
96
+ return it->second;
97
+ }
98
+
99
+ } // namespace RNJsi
@@ -0,0 +1,90 @@
1
+ #pragma once
2
+
3
+ #include <jsi/jsi.h>
4
+
5
+ #include <memory>
6
+ #include <mutex>
7
+ #include <typeindex>
8
+ #include <unordered_map>
9
+
10
+ namespace RNJsi {
11
+
12
+ namespace jsi = facebook::jsi;
13
+
14
+ /**
15
+ * Per-runtime store for JSI values that native code wants to keep across
16
+ * calls (currently: the prototype object of every NativeObject class).
17
+ *
18
+ * Ownership model (borrowed from react-native-nitro-modules' JSICache):
19
+ *
20
+ * - One JSICache exists per jsi::Runtime. It is attached as the NativeState
21
+ * of a hidden object stored on that runtime's `global`, so the RUNTIME owns
22
+ * the cache. When the runtime is destroyed the engine finalizes the object,
23
+ * ~JSICache runs while the runtime is still alive, and every cached
24
+ * jsi::Object is released correctly. Nothing outlives its runtime and
25
+ * nothing has to be leaked.
26
+ *
27
+ * - Native code reaches the cache through JSICache::get(runtime). A static
28
+ * map of std::weak_ptr, keyed by jsi::Runtime*, makes the lookup cheap; the
29
+ * pointer is only a map key, never proof that a runtime is alive. If the
30
+ * weak_ptr is expired (the runtime that owned it is gone) the entry is
31
+ * recreated on the runtime asking for it. This is what makes an in-process
32
+ * runtime recreate (expo-updates OTA apply, DevSettings.reload) safe even
33
+ * when the new runtime is allocated at the address of the freed one: the
34
+ * old cache was destroyed with the old runtime, so the reused address can
35
+ * only ever resolve to a fresh cache.
36
+ *
37
+ * - The cache itself is not locked: a jsi::Runtime is single-threaded, and
38
+ * the cache is only ever touched from its runtime's thread. Only the static
39
+ * runtime -> cache map is guarded.
40
+ *
41
+ * The same mechanism works for worklet runtimes (Reanimated UI runtime,
42
+ * createWorkletRuntime, ...); each gets its own JSICache.
43
+ */
44
+ class JSICache final : public jsi::NativeState {
45
+ public:
46
+ /**
47
+ * Key identifying one cached prototype: the C++ type of the NativeObject
48
+ * subclass (std::type_index(typeid(Derived))), as in Nitro. It prints as
49
+ * the class name in a debugger.
50
+ */
51
+ using PrototypeKey = std::type_index;
52
+
53
+ /**
54
+ * Returns the cache owned by `runtime`, creating it on first use. Must be
55
+ * called on the runtime's thread.
56
+ */
57
+ static JSICache &get(jsi::Runtime &runtime);
58
+
59
+ /**
60
+ * Returns the cached prototype for `key` or nullptr if none was stored.
61
+ *
62
+ * The pointer points into `_prototypes`. std::unordered_map never
63
+ * invalidates pointers/references to elements on insert or rehash (only on
64
+ * erase of that element, which we never do), so it stays valid for as long
65
+ * as the runtime does. Do not swap the container for one without that
66
+ * guarantee (std::vector, absl::flat_hash_map, ...).
67
+ */
68
+ jsi::Object *getPrototype(PrototypeKey key);
69
+
70
+ /**
71
+ * Stores (or replaces) the prototype for `key` and returns it.
72
+ */
73
+ jsi::Object &setPrototype(PrototypeKey key, jsi::Object prototype);
74
+
75
+ JSICache() = default;
76
+ ~JSICache() override = default;
77
+ JSICache(const JSICache &) = delete;
78
+ JSICache &operator=(const JSICache &) = delete;
79
+
80
+ private:
81
+ static std::shared_ptr<JSICache> getOrCreateOnGlobal(jsi::Runtime &runtime);
82
+
83
+ std::unordered_map<PrototypeKey, jsi::Object> _prototypes;
84
+
85
+ // Runtime -> cache. Weak on purpose: the runtime's global owns the cache.
86
+ static std::mutex _registryMutex;
87
+ static std::unordered_map<jsi::Runtime *, std::weak_ptr<JSICache>> _registry;
88
+ };
89
+
90
+ } // namespace RNJsi