From 20e5203d163fd9e0c3e7b7ddafaa09cddc995592 Mon Sep 17 00:00:00 2001 From: William Candillon Date: Thu, 11 Dec 2025 09:13:18 +0100 Subject: [PATCH 1/4] =?UTF-8?q?chore(=F0=9F=90=99):=20Enable=20m144=20for?= =?UTF-8?q?=20Ganesh?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/skia/package.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/skia/package.json b/packages/skia/package.json index ada899bba2..3f5dbf961d 100644 --- a/packages/skia/package.json +++ b/packages/skia/package.json @@ -10,13 +10,13 @@ "title": "React Native Skia", "version": "0.0.0", "skia": { - "version": "m142", + "version": "m144", "checksums": { - "android-armeabi-v7a": "5ae357eb26d9ec93797bacbc865b86eff6ac54b3a9466a1b0c41c27018cc3150", - "android-arm64-v8a": "c310bd5261de1ab5f4afdb05aa51bf0f56d625f6285617e149af517a2d375e52", - "android-x86": "26a18ade07de8318be2ec82bd82a1d7f2709893462229d70f0d353c6c8691cc3", - "android-x86_64": "46a5576b6f8e7d1372c27576ad750384c2d6f892bfe3fc0ee260c68d08ebd844", - "apple-xcframeworks": "343fad71c01e1f8331d4421c7efe0ae6dff3a7d25361c466dcda7dc3737cdd5c" + "android-armeabi-v7a": "8f7f83a4ae210cc28007f2c226d754e9ef7c929822ef60c56a4ccc134e726148", + "android-arm64-v8a": "5d799d988a6cbc0cb844a92882a2458e67990314791ea38f928d83d5e01cb828", + "android-x86": "711fbee4a1721b92604daa78fa6f8c7ce4a6a6c901973919bd030848adc4f81d", + "android-x86_64": "fe068aa70b5c8bcf991cfe99e9db38617aaeaf92defd08bc4f89384b4d4dbed4", + "apple-xcframeworks": "63e2789bd75236b16779abc7171b351aa50cd01c13f05b3e1d83b34336ceb77c" } }, "skia-graphite": { From 828a2da4753e2b2028869f5f11926ca0da8e3a9b Mon Sep 17 00:00:00 2001 From: William Candillon Date: Thu, 11 Dec 2025 13:29:59 +0100 Subject: [PATCH 2/4] :green_heart: --- .../__tests__/e2e/AnimatedImages.spec.tsx | 2 +- .../__tests__/e2e/DataEncoding.spec.tsx | 25 ++++++++----------- 2 files changed, 11 insertions(+), 16 deletions(-) diff --git a/packages/skia/src/renderer/__tests__/e2e/AnimatedImages.spec.tsx b/packages/skia/src/renderer/__tests__/e2e/AnimatedImages.spec.tsx index bca7a3e35b..6d4bb4a3d0 100644 --- a/packages/skia/src/renderer/__tests__/e2e/AnimatedImages.spec.tsx +++ b/packages/skia/src/renderer/__tests__/e2e/AnimatedImages.spec.tsx @@ -44,7 +44,7 @@ describe("Animated Images", () => { return frame.encodeToBase64(); }); expect(result).toEqual( - "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAAAXNSR0IArs4c6QAAAANzQklUCAgI2+FP4AAAAAxJREFUCJljSFwlDwACmgErMAA+hwAAAABJRU5ErkJggg==" + "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAAA3NCSVQICAjb4U/gAAAAAXNSR0IArs4c6QAAAAxJREFUCJljSFwlDwACmgErMAA+hwAAAABJRU5ErkJggg==" ); }); it("should decode the 3rd frame of the GIF", async () => { diff --git a/packages/skia/src/renderer/__tests__/e2e/DataEncoding.spec.tsx b/packages/skia/src/renderer/__tests__/e2e/DataEncoding.spec.tsx index 4366bec9f2..42e5b169bd 100644 --- a/packages/skia/src/renderer/__tests__/e2e/DataEncoding.spec.tsx +++ b/packages/skia/src/renderer/__tests__/e2e/DataEncoding.spec.tsx @@ -1,4 +1,4 @@ -import { importSkia, surface } from "../setup"; +import { surface } from "../setup"; describe("Data Encoding", () => { it("encodeToBytes() from CPU image", async () => { @@ -12,13 +12,13 @@ describe("Data Encoding", () => { } return Array.from(img.encodeToBytes()); }); - const { Skia } = importSkia(); - const data = Skia.Data.fromBase64( - "R0lGODlhAQABAIAAAGGqHwAAACH5BAAAAAAALAAAAAABAAEAAAICRAEAOw==" - ); - const img = Skia.Image.MakeImageFromEncoded(data)!; - expect(img).toBeTruthy(); - expect(result).toEqual(Array.from(img.encodeToBytes())); + expect(result).toEqual([ + 137, 80, 78, 71, 13, 10, 26, 10, 0, 0, 0, 13, 73, 72, 68, 82, 0, 0, 0, 1, + 0, 0, 0, 1, 8, 2, 0, 0, 0, 144, 119, 83, 222, 0, 0, 0, 3, 115, 66, 73, 84, + 8, 8, 8, 219, 225, 79, 224, 0, 0, 0, 1, 115, 82, 71, 66, 0, 174, 206, 28, + 233, 0, 0, 0, 12, 73, 68, 65, 84, 8, 153, 99, 72, 92, 37, 15, 0, 2, 154, + 1, 43, 48, 0, 62, 135, 0, 0, 0, 0, 73, 69, 78, 68, 174, 66, 96, 130, + ]); }); it("encodeToBase64() from CPU image", async () => { const result = await surface.eval((Skia) => { @@ -31,14 +31,9 @@ describe("Data Encoding", () => { } return img.encodeToBase64(); }); - const { Skia } = importSkia(); - - const data = Skia.Data.fromBase64( - "R0lGODlhAQABAIAAAGGqHwAAACH5BAAAAAAALAAAAAABAAEAAAICRAEAOw==" + expect(result).toEqual( + "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAAA3NCSVQICAjb4U/gAAAAAXNSR0IArs4c6QAAAAxJREFUCJljSFwlDwACmgErMAA+hwAAAABJRU5ErkJggg==" ); - const img = Skia.Image.MakeImageFromEncoded(data)!; - expect(img).toBeTruthy(); - expect(result).toEqual(img.encodeToBase64()); }); it("encodeToBytes() from GPU image", async () => { const result = await surface.eval((Skia) => { From 9d379b9a855dd69c7bc36884563b29c7ebe5ab22 Mon Sep 17 00:00:00 2001 From: William Candillon Date: Thu, 11 Dec 2025 14:41:50 +0100 Subject: [PATCH 3/4] =?UTF-8?q?fix(=F0=9F=8D=8F):=20Add=20missing=20system?= =?UTF-8?q?=20apple=20fonts=20in=20listFontFamilies?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/example/src/Examples/API/FontMgr.tsx | 8 +- .../android/cpp/jni/JniPlatformContext.cpp | 2 +- .../skia/apple/RNSkApplePlatformContext.h | 2 + .../skia/apple/RNSkApplePlatformContext.mm | 42 +++++++++ packages/skia/apple/SkiaCVPixelBufferUtils.mm | 12 ++- packages/skia/cpp/api/JsiSkFontMgr.h | 26 ++++-- packages/skia/cpp/api/JsiSkPictureFactory.h | 15 ++-- packages/skia/cpp/api/recorder/RNRecorder.h | 88 +++++++------------ .../skia/cpp/rnskia/RNSkPlatformContext.h | 7 ++ packages/skia/src/skia/core/Font.ts | 23 ++++- 10 files changed, 144 insertions(+), 81 deletions(-) diff --git a/apps/example/src/Examples/API/FontMgr.tsx b/apps/example/src/Examples/API/FontMgr.tsx index 3e937c7e97..0ff0c9d9bf 100644 --- a/apps/example/src/Examples/API/FontMgr.tsx +++ b/apps/example/src/Examples/API/FontMgr.tsx @@ -2,8 +2,8 @@ import React from "react"; import { ScrollView, useWindowDimensions } from "react-native"; import { Canvas, - Skia, Text, + listFontFamilies, matchFont, useFonts, } from "@shopify/react-native-skia"; @@ -16,11 +16,7 @@ const titleText = "Fonts from the System"; const titleY = titleFontSize + PADDING; const subtitleY = titleY + 14 + PADDING; -const fontMgr = Skia.FontMgr.System(); -const familyNames = new Array(fontMgr.countFamilies()) - .fill(0) - .map((_, i) => fontMgr.getFamilyName(i)); - +const familyNames = listFontFamilies(); const title2Y = subtitleY + 16 * familyNames.length + PADDING + titleFontSize; export const FontMgr = () => { diff --git a/packages/skia/android/cpp/jni/JniPlatformContext.cpp b/packages/skia/android/cpp/jni/JniPlatformContext.cpp index 3557955654..87d1b1e449 100644 --- a/packages/skia/android/cpp/jni/JniPlatformContext.cpp +++ b/packages/skia/android/cpp/jni/JniPlatformContext.cpp @@ -207,7 +207,7 @@ void JniPlatformContext::notifyTaskReadyExternal() { jni::ThreadScope ts; static auto method = - javaPart_->getClass()->getMethod("notifyTaskReady"); + javaPart_->getClass()->getMethod("notifyTaskReady"); method(javaPart_.get()); } diff --git a/packages/skia/apple/RNSkApplePlatformContext.h b/packages/skia/apple/RNSkApplePlatformContext.h index f1c6f5a83c..0d5d5b8f92 100644 --- a/packages/skia/apple/RNSkApplePlatformContext.h +++ b/packages/skia/apple/RNSkApplePlatformContext.h @@ -73,6 +73,8 @@ class RNSkApplePlatformContext : public RNSkPlatformContext { sk_sp createFontMgr() override; + std::vector getSystemFontFamilies() override; + private: ViewScreenshotService *_screenshotService; diff --git a/packages/skia/apple/RNSkApplePlatformContext.mm b/packages/skia/apple/RNSkApplePlatformContext.mm index c61cca9a1c..9be209d7fd 100644 --- a/packages/skia/apple/RNSkApplePlatformContext.mm +++ b/packages/skia/apple/RNSkApplePlatformContext.mm @@ -3,6 +3,7 @@ #import #include #import +#include #include #include @@ -300,6 +301,47 @@ return SkFontMgr_New_CoreText(nullptr); } +std::vector RNSkApplePlatformContext::getSystemFontFamilies() { + std::vector families; + + // List of system UI font types to check + CTFontUIFontType fontTypes[] = { + kCTFontUIFontUser, kCTFontUIFontUserFixedPitch, + kCTFontUIFontSystem, kCTFontUIFontEmphasizedSystem, + kCTFontUIFontSmallSystem, kCTFontUIFontSmallEmphasizedSystem, + kCTFontUIFontMiniSystem, kCTFontUIFontMiniEmphasizedSystem, + kCTFontUIFontLabel, kCTFontUIFontMessage, + kCTFontUIFontToolTip, + }; + + std::set uniqueFamilies; + + for (CTFontUIFontType fontType : fontTypes) { + CTFontRef font = CTFontCreateUIFontForLanguage(fontType, 12.0, nullptr); + if (font) { + CFStringRef familyName = CTFontCopyFamilyName(font); + if (familyName) { + const char *cstr = + CFStringGetCStringPtr(familyName, kCFStringEncodingUTF8); + if (cstr) { + uniqueFamilies.insert(std::string(cstr)); + } else { + char buffer[256]; + if (CFStringGetCString(familyName, buffer, sizeof(buffer), + kCFStringEncodingUTF8)) { + uniqueFamilies.insert(std::string(buffer)); + } + } + CFRelease(familyName); + } + CFRelease(font); + } + } + + families.assign(uniqueFamilies.begin(), uniqueFamilies.end()); + return families; +} + void RNSkApplePlatformContext::runOnMainThread(std::function func) { dispatch_async(dispatch_get_main_queue(), ^{ func(); diff --git a/packages/skia/apple/SkiaCVPixelBufferUtils.mm b/packages/skia/apple/SkiaCVPixelBufferUtils.mm index d477acdf1b..1cc8e108a5 100644 --- a/packages/skia/apple/SkiaCVPixelBufferUtils.mm +++ b/packages/skia/apple/SkiaCVPixelBufferUtils.mm @@ -30,12 +30,16 @@ #include #if TARGET_RT_BIG_ENDIAN #define FourCC2Str(fourcc) \ - (const char[]){*((char *)&fourcc), *(((char *)&fourcc) + 1), \ - *(((char *)&fourcc) + 2), *(((char *)&fourcc) + 3), 0} + (const char[]) { \ + *((char *)&fourcc), *(((char *)&fourcc) + 1), *(((char *)&fourcc) + 2), \ + *(((char *)&fourcc) + 3), 0 \ + } #else #define FourCC2Str(fourcc) \ - (const char[]){*(((char *)&fourcc) + 3), *(((char *)&fourcc) + 2), \ - *(((char *)&fourcc) + 1), *(((char *)&fourcc) + 0), 0} + (const char[]) { \ + *(((char *)&fourcc) + 3), *(((char *)&fourcc) + 2), \ + *(((char *)&fourcc) + 1), *(((char *)&fourcc) + 0), 0 \ + } #endif // pragma MARK: TextureHolder diff --git a/packages/skia/cpp/api/JsiSkFontMgr.h b/packages/skia/cpp/api/JsiSkFontMgr.h index 40ce60655a..e373a30cef 100644 --- a/packages/skia/cpp/api/JsiSkFontMgr.h +++ b/packages/skia/cpp/api/JsiSkFontMgr.h @@ -27,15 +27,28 @@ class JsiSkFontMgr : public JsiSkWrappingSkPtrHostObject { JsiSkFontMgr(std::shared_ptr context, sk_sp fontMgr) - : JsiSkWrappingSkPtrHostObject(std::move(context), fontMgr) {} + : JsiSkWrappingSkPtrHostObject(context, fontMgr), + _systemFontFamilies(context->getSystemFontFamilies()) {} - JSI_HOST_FUNCTION(countFamilies) { return getObject()->countFamilies(); } + JSI_HOST_FUNCTION(countFamilies) { + return static_cast(getObject()->countFamilies() + + _systemFontFamilies.size()); + } JSI_HOST_FUNCTION(getFamilyName) { auto i = static_cast(arguments[0].asNumber()); - SkString name; - getObject()->getFamilyName(i, &name); - return jsi::String::createFromUtf8(runtime, name.c_str()); + auto baseFamilyCount = getObject()->countFamilies(); + if (i < baseFamilyCount) { + SkString name; + getObject()->getFamilyName(i, &name); + return jsi::String::createFromUtf8(runtime, name.c_str()); + } + auto systemIndex = i - baseFamilyCount; + if (systemIndex < static_cast(_systemFontFamilies.size())) { + return jsi::String::createFromUtf8(runtime, + _systemFontFamilies[systemIndex]); + } + return jsi::String::createFromUtf8(runtime, ""); } JSI_HOST_FUNCTION(matchFamilyStyle) { @@ -55,6 +68,9 @@ class JsiSkFontMgr : public JsiSkWrappingSkPtrHostObject { JSI_EXPORT_FUNCTIONS(JSI_EXPORT_FUNC(JsiSkFontMgr, countFamilies), JSI_EXPORT_FUNC(JsiSkFontMgr, getFamilyName), JSI_EXPORT_FUNC(JsiSkFontMgr, matchFamilyStyle)) + +private: + std::vector _systemFontFamilies; }; } // namespace RNSkia diff --git a/packages/skia/cpp/api/JsiSkPictureFactory.h b/packages/skia/cpp/api/JsiSkPictureFactory.h index 6fb2e20851..f23047ab39 100644 --- a/packages/skia/cpp/api/JsiSkPictureFactory.h +++ b/packages/skia/cpp/api/JsiSkPictureFactory.h @@ -50,12 +50,15 @@ class JsiSkPictureFactory : public JsiSkHostObject { } } - // Get ArrayBuffer - try buffer property first (Uint8Array, etc.), then direct ArrayBuffer - jsi::ArrayBuffer buffer = obj.hasProperty(runtime, jsi::PropNameID::forAscii(runtime, "buffer")) - ? obj.getProperty(runtime, jsi::PropNameID::forAscii(runtime, "buffer")) - .asObject(runtime) - .getArrayBuffer(runtime) - : obj.getArrayBuffer(runtime); + // Get ArrayBuffer - try buffer property first (Uint8Array, etc.), then + // direct ArrayBuffer + jsi::ArrayBuffer buffer = + obj.hasProperty(runtime, jsi::PropNameID::forAscii(runtime, "buffer")) + ? obj.getProperty(runtime, + jsi::PropNameID::forAscii(runtime, "buffer")) + .asObject(runtime) + .getArrayBuffer(runtime) + : obj.getArrayBuffer(runtime); sk_sp data = SkData::MakeWithCopy(buffer.data(runtime), buffer.size(runtime)); diff --git a/packages/skia/cpp/api/recorder/RNRecorder.h b/packages/skia/cpp/api/recorder/RNRecorder.h index 8eb7bc9993..d96331f088 100644 --- a/packages/skia/cpp/api/recorder/RNRecorder.h +++ b/packages/skia/cpp/api/recorder/RNRecorder.h @@ -78,8 +78,8 @@ class Recorder { for (const auto &child : group->children) { if (child->type == CommandType::Group) { auto *childGroup = static_cast(child.get()); - pending.push_back( - {childGroup, sanitizeZIndex(childGroup), static_cast(pending.size())}); + pending.push_back({childGroup, sanitizeZIndex(childGroup), + static_cast(pending.size())}); } else { flushPendingGroups(ctx, pending); playCommand(ctx, child.get()); @@ -94,9 +94,7 @@ class Recorder { std::shared_ptr _context; Variables variables; - Recorder() { - commandStack.push_back(&commands); - } + Recorder() { commandStack.push_back(&commands); } ~Recorder() { if (!_context || commands.empty()) { return; @@ -128,14 +126,11 @@ class Recorder { pushCommand( std::make_unique(runtime, props, variables)); } else if (nodeType == "skColorShader") { - pushCommand( - std::make_unique(runtime, props, variables)); + pushCommand(std::make_unique(runtime, props, variables)); } else if (nodeType == "skTurbulence") { - pushCommand( - std::make_unique(runtime, props, variables)); + pushCommand(std::make_unique(runtime, props, variables)); } else if (nodeType == "skFractalNoise") { - pushCommand( - std::make_unique(runtime, props, variables)); + pushCommand(std::make_unique(runtime, props, variables)); } else if (nodeType == "skLinearGradient") { pushCommand( std::make_unique(runtime, props, variables)); @@ -146,12 +141,11 @@ class Recorder { pushCommand( std::make_unique(runtime, props, variables)); } else if (nodeType == "skTwoPointConicalGradient") { - pushCommand(std::make_unique( - runtime, props, variables)); + pushCommand(std::make_unique(runtime, props, + variables)); // TODO: should receive skBlendShader here } else if (nodeType == "skBlend") { - pushCommand( - std::make_unique(runtime, props, variables)); + pushCommand(std::make_unique(runtime, props, variables)); } } @@ -216,41 +210,36 @@ class Recorder { pushCommand( std::make_unique(runtime, props, variables)); } else if (nodeType == "skDropShadowImageFilter") { - pushCommand(std::make_unique( - runtime, props, variables)); + pushCommand(std::make_unique(runtime, props, + variables)); } else if (nodeType == "skMorphologyImageFilter") { - pushCommand(std::make_unique( - runtime, props, variables)); + pushCommand(std::make_unique(runtime, props, + variables)); } else if (nodeType == "skBlendImageFilter") { pushCommand( std::make_unique(runtime, props, variables)); } else if (nodeType == "skRuntimeShaderImageFilter") { - pushCommand(std::make_unique( - runtime, props, variables)); + pushCommand(std::make_unique(runtime, props, + variables)); } else if (nodeType == "skImageFilter") { - pushCommand( - std::make_unique(runtime, props, variables)); + pushCommand(std::make_unique(runtime, props, variables)); } } void composePathEffect() { - pushCommand( - std::make_unique(CommandType::ComposePathEffect)); + pushCommand(std::make_unique(CommandType::ComposePathEffect)); } void composeImageFilter() { - pushCommand( - std::make_unique(CommandType::ComposeImageFilter)); + pushCommand(std::make_unique(CommandType::ComposeImageFilter)); } void composeColorFilter() { - pushCommand( - std::make_unique(CommandType::ComposeColorFilter)); + pushCommand(std::make_unique(CommandType::ComposeColorFilter)); } void pushBlurMaskFilter(jsi::Runtime &runtime, const jsi::Object &props) { - pushCommand( - std::make_unique(runtime, props, variables)); + pushCommand(std::make_unique(runtime, props, variables)); } void saveCTM(jsi::Runtime &runtime, const jsi::Object &props) { @@ -278,8 +267,7 @@ class Recorder { } void drawTextPath(jsi::Runtime &runtime, const jsi::Object &props) { - pushCommand( - std::make_unique(runtime, props, variables)); + pushCommand(std::make_unique(runtime, props, variables)); } void drawText(jsi::Runtime &runtime, const jsi::Object &props) { @@ -296,8 +284,7 @@ class Recorder { void drawBox(jsi::Runtime &runtime, const jsi::Object &props, const jsi::Array &shadows) { - pushCommand( - std::make_unique(runtime, props, shadows, variables)); + pushCommand(std::make_unique(runtime, props, shadows, variables)); } void drawImage(jsi::Runtime &runtime, const jsi::Object &props) { @@ -322,18 +309,15 @@ class Recorder { } void drawVertices(jsi::Runtime &runtime, const jsi::Object &props) { - pushCommand( - std::make_unique(runtime, props, variables)); + pushCommand(std::make_unique(runtime, props, variables)); } void drawDiffRect(jsi::Runtime &runtime, const jsi::Object &props) { - pushCommand( - std::make_unique(runtime, props, variables)); + pushCommand(std::make_unique(runtime, props, variables)); } void drawTextBlob(jsi::Runtime &runtime, const jsi::Object &props) { - pushCommand( - std::make_unique(runtime, props, variables)); + pushCommand(std::make_unique(runtime, props, variables)); } void drawGlyphs(jsi::Runtime &runtime, const jsi::Object &props) { @@ -346,13 +330,11 @@ class Recorder { } void drawImageSVG(jsi::Runtime &runtime, const jsi::Object &props) { - pushCommand( - std::make_unique(runtime, props, variables)); + pushCommand(std::make_unique(runtime, props, variables)); } void drawParagraph(jsi::Runtime &runtime, const jsi::Object &props) { - pushCommand( - std::make_unique(runtime, props, variables)); + pushCommand(std::make_unique(runtime, props, variables)); } void drawAtlas(jsi::Runtime &runtime, const jsi::Object &props) { @@ -365,8 +347,7 @@ class Recorder { } void materializePaint() { - pushCommand( - std::make_unique(CommandType::MaterializePaint)); + pushCommand(std::make_unique(CommandType::MaterializePaint)); } void restorePaintDeclaration() { @@ -379,11 +360,11 @@ class Recorder { } void saveBackdropFilter() { - pushCommand( - std::make_unique(CommandType::SaveBackdropFilter)); + pushCommand(std::make_unique(CommandType::SaveBackdropFilter)); } - void saveGroup(jsi::Runtime &runtime, const jsi::Value *propsValue = nullptr) { + void saveGroup(jsi::Runtime &runtime, + const jsi::Value *propsValue = nullptr) { auto group = std::make_unique(); if (propsValue != nullptr && propsValue->isObject()) { auto object = propsValue->asObject(runtime); @@ -400,7 +381,6 @@ class Recorder { } } - void play(DrawingCtx *ctx) { for (const auto &cmd : commands) { playCommand(ctx, cmd.get()); @@ -497,8 +477,7 @@ inline void Recorder::playCommand(DrawingCtx *ctx, Command *cmd) { auto *offsetCmd = static_cast(cmd); offsetCmd->pushImageFilter(ctx); } else if (nodeType == "skDisplacementMapImageFilter") { - auto *displacementCmd = - static_cast(cmd); + auto *displacementCmd = static_cast(cmd); displacementCmd->pushImageFilter(ctx); } else if (nodeType == "skBlurImageFilter") { auto *blurCmd = static_cast(cmd); @@ -513,8 +492,7 @@ inline void Recorder::playCommand(DrawingCtx *ctx, Command *cmd) { auto *blendCmd = static_cast(cmd); blendCmd->pushImageFilter(ctx); } else if (nodeType == "skRuntimeShaderImageFilter") { - auto *runtimeShaderCmd = - static_cast(cmd); + auto *runtimeShaderCmd = static_cast(cmd); runtimeShaderCmd->pushImageFilter(ctx); } else if (nodeType == "skImageFilter") { auto *imageFilterCmd = static_cast(cmd); diff --git a/packages/skia/cpp/rnskia/RNSkPlatformContext.h b/packages/skia/cpp/rnskia/RNSkPlatformContext.h index 1a23181756..6de41e51d9 100644 --- a/packages/skia/cpp/rnskia/RNSkPlatformContext.h +++ b/packages/skia/cpp/rnskia/RNSkPlatformContext.h @@ -8,6 +8,7 @@ #include #include #include +#include #include "RNSkVideo.h" #include "RNWindowContext.h" @@ -145,6 +146,12 @@ class RNSkPlatformContext { */ virtual sk_sp createFontMgr() = 0; + /** + * Return platform-specific system font family names that aren't + * enumerated by the standard font manager (e.g., .AppleSystemUIFont on iOS) + */ + virtual std::vector getSystemFontFamilies() { return {}; } + /** * Creates an skImage containing the screenshot of a native view and its * children. diff --git a/packages/skia/src/skia/core/Font.ts b/packages/skia/src/skia/core/Font.ts index 7fc8296840..5e64fa583d 100644 --- a/packages/skia/src/skia/core/Font.ts +++ b/packages/skia/src/skia/core/Font.ts @@ -95,10 +95,25 @@ export const matchFont = ( return Skia.Font(typeface, fontStyle.fontSize); }; -export const listFontFamilies = (fontMgr: SkFontMgr = Skia.FontMgr.System()) => - new Array(fontMgr.countFamilies()) - .fill(0) - .map((_, i) => fontMgr.getFamilyName(i)); +export const listFontFamilies = ( + fontMgr: SkFontMgr = Skia.FontMgr.System() +) => { + const families = new Set(); + for (let i = 0; i < fontMgr.countFamilies(); i++) { + families.add(fontMgr.getFamilyName(i)); + } + return [...families].sort((a, b) => { + const aStartsWithAlpha = /^[a-zA-Z]/.test(a); + const bStartsWithAlpha = /^[a-zA-Z]/.test(b); + if (aStartsWithAlpha && !bStartsWithAlpha) { + return 1; + } + if (!aStartsWithAlpha && bStartsWithAlpha) { + return -1; + } + return a.localeCompare(b); + }); +}; const loadTypefaces = (typefacesToLoad: Record) => { const promises = Object.keys(typefacesToLoad).flatMap((familyName) => { From 6fab3d52301ff3886014e54190adc4556c5f3d3c Mon Sep 17 00:00:00 2001 From: William Candillon Date: Thu, 11 Dec 2025 14:56:54 +0100 Subject: [PATCH 4/4] Implement review comments --- packages/skia/apple/RNSkApplePlatformContext.mm | 6 +++++- packages/skia/cpp/api/JsiSkFontMgr.h | 6 +++++- packages/skia/src/skia/core/Font.ts | 12 +----------- 3 files changed, 11 insertions(+), 13 deletions(-) diff --git a/packages/skia/apple/RNSkApplePlatformContext.mm b/packages/skia/apple/RNSkApplePlatformContext.mm index 9be209d7fd..8effaafc1a 100644 --- a/packages/skia/apple/RNSkApplePlatformContext.mm +++ b/packages/skia/apple/RNSkApplePlatformContext.mm @@ -304,7 +304,11 @@ std::vector RNSkApplePlatformContext::getSystemFontFamilies() { std::vector families; - // List of system UI font types to check + // System UI fonts (e.g., .AppleSystemUIFont) are not enumerated by Skia's + // font manager. We retrieve them via Core Text's CTFontUIFontType constants. + // This list covers common system font types as of iOS 17 / macOS 14. + // Apple may add new CTFontUIFontType values in future OS versions, + // so this list may need to be updated periodically. CTFontUIFontType fontTypes[] = { kCTFontUIFontUser, kCTFontUIFontUserFixedPitch, kCTFontUIFontSystem, kCTFontUIFontEmphasizedSystem, diff --git a/packages/skia/cpp/api/JsiSkFontMgr.h b/packages/skia/cpp/api/JsiSkFontMgr.h index e373a30cef..6d910f4948 100644 --- a/packages/skia/cpp/api/JsiSkFontMgr.h +++ b/packages/skia/cpp/api/JsiSkFontMgr.h @@ -48,7 +48,11 @@ class JsiSkFontMgr : public JsiSkWrappingSkPtrHostObject { return jsi::String::createFromUtf8(runtime, _systemFontFamilies[systemIndex]); } - return jsi::String::createFromUtf8(runtime, ""); + throw jsi::JSError(runtime, "Font family index out of bounds: " + + std::to_string(i) + " (total families: " + + std::to_string(baseFamilyCount + + _systemFontFamilies.size()) + + ")"); } JSI_HOST_FUNCTION(matchFamilyStyle) { diff --git a/packages/skia/src/skia/core/Font.ts b/packages/skia/src/skia/core/Font.ts index 5e64fa583d..e5e1c2a505 100644 --- a/packages/skia/src/skia/core/Font.ts +++ b/packages/skia/src/skia/core/Font.ts @@ -102,17 +102,7 @@ export const listFontFamilies = ( for (let i = 0; i < fontMgr.countFamilies(); i++) { families.add(fontMgr.getFamilyName(i)); } - return [...families].sort((a, b) => { - const aStartsWithAlpha = /^[a-zA-Z]/.test(a); - const bStartsWithAlpha = /^[a-zA-Z]/.test(b); - if (aStartsWithAlpha && !bStartsWithAlpha) { - return 1; - } - if (!aStartsWithAlpha && bStartsWithAlpha) { - return -1; - } - return a.localeCompare(b); - }); + return Array.from(families); }; const loadTypefaces = (typefacesToLoad: Record) => {