fix(apple): better release and null checking on fallback fonts (#10562) e0baf4e1fa

* fix(apple): do not use symbolic trait font copy on failure

* fix(apple): release ctfont copy

* refactor(apple): do not manually ref rcp'd font, remove cache

Co-authored-by: David Skuza <david@rive.app>
This commit is contained in:
dskuza
2025-09-11 23:00:14 +00:00
parent 2be54c4137
commit f77e3421f4
6 changed files with 4 additions and 180 deletions

View File

@@ -1 +1 @@
15a6eedc710136a0ca313d4a1708da8b2235382c
e0baf4e1faa8b094aea8c7a4aa6155da9b613dac

View File

@@ -135,8 +135,6 @@
F2ECC23A2C66B949008B20E5 /* RiveFontTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2ECC2382C66B920008B20E5 /* RiveFontTests.swift */; };
F2FD94042CC9492B00C1FC85 /* RiveFont.m in Sources */ = {isa = PBXBuildFile; fileRef = F2FD94032CC9492B00C1FC85 /* RiveFont.m */; };
F2FD94052CC9492B00C1FC85 /* RiveFont.h in Headers */ = {isa = PBXBuildFile; fileRef = F2FD94022CC9492B00C1FC85 /* RiveFont.h */; settings = {ATTRIBUTES = (Public, ); }; };
F2FD94082CC94B1A00C1FC85 /* RiveFallbackFontCache.m in Sources */ = {isa = PBXBuildFile; fileRef = F2FD94072CC94B1A00C1FC85 /* RiveFallbackFontCache.m */; };
F2FD94092CC94B1A00C1FC85 /* RiveFallbackFontCache.h in Headers */ = {isa = PBXBuildFile; fileRef = F2FD94062CC94B1A00C1FC85 /* RiveFallbackFontCache.h */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@@ -283,8 +281,6 @@
F2ECC2382C66B920008B20E5 /* RiveFontTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RiveFontTests.swift; sourceTree = "<group>"; };
F2FD94022CC9492B00C1FC85 /* RiveFont.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RiveFont.h; sourceTree = "<group>"; };
F2FD94032CC9492B00C1FC85 /* RiveFont.m */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.objcpp; path = RiveFont.m; sourceTree = "<group>"; };
F2FD94062CC94B1A00C1FC85 /* RiveFallbackFontCache.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RiveFallbackFontCache.h; sourceTree = "<group>"; };
F2FD94072CC94B1A00C1FC85 /* RiveFallbackFontCache.m */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.objcpp; path = RiveFallbackFontCache.m; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -508,8 +504,6 @@
F2D285482C6D469900728340 /* RiveFallbackFontProvider.swift */,
F2FD94022CC9492B00C1FC85 /* RiveFont.h */,
F2FD94032CC9492B00C1FC85 /* RiveFont.m */,
F2FD94062CC94B1A00C1FC85 /* RiveFallbackFontCache.h */,
F2FD94072CC94B1A00C1FC85 /* RiveFallbackFontCache.m */,
);
path = Fonts;
sourceTree = "<group>";
@@ -606,7 +600,6 @@
F2FD94052CC9492B00C1FC85 /* RiveFont.h in Headers */,
04BE5436264D2A7500427B39 /* RivePrivateHeaders.h in Headers */,
C9C73EE224FC478900EF9516 /* RiveRuntime.h in Headers */,
F2FD94092CC94B1A00C1FC85 /* RiveFallbackFontCache.h in Headers */,
83DE4CA72AAAE72100B88B72 /* RenderContext.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
@@ -782,7 +775,6 @@
F259E5872D35B91700B78FEF /* RiveDataBindingViewModel.mm in Sources */,
F21C3D1B2DDFCD93005F82F4 /* RiveRenderImage+Extensions.swift in Sources */,
046FB7FF264EAA61000129B1 /* RiveFile.mm in Sources */,
F2FD94082CC94B1A00C1FC85 /* RiveFallbackFontCache.m in Sources */,
046FB7F2264EAA60000129B1 /* RiveArtboard.mm in Sources */,
F2610DD22CA5B4C40090D50B /* RiveLogger+StateMachine.swift in Sources */,
046FB7F4264EAA60000129B1 /* RiveLinearAnimationInstance.mm in Sources */,

