@shopify/react-native-skia 2.6.8 → 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 (146) 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 +20 -5
  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/SkiaCVPixelBufferUtils.mm +10 -0
  20. package/apple/SkiaPictureView.mm +1 -0
  21. package/apple/SkiaPictureViewManager.mm +5 -0
  22. package/apple/SkiaUIView.h +1 -0
  23. package/apple/SkiaUIView.mm +9 -0
  24. package/cpp/api/JsiSkSurface.h +8 -2
  25. package/cpp/rnskia/RNDawnContext.h +4 -3
  26. package/cpp/rnskia/RNDawnUtils.h +25 -6
  27. package/cpp/rnskia/RNDawnWindowContext.h +44 -10
  28. package/cpp/rnskia/RNMetalLayerColorSpace.h +16 -0
  29. package/cpp/rnskia/RNSkJsiViewApi.h +46 -24
  30. package/cpp/rnwgpu/SurfaceRegistry.h +10 -4
  31. package/lib/commonjs/Platform/Platform.web.js +2 -90
  32. package/lib/commonjs/Platform/Platform.web.js.map +1 -1
  33. package/lib/commonjs/mock/index.js +1 -0
  34. package/lib/commonjs/mock/index.js.map +1 -1
  35. package/lib/commonjs/renderer/Canvas.d.ts +10 -1
  36. package/lib/commonjs/renderer/Canvas.js +2 -0
  37. package/lib/commonjs/renderer/Canvas.js.map +1 -1
  38. package/lib/commonjs/skia/types/Surface/Surface.d.ts +5 -1
  39. package/lib/commonjs/skia/types/Surface/Surface.js.map +1 -1
  40. package/lib/commonjs/skia/web/JsiSkParagraphStyle.js +14 -3
  41. package/lib/commonjs/skia/web/JsiSkParagraphStyle.js.map +1 -1
  42. package/lib/commonjs/skia/web/JsiSkSurface.d.ts +1 -1
  43. package/lib/commonjs/skia/web/JsiSkSurface.js +2 -1
  44. package/lib/commonjs/skia/web/JsiSkSurface.js.map +1 -1
  45. package/lib/commonjs/sksg/Container.native.d.ts +1 -0
  46. package/lib/commonjs/sksg/Container.native.js +11 -0
  47. package/lib/commonjs/sksg/Container.native.js.map +1 -1
  48. package/lib/commonjs/sksg/Container.web.d.ts +1 -0
  49. package/lib/commonjs/sksg/Container.web.js +11 -0
  50. package/lib/commonjs/sksg/Container.web.js.map +1 -1
  51. package/lib/commonjs/specs/NativeSkiaModule.web.d.ts +2 -0
  52. package/lib/commonjs/specs/NativeSkiaModule.web.js +32 -7
  53. package/lib/commonjs/specs/NativeSkiaModule.web.js.map +1 -1
  54. package/lib/commonjs/specs/SkiaPictureViewNativeComponent.d.ts +1 -0
  55. package/lib/commonjs/specs/SkiaPictureViewNativeComponent.js.map +1 -1
  56. package/lib/commonjs/views/SkiaPictureView.js +2 -0
  57. package/lib/commonjs/views/SkiaPictureView.js.map +1 -1
  58. package/lib/commonjs/views/SkiaPictureView.web.js +279 -94
  59. package/lib/commonjs/views/SkiaPictureView.web.js.map +1 -1
  60. package/lib/commonjs/views/formats.d.ts +1 -0
  61. package/lib/commonjs/views/formats.js +28 -0
  62. package/lib/commonjs/views/formats.js.map +1 -0
  63. package/lib/commonjs/views/index.d.ts +1 -0
  64. package/lib/commonjs/views/index.js +11 -0
  65. package/lib/commonjs/views/index.js.map +1 -1
  66. package/lib/commonjs/views/types.d.ts +6 -0
  67. package/lib/commonjs/views/types.js.map +1 -1
  68. package/lib/module/Platform/Platform.web.js +3 -91
  69. package/lib/module/Platform/Platform.web.js.map +1 -1
  70. package/lib/module/mock/index.js +1 -0
  71. package/lib/module/mock/index.js.map +1 -1
  72. package/lib/module/renderer/Canvas.d.ts +10 -1
  73. package/lib/module/renderer/Canvas.js +2 -0
  74. package/lib/module/renderer/Canvas.js.map +1 -1
  75. package/lib/module/skia/types/Surface/Surface.d.ts +5 -1
  76. package/lib/module/skia/types/Surface/Surface.js.map +1 -1
  77. package/lib/module/skia/web/JsiSkParagraphStyle.js +14 -3
  78. package/lib/module/skia/web/JsiSkParagraphStyle.js.map +1 -1
  79. package/lib/module/skia/web/JsiSkSurface.d.ts +1 -1
  80. package/lib/module/skia/web/JsiSkSurface.js +2 -1
  81. package/lib/module/skia/web/JsiSkSurface.js.map +1 -1
  82. package/lib/module/sksg/Container.native.d.ts +1 -0
  83. package/lib/module/sksg/Container.native.js +11 -0
  84. package/lib/module/sksg/Container.native.js.map +1 -1
  85. package/lib/module/sksg/Container.web.d.ts +1 -0
  86. package/lib/module/sksg/Container.web.js +11 -0
  87. package/lib/module/sksg/Container.web.js.map +1 -1
  88. package/lib/module/specs/NativeSkiaModule.web.d.ts +2 -0
  89. package/lib/module/specs/NativeSkiaModule.web.js +32 -7
  90. package/lib/module/specs/NativeSkiaModule.web.js.map +1 -1
  91. package/lib/module/specs/SkiaPictureViewNativeComponent.d.ts +1 -0
  92. package/lib/module/specs/SkiaPictureViewNativeComponent.js.map +1 -1
  93. package/lib/module/views/SkiaPictureView.js +2 -0
  94. package/lib/module/views/SkiaPictureView.js.map +1 -1
  95. package/lib/module/views/SkiaPictureView.web.js +280 -95
  96. package/lib/module/views/SkiaPictureView.web.js.map +1 -1
  97. package/lib/module/views/formats.d.ts +1 -0
  98. package/lib/module/views/formats.js +22 -0
  99. package/lib/module/views/formats.js.map +1 -0
  100. package/lib/module/views/index.d.ts +1 -0
  101. package/lib/module/views/index.js +1 -0
  102. package/lib/module/views/index.js.map +1 -1
  103. package/lib/module/views/types.d.ts +6 -0
  104. package/lib/module/views/types.js.map +1 -1
  105. package/lib/typescript/lib/commonjs/Platform/Platform.web.d.ts +1 -2
  106. package/lib/typescript/lib/commonjs/mock/index.d.ts +1 -0
  107. package/lib/typescript/lib/commonjs/renderer/Canvas.d.ts +2 -1
  108. package/lib/typescript/lib/commonjs/skia/web/JsiSkSurface.d.ts +1 -1
  109. package/lib/typescript/lib/commonjs/sksg/Container.native.d.ts +1 -1
  110. package/lib/typescript/lib/commonjs/sksg/Container.web.d.ts +1 -1
  111. package/lib/typescript/lib/commonjs/views/formats.d.ts +2 -0
  112. package/lib/typescript/lib/module/Platform/Platform.web.d.ts +1 -3
  113. package/lib/typescript/lib/module/mock/index.d.ts +2 -2
  114. package/lib/typescript/lib/module/renderer/Canvas.d.ts +2 -1
  115. package/lib/typescript/lib/module/skia/web/JsiSkSurface.d.ts +1 -1
  116. package/lib/typescript/lib/module/sksg/Container.native.d.ts +1 -1
  117. package/lib/typescript/lib/module/sksg/Container.web.d.ts +1 -1
  118. package/lib/typescript/lib/module/views/formats.d.ts +1 -0
  119. package/lib/typescript/lib/module/views/index.d.ts +1 -0
  120. package/lib/typescript/src/renderer/Canvas.d.ts +10 -1
  121. package/lib/typescript/src/skia/types/Surface/Surface.d.ts +5 -1
  122. package/lib/typescript/src/skia/web/JsiSkSurface.d.ts +1 -1
  123. package/lib/typescript/src/sksg/Container.native.d.ts +1 -0
  124. package/lib/typescript/src/sksg/Container.web.d.ts +1 -0
  125. package/lib/typescript/src/specs/NativeSkiaModule.web.d.ts +2 -0
  126. package/lib/typescript/src/specs/SkiaPictureViewNativeComponent.d.ts +1 -0
  127. package/lib/typescript/src/views/formats.d.ts +1 -0
  128. package/lib/typescript/src/views/index.d.ts +1 -0
  129. package/lib/typescript/src/views/types.d.ts +6 -0
  130. package/package.json +4 -1
  131. package/react-native-skia.podspec +1 -0
  132. package/src/Platform/Platform.web.tsx +7 -99
  133. package/src/mock/index.ts +1 -0
  134. package/src/renderer/Canvas.tsx +11 -0
  135. package/src/skia/types/Surface/Surface.ts +5 -1
  136. package/src/skia/web/JsiSkParagraphStyle.ts +16 -2
  137. package/src/skia/web/JsiSkSurface.ts +2 -1
  138. package/src/sksg/Container.native.ts +12 -0
  139. package/src/sksg/Container.web.ts +12 -0
  140. package/src/specs/NativeSkiaModule.web.ts +39 -7
  141. package/src/specs/SkiaPictureViewNativeComponent.ts +1 -0
  142. package/src/views/SkiaPictureView.tsx +2 -0
  143. package/src/views/SkiaPictureView.web.tsx +318 -106
  144. package/src/views/formats.ts +21 -0
  145. package/src/views/index.ts +1 -0
  146. 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(); }
