react-native-vroom-chart 0.9.1 → 0.10.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/README.md CHANGED
@@ -16,8 +16,11 @@ import { VroomChart } from 'react-native-vroom-chart';
16
16
  ## Peer dependencies
17
17
 
18
18
  - `react-native` ≥ 0.76
19
- - `@shopify/react-native-skia` ≥ 1.5 — provides the Skia runtime we draw into
19
+ - `@shopify/react-native-skia` ≥ 2.11.0 — provides the Skia runtime we draw into. vroom links RN-Skia's native internals (`cpp/api`) and tracks that package closely; those headers have no semver guarantee.
20
+
20
21
  - `react-native-gesture-handler` ≥ 2.16 — long-press / pan / pinch recognition
22
+ - `react-native-reanimated` ≥ 4.0 — gesture worklets
23
+ - `react-native-worklets` ≥ 0.7.0 — required by Skia 2.11's Reanimated integration on native
21
24
 
22
25
  ## Status
23
26
 
@@ -4,20 +4,21 @@
4
4
  # core the iOS podspec compiles (see ../react-native-vroom-chart.podspec),
5
5
  # plus this directory's small JNI entry point (src/main/cpp/VroomChartJni.cpp).
6
6
  #
7
- # RN-Skia's headers (JsiSkHostObjects.h, RNSkPlatformContext.h needed by
8
- # ../cpp/VroomSkiaContext.cpp) aren't consumed via CMake's prefab mechanism;
9
- # instead we point directly at @shopify/react-native-skia's `cpp/` sources,
10
- # mirroring the podspec's `require.resolve('@shopify/react-native-skia/package.json')`
11
- # trick (VROOM_SKIA_CPP_DIR is computed the same way in build.gradle).
7
+ # RN-Skia's headers (JsiSkNativeObjects.h, JsiSkPicture.h, and the
8
+ # RNSkPlatformContext.h they pull in) aren't consumed via CMake's prefab
9
+ # mechanism; instead we point directly at @shopify/react-native-skia's `cpp/`
10
+ # sources, mirroring the podspec's
11
+ # `require.resolve('@shopify/react-native-skia/package.json')` trick
12
+ # (VROOM_SKIA_CPP_DIR is computed the same way in build.gradle).
12
13
  #
13
- # IMPORTANT SK_GRAPHITE ABI note: RNSkPlatformContext's vtable layout
14
- # differs depending on whether SK_GRAPHITE was defined when RN-Skia's own
15
- # Android build compiled RNSkAndroidPlatformContext (the concrete class whose
16
- # vtable we call into via VroomSkiaContext.cpp's `ctx->createFontMgr()`).
17
- # We must define SK_GRAPHITE identically to however RN-Skia's own CMakeLists
18
- # (android/CMakeLists.txt) decided it, or virtual calls resolve to the wrong
19
- # vtable slot. We replicate their libskia.a symbol probe below rather than
20
- # hard-coding an assumption.
14
+ # Prebuilt libskia.a lives in the sibling npm package react-native-skia-android
15
+ # (or react-native-skia-graphite-android). build.gradle resolves that package
16
+ # and passes VROOM_SKIA_ANDROID_LIBS_DIR = <pkg>/libs; we append ${ANDROID_ABI}.
17
+ #
18
+ # SK_GRAPHITE vs SK_GL/SK_GANESH must match RN-Skia's own build so our direct
19
+ # Skia calls see the same ABI. Graphite is detected from the
20
+ # <skia-pkg>/libs/.graphite marker (same file RN-Skia uses) and passed in as
21
+ # VROOM_SKIA_GRAPHITE.
21
22
  cmake_minimum_required(VERSION 3.22)
22
23
  project(vroomchart)
23
24
 
@@ -35,44 +36,30 @@ find_package(shopify_react-native-skia REQUIRED CONFIG)
35
36
  if(NOT DEFINED VROOM_SKIA_CPP_DIR)
36
37
  message(FATAL_ERROR "VROOM_SKIA_CPP_DIR not set — passed from android/build.gradle.")
37
38
  endif()
39
+ if(NOT DEFINED VROOM_SKIA_ANDROID_LIBS_DIR)
40
+ message(FATAL_ERROR "VROOM_SKIA_ANDROID_LIBS_DIR not set — passed from android/build.gradle.")
41
+ endif()
38
42
 
39
- # RN-Skia's own package root is one level up from its cpp/ dir; its prebuilt
40
- # Skia static libs live at <root>/libs/android/<abi>/libskia.a.
41
- get_filename_component(VROOM_SKIA_PKG_DIR "${VROOM_SKIA_CPP_DIR}" DIRECTORY)
42
- set(VROOM_SKIA_LIBS_DIR "${VROOM_SKIA_PKG_DIR}/libs/android/${ANDROID_ABI}")
43
+ set(VROOM_SKIA_LIBS_DIR "${VROOM_SKIA_ANDROID_LIBS_DIR}/${ANDROID_ABI}")
43
44
 
