@shopify/react-native-skia 2.4.20 → 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/MetalContext.h +2 -3
- package/apple/SkiaCVPixelBufferUtils.h +11 -8
- package/apple/SkiaCVPixelBufferUtils.mm +229 -49
- package/apple/SkiaUIView.mm +4 -0
- package/cpp/api/JsiSkColor.h +53 -1
- package/cpp/api/JsiSkContourMeasureIter.h +1 -1
- package/cpp/api/JsiSkFont.h +3 -1
- package/cpp/api/JsiSkFontStyle.h +3 -1
- package/cpp/api/JsiSkHostObjects.h +13 -1
- package/cpp/api/JsiSkImage.h +3 -0
- package/cpp/api/JsiSkImageInfo.h +3 -1
- package/cpp/api/JsiSkMatrix.h +3 -1
- package/cpp/api/JsiSkPaint.h +3 -1
- package/cpp/api/JsiSkPoint.h +3 -1
- package/cpp/api/JsiSkRRect.h +3 -1
- package/cpp/api/JsiSkRSXform.h +3 -1
- package/cpp/api/JsiSkRect.h +3 -1
- package/cpp/api/JsiSkRuntimeShaderBuilder.h +1 -1
- package/cpp/api/JsiSkSVGFactory.h +1 -1
- package/cpp/jsi2/NativeObject.h +6 -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/renderer/Offscreen.d.ts +3 -3
- package/lib/commonjs/renderer/Offscreen.js.map +1 -1
- package/lib/commonjs/skia/types/Image/Image.d.ts +2 -1
- package/lib/commonjs/skia/types/Image/Image.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/renderer/Offscreen.d.ts +3 -3
- package/lib/module/renderer/Offscreen.js.map +1 -1
- package/lib/module/skia/types/Image/Image.d.ts +2 -1
- package/lib/module/skia/types/Image/Image.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/lib/commonjs/renderer/Offscreen.d.ts +2 -2
- package/lib/typescript/lib/module/renderer/Offscreen.d.ts +2 -2
- package/lib/typescript/src/renderer/Canvas.d.ts +2 -0
- package/lib/typescript/src/renderer/Offscreen.d.ts +3 -3
- package/lib/typescript/src/skia/types/Image/Image.d.ts +2 -1
- package/package.json +10 -30
- package/react-native-skia.podspec +110 -58
- package/src/renderer/Canvas.tsx +2 -3
- package/src/renderer/Offscreen.tsx +9 -3
- package/src/skia/types/Image/Image.ts +2 -1
- 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()) {
|
package/apple/MetalContext.h
CHANGED
|
@@ -65,7 +65,6 @@ public:
|
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
sk_sp<SkImage> MakeImageFromBuffer(void *buffer) {
|
|
68
|
-
|
|
69
68
|
CVPixelBufferRef sampleBuffer = (CVPixelBufferRef)buffer;
|
|
70
69
|
SkiaCVPixelBufferUtils::CVPixelBufferBaseFormat format =
|
|
71
70
|
SkiaCVPixelBufferUtils::getCVPixelBufferBaseFormat(sampleBuffer);
|
|
@@ -73,12 +72,12 @@ public:
|
|
|
73
72
|
case SkiaCVPixelBufferUtils::CVPixelBufferBaseFormat::rgb: {
|
|
74
73
|
// CVPixelBuffer is in any RGB format, single-plane
|
|
75
74
|
return SkiaCVPixelBufferUtils::RGB::makeSkImageFromCVPixelBuffer(
|
|
76
|
-
_directContext.get(), sampleBuffer);
|
|
75
|
+
_device, _directContext.get(), sampleBuffer);
|
|
77
76
|
}
|
|
78
77
|
case SkiaCVPixelBufferUtils::CVPixelBufferBaseFormat::yuv: {
|
|
79
78
|
// CVPixelBuffer is in any YUV format, multi-plane
|
|
80
79
|
return SkiaCVPixelBufferUtils::YUV::makeSkImageFromCVPixelBuffer(
|
|
81
|
-
_directContext.get(), sampleBuffer);
|
|
80
|
+
_device, _directContext.get(), sampleBuffer);
|
|
82
81
|
}
|
|
83
82
|
default:
|
|
84
83
|
[[unlikely]] {
|
|
@@ -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
|
|
|
@@ -84,7 +84,7 @@ public:
|
|
|
84
84
|
CVPixelBuffer.
|
|
85
85
|
*/
|
|
86
86
|
static sk_sp<SkImage>
|
|
87
|
-
makeSkImageFromCVPixelBuffer(GrDirectContext *context,
|
|
87
|
+
makeSkImageFromCVPixelBuffer(id<MTLDevice> device, GrDirectContext *context,
|
|
88
88
|
CVPixelBufferRef pixelBuffer);
|
|
89
89
|
|
|
90
90
|
private:
|
|
@@ -98,21 +98,24 @@ public:
|
|
|
98
98
|
CVPixelBuffer.
|
|
99
99
|
*/
|
|
100
100
|
static sk_sp<SkImage>
|
|
101
|
-
makeSkImageFromCVPixelBuffer(GrDirectContext *context,
|
|
101
|
+
makeSkImageFromCVPixelBuffer(id<MTLDevice> device, GrDirectContext *context,
|
|
102
102
|
CVPixelBufferRef pixelBuffer);
|
|
103
103
|
|
|
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
|
|
|
111
115
|
private:
|
|
112
|
-
static CVMetalTextureCacheRef getTextureCache();
|
|
113
|
-
static TextureHolder *
|
|
114
|
-
|
|
115
|
-
size_t planeIndex);
|
|
116
|
+
static CVMetalTextureCacheRef getTextureCache(id<MTLDevice> device);
|
|
117
|
+
static TextureHolder *getSkiaTextureForCVPixelBufferPlane(
|
|
118
|
+
id<MTLDevice> device, CVPixelBufferRef pixelBuffer, size_t planeIndex);
|
|
116
119
|
static MTLPixelFormat
|
|
117
120
|
getMTLPixelFormatForCVPixelBufferPlane(CVPixelBufferRef pixelBuffer,
|
|
118
121
|
size_t planeIndex);
|
|
@@ -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:
|
|
@@ -134,13 +139,14 @@ SkColorType SkiaCVPixelBufferUtils::RGB::getCVPixelBufferColorType(
|
|
|
134
139
|
}
|
|
135
140
|
|
|
136
141
|
sk_sp<SkImage> SkiaCVPixelBufferUtils::RGB::makeSkImageFromCVPixelBuffer(
|
|
137
|
-
GrDirectContext *context,
|
|
142
|
+
id<MTLDevice> device, GrDirectContext *context,
|
|
143
|
+
CVPixelBufferRef pixelBuffer) {
|
|
138
144
|
// 1. Get Skia color type for RGB buffer
|
|
139
145
|
SkColorType colorType = getCVPixelBufferColorType(pixelBuffer);
|
|
140
146
|
|
|
141
147
|
// 2. Get texture, RGB buffers only have one plane
|
|
142
|
-
TextureHolder *texture =
|
|
143
|
-
|
|
148
|
+
TextureHolder *texture = getSkiaTextureForCVPixelBufferPlane(
|
|
149
|
+
device, pixelBuffer, /* planeIndex */ 0);
|
|
144
150
|
|
|
145
151
|
// 3. Convert to image with manual memory cleanup
|
|
146
152
|
return SkImages::BorrowTextureFrom(
|
|
@@ -152,15 +158,22 @@ sk_sp<SkImage> SkiaCVPixelBufferUtils::RGB::makeSkImageFromCVPixelBuffer(
|
|
|
152
158
|
// pragma MARK: YUV
|
|
153
159
|
|
|
154
160
|
sk_sp<SkImage> SkiaCVPixelBufferUtils::YUV::makeSkImageFromCVPixelBuffer(
|
|
155
|
-
GrDirectContext *context,
|
|
161
|
+
id<MTLDevice> device, GrDirectContext *context,
|
|
162
|
+
CVPixelBufferRef pixelBuffer) {
|
|
156
163
|
// 1. Get all planes (YUV, Y_UV, Y_U_V or Y_U_V_A)
|
|
157
|
-
size_t planesCount = CVPixelBufferGetPlaneCount(pixelBuffer);
|
|
164
|
+
const size_t planesCount = CVPixelBufferGetPlaneCount(pixelBuffer);
|
|
165
|
+
if (planesCount > SkYUVAInfo::kMaxPlanes) [[unlikely]] {
|
|
166
|
+
throw std::runtime_error(
|
|
167
|
+
"CVPixelBuffer has " + std::to_string(planesCount) +
|
|
168
|
+
" textures, but the platform only supports a maximum of " +
|
|
169
|
+
std::to_string(SkYUVAInfo::kMaxPlanes) + " textures!");
|
|
170
|
+
}
|
|
158
171
|
MultiTexturesHolder *texturesHolder = new MultiTexturesHolder();
|
|
159
|
-
GrBackendTexture textures[
|
|
172
|
+
GrBackendTexture textures[SkYUVAInfo::kMaxPlanes];
|
|
160
173
|
|
|
161
174
|
for (size_t planeIndex = 0; planeIndex < planesCount; planeIndex++) {
|
|
162
175
|
TextureHolder *textureHolder =
|
|
163
|
-
getSkiaTextureForCVPixelBufferPlane(pixelBuffer, planeIndex);
|
|
176
|
+
getSkiaTextureForCVPixelBufferPlane(device, pixelBuffer, planeIndex);
|
|
164
177
|
textures[planeIndex] = textureHolder->toGrBackendTexture();
|
|
165
178
|
texturesHolder->addTexture(textureHolder);
|
|
166
179
|
}
|
|
@@ -190,7 +203,7 @@ SkYUVAInfo SkiaCVPixelBufferUtils::YUV::getYUVAInfoForCVPixelBuffer(
|
|
|
190
203
|
OSType format = CVPixelBufferGetPixelFormatType(pixelBuffer);
|
|
191
204
|
SkYUVAInfo::PlaneConfig planeConfig = getPlaneConfig(format);
|
|
192
205
|
SkYUVAInfo::Subsampling subsampling = getSubsampling(format);
|
|
193
|
-
SkYUVColorSpace colorspace = getColorspace(
|
|
206
|
+
SkYUVColorSpace colorspace = getColorspace(pixelBuffer);
|
|
194
207
|
|
|
195
208
|
return SkYUVAInfo(size, planeConfig, subsampling, colorspace);
|
|
196
209
|
}
|
|
@@ -202,9 +215,13 @@ SkiaCVPixelBufferUtils::YUV::getPlaneConfig(OSType pixelFormat) {
|
|
|
202
215
|
switch (pixelFormat) {
|
|
203
216
|
case kCVPixelFormatType_420YpCbCr8Planar:
|
|
204
217
|
case kCVPixelFormatType_420YpCbCr8PlanarFullRange:
|
|
205
|
-
return SkYUVAInfo::PlaneConfig::
|
|
218
|
+
return SkYUVAInfo::PlaneConfig::kY_U_V;
|
|
206
219
|
case kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange:
|
|
207
220
|
case kCVPixelFormatType_420YpCbCr8BiPlanarFullRange:
|
|
221
|
+
case kCVPixelFormatType_422YpCbCr8BiPlanarVideoRange:
|
|
222
|
+
case kCVPixelFormatType_422YpCbCr8BiPlanarFullRange:
|
|
223
|
+
case kCVPixelFormatType_444YpCbCr8BiPlanarVideoRange:
|
|
224
|
+
case kCVPixelFormatType_444YpCbCr8BiPlanarFullRange:
|
|
208
225
|
case kCVPixelFormatType_420YpCbCr10BiPlanarVideoRange:
|
|
209
226
|
case kCVPixelFormatType_420YpCbCr10BiPlanarFullRange:
|
|
210
227
|
case kCVPixelFormatType_422YpCbCr10BiPlanarVideoRange:
|
|
@@ -233,9 +250,13 @@ SkiaCVPixelBufferUtils::YUV::getSubsampling(OSType pixelFormat) {
|
|
|
233
250
|
case kCVPixelFormatType_420YpCbCr10BiPlanarVideoRange:
|
|
234
251
|
case kCVPixelFormatType_420YpCbCr10BiPlanarFullRange:
|
|
235
252
|
[[likely]] return SkYUVAInfo::Subsampling::k420;
|
|
253
|
+
case kCVPixelFormatType_422YpCbCr8BiPlanarVideoRange:
|
|
254
|
+
case kCVPixelFormatType_422YpCbCr8BiPlanarFullRange:
|
|
236
255
|
case kCVPixelFormatType_422YpCbCr10BiPlanarVideoRange:
|
|
237
256
|
case kCVPixelFormatType_422YpCbCr10BiPlanarFullRange:
|
|
238
257
|
return SkYUVAInfo::Subsampling::k422;
|
|
258
|
+
case kCVPixelFormatType_444YpCbCr8BiPlanarVideoRange:
|
|
259
|
+
case kCVPixelFormatType_444YpCbCr8BiPlanarFullRange:
|
|
239
260
|
case kCVPixelFormatType_444YpCbCr10BiPlanarVideoRange:
|
|
240
261
|
case kCVPixelFormatType_444YpCbCr10BiPlanarFullRange:
|
|
241
262
|
return SkYUVAInfo::Subsampling::k444;
|
|
@@ -249,46 +270,122 @@ SkiaCVPixelBufferUtils::YUV::getSubsampling(OSType pixelFormat) {
|
|
|
249
270
|
|
|
250
271
|
// pragma MARK: YUV getColorspace()
|
|
251
272
|
|
|
252
|
-
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) {
|
|
253
295
|
switch (pixelFormat) {
|
|
254
|
-
case kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange:
|
|
255
|
-
[[likely]]
|
|
256
|
-
// 8-bit limited
|
|
257
|
-
return SkYUVColorSpace::kRec709_Limited_SkYUVColorSpace;
|
|
258
|
-
case kCVPixelFormatType_420YpCbCr8Planar:
|
|
259
296
|
case kCVPixelFormatType_420YpCbCr8PlanarFullRange:
|
|
260
297
|
case kCVPixelFormatType_420YpCbCr8BiPlanarFullRange:
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
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) {
|
|
264
311
|
case kCVPixelFormatType_420YpCbCr10BiPlanarVideoRange:
|
|
265
|
-
case kCVPixelFormatType_422YpCbCr10BiPlanarVideoRange:
|
|
266
|
-
case kCVPixelFormatType_444YpCbCr10BiPlanarVideoRange:
|
|
267
|
-
// 10-bit limited
|
|
268
|
-
return SkYUVColorSpace::kBT2020_10bit_Limited_SkYUVColorSpace;
|
|
269
312
|
case kCVPixelFormatType_420YpCbCr10BiPlanarFullRange:
|
|
313
|
+
case kCVPixelFormatType_422YpCbCr10BiPlanarVideoRange:
|
|
270
314
|
case kCVPixelFormatType_422YpCbCr10BiPlanarFullRange:
|
|
315
|
+
case kCVPixelFormatType_444YpCbCr10BiPlanarVideoRange:
|
|
271
316
|
case kCVPixelFormatType_444YpCbCr10BiPlanarFullRange:
|
|
272
|
-
|
|
273
|
-
return SkYUVColorSpace::kBT2020_10bit_Full_SkYUVColorSpace;
|
|
274
|
-
// This can be extended with branches for specific YUV formats if Apple
|
|
275
|
-
// uses new formats.
|
|
317
|
+
return true;
|
|
276
318
|
default:
|
|
277
|
-
|
|
278
|
-
|
|
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;
|
|
279
361
|
}
|
|
362
|
+
return isFullRange ? kRec709_Full_SkYUVColorSpace
|
|
363
|
+
: kRec709_Limited_SkYUVColorSpace;
|
|
280
364
|
}
|
|
281
365
|
|
|
282
366
|
// pragma MARK: CVPixelBuffer -> Skia Texture
|
|
283
367
|
|
|
284
368
|
TextureHolder *SkiaCVPixelBufferUtils::getSkiaTextureForCVPixelBufferPlane(
|
|
285
|
-
CVPixelBufferRef pixelBuffer, size_t planeIndex) {
|
|
369
|
+
id<MTLDevice> device, CVPixelBufferRef pixelBuffer, size_t planeIndex) {
|
|
286
370
|
// 1. Get cache
|
|
287
|
-
CVMetalTextureCacheRef textureCache = getTextureCache();
|
|
371
|
+
CVMetalTextureCacheRef textureCache = getTextureCache(device);
|
|
288
372
|
|
|
289
|
-
// 2. Get MetalTexture from
|
|
290
|
-
size_t
|
|
291
|
-
|
|
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);
|
|
292
389
|
MTLPixelFormat pixelFormat =
|
|
293
390
|
getMTLPixelFormatForCVPixelBufferPlane(pixelBuffer, planeIndex);
|
|
294
391
|
|
|
@@ -299,7 +396,7 @@ TextureHolder *SkiaCVPixelBufferUtils::getSkiaTextureForCVPixelBufferPlane(
|
|
|
299
396
|
|
|
300
397
|
if (result != kCVReturnSuccess) [[unlikely]] {
|
|
301
398
|
throw std::runtime_error(
|
|
302
|
-
"Failed to create Metal Texture from
|
|
399
|
+
"Failed to create Metal Texture from CVPixelBuffer! Result: " +
|
|
303
400
|
std::to_string(result));
|
|
304
401
|
}
|
|
305
402
|
|
|
@@ -308,13 +405,13 @@ TextureHolder *SkiaCVPixelBufferUtils::getSkiaTextureForCVPixelBufferPlane(
|
|
|
308
405
|
|
|
309
406
|
// pragma MARK: getTextureCache()
|
|
310
407
|
|
|
311
|
-
CVMetalTextureCacheRef
|
|
408
|
+
CVMetalTextureCacheRef
|
|
409
|
+
SkiaCVPixelBufferUtils::getTextureCache(id<MTLDevice> device) {
|
|
312
410
|
static CVMetalTextureCacheRef textureCache = nil;
|
|
313
411
|
if (textureCache == nil) {
|
|
314
412
|
// Create a new Texture Cache
|
|
315
|
-
auto result = CVMetalTextureCacheCreate(kCFAllocatorDefault, nil,
|
|
316
|
-
|
|
317
|
-
&textureCache);
|
|
413
|
+
auto result = CVMetalTextureCacheCreate(kCFAllocatorDefault, nil, device,
|
|
414
|
+
nil, &textureCache);
|
|
318
415
|
if (result != kCVReturnSuccess || textureCache == nil) {
|
|
319
416
|
throw std::runtime_error("Failed to create Metal Texture Cache!");
|
|
320
417
|
}
|
|
@@ -326,19 +423,102 @@ CVMetalTextureCacheRef SkiaCVPixelBufferUtils::getTextureCache() {
|
|
|
326
423
|
|
|
327
424
|
MTLPixelFormat SkiaCVPixelBufferUtils::getMTLPixelFormatForCVPixelBufferPlane(
|
|
328
425
|
CVPixelBufferRef pixelBuffer, size_t planeIndex) {
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
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;
|
|
333
511
|
if (bytesPerPixel == 1) {
|
|
334
512
|
return MTLPixelFormatR8Unorm;
|
|
335
|
-
}
|
|
513
|
+
}
|
|
514
|
+
if (bytesPerPixel == 2) {
|
|
336
515
|
return MTLPixelFormatRG8Unorm;
|
|
337
|
-
}
|
|
516
|
+
}
|
|
517
|
+
if (bytesPerPixel == 4) {
|
|
338
518
|
return MTLPixelFormatBGRA8Unorm;
|
|
339
|
-
} else [[unlikely]] {
|
|
340
|
-
throw std::runtime_error("Invalid bytes per row! Expected 1 (R), 2 (RG) or "
|
|
341
|
-
"4 (RGBA), but received " +
|
|
342
|
-
std::to_string(bytesPerPixel));
|
|
343
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));
|
|
344
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
|
|