@@ -119,27 +119,34 @@ public:
119
119
  AHardwareBuffer_Desc description;
120
120
  AHardwareBuffer_describe(hardwareBuffer, &description);
121
121
  GrBackendFormat format;
122
+ auto colorType = kRGBA_8888_SkColorType;
122
123
  switch (description.format) {
123
124
  // TODO: find out if we can detect, which graphic buffers support
124
125
  // GR_GL_TEXTURE_2D
125
126
  case AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM:
126
127
  format = GrBackendFormats::MakeGL(GR_GL_RGBA8, GR_GL_TEXTURE_EXTERNAL);
128
+ colorType = kRGBA_8888_SkColorType;
127
129
  break;
128
130
  case AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT:
129
131
  format = GrBackendFormats::MakeGL(GR_GL_RGBA16F, GR_GL_TEXTURE_EXTERNAL);
132
+ colorType = kRGBA_F16_SkColorType;
130
133
  break;
131
134
  case AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM:
132
- GrBackendFormats::MakeGL(GR_GL_RGB565, GR_GL_TEXTURE_EXTERNAL);
135
+ format = GrBackendFormats::MakeGL(GR_GL_RGB565, GR_GL_TEXTURE_EXTERNAL);
136
+ colorType = kRGB_565_SkColorType;
133
137
  break;
134
138
  case AHARDWAREBUFFER_FORMAT_R10G10B10A2_UNORM:
135
139
  format = GrBackendFormats::MakeGL(GR_GL_RGB10_A2, GR_GL_TEXTURE_EXTERNAL);
140
+ colorType = kRGBA_1010102_SkColorType;
136
141
  break;
137
142
  case AHARDWAREBUFFER_FORMAT_R8G8B8_UNORM:
138
143
  format = GrBackendFormats::MakeGL(GR_GL_RGB8, GR_GL_TEXTURE_EXTERNAL);
144
+ colorType = kRGB_888x_SkColorType;
139
145
  break;
140
146
  #if __ANDROID_API__ >= 33
141
147
  case AHARDWAREBUFFER_FORMAT_R8_UNORM:
142
148
  format = GrBackendFormats::MakeGL(GR_GL_R8, GR_GL_TEXTURE_EXTERNAL);
149
+ colorType = kAlpha_8_SkColorType;
143
150
  break;
144
151
  #endif
145
152
  default:
@@ -161,9 +168,8 @@ public:
161
168
  return nullptr;
162
169
  }