44
45
  # Our chart core calls Skia types (SkCanvas, SkPaint, SkFont, ...) and
45
- # RN-Skia's own JSI plumbing (RNJsi::JsiHostObject, JsiSkPicture, ...)
46
+ # RN-Skia's own JSI plumbing (JsiSkPicture / NativeObject, ...)
46
47
  # directly — none of that is header-only, and we need *both* libraries:
47
48
  # - librnskia.so (RN-Skia's prefab-published `rnskia` module, consumed above
48
- # via `implementation project(":shopify_react-native-skia")`) for RNJsi's
49
- # own symbols (JsiHostObject, JsiSkPicture, ...), which only exist there.
50
- # - libskia.a (RN-Skia's bundled prebuilt Skia static lib) for direct Skia
51
- # calls (SkCanvas::drawRect, SkFont, ...): librnskia.so links libskia.a
52
- # internally but doesn't re-export its symbols, so linking librnskia.so
53
- # alone leaves every direct Skia call undefined.
49
+ # via `implementation project(":shopify_react-native-skia")`) for RN-Skia's
50
+ # own symbols (JsiSkPicture, NativeObject, ...), which only exist there.
51
+ # - libskia.a (the prebuilt Skia static lib from react-native-skia-android)
52
+ # for direct Skia calls (SkCanvas::drawRect, SkFont, ...): librnskia.so
53
+ # links libskia.a internally but doesn't re-export its symbols, so linking
54
+ # librnskia.so alone leaves every direct Skia call undefined.
54
55
  # Recompiling RN-Skia's cpp/jsi/*.cpp ourselves instead of linking librnskia.so
55
- # was considered and rejected — it would duplicate RNJsi's symbols (and their
56
+ # was considered and rejected — it would duplicate RN-Skia's symbols (and their
56
57
  # static/global state) across two separately-loaded .so's, which is fine on
57
58
  # iOS's single static binary but not here.
58
59
  add_library(vroom_skia_prebuilt STATIC IMPORTED)
59
60
  set_property(TARGET vroom_skia_prebuilt PROPERTY
60
61
  IMPORTED_LOCATION "${VROOM_SKIA_LIBS_DIR}/libskia.a")
61
62
 
62
- set(SK_GRAPHITE_AVAILABLE OFF)
63
- if(EXISTS "${VROOM_SKIA_LIBS_DIR}/libskia.a")
64
- execute_process(
65
- COMMAND nm "${VROOM_SKIA_LIBS_DIR}/libskia.a"
66
- COMMAND grep "dawn::\\|wgpu\\|_ZN4dawn\\|DawnDevice\\|dawn_native"
67
- OUTPUT_VARIABLE VROOM_NM_OUTPUT
68
- ERROR_QUIET
69
- RESULT_VARIABLE VROOM_NM_RESULT
70
- )
71
- if(VROOM_NM_RESULT EQUAL 0 AND NOT "${VROOM_NM_OUTPUT}" STREQUAL "")
72
- set(SK_GRAPHITE_AVAILABLE ON)
73
- endif()
74
- endif()
75
-
76
63
  # The chart core (shared across iOS/Android/WASM — see ../../core/CMakeLists.txt).
