@shopify/react-native-skia 1.0.6 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- package/android/CMakeLists.txt +1 -0
- package/android/build.gradle +1 -0
- package/android/cpp/rnskia-android/AHardwareBufferUtils.cpp +31 -0
- package/android/cpp/rnskia-android/AHardwareBufferUtils.h +13 -0
- package/android/cpp/rnskia-android/RNSkAndroidPlatformContext.h +75 -0
- package/android/cpp/rnskia-android/SkiaOpenGLSurfaceFactory.cpp +29 -11
- package/android/cpp/rnskia-android/SkiaOpenGLSurfaceFactory.h +1 -2
- package/android/src/main/java/com/shopify/reactnative/skia/ViewScreenshotService.java +3 -2
- package/cpp/api/JsiPlatformBuffer.h +51 -0
- package/cpp/api/JsiSkApi.h +4 -0
- package/cpp/api/JsiSkImage.h +1 -1
- package/cpp/api/JsiSkImageFactory.h +15 -2
- package/cpp/api/JsiSkSurface.h +9 -1
- package/cpp/rnskia/RNSkJsiViewApi.h +48 -0
- package/cpp/rnskia/RNSkPlatformContext.h +13 -0
- package/ios/RNSkia-iOS/RNSkiOSPlatformContext.h +6 -0
- package/ios/RNSkia-iOS/RNSkiOSPlatformContext.mm +115 -0
- package/ios/RNSkia-iOS/SkiaMetalSurfaceFactory.h +4 -0
- package/ios/RNSkia-iOS/SkiaMetalSurfaceFactory.mm +12 -0
- package/lib/commonjs/external/reanimated/textures.js +11 -2
- package/lib/commonjs/external/reanimated/textures.js.map +1 -1
- package/lib/commonjs/external/reanimated/useAnimatedImageValue.d.ts +2 -1
- package/lib/commonjs/external/reanimated/useAnimatedImageValue.js +8 -4
- package/lib/commonjs/external/reanimated/useAnimatedImageValue.js.map +1 -1
- package/lib/commonjs/mock/index.js +1 -0
- package/lib/commonjs/mock/index.js.map +1 -1
- package/lib/commonjs/renderer/Offscreen.d.ts +2 -2
- package/lib/commonjs/renderer/Offscreen.js +2 -2
- package/lib/commonjs/renderer/Offscreen.js.map +1 -1
- package/lib/commonjs/skia/types/Image/ImageFactory.d.ts +14 -0
- package/lib/commonjs/skia/types/Image/ImageFactory.js.map +1 -1
- package/lib/commonjs/skia/types/PlatformBuffer/PlatformBufferFactory.d.ts +12 -0
- package/lib/commonjs/skia/types/PlatformBuffer/PlatformBufferFactory.js +6 -0
- package/lib/commonjs/skia/types/PlatformBuffer/PlatformBufferFactory.js.map +1 -0
- package/lib/commonjs/skia/types/PlatformBuffer/index.d.ts +1 -0
- package/lib/commonjs/skia/types/PlatformBuffer/index.js +17 -0
- package/lib/commonjs/skia/types/PlatformBuffer/index.js.map +1 -0
- package/lib/commonjs/skia/types/Skia.d.ts +2 -0
- package/lib/commonjs/skia/types/Skia.js.map +1 -1
- package/lib/commonjs/skia/types/Surface/Surface.d.ts +8 -0
- package/lib/commonjs/skia/types/Surface/Surface.js.map +1 -1
- package/lib/commonjs/skia/types/index.d.ts +1 -0
- package/lib/commonjs/skia/types/index.js +11 -0
- package/lib/commonjs/skia/types/index.js.map +1 -1
- package/lib/commonjs/skia/web/JsiSkImage.d.ts +1 -2
- package/lib/commonjs/skia/web/JsiSkImage.js +5 -12
- package/lib/commonjs/skia/web/JsiSkImage.js.map +1 -1
- package/lib/commonjs/skia/web/JsiSkImageFactory.d.ts +2 -2
- package/lib/commonjs/skia/web/JsiSkImageFactory.js +3 -0
- package/lib/commonjs/skia/web/JsiSkImageFactory.js.map +1 -1
- package/lib/commonjs/skia/web/JsiSkPlatformBufferFactory.d.ts +8 -0
- package/lib/commonjs/skia/web/JsiSkPlatformBufferFactory.js +20 -0
- package/lib/commonjs/skia/web/JsiSkPlatformBufferFactory.js.map +1 -0
- package/lib/commonjs/skia/web/JsiSkSurface.d.ts +3 -2
- package/lib/commonjs/skia/web/JsiSkSurface.js +8 -6
- package/lib/commonjs/skia/web/JsiSkSurface.js.map +1 -1
- package/lib/commonjs/skia/web/JsiSkSurfaceFactory.d.ts +1 -1
- package/lib/commonjs/skia/web/JsiSkSurfaceFactory.js +2 -17
- package/lib/commonjs/skia/web/JsiSkSurfaceFactory.js.map +1 -1
- package/lib/commonjs/skia/web/JsiSkia.js +3 -1
- package/lib/commonjs/skia/web/JsiSkia.js.map +1 -1
- package/lib/commonjs/views/SkiaDomView.d.ts +6 -0
- package/lib/commonjs/views/SkiaDomView.js +10 -0
- package/lib/commonjs/views/SkiaDomView.js.map +1 -1
- package/lib/commonjs/views/types.d.ts +1 -0
- package/lib/commonjs/views/types.js.map +1 -1
- package/lib/module/external/reanimated/textures.js +11 -2
- package/lib/module/external/reanimated/textures.js.map +1 -1
- package/lib/module/external/reanimated/useAnimatedImageValue.d.ts +2 -1
- package/lib/module/external/reanimated/useAnimatedImageValue.js +8 -4
- package/lib/module/external/reanimated/useAnimatedImageValue.js.map +1 -1
- package/lib/module/mock/index.js +1 -0
- package/lib/module/mock/index.js.map +1 -1
- package/lib/module/renderer/Offscreen.d.ts +2 -2
- package/lib/module/renderer/Offscreen.js +2 -2
- package/lib/module/renderer/Offscreen.js.map +1 -1
- package/lib/module/skia/types/Image/ImageFactory.d.ts +14 -0
- package/lib/module/skia/types/Image/ImageFactory.js.map +1 -1
- package/lib/module/skia/types/PlatformBuffer/PlatformBufferFactory.d.ts +12 -0
- package/lib/module/skia/types/PlatformBuffer/PlatformBufferFactory.js +2 -0
- package/lib/module/skia/types/PlatformBuffer/PlatformBufferFactory.js.map +1 -0
- package/lib/module/skia/types/PlatformBuffer/index.d.ts +1 -0
- package/lib/module/skia/types/PlatformBuffer/index.js +2 -0
- package/lib/module/skia/types/PlatformBuffer/index.js.map +1 -0
- package/lib/module/skia/types/Skia.d.ts +2 -0
- package/lib/module/skia/types/Skia.js.map +1 -1
- package/lib/module/skia/types/Surface/Surface.d.ts +8 -0
- package/lib/module/skia/types/Surface/Surface.js.map +1 -1
- package/lib/module/skia/types/index.d.ts +1 -0
- package/lib/module/skia/types/index.js +1 -0
- package/lib/module/skia/types/index.js.map +1 -1
- package/lib/module/skia/web/JsiSkImage.d.ts +1 -2
- package/lib/module/skia/web/JsiSkImage.js +5 -12
- package/lib/module/skia/web/JsiSkImage.js.map +1 -1
- package/lib/module/skia/web/JsiSkImageFactory.d.ts +2 -2
- package/lib/module/skia/web/JsiSkImageFactory.js +3 -0
- package/lib/module/skia/web/JsiSkImageFactory.js.map +1 -1
- package/lib/module/skia/web/JsiSkPlatformBufferFactory.d.ts +8 -0
- package/lib/module/skia/web/JsiSkPlatformBufferFactory.js +13 -0
- package/lib/module/skia/web/JsiSkPlatformBufferFactory.js.map +1 -0
- package/lib/module/skia/web/JsiSkSurface.d.ts +3 -2
- package/lib/module/skia/web/JsiSkSurface.js +8 -6
- package/lib/module/skia/web/JsiSkSurface.js.map +1 -1
- package/lib/module/skia/web/JsiSkSurfaceFactory.d.ts +1 -1
- package/lib/module/skia/web/JsiSkSurfaceFactory.js +2 -17
- package/lib/module/skia/web/JsiSkSurfaceFactory.js.map +1 -1
- package/lib/module/skia/web/JsiSkia.js +3 -1
- package/lib/module/skia/web/JsiSkia.js.map +1 -1
- package/lib/module/views/SkiaDomView.d.ts +6 -0
- package/lib/module/views/SkiaDomView.js +10 -0
- package/lib/module/views/SkiaDomView.js.map +1 -1
- package/lib/module/views/types.d.ts +1 -0
- package/lib/module/views/types.js.map +1 -1
- package/lib/typescript/src/external/reanimated/useAnimatedImageValue.d.ts +2 -1
- package/lib/typescript/src/renderer/Offscreen.d.ts +2 -2
- package/lib/typescript/src/skia/types/Image/ImageFactory.d.ts +14 -0
- package/lib/typescript/src/skia/types/PlatformBuffer/PlatformBufferFactory.d.ts +12 -0
- package/lib/typescript/src/skia/types/PlatformBuffer/index.d.ts +1 -0
- package/lib/typescript/src/skia/types/Skia.d.ts +2 -0
- package/lib/typescript/src/skia/types/Surface/Surface.d.ts +8 -0
- package/lib/typescript/src/skia/types/index.d.ts +1 -0
- package/lib/typescript/src/skia/web/JsiSkImage.d.ts +1 -2
- package/lib/typescript/src/skia/web/JsiSkImageFactory.d.ts +2 -2
- package/lib/typescript/src/skia/web/JsiSkPlatformBufferFactory.d.ts +8 -0
- package/lib/typescript/src/skia/web/JsiSkSurface.d.ts +3 -2
- package/lib/typescript/src/skia/web/JsiSkSurfaceFactory.d.ts +1 -1
- package/lib/typescript/src/views/SkiaDomView.d.ts +6 -0
- package/lib/typescript/src/views/types.d.ts +1 -0
- package/package.json +1 -1
- package/src/external/reanimated/textures.tsx +8 -2
- package/src/external/reanimated/useAnimatedImageValue.ts +12 -6
- package/src/mock/index.ts +1 -0
- package/src/renderer/Offscreen.tsx +3 -3
- package/src/skia/types/Image/ImageFactory.ts +15 -0
- package/src/skia/types/PlatformBuffer/PlatformBufferFactory.ts +14 -0
- package/src/skia/types/PlatformBuffer/index.ts +1 -0
- package/src/skia/types/Skia.ts +2 -1
- package/src/skia/types/Surface/Surface.ts +10 -0
- package/src/skia/types/index.ts +1 -0
- package/src/skia/web/JsiSkImage.ts +5 -22
- package/src/skia/web/JsiSkImageFactory.ts +13 -2
- package/src/skia/web/JsiSkPlatformBufferFactory.ts +22 -0
- package/src/skia/web/JsiSkSurface.ts +10 -9
- package/src/skia/web/JsiSkSurfaceFactory.ts +4 -20
- package/src/skia/web/JsiSkia.ts +2 -0
- package/src/views/SkiaDomView.tsx +10 -0
- package/src/views/types.ts +1 -0
- package/cpp/skia/include/third_party/vulkan/LICENSE +0 -29
- package/cpp/skia/modules/skcms/README.chromium +0 -6
- package/cpp/skia/readme.txt +0 -1
package/android/CMakeLists.txt
CHANGED
@@ -47,6 +47,7 @@ add_library(
|
|
47
47
|
"${PROJECT_SOURCE_DIR}/cpp/rnskia-android/RNSkOpenGLCanvasProvider.cpp"
|
48
48
|
"${PROJECT_SOURCE_DIR}/cpp/rnskia-android/SkiaOpenGLSurfaceFactory.cpp"
|
49
49
|
"${PROJECT_SOURCE_DIR}/cpp/rnskia-android/GrAHardwareBufferUtils.cpp"
|
50
|
+
"${PROJECT_SOURCE_DIR}/cpp/rnskia-android/AHardwareBufferUtils.cpp"
|
50
51
|
|
51
52
|
"${PROJECT_SOURCE_DIR}/../cpp/jsi/JsiHostObject.cpp"
|
52
53
|
"${PROJECT_SOURCE_DIR}/../cpp/jsi/JsiValue.cpp"
|
package/android/build.gradle
CHANGED
@@ -137,6 +137,7 @@ android {
|
|
137
137
|
targetSdkVersion safeExtGet('targetSdkVersion', DEFAULT_TARGET_SDK_VERSION)
|
138
138
|
versionCode 1
|
139
139
|
versionName "1.0"
|
140
|
+
buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString()
|
140
141
|
|
141
142
|
externalNativeBuild {
|
142
143
|
cmake {
|
@@ -0,0 +1,31 @@
|
|
1
|
+
#if __ANDROID_API__ >= 26
|
2
|
+
|
3
|
+
#include "AHardwareBufferUtils.h"
|
4
|
+
#include <android/hardware_buffer.h>
|
5
|
+
|
6
|
+
namespace RNSkia {
|
7
|
+
|
8
|
+
uint32_t GetBufferFormatFromSkColorType(SkColorType bufferFormat) {
|
9
|
+
switch (bufferFormat) {
|
10
|
+
case kRGBA_8888_SkColorType:
|
11
|
+
return AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM;
|
12
|
+
case kRGB_888x_SkColorType:
|
13
|
+
return AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM;
|
14
|
+
case kRGBA_F16_SkColorType:
|
15
|
+
return AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT;
|
16
|
+
case kRGB_565_SkColorType:
|
17
|
+
return AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM;
|
18
|
+
case kRGBA_1010102_SkColorType:
|
19
|
+
return AHARDWAREBUFFER_FORMAT_R10G10B10A2_UNORM;
|
20
|
+
#if __ANDROID_API__ >= 33
|
21
|
+
case kAlpha_8_SkColorType:
|
22
|
+
return AHARDWAREBUFFER_FORMAT_R8_UNORM;
|
23
|
+
#endif
|
24
|
+
default:
|
25
|
+
return AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM;
|
26
|
+
}
|
27
|
+
}
|
28
|
+
|
29
|
+
} // namespace RNSkia
|
30
|
+
|
31
|
+
#endif
|
@@ -1,10 +1,15 @@
|
|
1
1
|
#pragma once
|
2
2
|
|
3
|
+
// TODO: Add android flags
|
4
|
+
#if __ANDROID_API__ >= 26
|
5
|
+
#include <android/hardware_buffer.h>
|
6
|
+
#endif
|
3
7
|
#include <exception>
|
4
8
|
#include <functional>
|
5
9
|
#include <memory>
|
6
10
|
#include <string>
|
7
11
|
|
12
|
+
#include "AHardwareBufferUtils.h"
|
8
13
|
#include "JniPlatformContext.h"
|
9
14
|
#include "RNSkPlatformContext.h"
|
10
15
|
#include "SkiaOpenGLSurfaceFactory.h"
|
@@ -48,6 +53,76 @@ public:
|
|
48
53
|
return SkiaOpenGLSurfaceFactory::makeOffscreenSurface(width, height);
|
49
54
|
}
|
50
55
|
|
56
|
+
sk_sp<SkImage> makeImageFromPlatformBuffer(void *buffer) override {
|
57
|
+
return SkiaOpenGLSurfaceFactory::makeImageFromHardwareBuffer(buffer);
|
58
|
+
}
|
59
|
+
|
60
|
+
void releasePlatformBuffer(uint64_t pointer) override {
|
61
|
+
#if __ANDROID_API__ >= 26
|
62
|
+
AHardwareBuffer *buffer = reinterpret_cast<AHardwareBuffer *>(pointer);
|
63
|
+
AHardwareBuffer_release(buffer);
|
64
|
+
#endif
|
65
|
+
}
|
66
|
+
|
67
|
+
uint64_t makePlatformBuffer(sk_sp<SkImage> image) override {
|
68
|
+
#if __ANDROID_API__ >= 26
|
69
|
+
auto bytesPerPixel = image->imageInfo().bytesPerPixel();
|
70
|
+
int bytesPerRow = image->width() * bytesPerPixel;
|
71
|
+
auto buf = SkData::MakeUninitialized(image->width() * image->height() *
|
72
|
+
bytesPerPixel);
|
73
|
+
SkImageInfo info =
|
74
|
+
SkImageInfo::Make(image->width(), image->height(), image->colorType(),
|
75
|
+
image->alphaType());
|
76
|
+
image->readPixels(nullptr, info, const_cast<void *>(buf->data()),
|
77
|
+
bytesPerRow, 0, 0);
|
78
|
+
const void *pixelData = buf->data();
|
79
|
+
|
80
|
+
// Define the buffer description
|
81
|
+
AHardwareBuffer_Desc desc = {};
|
82
|
+
// TODO: use image info here
|
83
|
+
desc.width = image->width();
|
84
|
+
desc.height = image->height();
|
85
|
+
desc.layers = 1; // Single image layer
|
86
|
+
desc.format = GetBufferFormatFromSkColorType(
|
87
|
+
image->colorType()); // Assuming the image
|
88
|
+
// is in this format
|
89
|
+
desc.usage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN |
|
90
|
+
AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN |
|
91
|
+
AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT |
|
92
|
+
AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE;
|
93
|
+
desc.stride = bytesPerRow; // Stride in pixels, not in bytes
|
94
|
+
|
95
|
+
// Allocate the buffer
|
96
|
+
AHardwareBuffer *buffer = nullptr;
|
97
|
+
if (AHardwareBuffer_allocate(&desc, &buffer) != 0) {
|
98
|
+
// Handle allocation failure
|
99
|
+
return 0;
|
100
|
+
}
|
101
|
+
|
102
|
+
// Map the buffer to gain access to its memory
|
103
|
+
void *mappedBuffer = nullptr;
|
104
|
+
AHardwareBuffer_lock(buffer, AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN, -1,
|
105
|
+
nullptr, &mappedBuffer);
|
106
|
+
if (mappedBuffer == nullptr) {
|
107
|
+
// Handle mapping failure
|
108
|
+
AHardwareBuffer_release(buffer);
|
109
|
+
return 0;
|
110
|
+
}
|
111
|
+
|
112
|
+
// Copy the image data to the buffer
|
113
|
+
memcpy(mappedBuffer, pixelData, desc.height * bytesPerRow);
|
114
|
+
|
115
|
+
// Unmap the buffer
|
116
|
+
AHardwareBuffer_unlock(buffer, nullptr);
|
117
|
+
|
118
|
+
// Return the buffer pointer as a uint64_t. It's the caller's responsibility
|
119
|
+
// to manage this buffer.
|
120
|
+
return reinterpret_cast<uint64_t>(buffer);
|
121
|
+
#else
|
122
|
+
return 0;
|
123
|
+
#endif
|
124
|
+
}
|
125
|
+
|
51
126
|
sk_sp<SkFontMgr> createFontMgr() override {
|
52
127
|
return SkFontMgr_New_Android(nullptr);
|
53
128
|
}
|
@@ -7,6 +7,7 @@
|
|
7
7
|
|
8
8
|
#include "include/gpu/ganesh/SkImageGanesh.h"
|
9
9
|
#include "include/gpu/ganesh/gl/GrGLBackendSurface.h"
|
10
|
+
#include "src/gpu/ganesh/gl/GrGLDefines.h"
|
10
11
|
|
11
12
|
#pragma clang diagnostic pop
|
12
13
|
|
@@ -15,30 +16,47 @@ namespace RNSkia {
|
|
15
16
|
thread_local SkiaOpenGLContext ThreadContextHolder::ThreadSkiaOpenGLContext;
|
16
17
|
|
17
18
|
sk_sp<SkImage>
|
18
|
-
SkiaOpenGLSurfaceFactory::makeImageFromHardwareBuffer(
|
19
|
-
void *buffer) {
|
19
|
+
SkiaOpenGLSurfaceFactory::makeImageFromHardwareBuffer(void *buffer) {
|
20
20
|
#if __ANDROID_API__ >= 26
|
21
|
+
// Setup OpenGL and Skia:
|
22
|
+
if (!SkiaOpenGLHelper::createSkiaDirectContextIfNecessary(
|
23
|
+
&ThreadContextHolder::ThreadSkiaOpenGLContext)) {
|
24
|
+
|
25
|
+
RNSkLogger::logToConsole(
|
26
|
+
"Could not create Skia Surface from native window / surface. "
|
27
|
+
"Failed creating Skia Direct Context");
|
28
|
+
return nullptr;
|
29
|
+
}
|
21
30
|
const AHardwareBuffer *hardwareBuffer =
|
22
31
|
static_cast<AHardwareBuffer *>(buffer);
|
23
32
|
DeleteImageProc deleteImageProc = nullptr;
|
24
33
|
UpdateImageProc updateImageProc = nullptr;
|
25
34
|
TexImageCtx deleteImageCtx = nullptr;
|
35
|
+
|
36
|
+
AHardwareBuffer_Desc description;
|
37
|
+
AHardwareBuffer_describe(hardwareBuffer, &description);
|
38
|
+
if (description.format != AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM) {
|
39
|
+
throw std::runtime_error("AHardwareBuffer has unknown format (" +
|
40
|
+
std::to_string(description.format) +
|
41
|
+
") - cannot convert to SkImage!");
|
42
|
+
}
|
43
|
+
GrBackendFormat format =
|
44
|
+
GrBackendFormats::MakeGL(GR_GL_RGBA8, GR_GL_TEXTURE_EXTERNAL);
|
45
|
+
|
26
46
|
auto backendTex = MakeGLBackendTexture(
|
27
47
|
ThreadContextHolder::ThreadSkiaOpenGLContext.directContext.get(),
|
28
|
-
const_cast<AHardwareBuffer *>(hardwareBuffer),
|
29
|
-
|
30
|
-
|
31
|
-
// GR_GL_TEXTURE_EXTERNAL 0x8D65
|
32
|
-
GrBackendFormats::MakeGL(0x8058, 0x8D65), false);
|
48
|
+
const_cast<AHardwareBuffer *>(hardwareBuffer), description.width,
|
49
|
+
description.height, &deleteImageProc, &updateImageProc, &deleteImageCtx,
|
50
|
+
false, format, false);
|
33
51
|
sk_sp<SkImage> image = SkImages::BorrowTextureFrom(
|
34
52
|
ThreadContextHolder::ThreadSkiaOpenGLContext.directContext.get(),
|
35
53
|
backendTex, kTopLeft_GrSurfaceOrigin, kRGBA_8888_SkColorType,
|
36
|
-
|
54
|
+
kOpaque_SkAlphaType, nullptr);
|
37
55
|
return image;
|
38
56
|
#else
|
39
|
-
|
40
|
-
"
|
41
|
-
|
57
|
+
throw std::runtime_error(
|
58
|
+
"HardwareBuffers are only supported on Android API 26 or higher! Set "
|
59
|
+
"your minSdk to 26 (or higher) and try again.");
|
42
60
|
#endif
|
43
61
|
}
|
44
62
|
|
@@ -149,8 +149,7 @@ public:
|
|
149
149
|
*/
|
150
150
|
static sk_sp<SkSurface> makeOffscreenSurface(int width, int height);
|
151
151
|
|
152
|
-
static sk_sp<SkImage> makeImageFromHardwareBuffer(
|
153
|
-
void *buffer);
|
152
|
+
static sk_sp<SkImage> makeImageFromHardwareBuffer(void *buffer);
|
154
153
|
|
155
154
|
/**
|
156
155
|
* Creates a windowed Skia Surface holder.
|
@@ -17,7 +17,8 @@ import android.view.ViewGroup;
|
|
17
17
|
import android.widget.ScrollView;
|
18
18
|
import androidx.annotation.NonNull;
|
19
19
|
import com.facebook.react.bridge.ReactContext;
|
20
|
-
import com.facebook.react.
|
20
|
+
import com.facebook.react.bridge.UIManager;
|
21
|
+
import com.facebook.react.uimanager.UIManagerHelper;
|
21
22
|
import com.facebook.react.views.view.ReactViewGroup;
|
22
23
|
|
23
24
|
import java.lang.reflect.Method;
|
@@ -29,7 +30,7 @@ public class ViewScreenshotService {
|
|
29
30
|
private static final String TAG = "SkiaScreenshot";
|
30
31
|
|
31
32
|
public static Bitmap makeViewScreenshotFromTag(ReactContext context, int tag) {
|
32
|
-
|
33
|
+
UIManager uiManager = UIManagerHelper.getUIManagerForReactTag(context, tag);
|
33
34
|
View view = null;
|
34
35
|
try {
|
35
36
|
view = uiManager.resolveView(tag);
|
@@ -0,0 +1,51 @@
|
|
1
|
+
#pragma once
|
2
|
+
|
3
|
+
#include <memory>
|
4
|
+
#include <utility>
|
5
|
+
|
6
|
+
#include <jsi/jsi.h>
|
7
|
+
|
8
|
+
#include "JsiSkImage.h"
|
9
|
+
|
10
|
+
namespace RNSkia {
|
11
|
+
|
12
|
+
namespace jsi = facebook::jsi;
|
13
|
+
|
14
|
+
/**
|
15
|
+
Implementation of the ParagraphBuilderFactory for making ParagraphBuilder JSI
|
16
|
+
object
|
17
|
+
*/
|
18
|
+
class JsiPlatformBufferFactory : public JsiSkHostObject {
|
19
|
+
public:
|
20
|
+
JSI_HOST_FUNCTION(MakeFromImage) {
|
21
|
+
auto image = JsiSkImage::fromValue(runtime, arguments[0]);
|
22
|
+
image->makeNonTextureImage();
|
23
|
+
|
24
|
+
uint64_t pointer = getContext()->makePlatformBuffer(image);
|
25
|
+
jsi::HostFunctionType deleteFunc =
|
26
|
+
[=](jsi::Runtime &runtime, const jsi::Value &thisArg,
|
27
|
+
const jsi::Value *args, size_t count) -> jsi::Value {
|
28
|
+
getContext()->releasePlatformBuffer(pointer);
|
29
|
+
return jsi::Value::undefined();
|
30
|
+
};
|
31
|
+
return jsi::BigInt::fromUint64(runtime, pointer);
|
32
|
+
}
|
33
|
+
|
34
|
+
JSI_HOST_FUNCTION(Release) {
|
35
|
+
|
36
|
+
jsi::BigInt pointer = arguments[0].asBigInt(runtime);
|
37
|
+
const uintptr_t platformBufferPointer = pointer.asUint64(runtime);
|
38
|
+
|
39
|
+
getContext()->releasePlatformBuffer(platformBufferPointer);
|
40
|
+
return jsi::Value::undefined();
|
41
|
+
}
|
42
|
+
|
43
|
+
JSI_EXPORT_FUNCTIONS(JSI_EXPORT_FUNC(JsiPlatformBufferFactory, Release),
|
44
|
+
JSI_EXPORT_FUNC(JsiPlatformBufferFactory, MakeFromImage))
|
45
|
+
|
46
|
+
explicit JsiPlatformBufferFactory(
|
47
|
+
std::shared_ptr<RNSkPlatformContext> context)
|
48
|
+
: JsiSkHostObject(std::move(context)) {}
|
49
|
+
};
|
50
|
+
|
51
|
+
} // namespace RNSkia
|
package/cpp/api/JsiSkApi.h
CHANGED
@@ -6,6 +6,7 @@
|
|
6
6
|
|
7
7
|
#include "JsiSkHostObjects.h"
|
8
8
|
|
9
|
+
#include "JsiPlatformBuffer.h"
|
9
10
|
#include "JsiSkAnimatedImage.h"
|
10
11
|
#include "JsiSkAnimatedImageFactory.h"
|
11
12
|
#include "JsiSkColor.h"
|
@@ -122,6 +123,9 @@ public:
|
|
122
123
|
installReadonlyProperty(
|
123
124
|
"ParagraphBuilder",
|
124
125
|
std::make_shared<JsiSkParagraphBuilderFactory>(context));
|
126
|
+
|
127
|
+
installReadonlyProperty(
|
128
|
+
"PlatformBuffer", std::make_shared<JsiPlatformBufferFactory>(context));
|
125
129
|
}
|
126
130
|
};
|
127
131
|
} // namespace RNSkia
|
package/cpp/api/JsiSkImage.h
CHANGED
@@ -27,6 +27,18 @@ public:
|
|
27
27
|
runtime, std::make_shared<JsiSkImage>(getContext(), std::move(image)));
|
28
28
|
}
|
29
29
|
|
30
|
+
JSI_HOST_FUNCTION(MakeImageFromPlatformBuffer) {
|
31
|
+
jsi::BigInt pointer = arguments[0].asBigInt(runtime);
|
32
|
+
const uintptr_t platformBufferPointer = pointer.asUint64(runtime);
|
33
|
+
void *rawPointer = reinterpret_cast<void *>(platformBufferPointer);
|
34
|
+
auto image = getContext()->makeImageFromPlatformBuffer(rawPointer);
|
35
|
+
if (image == nullptr) {
|
36
|
+
throw std::runtime_error("Failed to convert PlatformBuffer to SkImage!");
|
37
|
+
}
|
38
|
+
return jsi::Object::createFromHostObject(
|
39
|
+
runtime, std::make_shared<JsiSkImage>(getContext(), std::move(image)));
|
40
|
+
}
|
41
|
+
|
30
42
|
JSI_HOST_FUNCTION(MakeImage) {
|
31
43
|
auto imageInfo = JsiSkImageInfo::fromValue(runtime, arguments[0]);
|
32
44
|
auto pixelData = JsiSkData::fromValue(runtime, arguments[1]);
|
@@ -47,7 +59,6 @@ public:
|
|
47
59
|
[context = std::move(context), viewTag](
|
48
60
|
jsi::Runtime &runtime,
|
49
61
|
std::shared_ptr<RNJsi::JsiPromises::Promise> promise) -> void {
|
50
|
-
// Create a stream operation - this will be run on the main thread
|
51
62
|
context->makeViewScreenshot(
|
52
63
|
viewTag, [&runtime, context = std::move(context),
|
53
64
|
promise = std::move(promise)](sk_sp<SkImage> image) {
|
@@ -69,7 +80,9 @@ public:
|
|
69
80
|
|
70
81
|
JSI_EXPORT_FUNCTIONS(JSI_EXPORT_FUNC(JsiSkImageFactory, MakeImageFromEncoded),
|
71
82
|
JSI_EXPORT_FUNC(JsiSkImageFactory, MakeImageFromViewTag),
|
72
|
-
JSI_EXPORT_FUNC(JsiSkImageFactory,
|
83
|
+
JSI_EXPORT_FUNC(JsiSkImageFactory,
|
84
|
+
MakeImageFromPlatformBuffer),
|
85
|
+
JSI_EXPORT_FUNC(JsiSkImageFactory, MakeImage))
|
73
86
|
|
74
87
|
explicit JsiSkImageFactory(std::shared_ptr<RNSkPlatformContext> context)
|
75
88
|
: JsiSkHostObject(std::move(context)) {}
|
package/cpp/api/JsiSkSurface.h
CHANGED
@@ -31,6 +31,12 @@ public:
|
|
31
31
|
|
32
32
|
EXPORT_JSI_API_TYPENAME(JsiSkSurface, Surface)
|
33
33
|
|
34
|
+
// TODO-API: Properties?
|
35
|
+
JSI_HOST_FUNCTION(width) { return static_cast<double>(getObject()->width()); }
|
36
|
+
JSI_HOST_FUNCTION(height) {
|
37
|
+
return static_cast<double>(getObject()->height());
|
38
|
+
}
|
39
|
+
|
34
40
|
JSI_HOST_FUNCTION(getCanvas) {
|
35
41
|
return jsi::Object::createFromHostObject(
|
36
42
|
runtime,
|
@@ -57,7 +63,9 @@ public:
|
|
57
63
|
runtime, std::make_shared<JsiSkImage>(getContext(), std::move(image)));
|
58
64
|
}
|
59
65
|
|
60
|
-
JSI_EXPORT_FUNCTIONS(JSI_EXPORT_FUNC(JsiSkSurface,
|
66
|
+
JSI_EXPORT_FUNCTIONS(JSI_EXPORT_FUNC(JsiSkSurface, width),
|
67
|
+
JSI_EXPORT_FUNC(JsiSkSurface, height),
|
68
|
+
JSI_EXPORT_FUNC(JsiSkSurface, getCanvas),
|
61
69
|
JSI_EXPORT_FUNC(JsiSkSurface, makeImageSnapshot),
|
62
70
|
JSI_EXPORT_FUNC(JsiSkSurface, flush),
|
63
71
|
JSI_EXPORT_FUNC(JsiSkSurface, dispose))
|
@@ -5,6 +5,7 @@
|
|
5
5
|
#include <mutex>
|
6
6
|
#include <string>
|
7
7
|
#include <unordered_map>
|
8
|
+
#include <utility>
|
8
9
|
#include <vector>
|
9
10
|
|
10
11
|
#include "JsiHostObject.h"
|
@@ -177,9 +178,56 @@ public:
|
|
177
178
|
return jsi::Value::undefined();
|
178
179
|
}
|
179
180
|
|
181
|
+
JSI_HOST_FUNCTION(makeImageSnapshotAsync) {
|
182
|
+
if (count < 1) {
|
183
|
+
_platformContext->raiseError(std::string(
|
184
|
+
"makeImageSnapshotAsync: Expected at least 1 argument, got " +
|
185
|
+
std::to_string(count) + "."));
|
186
|
+
return jsi::Value::undefined();
|
187
|
+
}
|
188
|
+
|
189
|
+
if (!arguments[0].isNumber()) {
|
190
|
+
_platformContext->raiseError(
|
191
|
+
"makeImageSnapshot: First argument must be a number");
|
192
|
+
return jsi::Value::undefined();
|
193
|
+
}
|
194
|
+
|
195
|
+
// find Skia view
|
196
|
+
int nativeId = arguments[0].asNumber();
|
197
|
+
auto info = getEnsuredViewInfo(nativeId);
|
198
|
+
auto context = _platformContext;
|
199
|
+
auto bounds =
|
200
|
+
count > 1 && !arguments[1].isUndefined() && !arguments[1].isNull()
|
201
|
+
? JsiSkRect::fromValue(runtime, arguments[1])
|
202
|
+
: nullptr;
|
203
|
+
return RNJsi::JsiPromises::createPromiseAsJSIValue(
|
204
|
+
runtime, [context = std::move(context), info, bounds](
|
205
|
+
jsi::Runtime &runtime,
|
206
|
+
std::shared_ptr<RNJsi::JsiPromises::Promise> promise) {
|
207
|
+
context->runOnMainThread([&runtime, info = std::move(info),
|
208
|
+
promise = std::move(promise),
|
209
|
+
context = std::move(context), bounds]() {
|
210
|
+
auto image = info->view->makeImageSnapshot(
|
211
|
+
bounds == nullptr ? nullptr : bounds.get());
|
212
|
+
context->runOnJavascriptThread(
|
213
|
+
[&runtime, context = std::move(context),
|
214
|
+
promise = std::move(promise), image = std::move(image)]() {
|
215
|
+
if (image == nullptr) {
|
216
|
+
promise->reject("Failed to make snapshot from view.");
|
217
|
+
return;
|
218
|
+
}
|
219
|
+
promise->resolve(jsi::Object::createFromHostObject(
|
220
|
+
runtime, std::make_shared<JsiSkImage>(std::move(context),
|
221
|
+
std::move(image))));
|
222
|
+
});
|
223
|
+
});
|
224
|
+
});
|
225
|
+
}
|
226
|
+
|
180
227
|
JSI_EXPORT_FUNCTIONS(JSI_EXPORT_FUNC(RNSkJsiViewApi, setJsiProperty),
|
181
228
|
JSI_EXPORT_FUNC(RNSkJsiViewApi, callJsiMethod),
|
182
229
|
JSI_EXPORT_FUNC(RNSkJsiViewApi, requestRedraw),
|
230
|
+
JSI_EXPORT_FUNC(RNSkJsiViewApi, makeImageSnapshotAsync),
|
183
231
|
JSI_EXPORT_FUNC(RNSkJsiViewApi, makeImageSnapshot))
|
184
232
|
|
185
233
|
/**
|
@@ -133,6 +133,19 @@ public:
|
|
133
133
|
*/
|
134
134
|
virtual sk_sp<SkSurface> makeOffscreenSurface(int width, int height) = 0;
|
135
135
|
|
136
|
+
/**
|
137
|
+
* Creates an image from a native platform buffer.
|
138
|
+
* - On iOS, this is a `CMSampleBuffer`
|
139
|
+
* - On Android, this is a `AHardwareBuffer*`
|
140
|
+
* @param buffer The native platform buffer.
|
141
|
+
* @return sk_sp<SkImage>
|
142
|
+
*/
|
143
|
+
virtual sk_sp<SkImage> makeImageFromPlatformBuffer(void *buffer) = 0;
|
144
|
+
|
145
|
+
virtual void releasePlatformBuffer(uint64_t pointer) = 0;
|
146
|
+
|
147
|
+
virtual uint64_t makePlatformBuffer(sk_sp<SkImage> image) = 0;
|
148
|
+
|
136
149
|
/**
|
137
150
|
* Return the Platform specific font manager
|
138
151
|
*/
|
@@ -59,6 +59,12 @@ public:
|
|
59
59
|
|
60
60
|
sk_sp<SkImage> takeScreenshotFromViewTag(size_t tag) override;
|
61
61
|
|
62
|
+
sk_sp<SkImage> makeImageFromPlatformBuffer(void *buffer) override;
|
63
|
+
|
64
|
+
uint64_t makePlatformBuffer(sk_sp<SkImage> image) override;
|
65
|
+
|
66
|
+
void releasePlatformBuffer(uint64_t pointer) override;
|
67
|
+
|
62
68
|
virtual void performStreamOperation(
|
63
69
|
const std::string &sourceUri,
|
64
70
|
const std::function<void(std::unique_ptr<SkStreamAsset>)> &op) override;
|
@@ -1,5 +1,6 @@
|
|
1
1
|
#include "RNSkiOSPlatformContext.h"
|
2
2
|
|
3
|
+
#import <CoreMedia/CMSampleBuffer.h>
|
3
4
|
#import <React/RCTUtils.h>
|
4
5
|
#include <thread>
|
5
6
|
#include <utility>
|
@@ -56,6 +57,89 @@ void RNSkiOSPlatformContext::performStreamOperation(
|
|
56
57
|
std::thread(loader).detach();
|
57
58
|
}
|
58
59
|
|
60
|
+
void RNSkiOSPlatformContext::releasePlatformBuffer(uint64_t pointer) {
|
61
|
+
CMSampleBufferRef sampleBuffer = reinterpret_cast<CMSampleBufferRef>(pointer);
|
62
|
+
CVPixelBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
|
63
|
+
if (sampleBuffer) {
|
64
|
+
CFRelease(sampleBuffer);
|
65
|
+
}
|
66
|
+
if (pixelBuffer) {
|
67
|
+
CFRelease(pixelBuffer);
|
68
|
+
}
|
69
|
+
}
|
70
|
+
|
71
|
+
uint64_t RNSkiOSPlatformContext::makePlatformBuffer(sk_sp<SkImage> image) {
|
72
|
+
auto bytesPerPixel = image->imageInfo().bytesPerPixel();
|
73
|
+
int bytesPerRow = image->width() * bytesPerPixel;
|
74
|
+
auto buf = SkData::MakeUninitialized(image->width() * image->height() *
|
75
|
+
bytesPerPixel);
|
76
|
+
SkImageInfo info = SkImageInfo::Make(image->width(), image->height(),
|
77
|
+
image->colorType(), image->alphaType());
|
78
|
+
image->readPixels(nullptr, info, const_cast<void *>(buf->data()), bytesPerRow,
|
79
|
+
0, 0);
|
80
|
+
auto pixelData = const_cast<void *>(buf->data());
|
81
|
+
|
82
|
+
// Create a CVPixelBuffer from the raw pixel data
|
83
|
+
CVPixelBufferRef pixelBuffer = nullptr;
|
84
|
+
// OSType pixelFormatType = MapSkColorTypeToOSType(image->colorType());
|
85
|
+
|
86
|
+
// You will need to fill in the details for creating the pixel buffer
|
87
|
+
// CVPixelBufferCreateWithBytes or CVPixelBufferCreateWithPlanarBytes
|
88
|
+
// Create the CVPixelBuffer with the image data
|
89
|
+
void *context = static_cast<void *>(
|
90
|
+
new sk_sp<SkData>(buf)); // Create a copy for the context
|
91
|
+
CVReturn r = CVPixelBufferCreateWithBytes(
|
92
|
+
nullptr, // allocator
|
93
|
+
image->width(), image->height(), kCVPixelFormatType_32BGRA,
|
94
|
+
pixelData, // pixel data
|
95
|
+
bytesPerRow, // bytes per row
|
96
|
+
[](void *releaseRefCon, const void *baseAddress) { // release callback
|
97
|
+
auto buf = static_cast<sk_sp<SkData> *>(releaseRefCon);
|
98
|
+
buf->reset(); // This effectively calls unref on the SkData object
|
99
|
+
delete buf; // Cleanup the dynamically allocated context
|
100
|
+
},
|
101
|
+
context, // release callback context
|
102
|
+
nullptr, // pixel buffer attributes
|
103
|
+
&pixelBuffer // the newly created pixel buffer
|
104
|
+
);
|
105
|
+
|
106
|
+
if (r != kCVReturnSuccess) {
|
107
|
+
return 0; // or handle error appropriately
|
108
|
+
}
|
109
|
+
|
110
|
+
// Wrap the CVPixelBuffer in a CMSampleBuffer
|
111
|
+
CMSampleBufferRef sampleBuffer = nullptr;
|
112
|
+
|
113
|
+
CMFormatDescriptionRef formatDescription = nullptr;
|
114
|
+
CMVideoFormatDescriptionCreateForImageBuffer(kCFAllocatorDefault, pixelBuffer,
|
115
|
+
&formatDescription);
|
116
|
+
|
117
|
+
// Assuming no specific timing is required, we initialize the timing info to
|
118
|
+
// zero.
|
119
|
+
CMSampleTimingInfo timingInfo = {0};
|
120
|
+
timingInfo.duration = kCMTimeInvalid; // Indicate an unknown duration.
|
121
|
+
timingInfo.presentationTimeStamp = kCMTimeZero; // Start at time zero.
|
122
|
+
timingInfo.decodeTimeStamp = kCMTimeInvalid; // No specific decode time.
|
123
|
+
|
124
|
+
// Create the sample buffer.
|
125
|
+
OSStatus status = CMSampleBufferCreateReadyWithImageBuffer(
|
126
|
+
kCFAllocatorDefault, pixelBuffer, formatDescription, &timingInfo,
|
127
|
+
&sampleBuffer);
|
128
|
+
|
129
|
+
if (status != noErr) {
|
130
|
+
if (formatDescription) {
|
131
|
+
CFRelease(formatDescription);
|
132
|
+
}
|
133
|
+
if (pixelBuffer) {
|
134
|
+
CFRelease(pixelBuffer);
|
135
|
+
}
|
136
|
+
return 0;
|
137
|
+
}
|
138
|
+
|
139
|
+
// Return sampleBuffer casted to uint64_t
|
140
|
+
return reinterpret_cast<uint64_t>(sampleBuffer);
|
141
|
+
}
|
142
|
+
|
59
143
|
void RNSkiOSPlatformContext::raiseError(const std::exception &err) {
|
60
144
|
RCTFatal(RCTErrorWithMessage([NSString stringWithUTF8String:err.what()]));
|
61
145
|
}
|
@@ -65,6 +149,37 @@ sk_sp<SkSurface> RNSkiOSPlatformContext::makeOffscreenSurface(int width,
|
|
65
149
|
return SkiaMetalSurfaceFactory::makeOffscreenSurface(width, height);
|
66
150
|
}
|
67
151
|
|
152
|
+
sk_sp<SkImage>
|
153
|
+
RNSkiOSPlatformContext::makeImageFromPlatformBuffer(void *buffer) {
|
154
|
+
CMSampleBufferRef sampleBuffer = (CMSampleBufferRef)buffer;
|
155
|
+
// DO the CPU transfer (debugging only)
|
156
|
+
// Step 1: Extract the CVPixelBufferRef from the CMSampleBufferRef
|
157
|
+
CVPixelBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
|
158
|
+
|
159
|
+
// Step 2: Lock the pixel buffer to access the raw pixel data
|
160
|
+
CVPixelBufferLockBaseAddress(pixelBuffer, 0);
|
161
|
+
|
162
|
+
// Step 3: Get information about the image
|
163
|
+
void *baseAddress = CVPixelBufferGetBaseAddress(pixelBuffer);
|
164
|
+
size_t width = CVPixelBufferGetWidth(pixelBuffer);
|
165
|
+
size_t height = CVPixelBufferGetHeight(pixelBuffer);
|
166
|
+
size_t bytesPerRow = CVPixelBufferGetBytesPerRow(pixelBuffer);
|
167
|
+
|
168
|
+
// Assuming the pixel format is 32BGRA, which is common for iOS video frames.
|
169
|
+
// You might need to adjust this based on the actual pixel format.
|
170
|
+
SkImageInfo info = SkImageInfo::Make(width, height, kRGBA_8888_SkColorType,
|
171
|
+
kUnpremul_SkAlphaType);
|
172
|
+
|
173
|
+
// Step 4: Create an SkImage from the pixel buffer
|
174
|
+
sk_sp<SkData> data =
|
175
|
+
SkData::MakeWithoutCopy(baseAddress, height * bytesPerRow);
|
176
|
+
sk_sp<SkImage> image = SkImages::RasterFromData(info, data, bytesPerRow);
|
177
|
+
auto texture = SkiaMetalSurfaceFactory::makeTextureFromImage(image);
|
178
|
+
// Step 5: Unlock the pixel buffer
|
179
|
+
CVPixelBufferUnlockBaseAddress(pixelBuffer, 0);
|
180
|
+
return texture;
|
181
|
+
}
|
182
|
+
|
68
183
|
sk_sp<SkFontMgr> RNSkiOSPlatformContext::createFontMgr() {
|
69
184
|
return SkFontMgr_New_CoreText(nullptr);
|
70
185
|
}
|
@@ -4,6 +4,8 @@
|
|
4
4
|
#pragma clang diagnostic ignored "-Wdocumentation"
|
5
5
|
|
6
6
|
#import "include/core/SkCanvas.h"
|
7
|
+
#import <CoreMedia/CMSampleBuffer.h>
|
8
|
+
#import <CoreVideo/CVMetalTextureCache.h>
|
7
9
|
#import <include/gpu/GrDirectContext.h>
|
8
10
|
|
9
11
|
#pragma clang diagnostic pop
|
@@ -24,6 +26,8 @@ public:
|
|
24
26
|
int height);
|
25
27
|
static sk_sp<SkSurface> makeOffscreenSurface(int width, int height);
|
26
28
|
|
29
|
+
static sk_sp<SkImage> makeTextureFromImage(sk_sp<SkImage> image);
|
30
|
+
|
27
31
|
private:
|
28
32
|
static id<MTLDevice> device;
|
29
33
|
static bool
|
@@ -11,7 +11,9 @@
|
|
11
11
|
|
12
12
|
#import <include/gpu/GrBackendSurface.h>
|
13
13
|
#import <include/gpu/GrDirectContext.h>
|
14
|
+
#import <include/gpu/ganesh/SkImageGanesh.h>
|
14
15
|
#import <include/gpu/ganesh/SkSurfaceGanesh.h>
|
16
|
+
#import <include/gpu/ganesh/mtl/SkSurfaceMetal.h>
|
15
17
|
|
16
18
|
#pragma clang diagnostic pop
|
17
19
|
|
@@ -103,3 +105,13 @@ sk_sp<SkSurface> SkiaMetalSurfaceFactory::makeOffscreenSurface(int width,
|
|
103
105
|
|
104
106
|
return surface;
|
105
107
|
}
|
108
|
+
|
109
|
+
sk_sp<SkImage>
|
110
|
+
SkiaMetalSurfaceFactory::makeTextureFromImage(sk_sp<SkImage> image) {
|
111
|
+
if (!SkiaMetalSurfaceFactory::createSkiaDirectContextIfNecessary(
|
112
|
+
&ThreadContextHolder::ThreadSkiaMetalContext)) {
|
113
|
+
throw std::runtime_error("Failed to create Skia Context for this Thread!");
|
114
|
+
}
|
115
|
+
return SkImages::TextureFromImage(
|
116
|
+
ThreadContextHolder::ThreadSkiaMetalContext.skContext.get(), image);
|
117
|
+
}
|