@shopify/react-native-skia 2.4.21 → 2.5.0
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.
- package/android/CMakeLists.txt +12 -49
- package/android/build.gradle +37 -1
- package/apple/SkiaCVPixelBufferUtils.h +6 -2
- package/apple/SkiaCVPixelBufferUtils.mm +208 -36
- package/apple/SkiaUIView.mm +4 -0
- package/cpp/api/JsiSkColor.h +53 -1
- package/cpp/rnwgpu/ArrayBuffer.h +7 -2
- package/lib/commonjs/renderer/Canvas.d.ts +2 -0
- package/lib/commonjs/renderer/Canvas.js +0 -3
- package/lib/commonjs/renderer/Canvas.js.map +1 -1
- package/lib/commonjs/sksg/Container.native.js +3 -5
- package/lib/commonjs/sksg/Container.native.js.map +1 -1
- package/lib/module/renderer/Canvas.d.ts +2 -0
- package/lib/module/renderer/Canvas.js +0 -3
- package/lib/module/renderer/Canvas.js.map +1 -1
- package/lib/module/sksg/Container.native.js +3 -5
- package/lib/module/sksg/Container.native.js.map +1 -1
- package/lib/typescript/src/renderer/Canvas.d.ts +2 -0
- package/package.json +10 -30
- package/react-native-skia.podspec +110 -58
- package/src/renderer/Canvas.tsx +2 -3
- package/src/sksg/Container.native.ts +3 -4
- package/scripts/install-skia.mjs +0 -728
package/android/CMakeLists.txt
CHANGED
|
@@ -4,73 +4,36 @@ cmake_minimum_required(VERSION 3.4.1)
|
|
|
4
4
|
set (CMAKE_VERBOSE_MAKEFILE ON)
|
|
5
5
|
set (CMAKE_CXX_STANDARD 20)
|
|
6
6
|
|
|
7
|
-
#
|
|
8
|
-
|
|
7
|
+
# SKIA_LIBS_PATH is passed from Gradle (resolved via Node.js package resolution)
|
|
8
|
+
# Append the ABI to get the full path
|
|
9
|
+
set (SKIA_LIBS_PATH "${SKIA_LIBS_PATH}/${ANDROID_ABI}")
|
|
9
10
|
|
|
10
11
|
# Check if Skia prebuilt binaries are installed
|
|
11
|
-
# The postinstall script downloads these - if missing, the user needs to run it
|
|
12
12
|
if(NOT EXISTS "${SKIA_LIBS_PATH}/libskia.a")
|
|
13
13
|
message("")
|
|
14
14
|
message("┌─────────────────────────────────────────────────────────────────────────────┐")
|
|
15
15
|
message("│ │")
|
|
16
16
|
message("│ ERROR: Skia prebuilt binaries not found! │")
|
|
17
17
|
message("│ │")
|
|
18
|
-
message("│
|
|
19
|
-
message("│ Skia binaries. Some package managers (pnpm, bun, yarn berry) require │")
|
|
20
|
-
message("│ explicit trust for packages with postinstall scripts. │")
|
|
18
|
+
message("│ Could not find libskia.a at: ${SKIA_LIBS_PATH} │")
|
|
21
19
|
message("│ │")
|
|
22
|
-
message("│
|
|
23
|
-
message("│
|
|
24
|
-
message("│ • npm/yarn classic: Run 'npm rebuild @shopify/react-native-skia' or │")
|
|
25
|
-
message("│ reinstall the package │")
|
|
26
|
-
message("│ │")
|
|
27
|
-
message("│ • bun: Run 'bun add --trust @shopify/react-native-skia' │")
|
|
28
|
-
message("│ │")
|
|
29
|
-
message("│ • pnpm: Add to package.json: │")
|
|
30
|
-
message("│ \"pnpm\": { \"onlyBuiltDependencies\": [\"@shopify/react-native-skia\"] }│")
|
|
31
|
-
message("│ Then reinstall the package │")
|
|
20
|
+
message("│ Make sure react-native-skia-android is installed: │")
|
|
21
|
+
message("│ yarn add react-native-skia-android │")
|
|
32
22
|
message("│ │")
|
|
33
23
|
message("│ See: https://shopify.github.io/react-native-skia/docs/getting-started/installation │")
|
|
34
24
|
message("│ │")
|
|
35
25
|
message("└─────────────────────────────────────────────────────────────────────────────┘")
|
|
36
26
|
message("")
|
|
37
|
-
message(FATAL_ERROR "Skia prebuilt binaries not found at ${SKIA_LIBS_PATH}
|
|
27
|
+
message(FATAL_ERROR "Skia prebuilt binaries not found at ${SKIA_LIBS_PATH}")
|
|
38
28
|
endif()
|
|
39
29
|
|
|
40
|
-
# Import libskia
|
|
30
|
+
# Import libskia
|
|
41
31
|
add_library(skia STATIC IMPORTED)
|
|
42
32
|
set_property(TARGET skia PROPERTY IMPORTED_LOCATION "${SKIA_LIBS_PATH}/libskia.a")
|
|
43
33
|
|
|
44
|
-
#
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
# 2. Marker file in libs directory (set during Skia build)
|
|
48
|
-
# 3. Fall back to nm symbol detection (slow on some CI systems)
|
|
49
|
-
set(SK_GRAPHITE_AVAILABLE OFF)
|
|
50
|
-
|
|
51
|
-
if(DEFINED ENV{SK_GRAPHITE})
|
|
52
|
-
# Explicit override via environment variable
|
|
53
|
-
if($ENV{SK_GRAPHITE})
|
|
54
|
-
set(SK_GRAPHITE_AVAILABLE ON)
|
|
55
|
-
message("-- SK_GRAPHITE detection: using environment variable (ON)")
|
|
56
|
-
else()
|
|
57
|
-
message("-- SK_GRAPHITE detection: using environment variable (OFF)")
|
|
58
|
-
endif()
|
|
59
|
-
elseif(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/../libs/android/graphite.enabled")
|
|
60
|
-
# Marker file indicates Graphite-enabled build
|
|
61
|
-
set(SK_GRAPHITE_AVAILABLE ON)
|
|
62
|
-
message("-- SK_GRAPHITE detection: marker file found")
|
|
63
|
-
else()
|
|
64
|
-
message("-- SK_GRAPHITE detection: no marker file, assuming OFF")
|
|
65
|
-
endif()
|
|
66
|
-
|
|
67
|
-
if(SK_GRAPHITE_AVAILABLE)
|
|
68
|
-
set(SK_GRAPHITE ON)
|
|
69
|
-
message("-- SK_GRAPHITE: ON")
|
|
70
|
-
else()
|
|
71
|
-
set(SK_GRAPHITE OFF)
|
|
72
|
-
message("-- SK_GRAPHITE: OFF")
|
|
73
|
-
endif()
|
|
34
|
+
# SK_GRAPHITE is passed from Gradle
|
|
35
|
+
message("-- SKIA_LIBS_PATH: ${SKIA_LIBS_PATH}")
|
|
36
|
+
message("-- SK_GRAPHITE: ${SK_GRAPHITE}")
|
|
74
37
|
|
|
75
38
|
string(APPEND CMAKE_CXX_FLAGS " -DSK_BUILD_FOR_ANDROID -DSK_DISABLE_LEGACY_SHAPER_FACTORY -DSK_IMAGE_READ_PIXELS_DISABLE_LEGACY_API -DFOLLY_NO_CONFIG=1 -DFOLLY_HAVE_CLOCK_GETTIME=1 -DFOLLY_HAVE_MEMRCHR=1 -DFOLLY_USE_LIBCPP=1 -DFOLLY_MOBILE=1 -DON_ANDROID -DONANDROID")
|
|
76
39
|
|
|
@@ -101,7 +64,7 @@ message("-- PREBUILT: " ${PREBUILT_DIR})
|
|
|
101
64
|
message("-- BUILD : " ${build_DIR})
|
|
102
65
|
message("-- LIBRN : " ${LIBRN_DIR})
|
|
103
66
|
|
|
104
|
-
link_directories(
|
|
67
|
+
link_directories(${SKIA_LIBS_PATH}/)
|
|
105
68
|
|
|
106
69
|
if(SK_GRAPHITE)
|
|
107
70
|
add_definitions(-DSK_GRAPHITE)
|
package/android/build.gradle
CHANGED
|
@@ -56,7 +56,41 @@ static def findNodeModules(baseDir) {
|
|
|
56
56
|
throw new GradleException("React-Native-Skia: Failed to find node_modules/ path!")
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
+
// Resolve npm package path using Node.js resolution (handles monorepos, pnpm, etc.)
|
|
60
|
+
def resolveSkiaPackage(packageName) {
|
|
61
|
+
def cmdResult = providers.exec {
|
|
62
|
+
commandLine "node", "-e", "console.log(require.resolve('${packageName}/package.json'))"
|
|
63
|
+
ignoreExitValue = true
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (cmdResult.result.get().exitValue == 0) {
|
|
67
|
+
def packageJsonPath = cmdResult.standardOutput.asText.get().trim()
|
|
68
|
+
return new File(packageJsonPath).parent
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Fallback: walk up directories looking for node_modules
|
|
72
|
+
def basePath = projectDir.toPath().normalize()
|
|
73
|
+
while (basePath) {
|
|
74
|
+
def candidate = Paths.get(basePath.toString(), "node_modules", packageName)
|
|
75
|
+
if (candidate.toFile().exists() && new File(candidate.toString(), "package.json").exists()) {
|
|
76
|
+
return candidate.toString()
|
|
77
|
+
}
|
|
78
|
+
basePath = basePath.getParent()
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
throw new GradleException("React-Native-Skia: Could not find ${packageName}. Make sure you have run 'yarn install' or 'npm install'.")
|
|
82
|
+
}
|
|
83
|
+
|
|
59
84
|
def nodeModules = findNodeModules(projectDir)
|
|
85
|
+
|
|
86
|
+
// Resolve Skia Android package
|
|
87
|
+
def useGraphite = System.getenv("SK_GRAPHITE") == "1" || System.getenv("SK_GRAPHITE") == "true"
|
|
88
|
+
def skiaPackageName = useGraphite ? "react-native-skia-graphite-android" : "react-native-skia-android"
|
|
89
|
+
def skiaAndroidPackage = resolveSkiaPackage(skiaPackageName)
|
|
90
|
+
def skiaLibsPath = "${skiaAndroidPackage}/libs"
|
|
91
|
+
|
|
92
|
+
logger.warn("react-native-skia: SK_GRAPHITE: ${useGraphite}")
|
|
93
|
+
logger.warn("react-native-skia: Skia Android package: ${skiaAndroidPackage}")
|
|
60
94
|
logger.warn("react-native-skia: node_modules/ found at: ${nodeModules}")
|
|
61
95
|
|
|
62
96
|
def sourceBuild = false
|
|
@@ -158,6 +192,8 @@ android {
|
|
|
158
192
|
"-DREACT_NATIVE_DIR=${defaultDir}",
|
|
159
193
|
"-DNODE_MODULES_DIR=${nodeModules}",
|
|
160
194
|
"-DPREBUILT_DIR=${prebuiltDir}",
|
|
195
|
+
"-DSKIA_LIBS_PATH=${skiaLibsPath}",
|
|
196
|
+
"-DSK_GRAPHITE=${useGraphite ? 'ON' : 'OFF'}",
|
|
161
197
|
"-DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON"
|
|
162
198
|
|
|
163
199
|
}
|
|
@@ -175,7 +211,7 @@ android {
|
|
|
175
211
|
|
|
176
212
|
sourceSets.main {
|
|
177
213
|
jniLibs {
|
|
178
|
-
srcDirs += [
|
|
214
|
+
srcDirs += [skiaLibsPath]
|
|
179
215
|
}
|
|
180
216
|
java {
|
|
181
217
|
if (!isNewArchitectureEnabled()) {
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
#import <vector>
|
|
11
11
|
|
|
12
|
-
#import <
|
|
12
|
+
#import <CoreVideo/CVPixelBuffer.h>
|
|
13
13
|
#import <CoreVideo/CVMetalTextureCache.h>
|
|
14
14
|
#import <MetalKit/MetalKit.h>
|
|
15
15
|
|
|
@@ -104,7 +104,11 @@ public:
|
|
|
104
104
|
private:
|
|
105
105
|
static SkYUVAInfo::PlaneConfig getPlaneConfig(OSType pixelFormat);
|
|
106
106
|
static SkYUVAInfo::Subsampling getSubsampling(OSType pixelFormat);
|
|
107
|
-
static SkYUVColorSpace getColorspace(
|
|
107
|
+
static SkYUVColorSpace getColorspace(CVPixelBufferRef pixelBuffer);
|
|
108
|
+
static bool isFullRangeYUVFormat(OSType pixelFormat);
|
|
109
|
+
static bool isTenBitYUVFormat(OSType pixelFormat);
|
|
110
|
+
static SkYUVColorSpace getSkYUVColorSpaceFromMatrix(CFStringRef matrix,
|
|
111
|
+
OSType pixelFormat);
|
|
108
112
|
static SkYUVAInfo getYUVAInfoForCVPixelBuffer(CVPixelBufferRef pixelBuffer);
|
|
109
113
|
};
|
|
110
114
|
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
#import "include/core/SkCanvas.h"
|
|
14
14
|
#import "include/core/SkColorSpace.h"
|
|
15
15
|
|
|
16
|
-
#import <
|
|
16
|
+
#import <CoreVideo/CVImageBuffer.h>
|
|
17
17
|
#import <CoreVideo/CVMetalTextureCache.h>
|
|
18
18
|
|
|
19
19
|
#import <include/gpu/ganesh/GrBackendSurface.h>
|
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
#pragma clang diagnostic pop
|
|
29
29
|
|
|
30
30
|
#include <TargetConditionals.h>
|
|
31
|
+
#include <cmath>
|
|
31
32
|
#if TARGET_RT_BIG_ENDIAN
|
|
32
33
|
#define FourCC2Str(fourcc) \
|
|
33
34
|
(const char[]) { \
|
|
@@ -90,6 +91,10 @@ SkiaCVPixelBufferUtils::getCVPixelBufferBaseFormat(
|
|
|
90
91
|
case kCVPixelFormatType_420YpCbCr8PlanarFullRange:
|
|
91
92
|
case kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange:
|
|
92
93
|
case kCVPixelFormatType_420YpCbCr8BiPlanarFullRange:
|
|
94
|
+
case kCVPixelFormatType_422YpCbCr8BiPlanarVideoRange:
|
|
95
|
+
case kCVPixelFormatType_422YpCbCr8BiPlanarFullRange:
|
|
96
|
+
case kCVPixelFormatType_444YpCbCr8BiPlanarVideoRange:
|
|
97
|
+
case kCVPixelFormatType_444YpCbCr8BiPlanarFullRange:
|
|
93
98
|
// 10-bit YUV formats
|
|
94
99
|
case kCVPixelFormatType_420YpCbCr10BiPlanarVideoRange:
|
|
95
100
|
case kCVPixelFormatType_420YpCbCr10BiPlanarFullRange:
|
|
@@ -198,7 +203,7 @@ SkYUVAInfo SkiaCVPixelBufferUtils::YUV::getYUVAInfoForCVPixelBuffer(
|
|
|
198
203
|
OSType format = CVPixelBufferGetPixelFormatType(pixelBuffer);
|
|
199
204
|
SkYUVAInfo::PlaneConfig planeConfig = getPlaneConfig(format);
|
|
200
205
|
SkYUVAInfo::Subsampling subsampling = getSubsampling(format);
|
|
201
|
-
SkYUVColorSpace colorspace = getColorspace(
|
|
206
|
+
SkYUVColorSpace colorspace = getColorspace(pixelBuffer);
|
|
202
207
|
|
|
203
208
|
return SkYUVAInfo(size, planeConfig, subsampling, colorspace);
|
|
204
209
|
}
|
|
@@ -210,9 +215,13 @@ SkiaCVPixelBufferUtils::YUV::getPlaneConfig(OSType pixelFormat) {
|
|
|
210
215
|
switch (pixelFormat) {
|
|
211
216
|
case kCVPixelFormatType_420YpCbCr8Planar:
|
|
212
217
|
case kCVPixelFormatType_420YpCbCr8PlanarFullRange:
|
|
213
|
-
return SkYUVAInfo::PlaneConfig::
|
|
218
|
+
return SkYUVAInfo::PlaneConfig::kY_U_V;
|
|
214
219
|
case kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange:
|
|
215
220
|
case kCVPixelFormatType_420YpCbCr8BiPlanarFullRange:
|
|
221
|
+
case kCVPixelFormatType_422YpCbCr8BiPlanarVideoRange:
|
|
222
|
+
case kCVPixelFormatType_422YpCbCr8BiPlanarFullRange:
|
|
223
|
+
case kCVPixelFormatType_444YpCbCr8BiPlanarVideoRange:
|
|
224
|
+
case kCVPixelFormatType_444YpCbCr8BiPlanarFullRange:
|
|
216
225
|
case kCVPixelFormatType_420YpCbCr10BiPlanarVideoRange:
|
|
217
226
|
case kCVPixelFormatType_420YpCbCr10BiPlanarFullRange:
|
|
218
227
|
case kCVPixelFormatType_422YpCbCr10BiPlanarVideoRange:
|
|
@@ -241,9 +250,13 @@ SkiaCVPixelBufferUtils::YUV::getSubsampling(OSType pixelFormat) {
|
|
|
241
250
|
case kCVPixelFormatType_420YpCbCr10BiPlanarVideoRange:
|
|
242
251
|
case kCVPixelFormatType_420YpCbCr10BiPlanarFullRange:
|
|
243
252
|
[[likely]] return SkYUVAInfo::Subsampling::k420;
|
|
253
|
+
case kCVPixelFormatType_422YpCbCr8BiPlanarVideoRange:
|
|
254
|
+
case kCVPixelFormatType_422YpCbCr8BiPlanarFullRange:
|
|
244
255
|
case kCVPixelFormatType_422YpCbCr10BiPlanarVideoRange:
|
|
245
256
|
case kCVPixelFormatType_422YpCbCr10BiPlanarFullRange:
|
|
246
257
|
return SkYUVAInfo::Subsampling::k422;
|
|
258
|
+
case kCVPixelFormatType_444YpCbCr8BiPlanarVideoRange:
|
|
259
|
+
case kCVPixelFormatType_444YpCbCr8BiPlanarFullRange:
|
|
247
260
|
case kCVPixelFormatType_444YpCbCr10BiPlanarVideoRange:
|
|
248
261
|
case kCVPixelFormatType_444YpCbCr10BiPlanarFullRange:
|
|
249
262
|
return SkYUVAInfo::Subsampling::k444;
|
|
@@ -257,34 +270,97 @@ SkiaCVPixelBufferUtils::YUV::getSubsampling(OSType pixelFormat) {
|
|
|
257
270
|
|
|
258
271
|
// pragma MARK: YUV getColorspace()
|
|
259
272
|
|
|
260
|
-
SkYUVColorSpace SkiaCVPixelBufferUtils::YUV::getColorspace(
|
|
273
|
+
SkYUVColorSpace SkiaCVPixelBufferUtils::YUV::getColorspace(
|
|
274
|
+
CVPixelBufferRef pixelBuffer) {
|
|
275
|
+
const OSType pixelFormat = CVPixelBufferGetPixelFormatType(pixelBuffer);
|
|
276
|
+
|
|
277
|
+
CFTypeRef matrixAttachment =
|
|
278
|
+
CVBufferCopyAttachment(pixelBuffer, kCVImageBufferYCbCrMatrixKey, nullptr);
|
|
279
|
+
CFStringRef matrix = nullptr;
|
|
280
|
+
if (matrixAttachment != nullptr &&
|
|
281
|
+
CFGetTypeID(matrixAttachment) == CFStringGetTypeID()) {
|
|
282
|
+
matrix = reinterpret_cast<CFStringRef>(matrixAttachment);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
SkYUVColorSpace colorspace = getSkYUVColorSpaceFromMatrix(matrix, pixelFormat);
|
|
286
|
+
if (matrixAttachment != nullptr) {
|
|
287
|
+
CFRelease(matrixAttachment);
|
|
288
|
+
}
|
|
289
|
+
return colorspace;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// pragma MARK: YUV helpers
|
|
293
|
+
|
|
294
|
+
bool SkiaCVPixelBufferUtils::YUV::isFullRangeYUVFormat(OSType pixelFormat) {
|
|
261
295
|
switch (pixelFormat) {
|
|
262
|
-
case kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange:
|
|
263
|
-
[[likely]]
|
|
264
|
-
// 8-bit limited
|
|
265
|
-
return SkYUVColorSpace::kRec709_Limited_SkYUVColorSpace;
|
|
266
|
-
case kCVPixelFormatType_420YpCbCr8Planar:
|
|
267
296
|
case kCVPixelFormatType_420YpCbCr8PlanarFullRange:
|
|
268
297
|
case kCVPixelFormatType_420YpCbCr8BiPlanarFullRange:
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
298
|
+
case kCVPixelFormatType_422YpCbCr8BiPlanarFullRange:
|
|
299
|
+
case kCVPixelFormatType_444YpCbCr8BiPlanarFullRange:
|
|
300
|
+
case kCVPixelFormatType_420YpCbCr10BiPlanarFullRange:
|
|
301
|
+
case kCVPixelFormatType_422YpCbCr10BiPlanarFullRange:
|
|
302
|
+
case kCVPixelFormatType_444YpCbCr10BiPlanarFullRange:
|
|
303
|
+
return true;
|
|
304
|
+
default:
|
|
305
|
+
return false;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
bool SkiaCVPixelBufferUtils::YUV::isTenBitYUVFormat(OSType pixelFormat) {
|
|
310
|
+
switch (pixelFormat) {
|
|
272
311
|
case kCVPixelFormatType_420YpCbCr10BiPlanarVideoRange:
|
|
273
|
-
case kCVPixelFormatType_422YpCbCr10BiPlanarVideoRange:
|
|
274
|
-
case kCVPixelFormatType_444YpCbCr10BiPlanarVideoRange:
|
|
275
|
-
// 10-bit limited
|
|
276
|
-
return SkYUVColorSpace::kBT2020_10bit_Limited_SkYUVColorSpace;
|
|
277
312
|
case kCVPixelFormatType_420YpCbCr10BiPlanarFullRange:
|
|
313
|
+
case kCVPixelFormatType_422YpCbCr10BiPlanarVideoRange:
|
|
278
314
|
case kCVPixelFormatType_422YpCbCr10BiPlanarFullRange:
|
|
315
|
+
case kCVPixelFormatType_444YpCbCr10BiPlanarVideoRange:
|
|
279
316
|
case kCVPixelFormatType_444YpCbCr10BiPlanarFullRange:
|
|
280
|
-
|
|
281
|
-
return SkYUVColorSpace::kBT2020_10bit_Full_SkYUVColorSpace;
|
|
282
|
-
// This can be extended with branches for specific YUV formats if Apple
|
|
283
|
-
// uses new formats.
|
|
317
|
+
return true;
|
|
284
318
|
default:
|
|
285
|
-
|
|
286
|
-
|
|
319
|
+
return false;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
SkYUVColorSpace SkiaCVPixelBufferUtils::YUV::getSkYUVColorSpaceFromMatrix(
|
|
324
|
+
CFStringRef matrix, OSType pixelFormat) {
|
|
325
|
+
const bool isFullRange = isFullRangeYUVFormat(pixelFormat);
|
|
326
|
+
const bool isTenBit = isTenBitYUVFormat(pixelFormat);
|
|
327
|
+
|
|
328
|
+
if (matrix != nullptr) {
|
|
329
|
+
if (CFEqual(matrix, kCVImageBufferYCbCrMatrix_ITU_R_2020)) {
|
|
330
|
+
if (isTenBit) {
|
|
331
|
+
return isFullRange ? kBT2020_10bit_Full_SkYUVColorSpace
|
|
332
|
+
: kBT2020_10bit_Limited_SkYUVColorSpace;
|
|
333
|
+
}
|
|
334
|
+
return isFullRange ? kBT2020_8bit_Full_SkYUVColorSpace
|
|
335
|
+
: kBT2020_8bit_Limited_SkYUVColorSpace;
|
|
336
|
+
}
|
|
337
|
+
if (CFEqual(matrix, kCVImageBufferYCbCrMatrix_ITU_R_601_4)) {
|
|
338
|
+
return isFullRange ? kJPEG_Full_SkYUVColorSpace
|
|
339
|
+
: kRec601_Limited_SkYUVColorSpace;
|
|
340
|
+
}
|
|
341
|
+
if (CFEqual(matrix, kCVImageBufferYCbCrMatrix_SMPTE_240M_1995)) {
|
|
342
|
+
return isFullRange ? kSMPTE240_Full_SkYUVColorSpace
|
|
343
|
+
: kSMPTE240_Limited_SkYUVColorSpace;
|
|
344
|
+
}
|
|
345
|
+
if (CFEqual(matrix, kCVImageBufferYCbCrMatrix_ITU_R_709_2)) {
|
|
346
|
+
return isFullRange ? kRec709_Full_SkYUVColorSpace
|
|
347
|
+
: kRec709_Limited_SkYUVColorSpace;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
if (pixelFormat == kCVPixelFormatType_422YpCbCr8BiPlanarVideoRange ||
|
|
352
|
+
pixelFormat == kCVPixelFormatType_422YpCbCr8BiPlanarFullRange) {
|
|
353
|
+
return isFullRange ? kJPEG_Full_SkYUVColorSpace
|
|
354
|
+
: kRec601_Limited_SkYUVColorSpace;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
// Fallback for buffers that don't provide matrix metadata.
|
|
358
|
+
if (isTenBit) {
|
|
359
|
+
return isFullRange ? kBT2020_10bit_Full_SkYUVColorSpace
|
|
360
|
+
: kBT2020_10bit_Limited_SkYUVColorSpace;
|
|
287
361
|
}
|
|
362
|
+
return isFullRange ? kRec709_Full_SkYUVColorSpace
|
|
363
|
+
: kRec709_Limited_SkYUVColorSpace;
|
|
288
364
|
}
|
|
289
365
|
|
|
290
366
|
// pragma MARK: CVPixelBuffer -> Skia Texture
|
|
@@ -294,9 +370,22 @@ TextureHolder *SkiaCVPixelBufferUtils::getSkiaTextureForCVPixelBufferPlane(
|
|
|
294
370
|
// 1. Get cache
|
|
295
371
|
CVMetalTextureCacheRef textureCache = getTextureCache(device);
|
|
296
372
|
|
|
297
|
-
// 2. Get MetalTexture from
|
|
298
|
-
size_t
|
|
299
|
-
|
|
373
|
+
// 2. Get MetalTexture from CVPixelBuffer
|
|
374
|
+
const size_t planesCount = CVPixelBufferGetPlaneCount(pixelBuffer);
|
|
375
|
+
if (planesCount == 0 && planeIndex != 0) [[unlikely]] {
|
|
376
|
+
throw std::runtime_error("Pixel buffer is not planar, but plane index " +
|
|
377
|
+
std::to_string(planeIndex) + " was requested.");
|
|
378
|
+
}
|
|
379
|
+
if (planesCount > 0 && planeIndex >= planesCount) [[unlikely]] {
|
|
380
|
+
throw std::runtime_error(
|
|
381
|
+
"Requested out-of-bounds plane index " + std::to_string(planeIndex) +
|
|
382
|
+
" for pixel buffer with " + std::to_string(planesCount) + " planes.");
|
|
383
|
+
}
|
|
384
|
+
size_t width = planesCount > 0 ? CVPixelBufferGetWidthOfPlane(pixelBuffer, planeIndex)
|
|
385
|
+
: CVPixelBufferGetWidth(pixelBuffer);
|
|
386
|
+
size_t height =
|
|
387
|
+
planesCount > 0 ? CVPixelBufferGetHeightOfPlane(pixelBuffer, planeIndex)
|
|
388
|
+
: CVPixelBufferGetHeight(pixelBuffer);
|
|
300
389
|
MTLPixelFormat pixelFormat =
|
|
301
390
|
getMTLPixelFormatForCVPixelBufferPlane(pixelBuffer, planeIndex);
|
|
302
391
|
|
|
@@ -307,7 +396,7 @@ TextureHolder *SkiaCVPixelBufferUtils::getSkiaTextureForCVPixelBufferPlane(
|
|
|
307
396
|
|
|
308
397
|
if (result != kCVReturnSuccess) [[unlikely]] {
|
|
309
398
|
throw std::runtime_error(
|
|
310
|
-
"Failed to create Metal Texture from
|
|
399
|
+
"Failed to create Metal Texture from CVPixelBuffer! Result: " +
|
|
311
400
|
std::to_string(result));
|
|
312
401
|
}
|
|
313
402
|
|
|
@@ -334,19 +423,102 @@ SkiaCVPixelBufferUtils::getTextureCache(id<MTLDevice> device) {
|
|
|
334
423
|
|
|
335
424
|
MTLPixelFormat SkiaCVPixelBufferUtils::getMTLPixelFormatForCVPixelBufferPlane(
|
|
336
425
|
CVPixelBufferRef pixelBuffer, size_t planeIndex) {
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
426
|
+
const OSType format = CVPixelBufferGetPixelFormatType(pixelBuffer);
|
|
427
|
+
auto throwInvalidPlaneIndexForFormat = [&](size_t expectedPlanes)
|
|
428
|
+
-> MTLPixelFormat {
|
|
429
|
+
throw std::runtime_error(
|
|
430
|
+
"Invalid plane index " + std::to_string(planeIndex) +
|
|
431
|
+
" for pixel format " + std::string(FourCC2Str(format)) + " (expected 0.." +
|
|
432
|
+
std::to_string(expectedPlanes - 1) + ").");
|
|
433
|
+
};
|
|
434
|
+
|
|
435
|
+
switch (format) {
|
|
436
|
+
case kCVPixelFormatType_32BGRA:
|
|
437
|
+
// 1 plane, 8-bit interleaved BGRA.
|
|
438
|
+
if (planeIndex != 0) {
|
|
439
|
+
return throwInvalidPlaneIndexForFormat(1);
|
|
440
|
+
}
|
|
441
|
+
return MTLPixelFormatBGRA8Unorm;
|
|
442
|
+
case kCVPixelFormatType_32RGBA:
|
|
443
|
+
// 1 plane, 8-bit interleaved RGBA.
|
|
444
|
+
if (planeIndex != 0) {
|
|
445
|
+
return throwInvalidPlaneIndexForFormat(1);
|
|
446
|
+
}
|
|
447
|
+
return MTLPixelFormatRGBA8Unorm;
|
|
448
|
+
case kCVPixelFormatType_420YpCbCr8Planar:
|
|
449
|
+
case kCVPixelFormatType_420YpCbCr8PlanarFullRange:
|
|
450
|
+
// 3 planes, 8-bit 4:2:0 planar (Y, U, V), each plane is single channel.
|
|
451
|
+
switch (planeIndex) {
|
|
452
|
+
case 0:
|
|
453
|
+
case 1:
|
|
454
|
+
case 2:
|
|
455
|
+
return MTLPixelFormatR8Unorm;
|
|
456
|
+
default:
|
|
457
|
+
return throwInvalidPlaneIndexForFormat(3);
|
|
458
|
+
}
|
|
459
|
+
case kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange:
|
|
460
|
+
case kCVPixelFormatType_420YpCbCr8BiPlanarFullRange:
|
|
461
|
+
case kCVPixelFormatType_422YpCbCr8BiPlanarVideoRange:
|
|
462
|
+
case kCVPixelFormatType_422YpCbCr8BiPlanarFullRange:
|
|
463
|
+
case kCVPixelFormatType_444YpCbCr8BiPlanarVideoRange:
|
|
464
|
+
case kCVPixelFormatType_444YpCbCr8BiPlanarFullRange:
|
|
465
|
+
// 2 planes, 8-bit bi-planar (plane 0 = Y, plane 1 = interleaved CbCr).
|
|
466
|
+
switch (planeIndex) {
|
|
467
|
+
case 0:
|
|
468
|
+
return MTLPixelFormatR8Unorm;
|
|
469
|
+
case 1:
|
|
470
|
+
return MTLPixelFormatRG8Unorm;
|
|
471
|
+
default:
|
|
472
|
+
return throwInvalidPlaneIndexForFormat(2);
|
|
473
|
+
}
|
|
474
|
+
case kCVPixelFormatType_420YpCbCr10BiPlanarVideoRange:
|
|
475
|
+
case kCVPixelFormatType_420YpCbCr10BiPlanarFullRange:
|
|
476
|
+
case kCVPixelFormatType_422YpCbCr10BiPlanarVideoRange:
|
|
477
|
+
case kCVPixelFormatType_422YpCbCr10BiPlanarFullRange:
|
|
478
|
+
case kCVPixelFormatType_444YpCbCr10BiPlanarVideoRange:
|
|
479
|
+
case kCVPixelFormatType_444YpCbCr10BiPlanarFullRange:
|
|
480
|
+
// 2 planes, 10-bit bi-planar stored in 16-bit lanes (Y + interleaved CbCr).
|
|
481
|
+
switch (planeIndex) {
|
|
482
|
+
case 0:
|
|
483
|
+
return MTLPixelFormatR16Unorm;
|
|
484
|
+
case 1:
|
|
485
|
+
return MTLPixelFormatRG16Unorm;
|
|
486
|
+
default:
|
|
487
|
+
return throwInvalidPlaneIndexForFormat(2);
|
|
488
|
+
}
|
|
489
|
+
default:
|
|
490
|
+
break;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
const size_t planesCount = CVPixelBufferGetPlaneCount(pixelBuffer);
|
|
494
|
+
const size_t width =
|
|
495
|
+
planesCount > 0 ? CVPixelBufferGetWidthOfPlane(pixelBuffer, planeIndex)
|
|
496
|
+
: CVPixelBufferGetWidth(pixelBuffer);
|
|
497
|
+
const size_t bytesPerRow =
|
|
498
|
+
planesCount > 0 ? CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, planeIndex)
|
|
499
|
+
: CVPixelBufferGetBytesPerRow(pixelBuffer);
|
|
500
|
+
if (width == 0) [[unlikely]] {
|
|
501
|
+
throw std::runtime_error("Invalid plane width for pixel format " +
|
|
502
|
+
std::string(FourCC2Str(format)) + "!");
|
|
503
|
+
}
|
|
504
|
+
if (bytesPerRow % width != 0) [[unlikely]] {
|
|
505
|
+
throw std::runtime_error(
|
|
506
|
+
"Invalid bytes per row! Bytes per row must be evenly divisible by width "
|
|
507
|
+
"for pixel format " +
|
|
508
|
+
std::string(FourCC2Str(format)) + "!");
|
|
509
|
+
}
|
|
510
|
+
const size_t bytesPerPixel = bytesPerRow / width;
|
|
341
511
|
if (bytesPerPixel == 1) {
|
|
342
512
|
return MTLPixelFormatR8Unorm;
|
|
343
|
-
}
|
|
513
|
+
}
|
|
514
|
+
if (bytesPerPixel == 2) {
|
|
344
515
|
return MTLPixelFormatRG8Unorm;
|
|
345
|
-
}
|
|
516
|
+
}
|
|
517
|
+
if (bytesPerPixel == 4) {
|
|
346
518
|
return MTLPixelFormatBGRA8Unorm;
|
|
347
|
-
} else [[unlikely]] {
|
|
348
|
-
throw std::runtime_error("Invalid bytes per row! Expected 1 (R), 2 (RG) or "
|
|
349
|
-
"4 (RGBA), but received " +
|
|
350
|
-
std::to_string(bytesPerPixel));
|
|
351
519
|
}
|
|
520
|
+
|
|
521
|
+
[[unlikely]] throw std::runtime_error(
|
|
522
|
+
"Invalid bytes per row! Expected 1 (R), 2 (RG) or 4 (RGBA), but received " +
|
|
523
|
+
std::to_string(bytesPerPixel));
|
|
352
524
|
}
|
package/apple/SkiaUIView.mm
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#import <React/RCTBridge.h>
|
|
2
|
+
#import <QuartzCore/CATransaction.h>
|
|
2
3
|
|
|
3
4
|
#import "RNSkiaModule.h"
|
|
4
5
|
#import "SkiaUIView.h"
|
|
@@ -140,7 +141,10 @@
|
|
|
140
141
|
- (void)layoutSubviews {
|
|
141
142
|
[super layoutSubviews];
|
|
142
143
|
if (_impl != nullptr) {
|
|
144
|
+
[CATransaction begin];
|
|
145
|
+
[CATransaction setDisableActions:YES];
|
|
143
146
|
_impl->setSize(self.bounds.size.width, self.bounds.size.height);
|
|
147
|
+
[CATransaction commit];
|
|
144
148
|
}
|
|
145
149
|
}
|
|
146
150
|
|
package/cpp/api/JsiSkColor.h
CHANGED
|
@@ -44,6 +44,23 @@ public:
|
|
|
44
44
|
|
|
45
45
|
static SkColor fromValue(jsi::Runtime &runtime, const jsi::Value &obj) {
|
|
46
46
|
const auto &object = obj.asObject(runtime);
|
|
47
|
+
|
|
48
|
+
// Handle regular JavaScript arrays
|
|
49
|
+
if (object.isArray(runtime)) {
|
|
50
|
+
auto array = object.asArray(runtime);
|
|
51
|
+
if (array.size(runtime) != 4) {
|
|
52
|
+
throw jsi::JSError(runtime,
|
|
53
|
+
"Expected array of length 4 for color, got " +
|
|
54
|
+
std::to_string(array.size(runtime)));
|
|
55
|
+
}
|
|
56
|
+
auto r = array.getValueAtIndex(runtime, 0).asNumber();
|
|
57
|
+
auto g = array.getValueAtIndex(runtime, 1).asNumber();
|
|
58
|
+
auto b = array.getValueAtIndex(runtime, 2).asNumber();
|
|
59
|
+
auto a = array.getValueAtIndex(runtime, 3).asNumber();
|
|
60
|
+
return SkColorSetARGB(a * 255, r * 255, g * 255, b * 255);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Handle Float32Array (has buffer property)
|
|
47
64
|
jsi::ArrayBuffer buffer =
|
|
48
65
|
object
|
|
49
66
|
.getProperty(runtime, jsi::PropNameID::forAscii(runtime, "buffer"))
|
|
@@ -76,7 +93,42 @@ public:
|
|
|
76
93
|
return JsiSkColor::toValue(
|
|
77
94
|
runtime, SkColorSetARGB(color.a * 255, color.r, color.g, color.b));
|
|
78
95
|
} else if (arguments[0].isObject()) {
|
|
79
|
-
|
|
96
|
+
auto obj = arguments[0].getObject(runtime);
|
|
97
|
+
|
|
98
|
+
// Check if it's a regular array - convert to Float32Array
|
|
99
|
+
if (obj.isArray(runtime)) {
|
|
100
|
+
auto arr = obj.getArray(runtime);
|
|
101
|
+
if (arr.size(runtime) != 4) {
|
|
102
|
+
throw jsi::JSError(runtime,
|
|
103
|
+
"Expected array of length 4 for color, got " +
|
|
104
|
+
std::to_string(arr.size(runtime)));
|
|
105
|
+
}
|
|
106
|
+
auto r = static_cast<float>(arr.getValueAtIndex(runtime, 0).asNumber());
|
|
107
|
+
auto g = static_cast<float>(arr.getValueAtIndex(runtime, 1).asNumber());
|
|
108
|
+
auto b = static_cast<float>(arr.getValueAtIndex(runtime, 2).asNumber());
|
|
109
|
+
auto a = static_cast<float>(arr.getValueAtIndex(runtime, 3).asNumber());
|
|
110
|
+
|
|
111
|
+
// Create Float32Array and populate
|
|
112
|
+
auto result = runtime.global()
|
|
113
|
+
.getPropertyAsFunction(runtime, "Float32Array")
|
|
114
|
+
.callAsConstructor(runtime, 4)
|
|
115
|
+
.getObject(runtime);
|
|
116
|
+
jsi::ArrayBuffer buffer =
|
|
117
|
+
result
|
|
118
|
+
.getProperty(runtime,
|
|
119
|
+
jsi::PropNameID::forAscii(runtime, "buffer"))
|
|
120
|
+
.asObject(runtime)
|
|
121
|
+
.getArrayBuffer(runtime);
|
|
122
|
+
auto bfrPtr = reinterpret_cast<float *>(buffer.data(runtime));
|
|
123
|
+
bfrPtr[0] = r;
|
|
124
|
+
bfrPtr[1] = g;
|
|
125
|
+
bfrPtr[2] = b;
|
|
126
|
+
bfrPtr[3] = a;
|
|
127
|
+
return result;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Already a Float32Array or similar - return as-is
|
|
131
|
+
return obj;
|
|
80
132
|
}
|
|
81
133
|
return jsi::Value::undefined();
|
|
82
134
|
};
|
package/cpp/rnwgpu/ArrayBuffer.h
CHANGED
|
@@ -49,8 +49,13 @@ template <> struct JSIConverter<std::shared_ptr<ArrayBuffer>> {
|
|
|
49
49
|
auto buff = bufferProp.getObject(runtime);
|
|
50
50
|
auto bytesPerElements =
|
|
51
51
|
obj.getProperty(runtime, "BYTES_PER_ELEMENT").asNumber();
|
|
52
|
-
|
|
53
|
-
|
|
52
|
+
auto arrayBuffer = buff.getArrayBuffer(runtime);
|
|
53
|
+
auto byteOffset = static_cast<size_t>(
|
|
54
|
+
obj.getProperty(runtime, "byteOffset").asNumber());
|
|
55
|
+
auto byteLength = static_cast<size_t>(
|
|
56
|
+
obj.getProperty(runtime, "byteLength").asNumber());
|
|
57
|
+
return std::make_shared<ArrayBuffer>(
|
|
58
|
+
arrayBuffer.data(runtime) + byteOffset, byteLength,
|
|
54
59
|
static_cast<size_t>(bytesPerElements));
|
|
55
60
|
}
|
|
56
61
|
}
|
|
@@ -19,6 +19,8 @@ export declare const useCanvasSize: (userRef?: RefObject<CanvasRef | null>) => {
|
|
|
19
19
|
export declare const isFabric: boolean;
|
|
20
20
|
export interface CanvasProps extends Omit<ViewProps, "onLayout"> {
|
|
21
21
|
debug?: boolean;
|
|
22
|
+
/** @deprecated Not supported on Fabric. Use `onSize` or `useCanvasSize()` instead. */
|
|
23
|
+
onLayout?: ViewProps["onLayout"];
|
|
22
24
|
opaque?: boolean;
|
|
23
25
|
onSize?: SharedValue<SkSize>;
|
|
24
26
|
colorSpace?: "p3" | "srgb";
|
|
@@ -56,9 +56,6 @@ const Canvas = ({
|
|
|
56
56
|
colorSpace = "p3",
|
|
57
57
|
androidWarmup = false,
|
|
58
58
|
ref,
|
|
59
|
-
// Here know this is a type error but this is done on purpose to check it at runtime
|
|
60
|
-
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
61
|
-
// @ts-expect-error
|
|
62
59
|
onLayout,
|
|
63
60
|
...viewProps
|
|
64
61
|
}) => {
|