View File

@@ -1,44 +0,0 @@
//
// RiveFallbackFontCache.h
// RiveRuntime
//
// Created by David Skuza on 10/23/24.
// Copyright © 2024 Rive. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <rive/text/font_hb.hpp>
#import <RiveRuntime/RiveRuntime-Swift.h>
@class RiveFontStyle;
NS_ASSUME_NONNULL_BEGIN
/// An object that can be used as a dictionary key when caching fallback fonts.
/// - Note: This implements NSCopying and overrides `isEqual` and `hash` to add
/// support for an object of this type to be used as a key in dictionaries.
@interface RiveFallbackFontCacheKey : NSObject <NSCopying>
/// The style of the requested fallback font to be cached.
@property(nonatomic, readonly, nonnull) RiveFontStyle* style;
/// The actual character for which a fallback font is being requested.
@property(nonatomic, readonly) rive::Unichar character;
/// The fallback index used when originally requesting a fallback.
@property(nonatomic, readonly) uint32_t index;
- (instancetype)initWithStyle:(RiveFontStyle*)style
character:(rive::Unichar)character
index:(uint32_t)index;
@end
/// An object that can be used as a dictionary value (typically keyed to
/// `RiveFallbackFontCacheKey`), which contains the cached font types.
@interface RiveFallbackFontCacheValue : NSObject
/// The native font type used as the fallback (passed to the C++
/// runtime). On iOS, this will be UIFont. On macOS, this
/// will be NSFont.
@property(nonatomic, readonly) id font;
/// Whether the font used the system shaper (i.e Core Text over Harfbuzz)
@property(nonatomic, readonly) BOOL usesSystemShaper;
- (instancetype)initWithFont:(id)font usesSystemShaper:(BOOL)usesSystemShaper;
@end
NS_ASSUME_NONNULL_END

View File

@@ -1,85 +0,0 @@
//
// RiveFallbackFontCache.m
// RiveRuntime
//
// Created by David Skuza on 10/23/24.
// Copyright © 2024 Rive. All rights reserved.
//
#ifdef WITH_RIVE_TEXT
#import "RiveFallbackFontCache.h"
#import "RiveFont.h"
@implementation RiveFallbackFontCacheKey
@synthesize style = _style;
@synthesize character = _character;
@synthesize index = _index;
- (instancetype)initWithStyle:(RiveFontStyle*)style
character:(rive::Unichar)character
index:(uint32_t)index
{
if (self = [super init])
{
_style = style;
_character = character;
_index = index;
}
return self;
}
- (BOOL)isEqual:(id)object
{
if (object == nil)
{
return NO;
}
if (![object isKindOfClass:[RiveFallbackFontCacheKey class]])
{
return NO;
}
if (self == object)
{
return YES;
}
RiveFallbackFontCacheKey* other = (RiveFallbackFontCacheKey*)object;
return [self.style isEqual:other.style] &&
self.character == other.character && self.index == other.index;
}
- (NSUInteger)hash
{
// This is a super basic hash function that may be able to be improved.
// However, I don't imagine many collisions will happen based on the
// simplicity of our current use case. - David
return [self.style hash] ^ self.character ^ self.index;
}
- (id)copyWithZone:(NSZone*)zone
{
return [[RiveFallbackFontCacheKey alloc] initWithStyle:[self.style copy]
character:self.character
index:self.index];
}
@end
@implementation RiveFallbackFontCacheValue
@synthesize font = _font;
@synthesize usesSystemShaper = _usesSystemShaper;
- (instancetype)initWithFont:(id)font usesSystemShaper:(BOOL)usesSystemShaper;
{
if (self = [super init])
{
_font = font;
_usesSystemShaper = usesSystemShaper;
}
return self;
}
@end
#endif

View File

