@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.
Files changed (96) hide show
  1. package/android/cpp/rnskia-android/RNSkAndroidPlatformContext.h +3 -3
  2. package/cpp/api/JsiNativeBuffer.h +43 -0
  3. package/cpp/api/JsiSkApi.h +3 -3
  4. package/cpp/api/JsiSkImageFactory.h +3 -3
  5. package/cpp/rnskia/RNSkPlatformContext.h +5 -5
  6. package/ios/RNSkia-iOS/RNSkiOSPlatformContext.h +3 -3
  7. package/ios/RNSkia-iOS/RNSkiOSPlatformContext.mm +69 -88
  8. package/ios/RNSkia-iOS/SkiaCVPixelBufferUtils.h +84 -0
  9. package/ios/RNSkia-iOS/SkiaCVPixelBufferUtils.mm +159 -0
  10. package/ios/RNSkia-iOS/SkiaMetalSurfaceFactory.h +2 -1
  11. package/ios/RNSkia-iOS/SkiaMetalSurfaceFactory.mm +30 -6
  12. package/lib/commonjs/skia/types/Image/ImageFactory.d.ts +8 -15
  13. package/lib/commonjs/skia/types/Image/ImageFactory.js +1 -10
  14. package/lib/commonjs/skia/types/Image/ImageFactory.js.map +1 -1
  15. package/lib/commonjs/skia/types/NativeBuffer/NativeBufferFactory.d.ts +18 -0
  16. package/lib/commonjs/skia/types/NativeBuffer/NativeBufferFactory.js +13 -0
  17. package/lib/commonjs/skia/types/NativeBuffer/NativeBufferFactory.js.map +1 -0
  18. package/lib/commonjs/skia/types/NativeBuffer/index.d.ts +1 -0
  19. package/lib/commonjs/skia/types/NativeBuffer/index.js +17 -0
  20. package/lib/commonjs/skia/types/NativeBuffer/index.js.map +1 -0
  21. package/lib/commonjs/skia/types/Skia.d.ts +2 -2
  22. package/lib/commonjs/skia/types/Skia.js.map +1 -1
  23. package/lib/commonjs/skia/types/index.d.ts +1 -1
  24. package/lib/commonjs/skia/types/index.js +4 -4
  25. package/lib/commonjs/skia/types/index.js.map +1 -1
  26. package/lib/commonjs/skia/web/JsiSkImageFactory.d.ts +3 -2
  27. package/lib/commonjs/skia/web/JsiSkImageFactory.js +18 -2
  28. package/lib/commonjs/skia/web/JsiSkImageFactory.js.map +1 -1
  29. package/lib/commonjs/skia/web/JsiSkNativeBufferFactory.d.ts +8 -0
  30. package/lib/commonjs/skia/web/JsiSkNativeBufferFactory.js +29 -0
  31. package/lib/commonjs/skia/web/JsiSkNativeBufferFactory.js.map +1 -0
  32. package/lib/commonjs/skia/web/JsiSkia.js +2 -2
  33. package/lib/commonjs/skia/web/JsiSkia.js.map +1 -1
  34. package/lib/module/skia/types/Image/ImageFactory.d.ts +8 -15
  35. package/lib/module/skia/types/Image/ImageFactory.js +1 -10
  36. package/lib/module/skia/types/Image/ImageFactory.js.map +1 -1
  37. package/lib/module/skia/types/NativeBuffer/NativeBufferFactory.d.ts +18 -0
  38. package/lib/module/skia/types/NativeBuffer/NativeBufferFactory.js +4 -0
  39. package/lib/module/skia/types/NativeBuffer/NativeBufferFactory.js.map +1 -0
  40. package/lib/module/skia/types/NativeBuffer/index.d.ts +1 -0
  41. package/lib/module/skia/types/NativeBuffer/index.js +2 -0
  42. package/lib/module/skia/types/NativeBuffer/index.js.map +1 -0
  43. package/lib/module/skia/types/Skia.d.ts +2 -2
  44. package/lib/module/skia/types/Skia.js.map +1 -1
  45. package/lib/module/skia/types/index.d.ts +1 -1
  46. package/lib/module/skia/types/index.js +1 -1
  47. package/lib/module/skia/types/index.js.map +1 -1
  48. package/lib/module/skia/web/JsiSkImageFactory.d.ts +3 -2
  49. package/lib/module/skia/web/JsiSkImageFactory.js +18 -2
  50. package/lib/module/skia/web/JsiSkImageFactory.js.map +1 -1
  51. package/lib/module/skia/web/JsiSkNativeBufferFactory.d.ts +8 -0
  52. package/lib/module/skia/web/JsiSkNativeBufferFactory.js +22 -0
  53. package/lib/module/skia/web/JsiSkNativeBufferFactory.js.map +1 -0
  54. package/lib/module/skia/web/JsiSkia.js +2 -2
  55. package/lib/module/skia/web/JsiSkia.js.map +1 -1
  56. package/lib/typescript/src/skia/types/Image/ImageFactory.d.ts +8 -15
  57. package/lib/typescript/src/skia/types/NativeBuffer/NativeBufferFactory.d.ts +18 -0
  58. package/lib/typescript/src/skia/types/NativeBuffer/index.d.ts +1 -0
  59. package/lib/typescript/src/skia/types/Skia.d.ts +2 -2
  60. package/lib/typescript/src/skia/types/index.d.ts +1 -1
  61. package/lib/typescript/src/skia/web/JsiSkImageFactory.d.ts +3 -2
  62. package/lib/typescript/src/skia/web/JsiSkNativeBufferFactory.d.ts +8 -0
  63. package/package.json +1 -1
  64. package/src/skia/types/Image/ImageFactory.ts +7 -23
  65. package/src/skia/types/NativeBuffer/NativeBufferFactory.ts +38 -0
  66. package/src/skia/types/NativeBuffer/index.ts +1 -0
  67. package/src/skia/types/Skia.ts +2 -2
  68. package/src/skia/types/index.ts +1 -1
  69. package/src/skia/web/JsiSkImageFactory.ts +28 -6
  70. package/src/skia/web/JsiSkNativeBufferFactory.ts +35 -0
  71. package/src/skia/web/JsiSkia.ts +2 -2
  72. package/cpp/api/JsiPlatformBuffer.h +0 -51
  73. package/lib/commonjs/skia/types/PlatformBuffer/PlatformBufferFactory.d.ts +0 -12
  74. package/lib/commonjs/skia/types/PlatformBuffer/PlatformBufferFactory.js +0 -6
  75. package/lib/commonjs/skia/types/PlatformBuffer/PlatformBufferFactory.js.map +0 -1
  76. package/lib/commonjs/skia/types/PlatformBuffer/index.d.ts +0 -1
  77. package/lib/commonjs/skia/types/PlatformBuffer/index.js +0 -17
  78. package/lib/commonjs/skia/types/PlatformBuffer/index.js.map +0 -1
  79. package/lib/commonjs/skia/web/JsiSkPlatformBufferFactory.d.ts +0 -8
  80. package/lib/commonjs/skia/web/JsiSkPlatformBufferFactory.js +0 -20
  81. package/lib/commonjs/skia/web/JsiSkPlatformBufferFactory.js.map +0 -1
  82. package/lib/module/skia/types/PlatformBuffer/PlatformBufferFactory.d.ts +0 -12
  83. package/lib/module/skia/types/PlatformBuffer/PlatformBufferFactory.js +0 -2
  84. package/lib/module/skia/types/PlatformBuffer/PlatformBufferFactory.js.map +0 -1
  85. package/lib/module/skia/types/PlatformBuffer/index.d.ts +0 -1
  86. package/lib/module/skia/types/PlatformBuffer/index.js +0 -2
  87. package/lib/module/skia/types/PlatformBuffer/index.js.map +0 -1
  88. package/lib/module/skia/web/JsiSkPlatformBufferFactory.d.ts +0 -8
  89. package/lib/module/skia/web/JsiSkPlatformBufferFactory.js +0 -13
  90. package/lib/module/skia/web/JsiSkPlatformBufferFactory.js.map +0 -1
  91. package/lib/typescript/src/skia/types/PlatformBuffer/PlatformBufferFactory.d.ts +0 -12
  92. package/lib/typescript/src/skia/types/PlatformBuffer/index.d.ts +0 -1
  93. package/lib/typescript/src/skia/web/JsiSkPlatformBufferFactory.d.ts +0 -8
  94. package/src/skia/types/PlatformBuffer/PlatformBufferFactory.ts +0 -14
  95. package/src/skia/types/PlatformBuffer/index.ts +0 -1
  96. 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> makeImageFromPlatformBuffer(void *buffer) override {
56
+ sk_sp<SkImage> makeImageFromNativeBuffer(void *buffer) override {
57
57
  return SkiaOpenGLSurfaceFactory::makeImageFromHardwareBuffer(buffer);
58
58
  }
59
59
 
60
- void releasePlatformBuffer(uint64_t pointer) override {
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 makePlatformBuffer(sk_sp<SkImage> image) override {
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
@@ -6,7 +6,7 @@
6
6
 
7
7
  #include "JsiSkHostObjects.h"
8
8
 
9
- #include "JsiPlatformBuffer.h"
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
- "PlatformBuffer", std::make_shared<JsiPlatformBufferFactory>(context));
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(MakeImageFromPlatformBuffer) {
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()->makeImageFromPlatformBuffer(rawPointer);
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
- MakeImageFromPlatformBuffer),
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 platform buffer.
138
- * - On iOS, this is a `CMSampleBuffer`
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> makeImageFromPlatformBuffer(void *buffer) = 0;
143
+ virtual sk_sp<SkImage> makeImageFromNativeBuffer(void *buffer) = 0;
144
144
 
145
- virtual void releasePlatformBuffer(uint64_t pointer) = 0;
145
+ virtual void releaseNativeBuffer(uint64_t pointer) = 0;
146
146
 
147
- virtual uint64_t makePlatformBuffer(sk_sp<SkImage> image) = 0;
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> makeImageFromPlatformBuffer(void *buffer) override;
62
+ sk_sp<SkImage> makeImageFromNativeBuffer(void *buffer) override;
63
63
 
64
- uint64_t makePlatformBuffer(sk_sp<SkImage> image) override;
64
+ uint64_t makeNativeBuffer(sk_sp<SkImage> image) override;
65
65
 
66
- void releasePlatformBuffer(uint64_t pointer) override;
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
- #include "RNSkiOSPlatformContext.h"
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
- #include "SkiaMetalSurfaceFactory.h"
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::releasePlatformBuffer(uint64_t pointer) {
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::makePlatformBuffer(sk_sp<SkImage> image) {
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 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
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
- // 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);
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
- if (status != noErr) {
130
- if (formatDescription) {
131
- CFRelease(formatDescription);
132
- }
133
- if (pixelBuffer) {
134
- CFRelease(pixelBuffer);
135
- }
136
- return 0;
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 sampleBuffer casted to uint64_t
140
- return reinterpret_cast<uint64_t>(sampleBuffer);
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
- 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;
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> makeTextureFromImage(sk_sp<SkImage> image);
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
- #include "SkiaMetalSurfaceFactory.h"
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
- SkiaMetalSurfaceFactory::makeTextureFromImage(sk_sp<SkImage> image) {
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
- return SkImages::TextureFromImage(
116
- ThreadContextHolder::ThreadSkiaMetalContext.skContext.get(), image);
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
  }