mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Correct offset when drawing text to TextStorage coordinate system. (#36771)
Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/36771 Changelog: [internal] Previously, when `NSTextStorage` was cached, we were not accounting for case where text which was aligned to centre or left, was used to size its container. The result was that it was painted outside of its container, therefore invisible. To fix this, we adjust the offset to make sure text is painted correctly. This bug only happens if: - Text is not aligned to left in right to left writing system. - The canvas where text is drawn is not stretched to full width of its parent. - The offset needs to be large enough for this to matter, otherwise the text is just slightly off. - Because of the caching mechanism, it had to be a piece of text that was rendered before. Otherwise it would work. This complexity is worth the trouble to avoid invalidation of layout inside NSTextContainer, which is expensive. Reviewed By: cipolleschi Differential Revision: D44624085 fbshipit-source-id: 1bb8ef88933a49b478a2606dba6bf16b4e728b2b
This commit is contained in:
committed by
Lorenzo Sciandra
parent
6d6b6a8345
commit
921bbb6cf1
+22
-4
@@ -52,8 +52,6 @@ static NSLineBreakMode RCTNSLineBreakModeFromEllipsizeMode(EllipsizeMode ellipsi
|
||||
textStorage = [self _textStorageForNSAttributesString:attributedString
|
||||
paragraphAttributes:paragraphAttributes
|
||||
size:maximumSize];
|
||||
} else {
|
||||
textStorage.layoutManagers.firstObject.textContainers.firstObject.size = maximumSize;
|
||||
}
|
||||
|
||||
return [self _measureTextStorage:textStorage];
|
||||
@@ -79,13 +77,33 @@ static NSLineBreakMode RCTNSLineBreakModeFromEllipsizeMode(EllipsizeMode ellipsi
|
||||
frame:(CGRect)frame
|
||||
textStorage:(NSTextStorage *_Nullable)textStorage
|
||||
{
|
||||
BOOL createdStorageForFrame = NO;
|
||||
|
||||
if (!textStorage) {
|
||||
textStorage = [self textStorageForAttributesString:attributedString
|
||||
paragraphAttributes:paragraphAttributes
|
||||
size:frame.size];
|
||||
createdStorageForFrame = YES;
|
||||
}
|
||||
|
||||
NSLayoutManager *layoutManager = textStorage.layoutManagers.firstObject;
|
||||
NSTextContainer *textContainer = layoutManager.textContainers.firstObject;
|
||||
CGPoint origin = frame.origin;
|
||||
|
||||
if (!createdStorageForFrame) {
|
||||
CGRect rect = [layoutManager usedRectForTextContainer:textContainer];
|
||||
static auto threshold = 1.0 / RCTScreenScale() + 0.01; // Size of a pixel plus some small threshold.
|
||||
|
||||
// `rect`'s width is stored in double precesion.
|
||||
// `frame`'s width is also in double precesion but was stored as float in Yoga previously, precesion was lost.
|
||||
if (std::abs(RCTCeilPixelValue(rect.size.width) - frame.size.width) < threshold) {
|
||||
// `textStorage` passed to this method was used to calculate size of frame. If that's the case, it's
|
||||
// width is the same as frame's width. Origin must be adjusted, otherwise glyhps will be painted in wrong
|
||||
// place.
|
||||
// We could create new `NSTextStorage` for the specific frame, but that is expensive.
|
||||
origin.x -= RCTCeilPixelValue(rect.origin.x);
|
||||
}
|
||||
}
|
||||
|
||||
#if TARGET_OS_MACCATALYST
|
||||
CGContextRef context = UIGraphicsGetCurrentContext();
|
||||
@@ -94,8 +112,8 @@ static NSLineBreakMode RCTNSLineBreakModeFromEllipsizeMode(EllipsizeMode ellipsi
|
||||
#endif
|
||||
|
||||
NSRange glyphRange = [layoutManager glyphRangeForTextContainer:textContainer];
|
||||
[layoutManager drawBackgroundForGlyphRange:glyphRange atPoint:frame.origin];
|
||||
[layoutManager drawGlyphsForGlyphRange:glyphRange atPoint:frame.origin];
|
||||
[layoutManager drawBackgroundForGlyphRange:glyphRange atPoint:origin];
|
||||
[layoutManager drawGlyphsForGlyphRange:glyphRange atPoint:origin];
|
||||
|
||||
#if TARGET_OS_MACCATALYST
|
||||
CGContextRestoreGState(context);
|
||||
|
||||
Reference in New Issue
Block a user