@shopify/react-native-skia 2.6.9 → 2.7.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 (145) hide show
  1. package/android/cpp/jni/include/JniSkiaBaseView.h +6 -4
  2. package/android/cpp/jni/include/JniSkiaPictureView.h +8 -6
  3. package/android/cpp/rnskia-android/OpenGLContext.h +10 -1
  4. package/android/cpp/rnskia-android/RNSkAndroidView.h +8 -8
  5. package/android/cpp/rnskia-android/RNSkOpenGLCanvasProvider.cpp +9 -5
  6. package/android/cpp/rnskia-android/RNSkOpenGLCanvasProvider.h +4 -2
  7. package/android/src/main/java/com/shopify/reactnative/skia/SkiaBaseView.java +48 -12
  8. package/android/src/main/java/com/shopify/reactnative/skia/SkiaBaseViewManager.java +5 -0
  9. package/android/src/main/java/com/shopify/reactnative/skia/SkiaPictureView.java +2 -2
  10. package/android/src/main/java/com/shopify/reactnative/skia/ViewScreenshotService.java +11 -7
  11. package/apple/MetalContext.h +7 -6
  12. package/apple/MetalLayerColorSpace.mm +39 -0
  13. package/apple/MetalLayerColorSpaceUtils.h +34 -0
  14. package/apple/MetalWindowContext.h +3 -1
  15. package/apple/MetalWindowContext.mm +9 -8
  16. package/apple/RNSkAppleView.h +6 -0
  17. package/apple/RNSkMetalCanvasProvider.h +2 -0
  18. package/apple/RNSkMetalCanvasProvider.mm +15 -2
  19. package/apple/SkiaPictureView.mm +1 -0
  20. package/apple/SkiaPictureViewManager.mm +5 -0
  21. package/apple/SkiaUIView.h +1 -0
  22. package/apple/SkiaUIView.mm +9 -0
  23. package/cpp/api/JsiSkSurface.h +8 -2
  24. package/cpp/rnskia/RNDawnContext.h +4 -3
  25. package/cpp/rnskia/RNDawnUtils.h +25 -6
  26. package/cpp/rnskia/RNDawnWindowContext.h +44 -10
  27. package/cpp/rnskia/RNMetalLayerColorSpace.h +16 -0
  28. package/cpp/rnskia/RNSkJsiViewApi.h +46 -24
  29. package/cpp/rnwgpu/SurfaceRegistry.h +10 -4
  30. package/lib/commonjs/Platform/Platform.web.js +2 -90
  31. package/lib/commonjs/Platform/Platform.web.js.map +1 -1
  32. package/lib/commonjs/mock/index.js +1 -0
  33. package/lib/commonjs/mock/index.js.map +1 -1
  34. package/lib/commonjs/renderer/Canvas.d.ts +10 -1
  35. package/lib/commonjs/renderer/Canvas.js +2 -0
  36. package/lib/commonjs/renderer/Canvas.js.map +1 -1
  37. package/lib/commonjs/skia/types/Surface/Surface.d.ts +5 -1
  38. package/lib/commonjs/skia/types/Surface/Surface.js.map +1 -1
  39. package/lib/commonjs/skia/web/JsiSkParagraphStyle.js +14 -3
  40. package/lib/commonjs/skia/web/JsiSkParagraphStyle.js.map +1 -1
  41. package/lib/commonjs/skia/web/JsiSkSurface.d.ts +1 -1
  42. package/lib/commonjs/skia/web/JsiSkSurface.js +2 -1
  43. package/lib/commonjs/skia/web/JsiSkSurface.js.map +1 -1
  44. package/lib/commonjs/sksg/Container.native.d.ts +1 -0
  45. package/lib/commonjs/sksg/Container.native.js +11 -0
  46. package/lib/commonjs/sksg/Container.native.js.map +1 -1
  47. package/lib/commonjs/sksg/Container.web.d.ts +1 -0
  48. package/lib/commonjs/sksg/Container.web.js +11 -0
  49. package/lib/commonjs/sksg/Container.web.js.map +1 -1
  50. package/lib/commonjs/specs/NativeSkiaModule.web.d.ts +2 -0
  51. package/lib/commonjs/specs/NativeSkiaModule.web.js +32 -7
  52. package/lib/commonjs/specs/NativeSkiaModule.web.js.map +1 -1
  53. package/lib/commonjs/specs/SkiaPictureViewNativeComponent.d.ts +1 -0
  54. package/lib/commonjs/specs/SkiaPictureViewNativeComponent.js.map +1 -1
  55. package/lib/commonjs/views/SkiaPictureView.js +2 -0
  56. package/lib/commonjs/views/SkiaPictureView.js.map +1 -1
  57. package/lib/commonjs/views/SkiaPictureView.web.js +279 -94
  58. package/lib/commonjs/views/SkiaPictureView.web.js.map +1 -1
  59. package/lib/commonjs/views/formats.d.ts +1 -0
  60. package/lib/commonjs/views/formats.js +28 -0
  61. package/lib/commonjs/views/formats.js.map +1 -0
  62. package/lib/commonjs/views/index.d.ts +1 -0
  63. package/lib/commonjs/views/index.js +11 -0
  64. package/lib/commonjs/views/index.js.map +1 -1
  65. package/lib/commonjs/views/types.d.ts +6 -0
  66. package/lib/commonjs/views/types.js.map +1 -1
  67. package/lib/module/Platform/Platform.web.js +3 -91
  68. package/lib/module/Platform/Platform.web.js.map +1 -1
  69. package/lib/module/mock/index.js +1 -0
  70. package/lib/module/mock/index.js.map +1 -1
  71. package/lib/module/renderer/Canvas.d.ts +10 -1
  72. package/lib/module/renderer/Canvas.js +2 -0
  73. package/lib/module/renderer/Canvas.js.map +1 -1
  74. package/lib/module/skia/types/Surface/Surface.d.ts +5 -1
  75. package/lib/module/skia/types/Surface/Surface.js.map +1 -1
  76. package/lib/module/skia/web/JsiSkParagraphStyle.js +14 -3
  77. package/lib/module/skia/web/JsiSkParagraphStyle.js.map +1 -1
  78. package/lib/module/skia/web/JsiSkSurface.d.ts +1 -1
  79. package/lib/module/skia/web/JsiSkSurface.js +2 -1
  80. package/lib/module/skia/web/JsiSkSurface.js.map +1 -1
  81. package/lib/module/sksg/Container.native.d.ts +1 -0
  82. package/lib/module/sksg/Container.native.js +11 -0
  83. package/lib/module/sksg/Container.native.js.map +1 -1
  84. package/lib/module/sksg/Container.web.d.ts +1 -0
  85. package/lib/module/sksg/Container.web.js +11 -0
  86. package/lib/module/sksg/Container.web.js.map +1 -1
  87. package/lib/module/specs/NativeSkiaModule.web.d.ts +2 -0
  88. package/lib/module/specs/NativeSkiaModule.web.js +32 -7
  89. package/lib/module/specs/NativeSkiaModule.web.js.map +1 -1
  90. package/lib/module/specs/SkiaPictureViewNativeComponent.d.ts +1 -0
  91. package/lib/module/specs/SkiaPictureViewNativeComponent.js.map +1 -1
  92. package/lib/module/views/SkiaPictureView.js +2 -0
  93. package/lib/module/views/SkiaPictureView.js.map +1 -1
  94. package/lib/module/views/SkiaPictureView.web.js +280 -95
  95. package/lib/module/views/SkiaPictureView.web.js.map +1 -1
  96. package/lib/module/views/formats.d.ts +1 -0
  97. package/lib/module/views/formats.js +22 -0
  98. package/lib/module/views/formats.js.map +1 -0
  99. package/lib/module/views/index.d.ts +1 -0
  100. package/lib/module/views/index.js +1 -0
  101. package/lib/module/views/index.js.map +1 -1
  102. package/lib/module/views/types.d.ts +6 -0
  103. package/lib/module/views/types.js.map +1 -1
  104. package/lib/typescript/lib/commonjs/Platform/Platform.web.d.ts +1 -2
  105. package/lib/typescript/lib/commonjs/mock/index.d.ts +1 -0
  106. package/lib/typescript/lib/commonjs/renderer/Canvas.d.ts +2 -1
  107. package/lib/typescript/lib/commonjs/skia/web/JsiSkSurface.d.ts +1 -1
  108. package/lib/typescript/lib/commonjs/sksg/Container.native.d.ts +1 -1
  109. package/lib/typescript/lib/commonjs/sksg/Container.web.d.ts +1 -1
  110. package/lib/typescript/lib/commonjs/views/formats.d.ts +2 -0
  111. package/lib/typescript/lib/module/Platform/Platform.web.d.ts +1 -3
  112. package/lib/typescript/lib/module/mock/index.d.ts +2 -2
  113. package/lib/typescript/lib/module/renderer/Canvas.d.ts +2 -1
  114. package/lib/typescript/lib/module/skia/web/JsiSkSurface.d.ts +1 -1
  115. package/lib/typescript/lib/module/sksg/Container.native.d.ts +1 -1
  116. package/lib/typescript/lib/module/sksg/Container.web.d.ts +1 -1
  117. package/lib/typescript/lib/module/views/formats.d.ts +1 -0
  118. package/lib/typescript/lib/module/views/index.d.ts +1 -0
  119. package/lib/typescript/src/renderer/Canvas.d.ts +10 -1
  120. package/lib/typescript/src/skia/types/Surface/Surface.d.ts +5 -1
  121. package/lib/typescript/src/skia/web/JsiSkSurface.d.ts +1 -1
  122. package/lib/typescript/src/sksg/Container.native.d.ts +1 -0
  123. package/lib/typescript/src/sksg/Container.web.d.ts +1 -0
  124. package/lib/typescript/src/specs/NativeSkiaModule.web.d.ts +2 -0
  125. package/lib/typescript/src/specs/SkiaPictureViewNativeComponent.d.ts +1 -0
  126. package/lib/typescript/src/views/formats.d.ts +1 -0
  127. package/lib/typescript/src/views/index.d.ts +1 -0
  128. package/lib/typescript/src/views/types.d.ts +6 -0
  129. package/package.json +4 -1
  130. package/react-native-skia.podspec +1 -0
  131. package/src/Platform/Platform.web.tsx +7 -99
  132. package/src/mock/index.ts +1 -0
  133. package/src/renderer/Canvas.tsx +11 -0
  134. package/src/skia/types/Surface/Surface.ts +5 -1
  135. package/src/skia/web/JsiSkParagraphStyle.ts +16 -2
  136. package/src/skia/web/JsiSkSurface.ts +2 -1
  137. package/src/sksg/Container.native.ts +12 -0
  138. package/src/sksg/Container.web.ts +12 -0
  139. package/src/specs/NativeSkiaModule.web.ts +39 -7
  140. package/src/specs/SkiaPictureViewNativeComponent.ts +1 -0
  141. package/src/views/SkiaPictureView.tsx +2 -0
  142. package/src/views/SkiaPictureView.web.tsx +318 -106
  143. package/src/views/formats.ts +21 -0
  144. package/src/views/index.ts +1 -0
  145. package/src/views/types.ts +7 -0