77
64
  set(VROOM_CORE_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../cpp/_core_include")
78
65
  set(VROOM_CORE_SRC_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../cpp/_core_src")
@@ -81,7 +68,7 @@ file(GLOB VROOM_CORE_SOURCES CONFIGURE_DEPENDS "${VROOM_CORE_SRC_DIR}/*.cpp")
81
68
  add_library(vroomchart SHARED
82
69
  "${CMAKE_CURRENT_SOURCE_DIR}/../cpp/VroomJsiInstaller.cpp"
83
70
  "${CMAKE_CURRENT_SOURCE_DIR}/../cpp/VroomChartHostObject.cpp"
84
- "${CMAKE_CURRENT_SOURCE_DIR}/../cpp/VroomSkiaContext.cpp"
71
+ "${CMAKE_CURRENT_SOURCE_DIR}/../cpp/VroomFontMgr.cpp"
85
72
  ${VROOM_CORE_SOURCES}
86
73
  "${CMAKE_CURRENT_SOURCE_DIR}/src/main/cpp/VroomChartJni.cpp"
87
74
  )
@@ -101,7 +88,9 @@ target_include_directories(vroomchart PRIVATE
101
88
  "${VROOM_SKIA_CPP_DIR}/dawn/include"
102
89
  # RNSkPlatformContext.h pulls in ReactCommon/CallInvoker.h directly, which
103
90
  # isn't part of the prefab-published jsi headers — same dirs RN-Skia's own
104
- # android/CMakeLists.txt adds for the same reason.
91
+ # android/CMakeLists.txt adds for the same reason. Still required: we include
92
+ # JsiSkPicture.h, which includes JsiSkNativeObjects.h, which includes
93
+ # RNSkPlatformContext.h.
105
94
  "${REACT_NATIVE_DIR}/ReactCommon"
106
95
  "${REACT_NATIVE_DIR}/ReactCommon/callinvoker"
107
96
  "${REACT_NATIVE_DIR}/ReactCommon/runtimeexecutor"
@@ -114,7 +103,7 @@ target_compile_definitions(vroomchart PRIVATE
114
103
  ONANDROID
115
104
  ON_ANDROID
116
105
  )
117
- if(SK_GRAPHITE_AVAILABLE)
106
+ if(VROOM_SKIA_GRAPHITE)
118
107
  target_compile_definitions(vroomchart PRIVATE SK_GRAPHITE)
119
108
  else()
120
109
  target_compile_definitions(vroomchart PRIVATE SK_GL SK_GANESH)
package/android/README.md CHANGED
@@ -8,31 +8,33 @@ Android equivalent of `../ios/`: a small TurboModule
8
8
  [`../cpp/VroomJsiInstaller.cpp`](../cpp/VroomJsiInstaller.cpp) the iOS bridge
9
9
  uses.
10
10
 
11
- The chart core itself (`../cpp/_core_src`) and the RN-Skia font/context glue
12
- (`../cpp/VroomSkiaContext.*`) are unmodified and shared across iOS and
13
- Android — only the platform glue (this directory + `../ios/`) differs.
11
+ The chart core (`../cpp/_core_src`) and the platform font-manager helper
12
+ (`../cpp/VroomFontMgr.*`) are unmodified and shared across iOS and Android —
13
+ only the platform glue (this directory + `../ios/`) differs.
14
14
  `../cpp/VroomChartHostObject.cpp` has one small `#if defined(__ANDROID__)`
15
15
  branch (see "Cross-`.so` Skia objects" below).
16
16
 
17
17
  `build.gradle` resolves `@shopify/react-native-skia`'s `cpp/` sources via
18
18
  Node module resolution (mirroring `../react-native-vroom-chart.podspec`'s
19
- `require.resolve` trick) so `VroomSkiaContext.cpp` can include RN-Skia's
20
- `JsiSkHostObjects.h` / `RNSkPlatformContext.h`. `CMakeLists.txt` also
21
- replicates RN-Skia's own Graphite-vs-Ganesh detection (probing its bundled
22
- `libskia.a`) so `SK_GRAPHITE` is defined identically to however RN-Skia's own
23
- Android build was compiled `RNSkPlatformContext`'s vtable layout depends on
24
- that macro, and a mismatch would corrupt the virtual call in
25
- `VroomJsiInstaller.cpp`'s `ensureAxisTypeface`.
19
+ `require.resolve` trick) so we can include RN-Skia's `JsiSkPicture.h` /
20
+ `JsiSkNativeObjects.h`. Android prebuilt `libskia.a` lives in the sibling
21
+ `react-native-skia-android` package (or `react-native-skia-graphite-android`
22
+ when `<skia-pkg>/libs/.graphite` exists); Gradle resolves that package and
23
+ passes its `libs/` dir to CMake. Graphite vs Ganesh is the same marker file
24
+ RN-Skia uses, so `SK_GRAPHITE` / `SK_GL`+`SK_GANESH` match RN-Skia's own
25
+ build — needed for vroom's direct Skia calls, not for a virtual call into
26
+ RN-Skia (we construct the font manager ourselves).
26
27
 
27
28
  We link two RN-Skia-adjacent libraries into `libvroomchart.so`, for two
28
29
  different reasons:
29
30
  - `shopify_react-native-skia::rnskia` (RN-Skia's own prefab-published
30
- shared library, `librnskia.so`) — for `RNJsi::JsiHostObject` and friends,
31
- whose out-of-line methods are compiled only there.
32
- - RN-Skia's bundled `libskia.a` (imported directly by path, since it isn't
33
- prefab-published) — for direct Skia calls (`SkCanvas::drawRect`, `SkFont`,
34
- ...), since `librnskia.so` links `libskia.a` internally but doesn't
35
- re-export its symbols.
31
+ shared library, `librnskia.so`) — for `JsiSkPicture` / `NativeObject`
32
+ and friends, whose out-of-line methods are compiled only there.
33
+ - `libskia.a` from `react-native-skia-android` (imported directly by path,
34
+ since it isn't prefab-published) — for direct Skia calls
35
+ (`SkCanvas::drawRect`, `SkFont`, `SkFontMgr_New_Android`, ...), since
36
+ `librnskia.so` links `libskia.a` internally but doesn't re-export its
37
+ symbols.
36
38
 
37
39
  ### Cross-`.so` Skia objects
38
40
 
@@ -43,11 +45,11 @@ of the JSI bridge, but **`RNSkia::JsiSkPicture` — the object `render()` /
43
45
  `libvroomchart.so` on Android.** RN-Skia's own C++ (e.g.
44
46
  `cpp/api/recorder/Convertor.h`'s `getPropertyValue<sk_sp<SkPicture>>`, which
45
47
  runs whenever `<Picture>` reads the value) does
