Skip to content
This repository was archived by the owner on Nov 11, 2024. It is now read-only.

Commit e289a8d

Browse files
committed
feat: support screen.backingScaleFactor changes
Perform a full relayout whenever a RCTWindow moves to a screen with a different "point scaling factor". At the same time, redraw any text and images (including view borders).
1 parent 8cce18b commit e289a8d

File tree

19 files changed

+145
-78
lines changed

19 files changed

+145
-78
lines changed

Libraries/Image/RCTImageBlurUtils.m

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
if (CGImageGetBitsPerPixel(imageRef) != 32 ||
2525
CGImageGetBitsPerComponent(imageRef) != 8 ||
2626
!((CGImageGetBitmapInfo(imageRef) & kCGBitmapAlphaInfoMask))) {
27-
UIGraphicsBeginImageContextWithOptions(inputImage.size, NO, 1.0f); //TODO: real scale
27+
UIGraphicsBeginImageContextWithOptions(inputImage.size, NO, 0.0);
2828
[inputImage drawInRect:NSMakeRect(0, 0, inputImage.size.width, inputImage.size.height)];
2929
imageRef = RCTGetCGImage(UIGraphicsGetImageFromCurrentImageContext());
3030
UIGraphicsEndImageContext();

Libraries/Image/RCTImageLoader.m

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -671,16 +671,16 @@ - (RCTImageLoaderCancellationBlock)decodeImageData:(NSData *)data
671671
// Decompress the image data (this may be CPU and memory intensive)
672672
NSImage *image = RCTDecodeImageWithData(data, size, scale, resizeMode);
673673

674-
#if RCT_DEV
675-
CGSize imagePixelSize = RCTSizeInPixels(image.size, 1.0f);
676-
CGSize screenPixelSize = RCTSizeInPixels(RCTScreenSize(), RCTScreenScale());
677-
if (imagePixelSize.width * imagePixelSize.height >
678-
screenPixelSize.width * screenPixelSize.height) {
679-
RCTLogInfo(@"[PERF ASSETS] Loading image at size %@, which is larger "
680-
"than the screen size %@", NSStringFromSize(imagePixelSize),
681-
NSStringFromSize(screenPixelSize));
682-
}
683-
#endif
674+
//#if RCT_DEV
675+
// CGSize imagePixelSize = RCTSizeInPixels(image.size, 1.0f);
676+
// CGSize screenPixelSize = RCTSizeInPixels(RCTScreenSize(), RCTScreenScale());
677+
// if (imagePixelSize.width * imagePixelSize.height >
678+
// screenPixelSize.width * screenPixelSize.height) {
679+
// RCTLogInfo(@"[PERF ASSETS] Loading image at size %@, which is larger "
680+
// "than the screen size %@", NSStringFromSize(imagePixelSize),
681+
// NSStringFromSize(screenPixelSize));
682+
// }
683+
//#endif
684684

685685
if (image) {
686686
completionHandler(nil, image);

Libraries/Image/RCTImageView.m

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,7 @@ - (RCTImageSource *)imageSourceForSize:(CGSize)size
244244
return nil;
245245
}
246246

247-
const CGFloat scale = RCTScreenScale();
247+
const CGFloat scale = self.window.screen.backingScaleFactor;
248248
const CGFloat targetImagePixels = size.width * size.height * scale * scale;
249249

250250
RCTImageSource *bestSource = nil;
@@ -307,7 +307,7 @@ - (void)reloadImage
307307
};
308308

309309
CGSize imageSize = self.bounds.size;
310-
CGFloat imageScale = RCTScreenScale();
310+
CGFloat imageScale = self.window.screen.backingScaleFactor;
311311
if (!UIEdgeInsetsEqualToEdgeInsets(_capInsets, NSEdgeInsetsZero)) {
312312
// Don't resize images that use capInsets
313313
imageSize = CGSizeZero;
@@ -378,8 +378,10 @@ - (void)imageLoaderLoadedImage:(NSImage *)loadedImage error:(NSError *)error for
378378
};
379379