@@ -31,13 +31,15 @@ public:
31
31
 
32
32
  protected:
33
33
  virtual void surfaceAvailable(jobject surface, int width, int height,
34
- bool opaque) {
35
- _skiaAndroidView->surfaceAvailable(surface, width, height, opaque);
34
+ bool opaque, bool highBitDepth) {
35
+ _skiaAndroidView->surfaceAvailable(surface, width, height, opaque,
36
+ highBitDepth);
36
37
  }
37
38
 
38
39
  virtual void surfaceSizeChanged(jobject surface, int width, int height,
39
- bool opaque) {
40
- _skiaAndroidView->surfaceSizeChanged(surface, width, height, opaque);
40
+ bool opaque, bool highBitDepth) {
41
+ _skiaAndroidView->surfaceSizeChanged(surface, width, height, opaque,
42
+ highBitDepth);
41
43
  }
42
44
 
43
45
  virtual void surfaceDestroyed() { _skiaAndroidView->surfaceDestroyed(); }
@@ -63,14 +63,16 @@ public:
63
63
  }
64
64
 
65
65
  protected:
66
- void surfaceAvailable(jobject surface, int width, int height,
67
- bool opaque) override {
68
- JniSkiaBaseView::surfaceAvailable(surface, width, height, opaque);
66
+ void surfaceAvailable(jobject surface, int width, int height, bool opaque,
67
+ bool highBitDepth) override {
68
+ JniSkiaBaseView::surfaceAvailable(surface, width, height, opaque,
69
+ highBitDepth);
69
70
  }
70
71
 
71
- void surfaceSizeChanged(jobject surface, int width, int height,
72
- bool opaque) override {
73
- JniSkiaBaseView::surfaceSizeChanged(surface, width, height, opaque);
72
+ void surfaceSizeChanged(jobject surface, int width, int height, bool opaque,
73
+ bool highBitDepth) override {
74
+ JniSkiaBaseView::surfaceSizeChanged(surface, width, height, opaque,
75
+ highBitDepth);
74
76
  }
75
77
 
76
78
  void surfaceDestroyed() override { JniSkiaBaseView::surfaceDestroyed(); }
@@ -179,8 +179,17 @@ public:
179
179
  }
180
180
 
181
181
  // TODO: remove width, height
182
- std::unique_ptr<WindowContext> MakeWindow(ANativeWindow *window) {
182
+ std::unique_ptr<WindowContext> MakeWindow(ANativeWindow *window,
183
+ bool highBitDepth = false) {
183
184
  auto display = OpenGLSharedContext::getInstance().getDisplay();
185
+ if (highBitDepth) {
186
+ // A 10-bit window surface would require the shared EGL context to be
187
+ // created without a config (EGL_KHR_no_config_context) for every app,
188
+ // which is too risky for existing 8-bit clients on brittle drivers.
189
+ RNSkLogger::logToConsole(
190
+ "highBitDepth is not supported on the OpenGL backend, falling back "
191
+ "to the 8-bit format (the Graphite backend supports it)");
192
+ }
184
193
  return std::make_unique<OpenGLWindowContext>(
185
194
  _directContext.get(), display, _glContext.get(), window,
186
195
  OpenGLSharedContext::getInstance().getConfig());
@@ -12,12 +12,12 @@ namespace RNSkia {
12
12
  class RNSkBaseAndroidView {
13
13
  public:
14
14
  virtual void surfaceAvailable(jobject surface, int width, int height,
15
- bool opaque) = 0;
15
+ bool opaque, bool highBitDepth) = 0;
16
16
 
17
17
  virtual void surfaceDestroyed() = 0;
18
18
 
19
19
  virtual void surfaceSizeChanged(jobject surface, int width, int height,
20
- bool opaque) = 0;
20
+ bool opaque, bool highBitDepth) = 0;
21
21
 
22
22
  virtual float getPixelDensity() = 0;
23
23
 
@@ -34,10 +34,10 @@ public:
34
34
  std::make_shared<RNSkOpenGLCanvasProvider>(
35
35
  std::bind(&RNSkia::RNSkView::requestRedraw, this), context)) {}
36
36
 
37
- void surfaceAvailable(jobject surface, int width, int height,
38
- bool opaque) override {
37
+ void surfaceAvailable(jobject surface, int width, int height, bool opaque,
38
+ bool highBitDepth) override {
39
39
  std::static_pointer_cast<RNSkOpenGLCanvasProvider>(T::getCanvasProvider())
40
- ->surfaceAvailable(surface, width, height, opaque);
40
+ ->surfaceAvailable(surface, width, height, opaque, highBitDepth);
41
41
  RNSkView::redraw();
42
42
  }
43
43
 
@@ -46,10 +46,10 @@ public:
46
46
  ->surfaceDestroyed();
47
47
  }
48
48
 
49
- void surfaceSizeChanged(jobject surface, int width, int height,
50
- bool opaque) override {
49
+ void surfaceSizeChanged(jobject surface, int width, int height, bool opaque,
50
+ bool highBitDepth) override {
51
51
  std::static_pointer_cast<RNSkOpenGLCanvasProvider>(T::getCanvasProvider())
52
- ->surfaceSizeChanged(surface, width, height, opaque);
52
+ ->surfaceSizeChanged(surface, width, height, opaque, highBitDepth);
53
53
  // This is only need for the first time to frame, this renderImmediate call
54
54
  // will invoke updateTexImage for the previous frame
55
55
  RNSkView::redraw();
@@ -78,7 +78,8 @@ bool RNSkOpenGLCanvasProvider::renderToCanvas(
78
78
 
79
79
  void RNSkOpenGLCanvasProvider::surfaceAvailable(jobject jSurfaceTexture,
80
80
  int width, int height,
81
- bool opaque) {
81
+ bool opaque,
82
+ bool highBitDepth) {
82
83
  // Release the old surface
83
84
  _surfaceHolder = nullptr;
84
85
 
@@ -108,9 +109,11 @@ void RNSkOpenGLCanvasProvider::surfaceAvailable(jobject jSurfaceTexture,
108
109
  window = ANativeWindow_fromSurface(env, jSurfaceTexture);
109
110
  }
110
111
  #if defined(SK_GRAPHITE)
111
- _surfaceHolder = DawnContext::getInstance().MakeWindow(window, width, height);
112
+ _surfaceHolder = DawnContext::getInstance().MakeWindow(window, width, height,
113
+ highBitDepth);
112
114
  #else
113
- _surfaceHolder = OpenGLContext::getInstance().MakeWindow(window);
115
+ _surfaceHolder =
116
+ OpenGLContext::getInstance().MakeWindow(window, highBitDepth);
114
117
  #endif
115
118
 
116
119
  // Post redraw request to ensure we paint in the next draw cycle.
@@ -128,7 +131,8 @@ void RNSkOpenGLCanvasProvider::surfaceDestroyed() {
128
131
  }
129
132
 
130
133
  void RNSkOpenGLCanvasProvider::surfaceSizeChanged(jobject jSurface, int width,
131
- int height, bool opaque) {
134
+ int height, bool opaque,
135
+ bool highBitDepth) {
132
136
  if (width == 0 && height == 0) {
133
137
  // Setting width/height to zero is nothing we need to care about when
134
138
  // it comes to invalidating the surface.
@@ -137,7 +141,7 @@ void RNSkOpenGLCanvasProvider::surfaceSizeChanged(jobject jSurface, int width,
137
141
 
138
142
  if (_surfaceHolder == nullptr) {
139
143
  _surfaceHolder = nullptr;
140
- surfaceAvailable(jSurface, width, height, opaque);
144
+ surfaceAvailable(jSurface, width, height, opaque, highBitDepth);
141
145
  } else {
142
146
  _surfaceHolder->resize(width, height);
143
147
  }
@@ -27,11 +27,13 @@ public:
27
27
 
28
28
  bool renderToCanvas(const std::function<void(SkCanvas *)> &cb) override;
29
29
 
30
- void surfaceAvailable(jobject surface, int width, int height, bool opaque);
30
+ void surfaceAvailable(jobject surface, int width, int height, bool opaque,
31
+ bool highBitDepth);
31
32
 
32
33
  void surfaceDestroyed();
33
34
 
34
- void surfaceSizeChanged(jobject jSurface, int width, int height, bool opaque);
35
+ void surfaceSizeChanged(jobject jSurface, int width, int height, bool opaque,
36
+ bool highBitDepth);
35
37
 
36
38
  private:
37
39
  std::unique_ptr<WindowContext> _surfaceHolder = nullptr;
@@ -13,6 +13,8 @@ import com.facebook.react.views.view.ReactViewGroup;
13
13
  public abstract class SkiaBaseView extends ReactViewGroup implements SkiaViewAPI {
14
14
  private View mView;
15
15
 
16
+ private boolean mHighBitDepth = false;
17
+
16
18
  private final boolean debug = false;
17
19
  private final String tag = "SkiaView";
18
20
 
@@ -34,14 +36,48 @@ public abstract class SkiaBaseView extends ReactViewGroup implements SkiaViewAPI
34
36
 
35
37
  public void setOpaque(boolean value) {
36
38
  if (value && mView instanceof SkiaTextureView) {
37
- removeView(mView);
38
- mView = new SkiaSurfaceView(getContext(), this, debug);
39
- addView(mView);
39
+ recreateView(true);
40
40
  } else if (!value && mView instanceof SkiaSurfaceView) {
41
- removeView(mView);
42
- mView = new SkiaTextureView(getContext(), this, debug);
43
- addView(mView);
41
+ recreateView(false);
42
+ }
43
+ }
44
+
45
+ public void setHighBitDepth(boolean value) {
46
+ if (mHighBitDepth == value) {
47
+ return;
48
+ }
49
+ mHighBitDepth = value;
50
+ // The flag only affects the opaque SurfaceView path (see
51
+ // highBitDepthIfOpaque), so only that surface needs to be recreated
52
+ // with the new buffer format.
53
+ if (mView instanceof SkiaSurfaceView) {
54
+ recreateView(true);
55
+ }
56
+ }
57
+
58
+ private void recreateView(boolean useSurfaceView) {
59
+ removeView(mView);
60
+ mView = useSurfaceView
61
+ ? new SkiaSurfaceView(getContext(), this, debug)
62
+ : new SkiaTextureView(getContext(), this, debug);
63
+ addView(mView);
64
+ // React Native sizes native children explicitly through onLayout, so
65
+ // the requestLayout triggered by addView is ignored; size the new
66
+ // child ourselves or it stays 0x0 and never gets a surface.
67
+ if (getWidth() > 0 || getHeight() > 0) {
68
+ mView.layout(0, 0, getWidth(), getHeight());
69
+ }
70
+ }
71
+
72
+ private boolean highBitDepthIfOpaque(boolean opaque) {
73
+ if (mHighBitDepth && !opaque) {
74
+ // The 10-bit buffer format only has 2 bits of alpha, which would
75
+ // visibly break translucency; the extra precision would also be
76
+ // lost in the 8-bit composition pass.
77
+ Log.w(tag, "highBitDepth requires the opaque prop on Android, falling back to the 8-bit format");
78
+ return false;
44
79
  }
80
+ return mHighBitDepth;
45
81
  }
46
82
 
47
83
  void dropInstance() {
@@ -59,24 +95,24 @@ public abstract class SkiaBaseView extends ReactViewGroup implements SkiaViewAPI
59
95
 
60
96
  @Override
61
97
  public void onSurfaceCreated(Surface surface, int width, int height) {
62
- surfaceAvailable(surface, width, height, true);
98
+ surfaceAvailable(surface, width, height, true, mHighBitDepth);
63
99
  }
64
100
 
65
101
  @Override
66
102
  public void onSurfaceChanged(Surface surface, int width, int height) {
67
103
  Log.i(tag, "onSurfaceTextureSizeChanged " + width + "/" + height);
68
- surfaceSizeChanged(surface, width, height, true);
104
+ surfaceSizeChanged(surface, width, height, true, mHighBitDepth);
69
105
  }
70
106
 
71
107
  @Override
72
108
  public void onSurfaceTextureCreated(SurfaceTexture surface, int width, int height) {
73
- surfaceAvailable(surface, width, height, false);
109
+ surfaceAvailable(surface, width, height, false, highBitDepthIfOpaque(false));
74
110
  }
75
111
 
76
112
  @Override
77
113
  public void onSurfaceTextureChanged(SurfaceTexture surface, int width, int height) {
78
114
  Log.i(tag, "onSurfaceTextureSizeChanged " + width + "/" + height);
79
- surfaceSizeChanged(surface, width, height, false);
115
+ surfaceSizeChanged(surface, width, height, false, highBitDepthIfOpaque(false));
80
116
  }
81
117
 
82
118
  @Override
@@ -84,9 +120,9 @@ public abstract class SkiaBaseView extends ReactViewGroup implements SkiaViewAPI
84
120
  surfaceDestroyed();
85
121
  }
86
122
 
87
- protected abstract void surfaceAvailable(Object surface, int width, int height, boolean opaque);
123
+ protected abstract void surfaceAvailable(Object surface, int width, int height, boolean opaque, boolean highBitDepth);
88
124
 
89
- protected abstract void surfaceSizeChanged(Object surface, int width, int height, boolean opaque);
125
+ protected abstract void surfaceSizeChanged(Object surface, int width, int height, boolean opaque, boolean highBitDepth);
90
126
 
91
127
  protected abstract void surfaceDestroyed();
92
128
 
@@ -28,6 +28,11 @@ public abstract class SkiaBaseViewManager<T extends SkiaBaseView> extends ReactV
28
28
  ((SkiaBaseView)view).setOpaque(value);
29
29
  }
30
30
 
31
+ @ReactProp(name = "highBitDepth")
32
+ public void setHighBitDepth(T view, boolean value) {
33
+ ((SkiaBaseView)view).setHighBitDepth(value);
34
+ }
35
+
31
36
  @ReactProp(name = ViewProps.POINTER_EVENTS)
32
37
  public void setPointerEvents(T view, @Nullable String pointerEventsStr) {
33
38
  view.setPointerEvents(PointerEvents.parsePointerEvents(pointerEventsStr));
@@ -67,9 +67,9 @@ public class SkiaPictureView extends SkiaBaseView {
67
67
 
68
68
  private native HybridData initHybrid(SkiaManager skiaManager);
69
69
 
70
- protected native void surfaceAvailable(Object surface, int width, int height, boolean opaque);
70
+ protected native void surfaceAvailable(Object surface, int width, int height, boolean opaque, boolean highBitDepth);
71
71
 
72
- protected native void surfaceSizeChanged(Object surface, int width, int height, boolean opaque);
72
+ protected native void surfaceSizeChanged(Object surface, int width, int height, boolean opaque, boolean highBitDepth);
73
73
 
74
74
  protected native void surfaceDestroyed();
75
75
 
@@ -14,6 +14,7 @@ import android.view.SurfaceView;
14
14
  import android.view.TextureView;
15
15
  import android.view.View;
16
16
  import android.view.ViewGroup;
17
+ import android.widget.HorizontalScrollView;
17
18
  import android.widget.ScrollView;
18
19
  import androidx.annotation.NonNull;
19
20
  import com.facebook.react.bridge.ReactContext;
@@ -82,13 +83,16 @@ public class ViewScreenshotService {
82
83
  canvas.save();
83
84
  applyTransformations(canvas, view);
84
85
 
85
- // If the view is a ScrollView or similar, clip to its bounds
86
- if (view instanceof ScrollView) {
87
- ScrollView scrollView = (ScrollView) view;
88
- int clipLeft = scrollView.getScrollX();
89
- int clipTop = scrollView.getScrollY();
90
- int clipRight = clipLeft + scrollView.getWidth();
91
- int clipBottom = clipTop + scrollView.getHeight();
86
+ // If the view is a scroll container, clip to its viewport bounds.
87
+ // HorizontalScrollView does NOT extend ScrollView (both extend
88
+ // FrameLayout), so it must be checked explicitly — otherwise an
89
+ // oversized child (e.g. a wide Skia <Canvas> TextureView) bleeds past
90
+ // a horizontal ScrollView's viewport in the screenshot.
91
+ if (view instanceof ScrollView || view instanceof HorizontalScrollView) {
92
+ int clipLeft = view.getScrollX();
93
+ int clipTop = view.getScrollY();
94
+ int clipRight = clipLeft + view.getWidth();
95
+ int clipBottom = clipTop + view.getHeight();
92
96
 
93
97
  canvas.clipRect(clipLeft, clipTop, clipRight, clipBottom);
94
98
  }
@@ -95,13 +95,14 @@ public:
95
95
  }
96
96
  }
97
97
 
98
- std::unique_ptr<RNSkia::WindowContext>
99
- MakeWindow(CALayer *window, int width, int height,
100
- bool useP3ColorSpace = true) {
98
+ std::unique_ptr<RNSkia::WindowContext> MakeWindow(CALayer *window, int width,
99
+ int height,
100
+ bool useP3ColorSpace = true,
101
+ bool highBitDepth = false) {
101
102
  auto device = _device;
102
- return std::make_unique<MetalWindowContext>(_directContext.get(), device,
103
- _commandQueue, window, width,
104
- height, useP3ColorSpace);
103
+ return std::make_unique<MetalWindowContext>(
104
+ _directContext.get(), device, _commandQueue, window, width, height,
105
+ useP3ColorSpace, highBitDepth);
105
106
  }
106
107
 
107
108
  GrDirectContext *getDirectContext() { return _directContext.get(); }
@@ -0,0 +1,39 @@
1
+ #ifdef SK_GRAPHITE
2
+
3
+ #import <CoreGraphics/CoreGraphics.h>
4
+ #import <Foundation/Foundation.h>
5
+ #import <QuartzCore/CAMetalLayer.h>
6
+ #import <QuartzCore/CATransaction.h>
7
+
8
+ #include "MetalLayerColorSpaceUtils.h"
9
+ #include "rnskia/RNMetalLayerColorSpace.h"
10
+
11
+ namespace RNSkia {
12
+
13
+ // WebGPU canvas values are sRGB-encoded regardless of the texture format
14
+ // (GPUCanvasConfiguration.colorSpace defaults to "srgb"), so the same shader
15
+ // output must display identically on bgra8unorm and rgba16float surfaces.
16
+ // Tagging the float layer as (gamma-encoded) extended sRGB matches the
17
+ // browser behavior: identical colors, with the extra precision of float16.
18
+ void applyCAMetalLayerColorSpace(void *nativeSurface,
19
+ wgpu::TextureFormat format) {
20
+ CALayer *layer = (__bridge CALayer *)nativeSurface;
21
+ if (![layer isKindOfClass:[CAMetalLayer class]]) {
22
+ return;
23
+ }
24
+ auto metalLayer = static_cast<CAMetalLayer *>(layer);
25
+ setCAMetalLayerColorSpace(metalLayer,
26
+ format == wgpu::TextureFormat::RGBA16Float, false);
27
+ // The change must be set synchronously so the first present already sees
28
+ // it, and it must reach the render server. On a non-main thread (RN JS or
29
+ // worklet runtime) the property lands in that thread's implicit
30
+ // CATransaction, which may never commit on threads without a spinning
31
+ // runloop, so flush it now. On the main thread the runloop commits it.
32
+ if (!NSThread.isMainThread) {
33
+ [CATransaction flush];
34
+ }
35
+ }
36
+
37
+ } // namespace RNSkia
38
+
39
+ #endif // SK_GRAPHITE
@@ -0,0 +1,34 @@
1
+ #pragma once
2
+
3
+ #import <CoreGraphics/CoreGraphics.h>
4
+ #import <QuartzCore/CAMetalLayer.h>
5
+
6
+ namespace RNSkia {
7
+
8
+ // The surface writes sRGB-encoded (SDR) values regardless of the texture
9
+ // format. With an 8-bit format a nil colorspace displays them as-is, but Core
10
+ // Animation interprets a float-format layer with a nil colorspace as extended
11
+ // linear sRGB, which displays the same values noticeably brighter. Tag the
12
+ // float layer with the matching gamma-encoded (extended) colorspace so colors
13
+ // are identical to the 8-bit path, only with more precision.
14
+ inline void setCAMetalLayerColorSpace(CAMetalLayer *layer, bool isFloatFormat,
15
+ bool useP3ColorSpace) {
16
+ if (isFloatFormat) {
17
+ CGColorSpaceRef colorSpace = CGColorSpaceCreateWithName(
18
+ useP3ColorSpace ? kCGColorSpaceExtendedDisplayP3
19
+ : kCGColorSpaceExtendedSRGB);
20
+ layer.colorspace = colorSpace;
21
+ CGColorSpaceRelease(colorSpace);
22
+ } else if (useP3ColorSpace) {
23
+ CGColorSpaceRef colorSpace =
24
+ CGColorSpaceCreateWithName(kCGColorSpaceDisplayP3);
25
+ layer.colorspace = colorSpace;
26
+ CGColorSpaceRelease(colorSpace);
27
+ } else if (layer.colorspace != nil) {
28
+ // Restore the default (no color matching) when reconfiguring a layer
29
+ // back to an 8-bit format.
30
+ layer.colorspace = nil;
31
+ }
32
+ }
33
+
34
+ } // namespace RNSkia
@@ -10,7 +10,8 @@ class MetalWindowContext : public RNSkia::WindowContext {
10
10
  public:
11
11
  MetalWindowContext(GrDirectContext *directContext, id<MTLDevice> device,
12
12
  id<MTLCommandQueue> commandQueue, CALayer *layer,
13
- int width, int height, bool useP3ColorSpace = true);
13
+ int width, int height, bool useP3ColorSpace = true,
14
+ bool highBitDepth = false);
14
15
  ~MetalWindowContext() = default;
15
16
 
16
17
  sk_sp<SkSurface> getSurface() override;
@@ -37,4 +38,5 @@ private:
37
38
  #pragma clang diagnostic pop
38
39
  id<CAMetalDrawable> _currentDrawable = nil;
39
40
  bool _useP3ColorSpace = false;
41
+ bool _highBitDepth = false;
40
42
  };
@@ -1,6 +1,7 @@
1
1
  #include "MetalWindowContext.h"
2
2
 
3
3
  #include "MetalContext.h"
4
+ #include "MetalLayerColorSpaceUtils.h"
4
5
  #include "RNSkLog.h"
5
6
  #include "include/core/SkColorSpace.h"
6
7
 
@@ -8,8 +9,9 @@ MetalWindowContext::MetalWindowContext(GrDirectContext *directContext,
8
9
  id<MTLDevice> device,
9
10
  id<MTLCommandQueue> commandQueue,
10
11
  CALayer *layer, int width, int height,
11
- bool useP3ColorSpace)
12
- : _directContext(directContext), _commandQueue(commandQueue) {
12
+ bool useP3ColorSpace, bool highBitDepth)
13
+ : _directContext(directContext), _commandQueue(commandQueue),
14
+ _highBitDepth(highBitDepth) {
13
15
  #pragma clang diagnostic push
14
16
  #pragma clang diagnostic ignored "-Wunguarded-availability-new"
15
17
  _layer = (CAMetalLayer *)layer;
@@ -22,7 +24,8 @@ MetalWindowContext::MetalWindowContext(GrDirectContext *directContext,
22
24
  #else
23
25
  _layer.contentsScale = [NSScreen mainScreen].backingScaleFactor;
24
26
  #endif // !TARGET_OS_OSX
25
- _layer.pixelFormat = MTLPixelFormatBGRA8Unorm;
27
+ _layer.pixelFormat =
28
+ _highBitDepth ? MTLPixelFormatRGBA16Float : MTLPixelFormatBGRA8Unorm;
26
29
  _layer.contentsGravity = kCAGravityBottomLeft;
27
30
  _layer.drawableSize = CGSizeMake(width, height);
28
31
  BOOL supportsWideColor = NO;
@@ -43,12 +46,9 @@ MetalWindowContext::MetalWindowContext(GrDirectContext *directContext,
43
46
  #endif // !TARGET_OS_OSX
44
47
  }
45
48
  if (supportsWideColor) {
46
- CGColorSpaceRef colorSpace =
47
- CGColorSpaceCreateWithName(kCGColorSpaceDisplayP3);
48
- _layer.colorspace = colorSpace;
49
- CGColorSpaceRelease(colorSpace);
50
49
  _useP3ColorSpace = true;
51
50
  }
51
+ RNSkia::setCAMetalLayerColorSpace(_layer, _highBitDepth, _useP3ColorSpace);
52
52
  }
53
53
 
54
54
  sk_sp<SkSurface> MetalWindowContext::getSurface() {
@@ -77,7 +77,8 @@ sk_sp<SkSurface> MetalWindowContext::getSurface() {
77
77
  : nullptr;
78
78
  _skSurface = SkSurfaces::WrapBackendRenderTarget(
79
79
  _directContext, backendRT, kTopLeft_GrSurfaceOrigin,
80
- kBGRA_8888_SkColorType, skColorSpace, nullptr);
80
+ _highBitDepth ? kRGBA_F16_SkColorType : kBGRA_8888_SkColorType,
81
+ skColorSpace, nullptr);
81
82
 
82
83
  return _skSurface;
83
84
  }
@@ -11,6 +11,7 @@ public:
11
11
  virtual CALayer *getLayer() = 0;
12
12
  virtual void setSize(int width, int height) = 0;
13
13
  virtual void setUseP3ColorSpace(bool useP3ColorSpace) = 0;
14
+ virtual void setHighBitDepth(bool highBitDepth) = 0;
14
15
  virtual std::shared_ptr<RNSkia::RNSkView> getDrawView() = 0;
15
16
  };
16
17
 
@@ -37,6 +38,11 @@ public:
37
38
  ->setUseP3ColorSpace(useP3ColorSpace);
38
39
  }
39
40
 
41
+ void setHighBitDepth(bool highBitDepth) override {
42
+ std::static_pointer_cast<RNSkMetalCanvasProvider>(this->getCanvasProvider())
43
+ ->setHighBitDepth(highBitDepth);
44
+ }
45
+
40
46
  std::shared_ptr<RNSkia::RNSkView> getDrawView() override {
41
47
  return this->shared_from_this();
42
48
  }
@@ -28,6 +28,7 @@ public:
28
28
 
29
29
  void setSize(int width, int height);
30
30
  void setUseP3ColorSpace(bool useP3ColorSpace);
31
+ void setHighBitDepth(bool highBitDepth);
31
32
  CALayer *getLayer();
32
33
 
33
34
  private:
@@ -38,4 +39,5 @@ private:
38
39
  CAMetalLayer *_layer;
39
40
  #pragma clang diagnostic pop
40
41
  bool _useP3ColorSpace = true;
42
+ bool _highBitDepth = false;
41
43
  };
@@ -96,9 +96,10 @@ void RNSkMetalCanvasProvider::setSize(int width, int height) {
96
96
  auto h = height * _context->getPixelDensity();
97
97
  #if defined(SK_GRAPHITE)
98
98
  _ctx = RNSkia::DawnContext::getInstance().MakeWindow((__bridge void *)_layer,
99
- w, h);
99
+ w, h, _highBitDepth);
100
100
  #else
101
- _ctx = MetalContext::getInstance().MakeWindow(_layer, w, h, _useP3ColorSpace);
101
+ _ctx = MetalContext::getInstance().MakeWindow(_layer, w, h, _useP3ColorSpace,
102
+ _highBitDepth);
102
103
  #endif
103
104
  _requestRedraw();
104
105
  }
@@ -108,3 +109,15 @@ CALayer *RNSkMetalCanvasProvider::getLayer() { return _layer; }
108
109
  void RNSkMetalCanvasProvider::setUseP3ColorSpace(bool useP3ColorSpace) {
109
110
  _useP3ColorSpace = useP3ColorSpace;
110
111
  }
112
+
113
+ void RNSkMetalCanvasProvider::setHighBitDepth(bool highBitDepth) {
114
+ if (_highBitDepth == highBitDepth) {
115
+ return;
116
+ }
117
+ _highBitDepth = highBitDepth;
118
+ if (_ctx) {
119
+ // Recreate the window context so the layer's pixel format matches the
120
+ // new bit depth.
121
+ setSize(_layer.frame.size.width, _layer.frame.size.height);
122
+ }
123
+ }
@@ -55,6 +55,7 @@ using namespace facebook::react;
55
55
  [self setNativeId:nativeId];
56
56
  [self setDebugMode:newProps.debug];
57
57
  [self setOpaque:newProps.opaque];
58
+ [self setHighBitDepth:newProps.highBitDepth];
58
59
  if (newProps.colorSpace == "" || newProps.colorSpace == "srgb") {
59
60
  bool useP3 = false;
60
61
  [self setUseP3ColorSpace:useP3];
@@ -37,6 +37,11 @@ RCT_CUSTOM_VIEW_PROPERTY(opaque, BOOL, SkiaUIView) {
37
37
  [(SkiaUIView *)view setOpaque:opaque];
38
38
  }
39
39
 
40
+ RCT_CUSTOM_VIEW_PROPERTY(highBitDepth, BOOL, SkiaUIView) {
41
+ bool highBitDepth = json != NULL ? [RCTConvert BOOL:json] : false;
42
+ [(SkiaUIView *)view setHighBitDepth:highBitDepth];
43
+ }
44
+
40
45
  #if !TARGET_OS_OSX
41
46
  - (UIView *)view {
42
47
  #else
@@ -42,5 +42,6 @@
42
42
  - (void)setOpaque:(bool)opaque;
43
43
  - (void)setNativeId:(size_t)nativeId;
44
44
  - (void)setUseP3ColorSpace:(bool)useP3ColorSpace;
45
+ - (void)setHighBitDepth:(bool)highBitDepth;
45
46
 
46
47
  @end
@@ -18,6 +18,7 @@
18
18
  bool _debugMode;
19
19
  bool _opaque;
20
20
  bool _useP3ColorSpace;
21
+ bool _highBitDepth;
21
22
  size_t _nativeId;
22
23
  }
23
24
 
@@ -76,6 +77,7 @@
76
77
  }
77
78
  _impl->getDrawView()->setShowDebugOverlays(_debugMode);
78
79
  _impl->setUseP3ColorSpace(_useP3ColorSpace);
80
+ _impl->setHighBitDepth(_highBitDepth);
79
81
  }
80
82
  }
81
83
  }
@@ -176,6 +178,13 @@
176
178
  }
177
179
  }
178
180
 
181
+ - (void)setHighBitDepth:(bool)highBitDepth {
182
+ _highBitDepth = highBitDepth;
183
+ if (_impl != nullptr) {
184
+ _impl->setHighBitDepth(_highBitDepth);
185
+ }
186
+ }
187
+
179
188
  #pragma mark External API
180
189
 
181
190
  - (std::shared_ptr<RNSkBaseAppleView>)impl {
@@ -84,16 +84,22 @@ public:
84
84
 
85
85
  JSI_HOST_FUNCTION(flush) {
86
86
  auto surface = getObject();
87
+ // When `sync` is true, block until the GPU has finished executing the
88
+ // submitted work. Required before a native consumer on a different command
89
+ // queue reads this surface's texture via getNativeTextureUnstable(). #3916
90
+ bool sync = count > 0 && arguments[0].isBool() && arguments[0].getBool();
87
91
  #if defined(SK_GRAPHITE)
88
92
  // A raster surface (e.g. Skia.Surface.Make) has no Graphite recorder;
89
93
  // only Graphite-backed surfaces need to snap and submit a recording.
90
94
  if (auto *recorder = surface->recorder()) {
91
95
  auto recording = recorder->snap();
92
- DawnContext::getInstance().submitRecording(recording.get());
96
+ DawnContext::getInstance().submitRecording(
97
+ recording.get(), sync ? skgpu::graphite::SyncToCpu::kYes
98
+ : skgpu::graphite::SyncToCpu::kNo);
93
99
  }
94
100
  #else
95
101
  if (auto dContext = GrAsDirectContext(surface->recordingContext())) {
96
- dContext->flushAndSubmit();
102
+ dContext->flushAndSubmit(sync ? GrSyncCpu::kYes : GrSyncCpu::kNo);
97
103
  }
98
104
  #endif
99
105
  return jsi::Value::undefined();