@shopify/react-native-skia 1.2.0 → 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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
  }