46
- `value.asObject(rt).asHostObject<JsiSkPicture>(rt)`, a `dynamic_pointer_cast`
47
- under the hood. A `JsiSkPicture` we construct ourselves has a vtable/typeinfo
48
- compiled into *our* `.so`; that cast — running inside `librnskia.so` — sees a
49
- different, unmerged RTTI record for "the same" class and fails with `Object
50
- is not a HostObject of desired type`.
48
+ `getJsiObject<JsiSkPicture>(rt, value)`, a `dynamic_pointer_cast` under the
49
+ hood. A `JsiSkPicture` we construct ourselves has a vtable/typeinfo compiled
50
+ into *our* `.so`; that cast — running inside `librnskia.so` — sees a
51
+ different, unmerged RTTI record for "the same" class and fails with
52
+ `Expected a Skia object of a different type`.
51
53
 
52
54
  `VroomChartHostObject.cpp`'s `wrapPicture()` works around this only on
53
55
  Android: instead of constructing `RNSkia::JsiSkPicture` directly (as iOS
@@ -28,7 +28,23 @@ def resolvePackageDir(packageJsonSpecifier) {
28
28
  }
29
29
 
30
30
  def reactNativeDir = resolvePackageDir("react-native/package.json")
31
- def skiaCppDir = new File(resolvePackageDir("@shopify/react-native-skia/package.json"), "cpp")
31
+ def skiaPkgDir = resolvePackageDir("@shopify/react-native-skia/package.json")
32
+ def skiaCppDir = new File(skiaPkgDir, "cpp")
33
+
34
+ // Skia 2.10+ ships Android prebuilts in react-native-skia-android (Ganesh)
35
+ // or react-native-skia-graphite-android (Graphite). Graphite is selected by
36
+ // the same libs/.graphite marker RN-Skia uses. Resolve from the skia package
37
+ // so pnpm's nested .pnpm layout still finds the sibling binary package.
38
+ def useGraphite = new File(skiaPkgDir, "libs/.graphite").exists()
39
+ def skiaAndroidPkgName = useGraphite
40
+ ? "react-native-skia-graphite-android"
41
+ : "react-native-skia-android"
42
+ def skiaAndroidPkgJson = providers.exec {
43
+ workingDir(rootDir)
44
+ commandLine("node", "--print",
45
+ "require.resolve('${skiaAndroidPkgName}/package.json', { paths: ['${skiaPkgDir}'] })")
46
+ }.standardOutput.asText.get().trim()
47
+ def skiaAndroidLibsDir = new File(file(skiaAndroidPkgJson).parentFile, "libs")
32
48
 
33
49
  // Mirror ../cpp/_core_{src,include} from ../../core/{src,include} whenever
34
50
  // the monorepo core is present, so edits to packages/core/ propagate on the
@@ -72,7 +88,9 @@ android {
72
88
  abiFilters(*reactNativeArchitectures())
73
89
  arguments "-DANDROID_STL=c++_shared",
74
90
  "-DREACT_NATIVE_DIR=${reactNativeDir}",
75
- "-DVROOM_SKIA_CPP_DIR=${skiaCppDir}"
91
+ "-DVROOM_SKIA_CPP_DIR=${skiaCppDir}",
92
+ "-DVROOM_SKIA_ANDROID_LIBS_DIR=${skiaAndroidLibsDir}",
93
+ "-DVROOM_SKIA_GRAPHITE=${useGraphite ? 'ON' : 'OFF'}"
76
94
  }
77
95
  }
78
96
  }