380380
if (_blurRadius > __FLT_EPSILON__) {
381+
NSScreen *screen = self.window.screen;
381382
// Blur on a background thread to avoid blocking interaction
382383
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
384+
RCTSetScreen(screen);
383385
NSImage *blurredImage = RCTBlurredImageWithRadius(loadedImage, self->_blurRadius);
384386
RCTExecuteOnMainQueue(^{
385387
setImageBlock(blurredImage);
@@ -407,8 +409,9 @@ - (void)reactSetFrame:(CGRect)frame
407409
[self reloadImage];
408410
} else if ([self shouldReloadImageSourceAfterResize]) {
409411
CGSize imageSize = self.image.size;
410-
CGFloat imageScale = RCTScreenScale();
411-
CGSize idealSize = RCTTargetSize(imageSize, imageScale, frame.size, RCTScreenScale(),
412+
CGFloat imageScale = 1.0;
413+
CGFloat screenScale = self.window.screen.backingScaleFactor;
414+
CGSize idealSize = RCTTargetSize(imageSize, imageScale, frame.size, screenScale,
412415
self.resizeMode, YES);
413416

414417
// Don't reload if the current image or target image size is close enough

Libraries/Text/Text/RCTTextShadowView.m

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
#import <React/RCTBridge.h>
1313
#import <React/RCTShadowView+Layout.h>
14+
#import <React/RCTRootShadowView.h>
1415
#import <React/RCTUIManager.h>
1516
#import <yoga/Yoga.h>
1617

@@ -284,12 +285,13 @@ - (void)layoutSubviewsWithContext:(RCTLayoutContext)layoutContext
284285

285286
NSFont *font = [textStorage attribute:NSFontAttributeName atIndex:range.location effectiveRange:nil];
286287

288+
CGFloat scale = YGNodeLayoutGetPointScaleFactor(shadowView.rootView.yogaNode);
287289
CGRect frame = {{
288-
RCTRoundPixelValue(glyphRect.origin.x),
289-
RCTRoundPixelValue(glyphRect.origin.y + glyphRect.size.height - attachmentSize.height + font.descender)
290+
RCTRoundPixelValue(glyphRect.origin.x, scale),
291+
RCTRoundPixelValue(glyphRect.origin.y + glyphRect.size.height - attachmentSize.height + font.descender, scale)
290292
}, {
291-
RCTRoundPixelValue(attachmentSize.width),
292-
RCTRoundPixelValue(attachmentSize.height)
293+
RCTRoundPixelValue(attachmentSize.width, scale),
294+
RCTRoundPixelValue(attachmentSize.height, scale)
293295
}};
294296

295297
RCTLayoutContext localLayoutContext = layoutContext;
@@ -327,9 +329,10 @@ static YGSize RCTTextShadowViewMeasure(YGNodeRef node, float width, YGMeasureMod
327329
size.width -= letterSpacing;
328330
}
329331

332+
CGFloat scale = YGNodeLayoutGetPointScaleFactor(shadowTextView.rootView.yogaNode);
330333
size = (CGSize){
331-
MIN(RCTCeilPixelValue(size.width), maximumSize.width),
332-
MIN(RCTCeilPixelValue(size.height), maximumSize.height)
334+
MIN(RCTCeilPixelValue(size.width, scale), maximumSize.width),
335+
MIN(RCTCeilPixelValue(size.height, scale), maximumSize.height)
333336
};
334337

335338
return (YGSize){

Libraries/Text/TextInput/RCTBaseTextInputShadowView.m

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -227,8 +227,8 @@ - (CGSize)sizeThatFitsMinimumSize:(CGSize)minimumSize maximumSize:(CGSize)maximu
227227
CGSize size = [_layoutManager usedRectForTextContainer:_textContainer].size;
228228

229229
return (CGSize){
230-
MAX(minimumSize.width, MIN(RCTCeilPixelValue(size.width), maximumSize.width)),
231-
MAX(minimumSize.height, MIN(RCTCeilPixelValue(size.height), maximumSize.height))
230+
MAX(minimumSize.width, MIN(RCTCeilPixelValue(size.width, 1.0), maximumSize.width)),
231+
MAX(minimumSize.height, MIN(RCTCeilPixelValue(size.height, 1.0), maximumSize.height))
232232
};
233233
}
234234

React/Base/RCTRootView.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,8 @@ extern NSString *const RCTContentDidAppearNotification;
150150
@property (nonatomic, assign) NSTimeInterval loadingViewFadeDelay;
151151
@property (nonatomic, assign) NSTimeInterval loadingViewFadeDuration;
152152

153+
@property (nonatomic, assign) CGFloat scaleFactor;
154+
153155
@end
154156

155157
@interface RCTRootView (Deprecated)

React/Base/RCTRootView.m

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#import "RCTUIManagerUtils.h"
2828
#import "RCTUtils.h"
2929
#import "RCTView.h"
30+
#import "RCTShadowView.h"
3031
#import "NSView+React.h"
3132

3233
#if TARGET_OS_TV
@@ -215,6 +216,22 @@ - (BOOL)isFlipped
215216
return NO;
216217
}
217218

219+
- (void)setScaleFactor:(CGFloat)scaleFactor
220+
{
221+
if (scaleFactor != _scaleFactor) {
222+
_scaleFactor = scaleFactor;
223+
if (scaleFactor > 0.0) {
224+
NSNumber *reactTag = self.reactTag;
225+
RCTUIManager *uiManager = _bridge.uiManager;
226+
RCTExecuteOnUIManagerQueue(^{
227+
RCTShadowView *shadowView = [uiManager shadowViewForReactTag:reactTag];
228+
YGNodeLayoutSetPointScaleFactor(shadowView.yogaNode, scaleFactor);
229+
[uiManager setNeedsLayout];
230+
});
231+
}
232+
}
233+
}
234+
218235
- (void)setLoadingView:(NSView *)loadingView
219236
{
220237
_loadingView = loadingView;

React/Base/RCTUtils.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,13 +44,14 @@ RCT_EXTERN void RCTExecuteOnMainQueue(dispatch_block_t block);
4444
RCT_EXTERN void RCTUnsafeExecuteOnMainQueueSync(dispatch_block_t block);
4545

4646
// Get screen metrics in a thread-safe way
47+
RCT_EXTERN void RCTSetScreen(NSScreen *);
4748
RCT_EXTERN CGFloat RCTScreenScale(void);
4849
RCT_EXTERN CGSize RCTScreenSize(void);
4950

5051
// Round float coordinates to nearest whole screen pixel (not point)
51-
RCT_EXTERN CGFloat RCTRoundPixelValue(CGFloat value);
52-
RCT_EXTERN CGFloat RCTCeilPixelValue(CGFloat value);
53-
RCT_EXTERN CGFloat RCTFloorPixelValue(CGFloat value);
52+
RCT_EXTERN CGFloat RCTRoundPixelValue(CGFloat value, CGFloat scale);
53+
RCT_EXTERN CGFloat RCTCeilPixelValue(CGFloat value, CGFloat scale);
54+
RCT_EXTERN CGFloat RCTFloorPixelValue(CGFloat value, CGFloat scale);
5455

5556
// Convert a size in points to pixels, rounded up to the nearest integral size
5657
RCT_EXTERN CGSize RCTSizeInPixels(CGSize pointSize, CGFloat scale);

React/Base/RCTUtils.m

Lines changed: 15 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -280,50 +280,38 @@ static void RCTUnsafeExecuteOnMainQueueOnceSync(dispatch_once_t *onceToken, disp
280280
}
281281
}
282282

283-
CGFloat RCTScreenScale()
284-
{
285-
static dispatch_once_t onceToken;
286-
static CGFloat scale;
283+
static __weak NSScreen *currentScreen = nil;
287284

288-
RCTUnsafeExecuteOnMainQueueOnceSync(&onceToken, ^{
289-
scale = [NSScreen mainScreen].backingScaleFactor; // TODO:
290-
});
285+
void RCTSetScreen(NSScreen *screen)
286+
{
287+
RCTAssertMainQueue();
288+
currentScreen = screen;
289+
}
291290

292-
return scale;
291+
CGFloat RCTScreenScale()
292+
{
293+
RCTAssertMainQueue();
294+
return currentScreen.backingScaleFactor;
293295
}
294296

295297
CGSize RCTScreenSize()
296298
{
297-
// FIXME: this caches the bounds at app start, whatever those were, and then
298-
// doesn't update when the device is rotated. We need to find another thread-
299-
// safe way to get the screen size.
300-
301-
static CGSize size;
302-
static dispatch_once_t onceToken;
303-
dispatch_once(&onceToken, ^{
304-
RCTUnsafeExecuteOnMainQueueSync(^{
305-
size = [NSScreen mainScreen].frame.size;
306-
});
307-
});
308-
309-
return size;
299+
RCTAssertMainQueue();
300+
return currentScreen.frame.size;
310301
}
311302

312-
CGFloat RCTRoundPixelValue(CGFloat value)
303+
CGFloat RCTRoundPixelValue(CGFloat value, CGFloat scale)
313304
{
314-
CGFloat scale = RCTScreenScale();
315305
return round(value * scale) / scale;
316306
}
317307

318-
CGFloat RCTCeilPixelValue(CGFloat value)
308+
CGFloat RCTCeilPixelValue(CGFloat value, CGFloat scale)
319309
{
320-
CGFloat scale = RCTScreenScale();
321310
return ceil(value * scale) / scale;
322311
}
323312

324-
CGFloat RCTFloorPixelValue(CGFloat value)
313+
CGFloat RCTFloorPixelValue(CGFloat value, CGFloat scale)
325314
{
326-
CGFloat scale = RCTScreenScale();
327315
return floor(value * scale) / scale;
328316
}
329317

React/Base/UIImageUtils.m

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,12 @@
55
// Copyright © 2015 Facebook. All rights reserved.
66
//
77

8-
#import "UIImageUtils.h"
98
#import <Foundation/Foundation.h>
109
#import <AppKit/AppKit.h>
1110

11+
#import "RCTUtils.h"
12+
#import "UIImageUtils.h"
13+
1214
NSData *UIImagePNGRepresentation(NSImage *image)
1315
{
1416
CGImageSourceRef source = CGImageSourceCreateWithData((CFDataRef)[image TIFFRepresentation], NULL);
@@ -57,7 +59,7 @@ void UIGraphicsPushContext(CGContextRef ctx)
5759
void UIGraphicsBeginImageContextWithOptions(CGSize size, BOOL opaque, CGFloat scale)
5860
{
5961
if (scale == 0.f) {
60-
scale = [NSScreen mainScreen].backingScaleFactor ?: 1;
62+
scale = RCTScreenScale();
6163
}
6264

6365
const size_t width = size.width * scale;

0 commit comments

Comments
 (0)