-
Notifications
You must be signed in to change notification settings - Fork 61
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Support supplying mobile fallback fonts by style with caching
This pull request builds on top of fallback font support on iOS by including the ability to provide fallback fonts based on the styling of the missing character. Currently, the style information only contains weight. This weight is grabbed by calling `getAxisValue`. According to Luigi, this is a linear search, so perhaps there's room for a performance optimization later on. There is one lower-level C++ change: `gFallbackProc` returns the font for the missing character, in addition _to_ the missing character (as a second parameter). This font will be used to generate the requested styling within the iOS runtime. This adds a new class property to `RiveFont`: `fallbackFontCallback` (whose name I'm open to changing). This is a block (i.e closure) that will be called when a fallback font is requested. It supplies the styling of the missing character so that, for example, different fonts can be used based on the weight of the missing character. For example usage, see `SwiftFallbackFonts.swift`. This provider is what's used under-the-hood, and utilizes the pre-existing `fallbackFonts` class property The "trickiest" bit here is the caching. NSDictionary requires equatable / hashable types as keys, and we want to minimize additional generation of a Rive font, so we cache any used fonts in a wrapper type, used as the value. When new fallback fonts are provided, either directly or when a new provider block is set, the cache will be reset. Once the weight is determined, generating the right key is as simple as calling the right initializer, and when set, generating the right value is simple as calling the right initializer with the created Rive font. Finally, `RiveFactory` was getting a little bloated, so I did a little file cleanup. This pull requests also includes Android support from #8621 Diffs= 7986d64d83 Support supplying mobile fallback fonts by style with caching (#8396) Co-authored-by: David Skuza <[email protected]> Co-authored-by: Umberto <[email protected]> Co-authored-by: Umberto Sonnino <[email protected]>
- Loading branch information
1 parent
ba494aa
commit 1c87f97
Showing
14 changed files
with
613 additions
and
94 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
fb8ecf3552aeece0b1d242804580a016b6297ad4 | ||
7986d64d8371531716ea3f038dcbec5da187e6cd |
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
// | ||
// 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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
// | ||
// RiveFallbackFontCache.m | ||
// RiveRuntime | ||
// | ||
// Created by David Skuza on 10/23/24. | ||
// Copyright © 2024 Rive. All rights reserved. | ||
// | ||
|
||
#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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
// | ||
// RiveFont.h | ||
// RiveRuntime | ||
// | ||
// Created by David Skuza on 10/23/24. | ||
// Copyright © 2024 Rive. All rights reserved. | ||
// | ||
|
||
#import <Foundation/Foundation.h> | ||
|
||
NS_ASSUME_NONNULL_BEGIN | ||
|
||
/// An enumeration of possible weight values, mirroring those of | ||
/// UIFont/NSFont.Weight | ||
typedef NS_ENUM(NSInteger, RiveFontStyleWeight) { | ||
RiveFontStyleWeightThin = 100, | ||
RiveFontStyleWeightUltraLight = 200, | ||
RiveFontStyleWeightLight = 300, | ||
RiveFontStyleWeightRegular = 400, | ||
RiveFontStyleWeightMedium = 500, | ||
RiveFontStyleWeightSemibold = 600, | ||
RiveFontStyleWeightBold = 700, | ||
RiveFontStyleWeightHeavy = 800, | ||
RiveFontStyleWeightBlack = 900 | ||
}; | ||
|
||
/// An object that represents the styling of a font. | ||
@interface RiveFontStyle : NSObject <NSCopying> | ||
/// The weight of the font. See `RiveFontStyleWeight` for possible values. | ||
/// This value is computed by rounding `rawWeight` to the nearest hundredth. | ||
@property(nonatomic, readonly) RiveFontStyleWeight weight; | ||
/// The raw weight of the font. This value is used to generate the `weight` of | ||
/// the style. | ||
@property(nonatomic, readonly) CGFloat rawWeight; | ||
- (instancetype)initWithWeight:(RiveFontStyleWeight)weight; | ||
- (instancetype)initWithRawWeight:(CGFloat)rawWeight; | ||
@end | ||
|
||
@protocol RiveFallbackFontProvider; | ||
|
||
typedef NSArray<id<RiveFallbackFontProvider>>* _Nonnull ( | ||
^RiveFallbackFontsCallback)(RiveFontStyle*); | ||
|
||
@interface RiveFont : NSObject | ||
/// An array of font descriptors to attempt to use when text being rendererd by | ||
/// Rive uses a font that is missing a glyph. The fonts will be tried in the | ||
/// order in which they are added to the array. | ||
/// - Note: If unset, the default fallback is a default system font, with | ||
/// regular font weight. | ||
@property(class, copy, nonnull) | ||
NSArray<id<RiveFallbackFontProvider>>* fallbackFonts; | ||
/// A block that requests fallback font providers, given a font style. | ||
/// This way, different fallback fonts can be used depending on the styling | ||
/// of the font at draw-time (e.g weight). | ||
@property(class, nonatomic, copy, nonnull) | ||
RiveFallbackFontsCallback fallbackFontsCallback; | ||
@end | ||
|
||
NS_ASSUME_NONNULL_END |
Oops, something went wrong.