163
170
  sk_sp<SkImage> image = SkImages::BorrowTextureFrom(
164
- _directContext.get(), backendTex, kTopLeft_GrSurfaceOrigin,
165
- kRGBA_8888_SkColorType, kOpaque_SkAlphaType, nullptr, deleteImageProc,
166
- deleteImageCtx);
171
+ _directContext.get(), backendTex, kTopLeft_GrSurfaceOrigin, colorType,
172
+ kOpaque_SkAlphaType, nullptr, deleteImageProc, deleteImageCtx);
167
173
  return image;
168
174
  #else
169
175
  throw std::runtime_error(
@@ -173,8 +179,17 @@ public:
173
179
  }
174
180
 
175
181
  // TODO: remove width, height
176
- std::unique_ptr<WindowContext> MakeWindow(ANativeWindow *window) {
182
+ std::unique_ptr<WindowContext> MakeWindow(ANativeWindow *window,
183
+ bool highBitDepth = false) {
177
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
+ }
178
193
  return std::make_unique<OpenGLWindowContext>(
179
194
  _directContext.get(), display, _glContext.get(), window,
180
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
+ }
@@ -105,6 +105,8 @@ SkiaCVPixelBufferUtils::getCVPixelBufferBaseFormat(
105
105
  case kCVPixelFormatType_32BGRA:
106
106
  case kCVPixelFormatType_32ABGR:
107
107
  case kCVPixelFormatType_32RGBA:
108
+ // 16-bit half-float RGBA
109
+ case kCVPixelFormatType_64RGBAHalf:
108
110
  return CVPixelBufferBaseFormat::rgb;
109
111
  default:
110
112
  [[unlikely]] throw std::runtime_error(
@@ -125,6 +127,8 @@ SkColorType SkiaCVPixelBufferUtils::RGB::getCVPixelBufferColorType(
125
127
  [[likely]] return kBGRA_8888_SkColorType;
126
128
  case kCVPixelFormatType_32RGBA:
127
129
  return kRGBA_8888_SkColorType;
130
+ case kCVPixelFormatType_64RGBAHalf:
131
+ return kRGBA_F16_SkColorType;
128
132
  // This can be extended with branches for specific RGB formats if Apple
129
133
  // uses new formats.
130
134
  default:
@@ -444,6 +448,12 @@ MTLPixelFormat SkiaCVPixelBufferUtils::getMTLPixelFormatForCVPixelBufferPlane(
444
448
  return throwInvalidPlaneIndexForFormat(1);
445
449
  }
446
450
  return MTLPixelFormatRGBA8Unorm;
451
+ case kCVPixelFormatType_64RGBAHalf:
452
+ // 1 plane, 16-bit half-float interleaved RGBA.
453
+ if (planeIndex != 0) {
454
+ return throwInvalidPlaneIndexForFormat(1);
455
+ }
456
+ return MTLPixelFormatRGBA16Float;
447
457
  case kCVPixelFormatType_420YpCbCr8Planar:
448
458
  case kCVPixelFormatType_420YpCbCr8PlanarFullRange:
449
459
  // 3 planes, 8-bit 4:2:0 planar (Y, U, V), each plane is single channel.
@@ -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];