@shopify/react-native-skia 0.1.200 → 0.1.202

Sign up to get free protection for your applications and to get access to all the features.
Files changed (31) hide show
  1. package/android/CMakeLists.txt +1 -1
  2. package/android/cpp/jni/include/JniSkiaBaseView.h +74 -13
  3. package/android/cpp/jni/include/JniSkiaDomView.h +20 -12
  4. package/android/cpp/jni/include/JniSkiaDrawView.h +20 -14
  5. package/android/cpp/jni/include/JniSkiaPictureView.h +24 -15
  6. package/android/cpp/rnskia-android/RNSkAndroidPlatformContext.h +2 -2
  7. package/android/cpp/rnskia-android/RNSkOpenGLCanvasProvider.cpp +41 -44
  8. package/android/cpp/rnskia-android/RNSkOpenGLCanvasProvider.h +4 -6
  9. package/android/cpp/rnskia-android/SkiaOpenGLHelper.h +310 -0
  10. package/android/cpp/rnskia-android/SkiaOpenGLSurfaceFactory.cpp +132 -0
  11. package/android/cpp/rnskia-android/SkiaOpenGLSurfaceFactory.h +125 -0
  12. package/android/src/main/java/com/shopify/reactnative/skia/SkiaBaseView.java +80 -11
  13. package/android/src/main/java/com/shopify/reactnative/skia/SkiaDomView.java +2 -0
  14. package/android/src/main/java/com/shopify/reactnative/skia/SkiaDrawView.java +2 -0
  15. package/android/src/main/java/com/shopify/reactnative/skia/SkiaPictureView.java +3 -0
  16. package/android/src/main/java/com/shopify/reactnative/skia/ViewScreenshotService.java +7 -5
  17. package/cpp/api/JsiSkHostObjects.h +0 -10
  18. package/cpp/rnskia/RNSkJsView.cpp +35 -10
  19. package/cpp/rnskia/RNSkJsiViewApi.h +1 -1
  20. package/cpp/rnskia/RNSkView.h +14 -11
  21. package/ios/RNSkia-iOS/RNSkMetalCanvasProvider.h +5 -14
  22. package/ios/RNSkia-iOS/RNSkMetalCanvasProvider.mm +7 -56
  23. package/ios/RNSkia-iOS/RNSkiOSPlatformContext.mm +2 -2
  24. package/ios/RNSkia-iOS/SkiaMetalSurfaceFactory.h +31 -0
  25. package/ios/RNSkia-iOS/SkiaMetalSurfaceFactory.mm +105 -0
  26. package/ios/RNSkia-iOS/ViewScreenshotService.mm +1 -0
  27. package/package.json +1 -1
  28. package/android/cpp/rnskia-android/SkiaOpenGLRenderer.cpp +0 -347
  29. package/android/cpp/rnskia-android/SkiaOpenGLRenderer.h +0 -124
  30. package/ios/RNSkia-iOS/SkiaMetalRenderer.h +0 -5
  31. package/ios/RNSkia-iOS/SkiaMetalRenderer.mm +0 -55
