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,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;