@@ -7,7 +7,7 @@
//
#import "RiveFont.h"
#import "RiveFallbackFontCache.h"
#import <rive/text/font_hb.hpp>
#import <RiveRuntime/RiveRuntime-Swift.h>
#import <CoreText/CoreText.h>
@@ -99,10 +99,6 @@ static RiveFontStyleWeight RiveFontStyleWeightFromFloat(float value)
}
@end
/// A cache of all used (Rive) fonts, keyed by style and character.
static NSMutableDictionary<RiveFallbackFontCacheKey*,
RiveFallbackFontCacheValue*>* _fallbackFontCache =
nil;
/// A user-specified array of fallback fonts.
static NSArray<id<RiveFallbackFontProvider>>* _fallbackFonts = nil;
/// A user-specified block that returns usable font providers.
@@ -144,27 +140,6 @@ static rive::rcp<rive::Font> findFallbackFont(const rive::Unichar missing,
float value = hbFont->getWeight();
RiveFontStyle* style = [[RiveFontStyle alloc] initWithRawWeight:value];
// Using the above style, check the cache keyed by the given style and
// missing character.
RiveFallbackFontCacheKey* cache =
[[RiveFallbackFontCacheKey alloc] initWithStyle:style
character:missing
index:fallbackIndex];
// If there is a cached fallback font, use that.
RiveFallbackFontCacheValue* cachedValue = _fallbackFontCache[cache];
if (cachedValue != nil)
{
auto font = riveFontFromNativeFont(cachedValue.font,
cachedValue.usesSystemShaper);
rive::rcp<rive::Font> rcFont = rive::rcp<rive::Font>(font);
// because the font was released at load time, we need to give
// it an extra ref whenever we bump it to a reference counted
// pointer.
rcFont->ref();
return rcFont;
}
// Otherwise, request possible fallback providers based on the missing
// character and style. fallbackFontsCallback will always be non-nil,
// and use a default array if no explicit callback has been set.
@@ -178,18 +153,7 @@ static rive::rcp<rive::Font> findFallbackFont(const rive::Unichar missing,
id fallbackFont = provider.fallbackFont;
BOOL usesSystemShaper = fallbackIndex >= providers.count;
auto font = riveFontFromNativeFont(fallbackFont, usesSystemShaper);
rive::rcp<rive::Font> rcFont = rive::rcp<rive::Font>(font);
// because the font was released at load time, we need to give
// it an extra ref whenever we bump it to a reference counted
// pointer.
rcFont->ref();
// Once we've used a font, cache it for later use.
_fallbackFontCache[cache] =
[[RiveFallbackFontCacheValue alloc] initWithFont:fallbackFont
usesSystemShaper:usesSystemShaper];
return rcFont;
return rive::rcp<rive::Font>(font);
}
return nullptr;
@@ -207,7 +171,6 @@ static rive::rcp<rive::Font> findFallbackFont(const rive::Unichar missing,
#ifdef WITH_RIVE_TEXT
rive::Font::gFallbackProc = findFallbackFont;
#endif
_fallbackFontCache = [NSMutableDictionary dictionary];
}
- (instancetype)initWithFont:(rive::rcp<rive::Font>)font
@@ -250,7 +213,6 @@ static rive::rcp<rive::Font> findFallbackFont(const rive::Unichar missing,
#ifdef WITH_RIVE_TEXT
// Set the user-specified fallbacks, and reset the cache.
_fallbackFonts = [fallbackFonts copy];
_fallbackFontCache = [NSMutableDictionary dictionary];
// "Reset" fallback fonts callback so that array can take priority
_fallbackFontsCallback = nil;
@@ -262,7 +224,6 @@ static rive::rcp<rive::Font> findFallbackFont(const rive::Unichar missing,
#ifdef WITH_RIVE_TEXT
// Set the user-specified fallback block, and reset the cache.
_fallbackFontsCallback = [fallbackFontCallback copy];
_fallbackFontCache = [NSMutableDictionary dictionary];
// "Reset" fallback fonts array so that callback can take priority
_fallbackFonts = nil;