@@ -0,0 +1,310 @@
1
+ #pragma once
2
+
3
+ #include "EGL/egl.h"
4
+ #include "GLES2/gl2.h"
5
+ #include <fbjni/fbjni.h>
6
+ #include <jni.h>
7
+
8
+ #include <atomic>
9
+
10
+ #include "RNSkLog.h"
11
+
12
+ #pragma clang diagnostic push
13
+ #pragma clang diagnostic ignored "-Wdocumentation"
14
+
15
+ #include "SkCanvas.h"
16
+ #include "SkColorSpace.h"
17
+ #include "SkSurface.h"
18
+ #include "include/gpu/GrDirectContext.h"
19
+ #include "include/gpu/gl/GrGLInterface.h"
20
+
21
+ #pragma clang diagnostic pop
22
+
23
+ namespace RNSkia {
24
+
25
+ /**
26
+ * Singleton holding the default display and shared eglContext that will be the
27
+ * first context we create so that we can share data between contexts.
28
+ */
29
+ class OpenGLResourceHolder {
30
+ private:
31
+ OpenGLResourceHolder() {
32
+ // Initialize OpenGL
33
+ glDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
34
+ if (glDisplay == EGL_NO_DISPLAY) {
35
+ RNSkLogger::logToConsole("eglGetDisplay failed : %i", glGetError());
36
+ return;
37
+ }
38
+
39
+ EGLint major;
40
+ EGLint minor;
41
+ if (eglInitialize(glDisplay, &major, &minor) != EGL_TRUE) {
42
+ RNSkLogger::logToConsole("eglInitialize failed : %i", glGetError());
43
+ return;
44
+ }
45
+
46
+ // Create a default shared context
47
+ glConfig = getConfig(glDisplay);
48
+
49
+ // Create OpenGL context attributes
50
+ EGLint contextAttribs[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE};
51
+
52
+ // Initialize the offscreen context for this thread
53
+ glContext =
54
+ eglCreateContext(glDisplay, glConfig, glContext, contextAttribs);
55
+ if (glContext == EGL_NO_CONTEXT) {
56
+ RNSkLogger::logToConsole("eglCreateContext failed : %i", glGetError());
57
+ }
58
+ }
59
+
60
+ ~OpenGLResourceHolder() {
61
+ if (glContext != EGL_NO_CONTEXT) {
62
+ eglDestroyContext(glDisplay, glContext);
63
+ glContext = EGL_NO_CONTEXT;
64
+ }
65
+
66
+ if (glDisplay != EGL_NO_DISPLAY) {
67
+ eglTerminate(glDisplay);
68
+ glDisplay = EGL_NO_DISPLAY;
69
+ }
70
+ }
71
+ /* Explicitly disallow copying. */
72
+ OpenGLResourceHolder(const OpenGLResourceHolder &) = delete;
73
+ OpenGLResourceHolder &operator=(const OpenGLResourceHolder &) = delete;
74
+
75
+ public:
76
+ static OpenGLResourceHolder &getInstance() {
77
+ static OpenGLResourceHolder Instance;
78
+ return Instance;
79
+ }
80
+
81
+ /**
82
+ * The first context created will be considered the parent / shared context
83
+ * and will be used as the parent / shareable context when creating subsequent
84
+ * contexts.
85
+ */
86
+ std::atomic<EGLContext> glContext = EGL_NO_CONTEXT;
87
+ /**
88
+ * Shared egl display
89
+ */
90
+ std::atomic<EGLDisplay> glDisplay = EGL_NO_DISPLAY;
91
+
92
+ /**
93
+ * Shared eglConfig
94
+ */
95
+ std::atomic<EGLConfig> glConfig = 0;
96
+
97
+ private:
98
+ /**
99
+ * Finds the correct EGL Config for the given parameters
100
+ * @param glDisplay
101
+ * @return Config or zero if no matching context could be found.
102
+ */
103
+ static EGLConfig getConfig(EGLDisplay glDisplay) {
104
+
105
+ EGLint att[] = {EGL_RENDERABLE_TYPE,
106
+ EGL_OPENGL_ES2_BIT,
107
+ EGL_ALPHA_SIZE,
108
+ 8,
109
+ EGL_BLUE_SIZE,
110
+ 8,
111
+ EGL_GREEN_SIZE,
112
+ 8,
113
+ EGL_RED_SIZE,
114
+ 8,
115
+ EGL_DEPTH_SIZE,
116
+ 0,
117
+ EGL_STENCIL_SIZE,
118
+ 0,
119
+ EGL_SAMPLE_BUFFERS,
120
+ 0,
121
+ EGL_NONE};
122
+
123
+ EGLint numConfigs;
124
+ EGLConfig glConfig = 0;
125
+ if (eglChooseConfig(glDisplay, att, &glConfig, 1, &numConfigs) !=
126
+ EGL_TRUE ||
127
+ numConfigs == 0) {
128
+ RNSkLogger::logToConsole(
129
+ "Failed to choose a config for %s surface. Error code: %d\n",
130
+ eglGetError());
131
+ return 0;
132
+ }
133
+
134
+ return glConfig;
135
+ }
136
+ };
137
+
138
+ struct SkiaOpenGLContext {
139
+ SkiaOpenGLContext() {
140
+ glContext = EGL_NO_CONTEXT;
141
+ gl1x1Surface = EGL_NO_SURFACE;
142
+ directContext = nullptr;
143
+ }
144
+ ~SkiaOpenGLContext() {
145
+ if (gl1x1Surface != EGL_NO_SURFACE) {
146
+ eglDestroySurface(OpenGLResourceHolder::getInstance().glDisplay,
147
+ gl1x1Surface);
148
+ gl1x1Surface = EGL_NO_SURFACE;
149
+ }
150
+
151
+ if (directContext) {
152
+ directContext->releaseResourcesAndAbandonContext();
153
+ directContext = nullptr;
154
+ }
155
+
156
+ if (glContext != EGL_NO_CONTEXT) {
157
+ eglDestroyContext(OpenGLResourceHolder::getInstance().glDisplay,
158
+ glContext);
159
+ glContext = EGL_NO_CONTEXT;
160
+ }
161
+ }
162
+ EGLContext glContext;
163
+ EGLSurface gl1x1Surface;
164
+ sk_sp<GrDirectContext> directContext;
165
+ };
166
+
167
+ class SkiaOpenGLHelper {
168
+ public:
169
+ /**
170
+ * Calls eglMakeCurrent on the surface provided using the provided
171
+ * thread context.
172
+ * @param context Skia OpenGL context to use
173
+ * @param surface Surface to set as current
174
+ * @return true if eglMakeCurrent was successfull
175
+ */
176
+ static bool makeCurrent(SkiaOpenGLContext *context, EGLSurface glSurface) {
177
+ // We don't need to call make current if we already are current:
178
+ if (eglGetCurrentSurface(EGL_DRAW) != glSurface ||
179
+ eglGetCurrentSurface(EGL_READ) != glSurface ||
180
+ eglGetCurrentContext() != context->glContext) {
181
+
182
+ // Make current!
183
+ if (eglMakeCurrent(OpenGLResourceHolder::getInstance().glDisplay,
184
+ glSurface, glSurface,
185
+ context->glContext) != EGL_TRUE) {
186
+ RNSkLogger::logToConsole("eglMakeCurrent failed: %d\n", eglGetError());
187
+ return false;
188
+ }
189
+ return true;
190
+ }
191
+ return true;
192
+ }
193
+
194
+ /**
195
+ * Creates a new windowed surface
196
+ * @param window ANativeWindow to create surface in
197
+ * @return EGLSurface or EGL_NO_SURFACE if the call failed
198
+ */
199
+ static EGLSurface createWindowedSurface(ANativeWindow *window) {
200
+ const EGLint attribs[] = {EGL_NONE};
201
+ return eglCreateWindowSurface(OpenGLResourceHolder::getInstance().glDisplay,
202
+ OpenGLResourceHolder::getInstance().glConfig,
203
+ window, attribs);
204
+ }
205
+
206
+ /**
207
+ * Destroys an egl surface
208
+ * @param glSurface
209
+ * @return
210
+ */
211
+ static bool destroySurface(EGLSurface glSurface) {
212
+ if (eglMakeCurrent(OpenGLResourceHolder::getInstance().glDisplay,
213
+ EGL_NO_SURFACE, EGL_NO_SURFACE,
214
+ EGL_NO_CONTEXT) != EGL_TRUE) {
215
+ RNSkLogger::logToConsole(
216
+ "destroySurface: Could not clear selected surface");
217
+ return false;
218
+ }
219
+ return eglDestroySurface(OpenGLResourceHolder::getInstance().glDisplay,
220
+ glSurface) == EGL_TRUE;
221
+ }
222
+
223
+ /**
224
+ * Calls the eglSwapBuffer in the current thread with the provided surface
225
+ * @param context Thread context
226
+ * @param glSurface surface to present
227
+ * @return true if eglSwapBuffers succeeded.
228
+ */
229
+ static bool swapBuffers(SkiaOpenGLContext *context, EGLSurface glSurface) {
230
+ if (eglSwapBuffers(OpenGLResourceHolder::getInstance().glDisplay,
231
+ glSurface) != EGL_TRUE) {
232
+ RNSkLogger::logToConsole("eglSwapBuffers failed: %d\n", eglGetError());
233
+ return false;
234
+ }
235
+ return true;
236
+ }
237
+
238
+ /***
239
+ * Creates a new Skia direct context backed by the provided eglContext in the
240
+ * SkiaOpenGLContext.
241
+ * @param context Context to store results in
242
+ * @param sharedContext Shared Context
243
+ * @return true if the call to create a skia direct context suceeded.
244
+ */
245
+ static bool createSkiaDirectContextIfNecessary(SkiaOpenGLContext *context) {
246
+ if (context->directContext == nullptr) {
247
+
248
+ // Create OpenGL context
249
+ createOpenGLContext(context);
250
+
251
+ // Create attributes for a simple 1x1 pbuffer surface that we can
252
+ // use to activate and create Skia direct context for
253
+ const EGLint offScreenSurfaceAttribs[] = {EGL_WIDTH, 1, EGL_HEIGHT, 1,
254
+ EGL_NONE};
255
+
256
+ context->gl1x1Surface =
257
+ eglCreatePbufferSurface(OpenGLResourceHolder::getInstance().glDisplay,
258
+ OpenGLResourceHolder::getInstance().glConfig,
259
+ offScreenSurfaceAttribs);
260
+
261
+ if (context->gl1x1Surface == EGL_NO_SURFACE) {
262
+ RNSkLogger::logToConsole("Failed creating a 1x1 pbuffer surface");
263
+ return false;
264
+ }
265
+
266
+ // Activate
267
+ if (!makeCurrent(context, context->gl1x1Surface)) {
268
+ return false;
269
+ }
270
+
271
+ // Create the Skia context
272
+ auto backendInterface = GrGLMakeNativeInterface();
273
+ context->directContext = GrDirectContext::MakeGL(backendInterface);
274
+
275
+ if (context->directContext == nullptr) {
276
+ RNSkLogger::logToConsole("GrDirectContext::MakeGL failed");
277
+ return false;
278
+ }
279
+ }
280
+
281
+ // It all went well!
282
+ return true;
283
+ }
284
+
285
+ private:
286
+ /**
287
+ * Creates a new GLContext.
288
+ * @param context Context to save results in
289
+ * @return True if the call to eglCreateContext returned a valid OpenGL
290
+ * Context or if the context already is setup.
291
+ */
292
+ static bool createOpenGLContext(SkiaOpenGLContext *context) {
293
+ // Create OpenGL context attributes
294
+ EGLint contextAttribs[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE};
295
+
296
+ // Initialize the offscreen context for this thread
297
+ context->glContext = eglCreateContext(
298
+ OpenGLResourceHolder::getInstance().glDisplay,
299
+ OpenGLResourceHolder::getInstance().glConfig,
300
+ OpenGLResourceHolder::getInstance().glContext, contextAttribs);
301
+
302
+ if (context->glContext == EGL_NO_CONTEXT) {
303
+ RNSkLogger::logToConsole("eglCreateContext failed: %d\n", eglGetError());
304
+ return EGL_NO_CONTEXT;
305
+ }
306
+
307
+ return true;
308
+ }
309
+ };
310
+ } // namespace RNSkia
@@ -0,0 +1,132 @@
1
+ #include "SkiaOpenGLHelper.h"
2
+ #include <SkiaOpenGLSurfaceFactory.h>
3
+
4
+ namespace RNSkia {
5
+
6
+ thread_local SkiaOpenGLContext ThreadContextHolder::ThreadSkiaOpenGLContext;
7
+
8
+ sk_sp<SkSurface> SkiaOpenGLSurfaceFactory::makeOffscreenSurface(int width,
9
+ int height) {
10
+ // Setup OpenGL and Skia:
11
+ if (!SkiaOpenGLHelper::createSkiaDirectContextIfNecessary(
12
+ &ThreadContextHolder::ThreadSkiaOpenGLContext)) {
13
+
14
+ RNSkLogger::logToConsole(
15
+ "Could not create Skia Surface from native window / surface. "
16
+ "Failed creating Skia Direct Context");
17
+ return nullptr;
18
+ }
19
+
20
+ auto colorType = kN32_SkColorType;
21
+
22
+ SkSurfaceProps props(0, kUnknown_SkPixelGeometry);
23
+
24
+ // Create texture
25
+ auto texture =
26
+ ThreadContextHolder::ThreadSkiaOpenGLContext.directContext
27
+ ->createBackendTexture(width, height, colorType, GrMipMapped::kNo,
28
+ GrRenderable::kYes);
29
+
30
+ struct ReleaseContext {
31
+ SkiaOpenGLContext *context;
32
+ GrBackendTexture texture;
33
+ };
34
+
35
+ auto releaseCtx = new ReleaseContext(
36
+ {&ThreadContextHolder::ThreadSkiaOpenGLContext, texture});
37
+
38
+ // Create a SkSurface from the GrBackendTexture
39
+ return SkSurfaces::WrapBackendTexture(
40
+ ThreadContextHolder::ThreadSkiaOpenGLContext.directContext.get(), texture,
41
+ kTopLeft_GrSurfaceOrigin, 0, colorType, nullptr, &props,
42
+ [](void *addr) {
43
+ auto releaseCtx = reinterpret_cast<ReleaseContext *>(addr);
44
+
45
+ releaseCtx->context->directContext->deleteBackendTexture(
46
+ releaseCtx->texture);
47
+ },
48
+ releaseCtx);
49
+ }
50
+
51
+ sk_sp<SkSurface> WindowSurfaceHolder::getSurface() {
52
+ if (_skSurface == nullptr) {
53
+
54
+ // Setup OpenGL and Skia
55
+ if (!SkiaOpenGLHelper::createSkiaDirectContextIfNecessary(
56
+ &ThreadContextHolder::ThreadSkiaOpenGLContext)) {
57
+ RNSkLogger::logToConsole(
58
+ "Could not create Skia Surface from native window / surface. "
59
+ "Failed creating Skia Direct Context");
60
+ return nullptr;
61
+ }
62
+
63
+ // Now we can create a surface
64
+ _glSurface = SkiaOpenGLHelper::createWindowedSurface(_window);
65
+ if (_glSurface == EGL_NO_SURFACE) {
66
+ RNSkLogger::logToConsole(
67
+ "Could not create EGL Surface from native window / surface.");
68
+ return nullptr;
69
+ }
70
+
71
+ // Now make this one current
72
+ if (!SkiaOpenGLHelper::makeCurrent(
73
+ &ThreadContextHolder::ThreadSkiaOpenGLContext, _glSurface)) {
74
+ RNSkLogger::logToConsole(
75
+ "Could not create EGL Surface from native window / surface. Could "
76
+ "not set new surface as current surface.");
77
+ return nullptr;
78
+ }
79
+
80
+ // Set up parameters for the render target so that it
81
+ // matches the underlying OpenGL context.
82
+ GrGLFramebufferInfo fboInfo;
83
+
84
+ // We pass 0 as the framebuffer id, since the
85
+ // underlying Skia GrGlGpu will read this when wrapping the context in the
86
+ // render target and the GrGlGpu object.
87
+ fboInfo.fFBOID = 0;
88
+ fboInfo.fFormat = 0x8058; // GL_RGBA8
89
+
90
+ GLint stencil;
91
+ glGetIntegerv(GL_STENCIL_BITS, &stencil);
92
+
93
+ GLint samples;
94
+ glGetIntegerv(GL_SAMPLES, &samples);
95
+
96
+ auto colorType = kN32_SkColorType;
97
+
98
+ auto maxSamples =
99
+ ThreadContextHolder::ThreadSkiaOpenGLContext.directContext
100
+ ->maxSurfaceSampleCountForColorType(colorType);
101
+
102
+ if (samples > maxSamples) {
103
+ samples = maxSamples;
104
+ }
105
+
106
+ GrBackendRenderTarget renderTarget(_width, _height, samples, stencil,
107
+ fboInfo);
108
+
109
+ SkSurfaceProps props(0, kUnknown_SkPixelGeometry);
110
+
111
+ struct ReleaseContext {
112
+ EGLSurface glSurface;
113
+ };
114
+
115
+ auto releaseCtx = new ReleaseContext({_glSurface});
116
+
117
+ // Create surface object
118
+ _skSurface = SkSurfaces::WrapBackendRenderTarget(
119
+ ThreadContextHolder::ThreadSkiaOpenGLContext.directContext.get(),
120
+ renderTarget, kBottomLeft_GrSurfaceOrigin, colorType, nullptr, &props,
121
+ [](void *addr) {
122
+ auto releaseCtx = reinterpret_cast<ReleaseContext *>(addr);
123
+ SkiaOpenGLHelper::destroySurface(releaseCtx->glSurface);
124
+ delete releaseCtx;
125
+ },
126
+ reinterpret_cast<void *>(releaseCtx));
127
+ }
128
+
129
+ return _skSurface;
130
+ }
131
+
132
+ } // namespace RNSkia
@@ -0,0 +1,125 @@
1
+ #pragma once
2
+
3
+ #include <RNSkLog.h>
4
+
5
+ #include <fbjni/fbjni.h>
6
+ #include <jni.h>
7
+
8
+ #include <android/native_window_jni.h>
9
+ #include <condition_variable>
10
+ #include <memory>
11
+ #include <thread>
12
+ #include <unordered_map>
13
+
14
+ #include "SkiaOpenGLHelper.h"
15
+
16
+ #pragma clang diagnostic push
17
+ #pragma clang diagnostic ignored "-Wdocumentation"
18
+
19
+ #include "SkCanvas.h"
20
+ #include "SkColorSpace.h"
21
+ #include "SkSurface.h"
22
+ #include "include/gpu/GrBackendSurface.h"
23
+ #include "include/gpu/GrDirectContext.h"
24
+ #include "include/gpu/ganesh/SkSurfaceGanesh.h"
25
+ #include "include/gpu/gl/GrGLInterface.h"
26
+
27
+ #pragma clang diagnostic pop
28
+
29
+ namespace RNSkia {
30
+
31
+ /**
32
+ * Holder of the thread local SkiaOpenGLContext member
33
+ */
34
+ class ThreadContextHolder {
35
+ public:
36
+ static thread_local SkiaOpenGLContext ThreadSkiaOpenGLContext;
37
+ };
38
+
39
+ /**
40
+ * Holder of the Windowed SkSurface with support for making current
41
+ * and presenting to screen
42
+ */
43
+ class WindowSurfaceHolder {
44
+ public:
45
+ WindowSurfaceHolder(jobject surface, int width, int height)
46
+ : _width(width), _height(height),
47
+ _window(ANativeWindow_fromSurface(facebook::jni::Environment::current(),
48
+ surface)) {}
49
+
50
+ ~WindowSurfaceHolder() { ANativeWindow_release(_window); }
51
+
52
+ int getWidth() { return _width; }
53
+ int getHeight() { return _height; }
54
+
55
+ /*
56
+ * Ensures that the holder has a valid surface and returns the surface.
57
+ */
58
+ sk_sp<SkSurface> getSurface();
59
+
60
+ /**
61
+ * Resizes the surface
62
+ * @param width
63
+ * @param height
64
+ */
65
+ void resize(int width, int height) {
66
+ _width = width;
67
+ _height = height;
68
+ _skSurface = nullptr;
69
+ }
70
+
71
+ /**
72
+ * Sets the current surface as the active surface
73
+ * @return true if make current succeeds
74
+ */
75
+ bool makeCurrent() {
76
+ return SkiaOpenGLHelper::makeCurrent(
77
+ &ThreadContextHolder::ThreadSkiaOpenGLContext, _glSurface);
78
+ }
79
+
80
+ /**
81
+ * Presents the current drawing operations by swapping buffers
82
+ * @return true if make current succeeds
83
+ */
84
+ bool present() {
85
+ // Flush and submit the direct context
86
+ ThreadContextHolder::ThreadSkiaOpenGLContext.directContext
87
+ ->flushAndSubmit();
88
+
89
+ // Swap buffers
90
+ return SkiaOpenGLHelper::swapBuffers(
91
+ &ThreadContextHolder::ThreadSkiaOpenGLContext, _glSurface);
92
+ }
93
+
94
+ private:
95
+ ANativeWindow *_window = nullptr;
96
+ sk_sp<SkSurface> _skSurface = nullptr;
97
+ EGLSurface _glSurface = EGL_NO_SURFACE;
98
+ int _width = 0;
99
+ int _height = 0;
100
+ };
101
+
102
+ class SkiaOpenGLSurfaceFactory {
103
+ public:
104
+ /**
105
+ * Creates a new Skia surface that is backed by a texture.
106
+ * @param width Width of surface
107
+ * @param height Height of surface
108
+ * @return An SkSurface backed by a texture.
109
+ */
110
+ static sk_sp<SkSurface> makeOffscreenSurface(int width, int height);
111
+
112
+ /**
113
+ * Creates a windowed Skia Surface holder.
114
+ * @param width Initial width of surface
115
+ * @param height Initial height of surface
116
+ * @param window Window coming from Java
117
+ * @return A Surface holder
118
+ */
119
+ static std::unique_ptr<WindowSurfaceHolder>
120
+ makeWindowedSurface(jobject window, int width, int height) {
121
+ return std::make_unique<WindowSurfaceHolder>(window, width, height);
122
+ }
123
+ };
124
+
125
+ } // namespace RNSkia