@shopify/react-native-skia 1.2.0 → 1.2.1
Sign up to get free protection for your applications and to get access to all the features.
- package/android/cpp/rnskia-android/RNSkAndroidPlatformContext.h +3 -3
- package/cpp/api/JsiNativeBuffer.h +43 -0
- package/cpp/api/JsiSkApi.h +3 -3
- package/cpp/api/JsiSkImageFactory.h +3 -3
- package/cpp/rnskia/RNSkPlatformContext.h +5 -5
- package/ios/RNSkia-iOS/RNSkiOSPlatformContext.h +3 -3
- package/ios/RNSkia-iOS/RNSkiOSPlatformContext.mm +69 -88
- package/ios/RNSkia-iOS/SkiaCVPixelBufferUtils.h +84 -0
- package/ios/RNSkia-iOS/SkiaCVPixelBufferUtils.mm +159 -0
- package/ios/RNSkia-iOS/SkiaMetalSurfaceFactory.h +2 -1
- package/ios/RNSkia-iOS/SkiaMetalSurfaceFactory.mm +30 -6
- package/lib/commonjs/skia/types/Image/ImageFactory.d.ts +8 -15
- package/lib/commonjs/skia/types/Image/ImageFactory.js +1 -10
- package/lib/commonjs/skia/types/Image/ImageFactory.js.map +1 -1
- package/lib/commonjs/skia/types/NativeBuffer/NativeBufferFactory.d.ts +18 -0
- package/lib/commonjs/skia/types/NativeBuffer/NativeBufferFactory.js +13 -0
- package/lib/commonjs/skia/types/NativeBuffer/NativeBufferFactory.js.map +1 -0
- package/lib/commonjs/skia/types/NativeBuffer/index.d.ts +1 -0
- package/lib/commonjs/skia/types/NativeBuffer/index.js +17 -0
- package/lib/commonjs/skia/types/NativeBuffer/index.js.map +1 -0
- package/lib/commonjs/skia/types/Skia.d.ts +2 -2
- package/lib/commonjs/skia/types/Skia.js.map +1 -1
- package/lib/commonjs/skia/types/index.d.ts +1 -1
- package/lib/commonjs/skia/types/index.js +4 -4
- package/lib/commonjs/skia/types/index.js.map +1 -1
- package/lib/commonjs/skia/web/JsiSkImageFactory.d.ts +3 -2
- package/lib/commonjs/skia/web/JsiSkImageFactory.js +18 -2
- package/lib/commonjs/skia/web/JsiSkImageFactory.js.map +1 -1
- package/lib/commonjs/skia/web/JsiSkNativeBufferFactory.d.ts +8 -0
- package/lib/commonjs/skia/web/JsiSkNativeBufferFactory.js +29 -0
- package/lib/commonjs/skia/web/JsiSkNativeBufferFactory.js.map +1 -0
- package/lib/commonjs/skia/web/JsiSkia.js +2 -2
- package/lib/commonjs/skia/web/JsiSkia.js.map +1 -1
- package/lib/module/skia/types/Image/ImageFactory.d.ts +8 -15
- package/lib/module/skia/types/Image/ImageFactory.js +1 -10
- package/lib/module/skia/types/Image/ImageFactory.js.map +1 -1
- package/lib/module/skia/types/NativeBuffer/NativeBufferFactory.d.ts +18 -0
- package/lib/module/skia/types/NativeBuffer/NativeBufferFactory.js +4 -0
- package/lib/module/skia/types/NativeBuffer/NativeBufferFactory.js.map +1 -0
- package/lib/module/skia/types/NativeBuffer/index.d.ts +1 -0
- package/lib/module/skia/types/NativeBuffer/index.js +2 -0
- package/lib/module/skia/types/NativeBuffer/index.js.map +1 -0
- package/lib/module/skia/types/Skia.d.ts +2 -2
- package/lib/module/skia/types/Skia.js.map +1 -1
- package/lib/module/skia/types/index.d.ts +1 -1
- package/lib/module/skia/types/index.js +1 -1
- package/lib/module/skia/types/index.js.map +1 -1
- package/lib/module/skia/web/JsiSkImageFactory.d.ts +3 -2
- package/lib/module/skia/web/JsiSkImageFactory.js +18 -2
- package/lib/module/skia/web/JsiSkImageFactory.js.map +1 -1
- package/lib/module/skia/web/JsiSkNativeBufferFactory.d.ts +8 -0
- package/lib/module/skia/web/JsiSkNativeBufferFactory.js +22 -0
- package/lib/module/skia/web/JsiSkNativeBufferFactory.js.map +1 -0
- package/lib/module/skia/web/JsiSkia.js +2 -2
- package/lib/module/skia/web/JsiSkia.js.map +1 -1
- package/lib/typescript/src/skia/types/Image/ImageFactory.d.ts +8 -15
- package/lib/typescript/src/skia/types/NativeBuffer/NativeBufferFactory.d.ts +18 -0
- package/lib/typescript/src/skia/types/NativeBuffer/index.d.ts +1 -0
- package/lib/typescript/src/skia/types/Skia.d.ts +2 -2
- package/lib/typescript/src/skia/types/index.d.ts +1 -1
- package/lib/typescript/src/skia/web/JsiSkImageFactory.d.ts +3 -2
- package/lib/typescript/src/skia/web/JsiSkNativeBufferFactory.d.ts +8 -0
- package/package.json +1 -1
- package/src/skia/types/Image/ImageFactory.ts +7 -23
- package/src/skia/types/NativeBuffer/NativeBufferFactory.ts +38 -0
- package/src/skia/types/NativeBuffer/index.ts +1 -0
- package/src/skia/types/Skia.ts +2 -2
- package/src/skia/types/index.ts +1 -1
- package/src/skia/web/JsiSkImageFactory.ts +28 -6
- package/src/skia/web/JsiSkNativeBufferFactory.ts +35 -0
- package/src/skia/web/JsiSkia.ts +2 -2
- package/cpp/api/JsiPlatformBuffer.h +0 -51
- package/lib/commonjs/skia/types/PlatformBuffer/PlatformBufferFactory.d.ts +0 -12
- package/lib/commonjs/skia/types/PlatformBuffer/PlatformBufferFactory.js +0 -6
- package/lib/commonjs/skia/types/PlatformBuffer/PlatformBufferFactory.js.map +0 -1
- package/lib/commonjs/skia/types/PlatformBuffer/index.d.ts +0 -1
- package/lib/commonjs/skia/types/PlatformBuffer/index.js +0 -17
- package/lib/commonjs/skia/types/PlatformBuffer/index.js.map +0 -1
- package/lib/commonjs/skia/web/JsiSkPlatformBufferFactory.d.ts +0 -8
- package/lib/commonjs/skia/web/JsiSkPlatformBufferFactory.js +0 -20
- package/lib/commonjs/skia/web/JsiSkPlatformBufferFactory.js.map +0 -1
- package/lib/module/skia/types/PlatformBuffer/PlatformBufferFactory.d.ts +0 -12
- package/lib/module/skia/types/PlatformBuffer/PlatformBufferFactory.js +0 -2
- package/lib/module/skia/types/PlatformBuffer/PlatformBufferFactory.js.map +0 -1
- package/lib/module/skia/types/PlatformBuffer/index.d.ts +0 -1
- package/lib/module/skia/types/PlatformBuffer/index.js +0 -2
- package/lib/module/skia/types/PlatformBuffer/index.js.map +0 -1
- package/lib/module/skia/web/JsiSkPlatformBufferFactory.d.ts +0 -8
- package/lib/module/skia/web/JsiSkPlatformBufferFactory.js +0 -13
- package/lib/module/skia/web/JsiSkPlatformBufferFactory.js.map +0 -1
- package/lib/typescript/src/skia/types/PlatformBuffer/PlatformBufferFactory.d.ts +0 -12
- package/lib/typescript/src/skia/types/PlatformBuffer/index.d.ts +0 -1
- package/lib/typescript/src/skia/web/JsiSkPlatformBufferFactory.d.ts +0 -8
- package/src/skia/types/PlatformBuffer/PlatformBufferFactory.ts +0 -14
- package/src/skia/types/PlatformBuffer/index.ts +0 -1
- package/src/skia/web/JsiSkPlatformBufferFactory.ts +0 -22
@@ -53,18 +53,18 @@ public:
|
|
53
53
|
return SkiaOpenGLSurfaceFactory::makeOffscreenSurface(width, height);
|
54
54
|
}
|
55
55
|
|
56
|
-
sk_sp<SkImage>
|
56
|
+
sk_sp<SkImage> makeImageFromNativeBuffer(void *buffer) override {
|
57
57
|
return SkiaOpenGLSurfaceFactory::makeImageFromHardwareBuffer(buffer);
|
58
58
|
}
|
59
59
|
|
60
|
-
void
|
60
|
+
void releaseNativeBuffer(uint64_t pointer) override {
|
61
61
|
#if __ANDROID_API__ >= 26
|
62
62
|
AHardwareBuffer *buffer = reinterpret_cast<AHardwareBuffer *>(pointer);
|
63
63
|
AHardwareBuffer_release(buffer);
|
64
64
|
#endif
|
65
65
|
}
|
66
66
|
|
67
|
-
uint64_t
|
67
|
+
uint64_t makeNativeBuffer(sk_sp<SkImage> image) override {
|
68
68
|
#if __ANDROID_API__ >= 26
|
69
69
|
auto bytesPerPixel = image->imageInfo().bytesPerPixel();
|
70
70
|
int bytesPerRow = image->width() * bytesPerPixel;
|
@@ -0,0 +1,43 @@
|
|
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 JsiNativeBufferFactory : public JsiSkHostObject {
|
19
|
+
public:
|
20
|
+
JSI_HOST_FUNCTION(MakeFromImage) {
|
21
|
+
auto image = JsiSkImage::fromValue(runtime, arguments[0]);
|
22
|
+
image->makeNonTextureImage();
|
23
|
+
uint64_t pointer = getContext()->makeNativeBuffer(image);
|
24
|
+
return jsi::BigInt::fromUint64(runtime, pointer);
|
25
|
+
}
|
26
|
+
|
27
|
+
JSI_HOST_FUNCTION(Release) {
|
28
|
+
|
29
|
+
jsi::BigInt pointer = arguments[0].asBigInt(runtime);
|
30
|
+
const uintptr_t nativeBufferPointer = pointer.asUint64(runtime);
|
31
|
+
|
32
|
+
getContext()->releaseNativeBuffer(nativeBufferPointer);
|
33
|
+
return jsi::Value::undefined();
|
34
|
+
}
|
35
|
+
|
36
|
+
JSI_EXPORT_FUNCTIONS(JSI_EXPORT_FUNC(JsiNativeBufferFactory, Release),
|
37
|
+
JSI_EXPORT_FUNC(JsiNativeBufferFactory, MakeFromImage))
|
38
|
+
|
39
|
+
explicit JsiNativeBufferFactory(std::shared_ptr<RNSkPlatformContext> context)
|
40
|
+
: JsiSkHostObject(std::move(context)) {}
|
41
|
+
};
|
42
|
+
|
43
|
+
} // namespace RNSkia
|
package/cpp/api/JsiSkApi.h
CHANGED
@@ -6,7 +6,7 @@
|
|
6
6
|
|
7
7
|
#include "JsiSkHostObjects.h"
|
8
8
|
|
9
|
-
#include "
|
9
|
+
#include "JsiNativeBuffer.h"
|
10
10
|
#include "JsiSkAnimatedImage.h"
|
11
11
|
#include "JsiSkAnimatedImageFactory.h"
|
12
12
|
#include "JsiSkColor.h"
|
@@ -124,8 +124,8 @@ public:
|
|
124
124
|
"ParagraphBuilder",
|
125
125
|
std::make_shared<JsiSkParagraphBuilderFactory>(context));
|
126
126
|
|
127
|
-
installReadonlyProperty(
|
128
|
-
|
127
|
+
installReadonlyProperty("NativeBuffer",
|
128
|
+
std::make_shared<JsiNativeBufferFactory>(context));
|
129
129
|
}
|
130
130
|
};
|
131
131
|
} // namespace RNSkia
|
@@ -27,11 +27,11 @@ public:
|
|
27
27
|
runtime, std::make_shared<JsiSkImage>(getContext(), std::move(image)));
|
28
28
|
}
|
29
29
|
|
30
|
-
JSI_HOST_FUNCTION(
|
30
|
+
JSI_HOST_FUNCTION(MakeImageFromNativeBuffer) {
|
31
31
|
jsi::BigInt pointer = arguments[0].asBigInt(runtime);
|
32
32
|
const uintptr_t platformBufferPointer = pointer.asUint64(runtime);
|
33
33
|
void *rawPointer = reinterpret_cast<void *>(platformBufferPointer);
|
34
|
-
auto image = getContext()->
|
34
|
+
auto image = getContext()->makeImageFromNativeBuffer(rawPointer);
|
35
35
|
if (image == nullptr) {
|
36
36
|
throw std::runtime_error("Failed to convert PlatformBuffer to SkImage!");
|
37
37
|
}
|
@@ -81,7 +81,7 @@ public:
|
|
81
81
|
JSI_EXPORT_FUNCTIONS(JSI_EXPORT_FUNC(JsiSkImageFactory, MakeImageFromEncoded),
|
82
82
|
JSI_EXPORT_FUNC(JsiSkImageFactory, MakeImageFromViewTag),
|
83
83
|
JSI_EXPORT_FUNC(JsiSkImageFactory,
|
84
|
-
|
84
|
+
MakeImageFromNativeBuffer),
|
85
85
|
JSI_EXPORT_FUNC(JsiSkImageFactory, MakeImage))
|
86
86
|
|
87
87
|
explicit JsiSkImageFactory(std::shared_ptr<RNSkPlatformContext> context)
|
@@ -134,17 +134,17 @@ public:
|
|
134
134
|
virtual sk_sp<SkSurface> makeOffscreenSurface(int width, int height) = 0;
|
135
135
|
|
136
136
|
/**
|
137
|
-
* Creates an image from a native
|
138
|
-
* - On iOS, this is a `
|
137
|
+
* Creates an image from a native buffer. (for testing purposes only)
|
138
|
+
* - On iOS, this is a `CVPixelBufferRef*`
|
139
139
|
* - On Android, this is a `AHardwareBuffer*`
|
140
140
|
* @param buffer The native platform buffer.
|
141
141
|
* @return sk_sp<SkImage>
|
142
142
|
*/
|
143
|
-
virtual sk_sp<SkImage>
|
143
|
+
virtual sk_sp<SkImage> makeImageFromNativeBuffer(void *buffer) = 0;
|
144
144
|
|
145
|
-
virtual void
|
145
|
+
virtual void releaseNativeBuffer(uint64_t pointer) = 0;
|
146
146
|
|
147
|
-
virtual uint64_t
|
147
|
+
virtual uint64_t makeNativeBuffer(sk_sp<SkImage> image) = 0;
|
148
148
|
|
149
149
|
/**
|
150
150
|
* Return the Platform specific font manager
|
@@ -59,11 +59,11 @@ public:
|
|
59
59
|
|
60
60
|
sk_sp<SkImage> takeScreenshotFromViewTag(size_t tag) override;
|
61
61
|
|
62
|
-
sk_sp<SkImage>
|
62
|
+
sk_sp<SkImage> makeImageFromNativeBuffer(void *buffer) override;
|
63
63
|
|
64
|
-
uint64_t
|
64
|
+
uint64_t makeNativeBuffer(sk_sp<SkImage> image) override;
|
65
65
|
|
66
|
-
void
|
66
|
+
void releaseNativeBuffer(uint64_t pointer) override;
|
67
67
|
|
68
68
|
virtual void performStreamOperation(
|
69
69
|
const std::string &sourceUri,
|
@@ -1,11 +1,12 @@
|
|
1
|
-
#
|
1
|
+
#import "RNSkiOSPlatformContext.h"
|
2
2
|
|
3
3
|
#import <CoreMedia/CMSampleBuffer.h>
|
4
4
|
#import <React/RCTUtils.h>
|
5
5
|
#include <thread>
|
6
6
|
#include <utility>
|
7
7
|
|
8
|
-
#
|
8
|
+
#import "SkiaCVPixelBufferUtils.h"
|
9
|
+
#import "SkiaMetalSurfaceFactory.h"
|
9
10
|
|
10
11
|
#pragma clang diagnostic push
|
11
12
|
#pragma clang diagnostic ignored "-Wdocumentation"
|
@@ -57,7 +58,7 @@ void RNSkiOSPlatformContext::performStreamOperation(
|
|
57
58
|
std::thread(loader).detach();
|
58
59
|
}
|
59
60
|
|
60
|
-
void RNSkiOSPlatformContext::
|
61
|
+
void RNSkiOSPlatformContext::releaseNativeBuffer(uint64_t pointer) {
|
61
62
|
CMSampleBufferRef sampleBuffer = reinterpret_cast<CMSampleBufferRef>(pointer);
|
62
63
|
CVPixelBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
|
63
64
|
if (sampleBuffer) {
|
@@ -68,76 +69,82 @@ void RNSkiOSPlatformContext::releasePlatformBuffer(uint64_t pointer) {
|
|
68
69
|
}
|
69
70
|
}
|
70
71
|
|
71
|
-
uint64_t RNSkiOSPlatformContext::
|
72
|
+
uint64_t RNSkiOSPlatformContext::makeNativeBuffer(sk_sp<SkImage> image) {
|
73
|
+
// 0. If Image is not in BGRA, convert to BGRA as only BGRA is supported.
|
74
|
+
if (image->colorType() != kBGRA_8888_SkColorType) {
|
75
|
+
// on iOS, 32_BGRA is the only supported RGB format for CVPixelBuffers.
|
76
|
+
image = image->makeColorTypeAndColorSpace(
|
77
|
+
ThreadContextHolder::ThreadSkiaMetalContext.skContext.get(),
|
78
|
+
kBGRA_8888_SkColorType, SkColorSpace::MakeSRGB());
|
79
|
+
if (image == nullptr) {
|
80
|
+
throw std::runtime_error(
|
81
|
+
"Failed to convert image to BGRA_8888 colortype! Only BGRA_8888 "
|
82
|
+
"PlatformBuffers are supported.");
|
83
|
+
}
|
84
|
+
}
|
85
|
+
|
86
|
+
// 1. Get image info
|
72
87
|
auto bytesPerPixel = image->imageInfo().bytesPerPixel();
|
73
88
|
int bytesPerRow = image->width() * bytesPerPixel;
|
74
89
|
auto buf = SkData::MakeUninitialized(image->width() * image->height() *
|
75
90
|
bytesPerPixel);
|
76
91
|
SkImageInfo info = SkImageInfo::Make(image->width(), image->height(),
|
77
92
|
image->colorType(), image->alphaType());
|
93
|
+
// 2. Copy pixels into our buffer
|
78
94
|
image->readPixels(nullptr, info, const_cast<void *>(buf->data()), bytesPerRow,
|
79
95
|
0, 0);
|
80
|
-
auto pixelData = const_cast<void *>(buf->data());
|
81
96
|
|
82
|
-
// Create
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
97
|
+
// 3. Create an IOSurface (GPU + CPU memory)
|
98
|
+
CFMutableDictionaryRef dict = CFDictionaryCreateMutable(
|
99
|
+
kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks,
|
100
|
+
&kCFTypeDictionaryValueCallBacks);
|
101
|
+
int width = image->width();
|
102
|
+
int height = image->height();
|
103
|
+
int pitch = width * bytesPerPixel;
|
104
|
+
int size = width * height * bytesPerPixel;
|
105
|
+
OSType pixelFormat = kCVPixelFormatType_32BGRA;
|
106
|
+
CFDictionarySetValue(
|
107
|
+
dict, kIOSurfaceBytesPerRow,
|
108
|
+
CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &pitch));
|
109
|
+
CFDictionarySetValue(
|
110
|
+
dict, kIOSurfaceBytesPerElement,
|
111
|
+
CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &bytesPerPixel));
|
112
|
+
CFDictionarySetValue(
|
113
|
+
dict, kIOSurfaceWidth,
|
114
|
+
CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &width));
|
115
|
+
CFDictionarySetValue(
|
116
|
+
dict, kIOSurfaceHeight,
|
117
|
+
CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &height));
|
118
|
+
CFDictionarySetValue(
|
119
|
+
dict, kIOSurfacePixelFormat,
|
120
|
+
CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &pixelFormat));
|
121
|
+
CFDictionarySetValue(
|
122
|
+
dict, kIOSurfaceAllocSize,
|
123
|
+
CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &size));
|
124
|
+
IOSurfaceRef surface = IOSurfaceCreate(dict);
|
125
|
+
if (surface == nil) {
|
126
|
+
throw std::runtime_error("Failed to create " + std::to_string(width) + "x" +
|
127
|
+
std::to_string(height) + " IOSurface!");
|
108
128
|
}
|
109
129
|
|
110
|
-
//
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
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);
|
130
|
+
// 4. Copy over the memory from the pixels into the IOSurface
|
131
|
+
IOSurfaceLock(surface, 0, nil);
|
132
|
+
void *base = IOSurfaceGetBaseAddress(surface);
|
133
|
+
memcpy(base, buf->data(), buf->size());
|
134
|
+
IOSurfaceUnlock(surface, 0, nil);
|
128
135
|
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
136
|
+
// 5. Create a CVPixelBuffer from the IOSurface
|
137
|
+
CVPixelBufferRef pixelBuffer = nullptr;
|
138
|
+
CVReturn result =
|
139
|
+
CVPixelBufferCreateWithIOSurface(nil, surface, nil, &pixelBuffer);
|
140
|
+
if (result != kCVReturnSuccess) {
|
141
|
+
throw std::runtime_error(
|
142
|
+
"Failed to create CVPixelBuffer from SkImage! Return value: " +
|
143
|
+
std::to_string(result));
|
137
144
|
}
|
138
145
|
|
139
|
-
// Return
|
140
|
-
return reinterpret_cast<uint64_t>(
|
146
|
+
// 8. Return CVPixelBuffer casted to uint64_t
|
147
|
+
return reinterpret_cast<uint64_t>(pixelBuffer);
|
141
148
|
}
|
142
149
|
|
143
150
|
void RNSkiOSPlatformContext::raiseError(const std::exception &err) {
|
@@ -149,35 +156,9 @@ sk_sp<SkSurface> RNSkiOSPlatformContext::makeOffscreenSurface(int width,
|
|
149
156
|
return SkiaMetalSurfaceFactory::makeOffscreenSurface(width, height);
|
150
157
|
}
|
151
158
|
|
152
|
-
sk_sp<SkImage>
|
153
|
-
|
154
|
-
|
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;
|
159
|
+
sk_sp<SkImage> RNSkiOSPlatformContext::makeImageFromNativeBuffer(void *buffer) {
|
160
|
+
CVPixelBufferRef sampleBuffer = (CVPixelBufferRef)buffer;
|
161
|
+
return SkiaMetalSurfaceFactory::makeTextureFromCVPixelBuffer(sampleBuffer);
|
181
162
|
}
|
182
163
|
|
183
164
|
sk_sp<SkFontMgr> RNSkiOSPlatformContext::createFontMgr() {
|
@@ -0,0 +1,84 @@
|
|
1
|
+
//
|
2
|
+
// SkiaCVPixelBufferUtils.h
|
3
|
+
// react-native-skia
|
4
|
+
//
|
5
|
+
// Created by Marc Rousavy on 10.04.24.
|
6
|
+
//
|
7
|
+
|
8
|
+
#pragma once
|
9
|
+
#import <CoreMedia/CMSampleBuffer.h>
|
10
|
+
#import <CoreVideo/CVMetalTextureCache.h>
|
11
|
+
#import <MetalKit/MetalKit.h>
|
12
|
+
|
13
|
+
#pragma clang diagnostic push
|
14
|
+
#pragma clang diagnostic ignored "-Wdocumentation"
|
15
|
+
#import "include/core/SkColorSpace.h"
|
16
|
+
#import <include/gpu/GrBackendSurface.h>
|
17
|
+
#pragma clang diagnostic pop
|
18
|
+
|
19
|
+
/**
|
20
|
+
Holds a Metal Texture.
|
21
|
+
When the `TextureHolder` is destroyed, the underlying Metal Texture
|
22
|
+
is marked as invalid and might be overwritten by the Texture Cache,
|
23
|
+
so make sure to delete the `TextureHolder` only when the `SkImage`
|
24
|
+
has been deleted.
|
25
|
+
|
26
|
+
For example, use the `releaseProc` parameter in `BorrowImageFromTexture`
|
27
|
+
to delete the `TextureHolder`.
|
28
|
+
*/
|
29
|
+
class TextureHolder {
|
30
|
+
public:
|
31
|
+
/**
|
32
|
+
Create a new instance of TextureHolder which holds
|
33
|
+
the given `CVMetalTextureRef`.
|
34
|
+
|
35
|
+
The given `CVMetalTextureRef` is assumed to already be
|
36
|
+
retained with `CFRetain`, and will later be manually
|
37
|
+
released with `CFRelease`.
|
38
|
+
*/
|
39
|
+
explicit TextureHolder(CVMetalTextureRef texture);
|
40
|
+
~TextureHolder();
|
41
|
+
|
42
|
+
/**
|
43
|
+
Converts this Texture to a Skia GrBackendTexture.
|
44
|
+
*/
|
45
|
+
GrBackendTexture toGrBackendTexture();
|
46
|
+
|
47
|
+
private:
|
48
|
+
CVMetalTextureRef _texture;
|
49
|
+
};
|
50
|
+
|
51
|
+
class SkiaCVPixelBufferUtils {
|
52
|
+
public:
|
53
|
+
enum class CVPixelBufferBaseFormat { rgb };
|
54
|
+
|
55
|
+
/**
|
56
|
+
Get the base format (currently only RGB) of the PixelBuffer.
|
57
|
+
Depending on the base-format, different methods have to be used to create
|
58
|
+
Skia buffers.
|
59
|
+
*/
|
60
|
+
static CVPixelBufferBaseFormat
|
61
|
+
getCVPixelBufferBaseFormat(CVPixelBufferRef pixelBuffer);
|
62
|
+
|
63
|
+
class RGB {
|
64
|
+
public:
|
65
|
+
/**
|
66
|
+
Gets the Skia Color Type of the RGB pixel-buffer.
|
67
|
+
*/
|
68
|
+
static SkColorType getCVPixelBufferColorType(CVPixelBufferRef pixelBuffer);
|
69
|
+
/**
|
70
|
+
Gets a GPU-backed Skia Texture for the given RGB CVPixelBuffer.
|
71
|
+
*/
|
72
|
+
static TextureHolder *
|
73
|
+
getSkiaTextureForCVPixelBuffer(CVPixelBufferRef pixelBuffer);
|
74
|
+
};
|
75
|
+
|
76
|
+
private:
|
77
|
+
static CVMetalTextureCacheRef getTextureCache();
|
78
|
+
static TextureHolder *
|
79
|
+
getSkiaTextureForCVPixelBufferPlane(CVPixelBufferRef pixelBuffer,
|
80
|
+
size_t planeIndex);
|
81
|
+
static MTLPixelFormat
|
82
|
+
getMTLPixelFormatForCVPixelBufferPlane(CVPixelBufferRef pixelBuffer,
|
83
|
+
size_t planeIndex);
|
84
|
+
};
|
@@ -0,0 +1,159 @@
|
|
1
|
+
//
|
2
|
+
// SkiaCVPixelBufferUtils.mm
|
3
|
+
// react-native-skia
|
4
|
+
//
|
5
|
+
// Created by Marc Rousavy on 10.04.24.
|
6
|
+
//
|
7
|
+
|
8
|
+
#import "SkiaCVPixelBufferUtils.h"
|
9
|
+
#import "SkiaMetalSurfaceFactory.h"
|
10
|
+
|
11
|
+
#pragma clang diagnostic push
|
12
|
+
#pragma clang diagnostic ignored "-Wdocumentation"
|
13
|
+
#import "include/core/SkColorSpace.h"
|
14
|
+
#import <include/gpu/GrBackendSurface.h>
|
15
|
+
#pragma clang diagnostic pop
|
16
|
+
|
17
|
+
#include <TargetConditionals.h>
|
18
|
+
#if TARGET_RT_BIG_ENDIAN
|
19
|
+
#define FourCC2Str(fourcc) \
|
20
|
+
(const char[]) { \
|
21
|
+
*((char *)&fourcc), *(((char *)&fourcc) + 1), *(((char *)&fourcc) + 2), \
|
22
|
+
*(((char *)&fourcc) + 3), 0 \
|
23
|
+
}
|
24
|
+
#else
|
25
|
+
#define FourCC2Str(fourcc) \
|
26
|
+
(const char[]) { \
|
27
|
+
*(((char *)&fourcc) + 3), *(((char *)&fourcc) + 2), \
|
28
|
+
*(((char *)&fourcc) + 1), *(((char *)&fourcc) + 0), 0 \
|
29
|
+
}
|
30
|
+
#endif
|
31
|
+
|
32
|
+
// pragma MARK: TextureHolder
|
33
|
+
|
34
|
+
TextureHolder::TextureHolder(CVMetalTextureRef texture) : _texture(texture) {}
|
35
|
+
TextureHolder::~TextureHolder() { CFRelease(_texture); }
|
36
|
+
|
37
|
+
GrBackendTexture TextureHolder::toGrBackendTexture() {
|
38
|
+
// Unwrap the underlying MTLTexture
|
39
|
+
id<MTLTexture> mtlTexture = CVMetalTextureGetTexture(_texture);
|
40
|
+
if (mtlTexture == nil) [[unlikely]] {
|
41
|
+
throw std::runtime_error(
|
42
|
+
"Failed to get MTLTexture from CVMetalTextureRef!");
|
43
|
+
}
|
44
|
+
|
45
|
+
// Wrap MTLTexture in Skia's GrBackendTexture
|
46
|
+
GrMtlTextureInfo textureInfo;
|
47
|
+
textureInfo.fTexture.retain((__bridge void *)mtlTexture);
|
48
|
+
GrBackendTexture texture =
|
49
|
+
GrBackendTexture((int)mtlTexture.width, (int)mtlTexture.height,
|
50
|
+
skgpu::Mipmapped::kNo, textureInfo);
|
51
|
+
return texture;
|
52
|
+
}
|
53
|
+
|
54
|
+
// pragma MARK: Base
|
55
|
+
|
56
|
+
SkiaCVPixelBufferUtils::CVPixelBufferBaseFormat
|
57
|
+
SkiaCVPixelBufferUtils::getCVPixelBufferBaseFormat(
|
58
|
+
CVPixelBufferRef pixelBuffer) {
|
59
|
+
OSType format = CVPixelBufferGetPixelFormatType(pixelBuffer);
|
60
|
+
|
61
|
+
switch (format) {
|
62
|
+
case kCVPixelFormatType_32BGRA:
|
63
|
+
case kCVPixelFormatType_32RGBA:
|
64
|
+
return CVPixelBufferBaseFormat::rgb;
|
65
|
+
default:
|
66
|
+
[[unlikely]] throw std::runtime_error(
|
67
|
+
"CVPixelBuffer has unsupported pixel-format! " +
|
68
|
+
std::string(FourCC2Str(format)));
|
69
|
+
}
|
70
|
+
}
|
71
|
+
|
72
|
+
// pragma MARK: RGB
|
73
|
+
|
74
|
+
SkColorType SkiaCVPixelBufferUtils::RGB::getCVPixelBufferColorType(
|
75
|
+
CVPixelBufferRef pixelBuffer) {
|
76
|
+
OSType format = CVPixelBufferGetPixelFormatType(pixelBuffer);
|
77
|
+
|
78
|
+
switch (format) {
|
79
|
+
case kCVPixelFormatType_32BGRA:
|
80
|
+
[[likely]] return kBGRA_8888_SkColorType;
|
81
|
+
case kCVPixelFormatType_32RGBA:
|
82
|
+
return kRGBA_8888_SkColorType;
|
83
|
+
// This can be extended with branches for specific RGB formats if new Apple
|
84
|
+
// uses new formats.
|
85
|
+
default:
|
86
|
+
[[unlikely]] throw std::runtime_error(
|
87
|
+
"CVPixelBuffer has unknown RGB format! " +
|
88
|
+
std::string(FourCC2Str(format)));
|
89
|
+
}
|
90
|
+
}
|
91
|
+
|
92
|
+
TextureHolder *SkiaCVPixelBufferUtils::RGB::getSkiaTextureForCVPixelBuffer(
|
93
|
+
CVPixelBufferRef pixelBuffer) {
|
94
|
+
return getSkiaTextureForCVPixelBufferPlane(pixelBuffer, /* planeIndex */ 0);
|
95
|
+
}
|
96
|
+
|
97
|
+
// pragma MARK: CVPixelBuffer -> Skia Texture
|
98
|
+
|
99
|
+
TextureHolder *SkiaCVPixelBufferUtils::getSkiaTextureForCVPixelBufferPlane(
|
100
|
+
CVPixelBufferRef pixelBuffer, size_t planeIndex) {
|
101
|
+
// 1. Get cache
|
102
|
+
CVMetalTextureCacheRef textureCache = getTextureCache();
|
103
|
+
|
104
|
+
// 2. Get MetalTexture from CMSampleBuffer
|
105
|
+
size_t width = CVPixelBufferGetWidthOfPlane(pixelBuffer, planeIndex);
|
106
|
+
size_t height = CVPixelBufferGetHeightOfPlane(pixelBuffer, planeIndex);
|
107
|
+
MTLPixelFormat pixelFormat =
|
108
|
+
getMTLPixelFormatForCVPixelBufferPlane(pixelBuffer, planeIndex);
|
109
|
+
|
110
|
+
CVMetalTextureRef textureHolder;
|
111
|
+
CVReturn result = CVMetalTextureCacheCreateTextureFromImage(
|
112
|
+
kCFAllocatorDefault, textureCache, pixelBuffer, nil, pixelFormat, width,
|
113
|
+
height, planeIndex, &textureHolder);
|
114
|
+
|
115
|
+
if (result != kCVReturnSuccess) [[unlikely]] {
|
116
|
+
throw std::runtime_error(
|
117
|
+
"Failed to create Metal Texture from CMSampleBuffer! Result: " +
|
118
|
+
std::to_string(result));
|
119
|
+
}
|
120
|
+
|
121
|
+
return new TextureHolder(textureHolder);
|
122
|
+
}
|
123
|
+
|
124
|
+
// pragma MARK: getTextureCache()
|
125
|
+
|
126
|
+
CVMetalTextureCacheRef SkiaCVPixelBufferUtils::getTextureCache() {
|
127
|
+
static CVMetalTextureCacheRef textureCache = nil;
|
128
|
+
if (textureCache == nil) {
|
129
|
+
// Create a new Texture Cache
|
130
|
+
auto result = CVMetalTextureCacheCreate(kCFAllocatorDefault, nil,
|
131
|
+
MTLCreateSystemDefaultDevice(), nil,
|
132
|
+
&textureCache);
|
133
|
+
if (result != kCVReturnSuccess || textureCache == nil) {
|
134
|
+
throw std::runtime_error("Failed to create Metal Texture Cache!");
|
135
|
+
}
|
136
|
+
}
|
137
|
+
return textureCache;
|
138
|
+
}
|
139
|
+
|
140
|
+
// pragma MARK: Get CVPixelBuffer MTLPixelFormat
|
141
|
+
|
142
|
+
MTLPixelFormat SkiaCVPixelBufferUtils::getMTLPixelFormatForCVPixelBufferPlane(
|
143
|
+
CVPixelBufferRef pixelBuffer, size_t planeIndex) {
|
144
|
+
size_t width = CVPixelBufferGetWidthOfPlane(pixelBuffer, planeIndex);
|
145
|
+
size_t bytesPerRow =
|
146
|
+
CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, planeIndex);
|
147
|
+
double bytesPerPixel = round(static_cast<double>(bytesPerRow) / width);
|
148
|
+
if (bytesPerPixel == 1) {
|
149
|
+
return MTLPixelFormatR8Unorm;
|
150
|
+
} else if (bytesPerPixel == 2) {
|
151
|
+
return MTLPixelFormatRG8Unorm;
|
152
|
+
} else if (bytesPerPixel == 4) {
|
153
|
+
return MTLPixelFormatBGRA8Unorm;
|
154
|
+
} else [[unlikely]] {
|
155
|
+
throw std::runtime_error("Invalid bytes per row! Expected 1 (R), 2 (RG) or "
|
156
|
+
"4 (RGBA), but received " +
|
157
|
+
std::to_string(bytesPerPixel));
|
158
|
+
}
|
159
|
+
}
|
@@ -26,7 +26,8 @@ public:
|
|
26
26
|
int height);
|
27
27
|
static sk_sp<SkSurface> makeOffscreenSurface(int width, int height);
|
28
28
|
|
29
|
-
static sk_sp<SkImage>
|
29
|
+
static sk_sp<SkImage>
|
30
|
+
makeTextureFromCVPixelBuffer(CVPixelBufferRef pixelBuffer);
|
30
31
|
|
31
32
|
private:
|
32
33
|
static id<MTLDevice> device;
|
@@ -1,6 +1,7 @@
|
|
1
1
|
#import "RNSkLog.h"
|
2
2
|
|
3
|
-
#
|
3
|
+
#import "SkiaCVPixelBufferUtils.h"
|
4
|
+
#import "SkiaMetalSurfaceFactory.h"
|
4
5
|
|
5
6
|
#pragma clang diagnostic push
|
6
7
|
#pragma clang diagnostic ignored "-Wdocumentation"
|
@@ -106,12 +107,35 @@ sk_sp<SkSurface> SkiaMetalSurfaceFactory::makeOffscreenSurface(int width,
|
|
106
107
|
return surface;
|
107
108
|
}
|
108
109
|
|
109
|
-
sk_sp<SkImage>
|
110
|
-
|
110
|
+
sk_sp<SkImage> SkiaMetalSurfaceFactory::makeTextureFromCVPixelBuffer(
|
111
|
+
CVPixelBufferRef pixelBuffer) {
|
111
112
|
if (!SkiaMetalSurfaceFactory::createSkiaDirectContextIfNecessary(
|
112
|
-
&ThreadContextHolder::ThreadSkiaMetalContext)) {
|
113
|
+
&ThreadContextHolder::ThreadSkiaMetalContext)) [[unlikely]] {
|
113
114
|
throw std::runtime_error("Failed to create Skia Context for this Thread!");
|
114
115
|
}
|
115
|
-
|
116
|
-
|
116
|
+
const SkiaMetalContext &context = ThreadContextHolder::ThreadSkiaMetalContext;
|
117
|
+
|
118
|
+
SkiaCVPixelBufferUtils::CVPixelBufferBaseFormat format =
|
119
|
+
SkiaCVPixelBufferUtils::getCVPixelBufferBaseFormat(pixelBuffer);
|
120
|
+
switch (format) {
|
121
|
+
case SkiaCVPixelBufferUtils::CVPixelBufferBaseFormat::rgb: {
|
122
|
+
// CVPixelBuffer is in any RGB format.
|
123
|
+
SkColorType colorType =
|
124
|
+
SkiaCVPixelBufferUtils::RGB::getCVPixelBufferColorType(pixelBuffer);
|
125
|
+
TextureHolder *texture =
|
126
|
+
SkiaCVPixelBufferUtils::RGB::getSkiaTextureForCVPixelBuffer(
|
127
|
+
pixelBuffer);
|
128
|
+
return SkImages::BorrowTextureFrom(
|
129
|
+
context.skContext.get(), texture->toGrBackendTexture(),
|
130
|
+
kTopLeft_GrSurfaceOrigin, colorType, kOpaque_SkAlphaType, nullptr,
|
131
|
+
[](void *texture) { delete (TextureHolder *)texture; },
|
132
|
+
(void *)texture);
|
133
|
+
}
|
134
|
+
default:
|
135
|
+
[[unlikely]] {
|
136
|
+
throw std::runtime_error("Failed to convert PlatformBuffer to SkImage - "
|
137
|
+
"PlatformBuffer has unsupported PixelFormat! " +
|
138
|
+
std::to_string(static_cast<int>(format)));
|
139
|
+
}
|
140
|
+
}
|
117
141
|
}
|