@@ -105,11 +123,11 @@ repositories {
105
123
 
106
124
  dependencies {
107
125
  implementation "com.facebook.react:react-android"
108
- // ../cpp/VroomChartHostObject.cpp constructs RN-Skia JSI host objects
109
- // (JsiSkPicture, etc.) whose base class (RNJsi::JsiHostObject) has
110
- // out-of-line methods compiled into RN-Skia's own native library — we link
111
- // against its prefab-published `rnskia` package (below, in CMakeLists.txt)
112
- // rather than recompiling those .cpp files ourselves, to avoid duplicate
113
- // definitions of RNSkia's internals across two separately-loaded .so's.
126
+ // ../cpp/VroomChartHostObject.cpp constructs RN-Skia JSI objects
127
+ // (JsiSkPicture, etc.) whose NativeObject base has out-of-line methods
128
+ // compiled into RN-Skia's own native library — we link against its
129
+ // prefab-published `rnskia` package (below, in CMakeLists.txt) rather than
130
+ // recompiling those .cpp files ourselves, to avoid duplicate definitions of
131
+ // RN-Skia's internals across two separately-loaded .so's.
114
132
  implementation project(":shopify_react-native-skia")
115
133
  }
@@ -8,7 +8,7 @@
8
8
  #include "vroom/vroom_chart.h"
9
9
 
10
10
  // RN-Skia headers.
11
- #include "JsiSkHostObjects.h"
11
+ #include "JsiSkNativeObjects.h"
12
12
  #include "JsiSkPicture.h"
13
13
 
14
14
  namespace vroom {
@@ -78,9 +78,9 @@ std::vector<jsi::PropNameID> ChartHostObject::getPropertyNames(
78
78
  // does, below) gives it a vtable/typeinfo from *our* .so; when RN-Skia's own
79
79
  // compiled code later consumes it (e.g. Convertor.h's
80
80
  // `getPropertyValue<sk_sp<SkPicture>>`, which does
81
- // `value.asObject(rt).asHostObject<JsiSkPicture>(rt)` — a dynamic_pointer_cast
82
- // under the hood), the cast fails cross-.so with "Object is not a HostObject
83
- // of desired type", because the object's *runtime* type was never actually
81
+ // `getJsiObject<JsiSkPicture>(rt, value)` — a dynamic_pointer_cast
82
+ // under the hood), the cast fails cross-.so with "Expected a Skia object of
83
+ // a different type", because the object's *runtime* type was never actually
84
84
  // compiled inside librnskia.so. iOS statically links everything into one
85
85
  // binary, so no such mismatch exists there.
86
86
  //
@@ -122,26 +122,30 @@ static facebook::jsi::Value wrapPicture(facebook::jsi::Runtime& rt,
122
122
  if (!skiaApi.isObject()) return facebook::jsi::Value::null();
123
123
  auto pictureFactory = skiaApi.asObject(rt).getProperty(rt, "Picture");
124
124
  if (!pictureFactory.isObject()) return facebook::jsi::Value::null();
125
+ auto pictureFactoryObj = pictureFactory.asObject(rt);
125
126
  auto makePicture =
126
- pictureFactory.asObject(rt).getPropertyAsFunction(rt, "MakePicture");
127
+ pictureFactoryObj.getPropertyAsFunction(rt, "MakePicture");
127
128
 
128
129
  // Mirrors JsiSkPictureFactory::MakePicture's expected argument shape: an
129
130
  // object with a `.buffer` property holding the ArrayBuffer (matching a
130
131
  // Uint8Array-like value; the factory ignores everything else about it).
132
+ //
133
+ // Skia 2.11's NativeState host methods recover the factory via `this`
134
+ // (`fromThis` → "Expected PictureFactory but got non-object" if unbound).
131
135
  jsi::Object arg(rt);
132
136
  arg.setProperty(rt, "buffer", arrayBuffer);
133
- return makePicture.call(rt, arg);
137
+ return makePicture.callWithThis(rt, pictureFactoryObj, arg);
134
138
  }
135
139
  #else
136
- // Shared helper: wraps a fresh picture for return to JS, with memory pressure
137
- // reported to Hermes so GC keeps up under gesture-rate churn.
140
+ // Shared helper: wraps a fresh picture for return to JS. Memory pressure is
141
+ // applied inside JsiSkPicture::create (NativeObject::create reads
142
+ // getMemoryPressure() → picture->approximateBytesUsed()).
138
143
  static facebook::jsi::Value wrapPicture(facebook::jsi::Runtime& rt,
139
144
  const sk_sp<SkPicture>& pic) {
140
145
  if (!pic) return facebook::jsi::Value::null();
141
146
  auto host =
142
147
  std::make_shared<RNSkia::JsiSkPicture>(/*context=*/nullptr, pic);
143
- return JSI_CREATE_HOST_OBJECT_WITH_MEMORY_PRESSURE(rt, host,
144
- /*context=*/nullptr);
148
+ return RNSkia::makeJsiObject(rt, host);
145
149
  }
146
150
  #endif
147
151
 
@@ -0,0 +1,25 @@
1
+ #include "VroomFontMgr.h"
2
+
3
+ #pragma clang diagnostic push
4
+ #pragma clang diagnostic ignored "-Wdocumentation"
5
+ #if defined(__APPLE__)
6
+ #include "include/ports/SkFontMgr_mac_ct.h"
7
+ #elif defined(__ANDROID__)
8
+ #include "include/ports/SkFontMgr_android.h"
9
+ #include "include/ports/SkFontScanner_FreeType.h"
10
+ #endif
11
+ #pragma clang diagnostic pop
12
+
13
+ namespace vroom {
14
+
15
+ sk_sp<SkFontMgr> makePlatformFontMgr() {
16
+ #if defined(__APPLE__)
17
+ return SkFontMgr_New_CoreText(nullptr);
18
+ #elif defined(__ANDROID__)
19
+ return SkFontMgr_New_Android(nullptr, SkFontScanner_Make_FreeType());
20
+ #else
21
+ return nullptr;
22
+ #endif
23
+ }
24
+
25
+ } // namespace vroom
@@ -0,0 +1,14 @@
1
+ #pragma once
2
+
3
+ #pragma clang diagnostic push
4
+ #pragma clang diagnostic ignored "-Wdocumentation"
5
+ #include "include/core/SkFontMgr.h"
6
+ #include "include/core/SkRefCnt.h"
7
+ #pragma clang diagnostic pop
8
+
9
+ namespace vroom {
10
+
11
+ // Returns the platform system font manager, or nullptr if unavailable.
12
+ sk_sp<SkFontMgr> makePlatformFontMgr();
13
+
14
+ } // namespace vroom
@@ -15,7 +15,7 @@
15
15
  #include <memory>
16
16
 
17
17
  #include "VroomChartHostObject.h"
18
- #include "VroomSkiaContext.h"
18
+ #include "VroomFontMgr.h"
19
19
 
20
20
  #include "chart_internal.h"
21
21
 
@@ -26,20 +26,36 @@
26
26
  #include "include/core/SkTypeface.h"
27
27
  #pragma clang diagnostic pop
28
28
 
29
+ #if defined(__ANDROID__)
30
+ #include <android/log.h>
31
+ #define VROOM_WARN(...) \
32
+ __android_log_print(ANDROID_LOG_WARN, "VroomChart", __VA_ARGS__)
33
+ #elif defined(__APPLE__)
34
+ #include <os/log.h>
35
+ #define VROOM_WARN(fmt, ...) \
36
+ os_log_error(OS_LOG_DEFAULT, "VroomChart: " fmt, ##__VA_ARGS__)
37
+ #else
38
+ #define VROOM_WARN(...)
39
+ #endif
40
+
29
41
  namespace vroom {
30
42
 
31
43
  namespace jsi = facebook::jsi;
32
44
 
33
- // Loads the system typeface once via RN-Skia's platform context and hands
34
- // it to the chart core. Retries on each create() until it succeeds (in case
35
- // SkiaApi isn't installed yet when our first chart instance is created).
36
- static void ensureAxisTypeface(jsi::Runtime& runtime) {
45
+ // Loads the system typeface once via the platform SkFontMgr and hands it to
46
+ // the chart core. Built only once constructing a font manager enumerates
47
+ // the system font set and is not cheap. Failure is logged once; we do not
48
+ // retry, because this no longer depends on RN-Skia's JSI bindings appearing.
49
+ static void ensureAxisTypeface() {
37
50
  static bool done = false;
38
51
  if (done) return;
39
- auto ctx = vroom::getRNSkContext(runtime);
40
- if (!ctx) return;
41
- auto mgr = ctx->createFontMgr();
42
- if (!mgr) return;
52
+ done = true;
53
+
54
+ auto mgr = vroom::makePlatformFontMgr();
55
+ if (!mgr) {
56
+ VROOM_WARN("axis typeface: platform font manager unavailable");
57
+ return;
58
+ }
43
59
 
44
60
  // A null family name is meant to request "the platform default" — CoreText
45
61
  // (iOS) honors that, but Android's SkFontMgr_New_Android does not: it
@@ -51,12 +67,16 @@ static void ensureAxisTypeface(jsi::Runtime& runtime) {
51
67
  if (!tf) {
52
68
  tf = mgr->matchFamilyStyle("sans-serif", SkFontStyle());
53
69
  }
54
- if (!tf) return;
70
+ if (!tf) {
71
+ VROOM_WARN("axis typeface: no default or sans-serif typeface");
72
+ return;
73
+ }
55
74
  vroom::set_axis_typeface(tf);
56
- done = true;
57
75
  }
58
76
 
59
77
  void installJsi(jsi::Runtime& runtime) {
78
+ ensureAxisTypeface();
79
+
60
80
  auto create = jsi::Function::createFromHostFunction(
61
81
  runtime,
62
82
  jsi::PropNameID::forAscii(runtime, "create"),
@@ -65,7 +85,6 @@ void installJsi(jsi::Runtime& runtime) {
65
85
  const jsi::Value& /*thisVal*/,
66
86
  const jsi::Value* /*args*/,
67
87
  size_t /*count*/) -> jsi::Value {
68
- ensureAxisTypeface(rt);
69
88
  auto host = std::make_shared<ChartHostObject>();
70
89
  return jsi::Object::createFromHostObject(rt, host);
71
90
  });
@@ -23,8 +23,8 @@ sk_sp<SkPicture> render_chart_picture(VroomChart* chart);
23
23
  bool is_animating(VroomChart* chart);
24
24
 
25
25
  // Sets the typeface used for axis labels. Bridge layer calls this once at
26
- // startup with a system typeface loaded via RN-Skia's font manager. Labels
27
- // are skipped if no typeface is set.
26
+ // startup with a system typeface from the platform SkFontMgr. Labels are
27
+ // skipped if no typeface is set.
28
28
  void set_axis_typeface(sk_sp<SkTypeface> typeface);
29
29
 
30
30
  } // namespace vroom
@@ -1,8 +1,8 @@
1
1
  // Global typeface for axis/label text rendering.
2
2
  //
3
- // Loaded once at startup by the bridge layer (which uses RN-Skia's platform-
4
- // context font manager to grab the system default). Held as a process-global
5
- // so all charts share it; per-chart font configuration is not modeled yet.
3
+ // Loaded once at startup by the bridge layer (which constructs a platform
4
+ // SkFontMgr and grabs the system default). Held as a process-global so all
5
+ // charts share it; per-chart font configuration is not modeled yet.
6
6
 
7
7
  #pragma once
8
8
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-vroom-chart",
3
- "version": "0.9.1",
3
+ "version": "0.10.0",
4
4
  "description": "Mobile-first Skia candlestick chart for React Native",
5
5
  "license": "MIT",
6
6
  "author": "Darion Welch",
@@ -59,20 +59,21 @@
59
59
  "access": "public"
60
60
  },
61
61
  "peerDependencies": {
62
- "@shopify/react-native-skia": ">=2.0.0",
62
+ "@shopify/react-native-skia": ">=2.11.0",
63
63
  "react": "*",
64
64
  "react-native": "*",
65
65
  "react-native-gesture-handler": ">=2.16.0",
66
- "react-native-reanimated": ">=4.0.0"
66
+ "react-native-reanimated": ">=4.0.0",
67
+ "react-native-worklets": ">=0.7.0"
67
68
  },
68
69
  "devDependencies": {
69
- "@shopify/react-native-skia": "2.2.12",
70
+ "@shopify/react-native-skia": "2.11.0",
70
71
  "@types/react": "~19.1.0",
71
72
  "react": "19.1.0",
72
73
  "react-native": "0.81.5",
73
74
  "react-native-gesture-handler": "~2.28.0",
74
75
  "react-native-reanimated": "~4.1.7",
75
- "react-native-worklets": "0.5.1",
76
+ "react-native-worklets": "0.7.4",
76
77
  "tsup": "^8.5.1",
77
78
  "typescript": "~5.9.2",
78
79
  "vitest": "^3.2.6",
@@ -10,6 +10,11 @@ skia_pkg_json = `node --print "require.resolve('@shopify/react-native-skia/packa
10
10
  skia_src_dir = File.dirname(skia_pkg_json)
11
11
  skia_cpp_dir = File.join(skia_src_dir, "cpp")
12
12
  skia_skia_dir = File.join(skia_src_dir, "cpp", "skia")
13
+ skia_api_dir = File.join(skia_src_dir, "cpp", "api")
14
+ use_graphite = File.exist?(File.join(skia_src_dir, "libs", ".graphite"))
15
+ skia_preprocessor_defs = use_graphite ?
16
+ "$(inherited) SK_GRAPHITE=1 SK_IMAGE_READ_PIXELS_DISABLE_LEGACY_API=1 SK_DISABLE_LEGACY_SHAPER_FACTORY=1" :
17
+ "$(inherited) SK_METAL=1 SK_GANESH=1 SK_IMAGE_READ_PIXELS_DISABLE_LEGACY_API=1 SK_DISABLE_LEGACY_SHAPER_FACTORY=1"
13
18
 
14
19
  Pod::Spec.new do |s|
15
20
  s.name = "react-native-vroom-chart"
@@ -60,12 +65,13 @@ Pod::Spec.new do |s|
60
65
  '"$(PODS_TARGET_SRCROOT)/cpp"',
61
66
  "\"#{skia_cpp_dir}\"",
62
67
  "\"#{skia_skia_dir}\"",
68
+ "\"#{skia_api_dir}\"",
63
69
  ].join(" "),
64
- "CLANG_CXX_LANGUAGE_STANDARD" => "c++17",
65
- # Match RN-Skia's Skia build flags so our SkCanvas/SkPicture code sees the
66
- # same SK_METAL / SK_GANESH defines and doesn't mis-link.
67
- "GCC_PREPROCESSOR_DEFINITIONS" =>
68
- "$(inherited) SK_METAL=1 SK_GANESH=1 SK_DISABLE_LEGACY_SHAPER_FACTORY=1",
70
+ "CLANG_CXX_LANGUAGE_STANDARD" => "c++20",
71
+ # Match RN-Skia's Skia build flags (Ganesh vs Graphite, plus the
72
+ # SK_IMAGE_READ_PIXELS_DISABLE_LEGACY_API define both branches set) so our
73
+ # SkCanvas/SkPicture code sees the same ABI and doesn't mis-link.
74
+ "GCC_PREPROCESSOR_DEFINITIONS" => skia_preprocessor_defs,
69
75
  }
70
76
 
71
77
  s.dependency "React-Core"
@@ -1,33 +0,0 @@
1
- #include "VroomSkiaContext.h"
2
-
3
- namespace vroom {
4
-
5
- namespace jsi = facebook::jsi;
6
-
7
- std::shared_ptr<RNSkia::RNSkPlatformContext> getRNSkContext(
8
- jsi::Runtime& runtime) {
9
- auto val = runtime.global().getProperty(runtime, "SkiaApi");
10
- if (!val.isObject()) return nullptr;
11
- auto obj = val.asObject(runtime);
12
- if (!obj.isHostObject(runtime)) return nullptr;
13
- auto host = obj.asHostObject(runtime);
14
-
15
- // `global.SkiaApi` is always installed by RN-Skia's own
16
- // RNSkManager::installBindings() as exactly a `JsiSkApi` host object — never
17
- // any other JsiSkHostObject subclass — so this cast is safe as a plain
18
- // static_cast, without any RTTI check. We deliberately avoid
19
- // dynamic_pointer_cast here: on Android, RN-Skia is compiled into its own
20
- // librnskia.so, separate from libvroomchart.so, so a `JsiSkApi` constructed
21
- // by librnskia.so's code carries typeinfo from that .so; a dynamic_cast
22
- // performed here (in libvroomchart.so) sees an unmerged RTTI record for
23
- // "the same" class and always fails, even though the object is perfectly
24
- // valid to use. (Same underlying issue as the JsiSkPicture cross-.so cast
25
- // documented in VroomChartHostObject.cpp — but there RN-Skia's own compiled
26
- // code does the cast on an object *we* construct, so we can't avoid it;
27
- // here *we* do the cast on an object *they* construct, so we can just skip
28
- // the runtime check we don't need.)
29
- auto* accessor = static_cast<HostObjectAccessor*>(host.get());
30
- return accessor->getContext();
31
- }
32
-
33
- } // namespace vroom
@@ -1,30 +0,0 @@
1
- // Helpers for reaching into RN-Skia's installed JSI bindings to grab its
2
- // RNSkPlatformContext — which we need so we can use JsiSkFontMgrFactory to
3
- // get the system font manager cross-platform.
4
- //
5
- // JsiSkHostObject::getContext() is protected, so we use a "friend helper"
6
- // subclass that exposes it via `using`. We never construct an instance —
7
- // we just static_cast existing host objects to the accessor type.
8
-
9
- #pragma once
10
-
11
- #include <memory>
12
-
13
- #include <jsi/jsi.h>
14
-
15
- #include "JsiSkHostObjects.h" // RNSkia::JsiSkHostObject
16
- #include "RNSkPlatformContext.h" // RNSkia::RNSkPlatformContext
17
-
18
- namespace vroom {
19
-
20
- class HostObjectAccessor : public RNSkia::JsiSkHostObject {
21
- public:
22
- using RNSkia::JsiSkHostObject::getContext;
23
- };
24
-
25
- // Returns the RN-Skia platform context by reading global.SkiaApi.
26
- // Returns nullptr if SkiaApi isn't on the global yet (RN-Skia not installed).
27
- std::shared_ptr<RNSkia::RNSkPlatformContext> getRNSkContext(
28
- facebook::jsi::Runtime& runtime);
29
-
30
- } // namespace vroom