| Flutter iOS Embedder |

FlutterView.mm
Go to the documentation of this file.
1 // Copyright 2013 The Flutter Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterView.h"
6
7 #include "flutter/common/settings.h"
8 #include "flutter/common/task_runners.h"
9 #include "flutter/flow/layers/layer_tree.h"
10 #include "flutter/fml/platform/darwin/cf_utils.h"
11 #include "flutter/fml/synchronization/waitable_event.h"
12 #include "flutter/fml/trace_event.h"
13 #include "flutter/shell/common/platform_view.h"
14 #include "flutter/shell/common/rasterizer.h"
15 #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterViewController_Internal.h"
16 #import "flutter/shell/platform/darwin/ios/ios_surface_software.h"
17 #include "third_party/skia/include/utils/mac/SkCGUtils.h"
18
19 @implementation FlutterView {
20 id<FlutterViewEngineDelegate> _delegate;
21 BOOL _isWideGamutEnabled;
22 }
23
24 - (instancetype)init {
25 NSAssert(NO, @"FlutterView must initWithDelegate");
26 return nil;
27 }
28
29 - (instancetype)initWithFrame:(CGRect)frame {
30 NSAssert(NO, @"FlutterView must initWithDelegate");
31 return nil;
32 }
33
34 - (instancetype)initWithCoder:(NSCoder*)aDecoder {
35 NSAssert(NO, @"FlutterView must initWithDelegate");
36 return nil;
37 }
38
39 - (UIScreen*)screen {
40 if (@available(iOS 13.0, *)) {
41 return self.window.windowScene.screen;
42 }
43 return UIScreen.mainScreen;
44 }
45
46 - (BOOL)isWideGamutSupported {
47 if (![_delegate isUsingImpeller]) {
48 return NO;
49 }
50
51 FML_DCHECK(self.screen);
52
53 // This predicates the decision on the capabilities of the iOS device's
54 // display. This means external displays will not support wide gamut if the
55 // device's display doesn't support it. It practice that should be never.
56 return self.screen.traitCollection.displayGamut != UIDisplayGamutSRGB;
57 }
58
59 - (instancetype)initWithDelegate:(id<FlutterViewEngineDelegate>)delegate
60 opaque:(BOOL)opaque
61 enableWideGamut:(BOOL)isWideGamutEnabled {
62 if (delegate == nil) {
63 NSLog(@"FlutterView delegate was nil.");
64 [self release];
65 return nil;
66 }
67
68 self = [super initWithFrame:CGRectNull];
69
70 if (self) {
71 _delegate = delegate;
72 _isWideGamutEnabled = isWideGamutEnabled;
73 self.layer.opaque = opaque;
74
75 // This line is necessary. CoreAnimation(or UIKit) may take this to do
76 // something to compute the final frame presented on screen, if we don't set this,
77 // it will make it take long time for us to take next CAMetalDrawable and will
78 // cause constant junk during rendering.
79 self.backgroundColor = UIColor.clearColor;
80 }
81
82 return self;
83 }
84
85 static void PrintWideGamutWarningOnce() {
86 static BOOL did_print = NO;
87 if (did_print) {
88 return;
89 }
90 FML_DLOG(WARNING) << "Rendering wide gamut colors is turned on but isn't "
91 "supported, downgrading the color gamut to sRGB.";
92 did_print = YES;
93 }
94
95 - (void)layoutSubviews {
96 if ([self.layer isKindOfClass:NSClassFromString(@"CAMetalLayer")]) {
97 // It is a known Apple bug that CAMetalLayer incorrectly reports its supported
98 // SDKs. It is, in fact, available since iOS 8.
99 #pragma clang diagnostic push
100 #pragma clang diagnostic ignored "-Wunguarded-availability-new"
101 CAMetalLayer* layer = (CAMetalLayer*)self.layer;
102 #pragma clang diagnostic pop
103 CGFloat screenScale = [UIScreen mainScreen].scale;
104 layer.allowsGroupOpacity = YES;
105 layer.contentsScale = screenScale;
106 layer.rasterizationScale = screenScale;
107 layer.framebufferOnly = flutter::Settings::kSurfaceDataAccessible ? NO : YES;
108 BOOL isWideGamutSupported = self.isWideGamutSupported;
109 if (_isWideGamutEnabled && isWideGamutSupported) {
110 CGColorSpaceRef srgb = CGColorSpaceCreateWithName(kCGColorSpaceExtendedSRGB);
111 layer.colorspace = srgb;
112 CFRelease(srgb);
113 // MTLPixelFormatRGBA16Float was chosen since it is compatible with
114 // impeller's offscreen buffers which need to have transparency. Also,
115 // F16 was chosen over BGRA10_XR since Skia does not support decoding
116 // BGRA10_XR.
117 layer.pixelFormat = MTLPixelFormatRGBA16Float;
118 } else if (_isWideGamutEnabled && !isWideGamutSupported) {
119 PrintWideGamutWarningOnce();
120 }
121 }
122
123 [super layoutSubviews];
124 }
125
126 static BOOL _forceSoftwareRendering;
127
128 + (BOOL)forceSoftwareRendering {
129 return _forceSoftwareRendering;
130 }
131
132 + (void)setForceSoftwareRendering:(BOOL)forceSoftwareRendering {
133 _forceSoftwareRendering = forceSoftwareRendering;
134 }
135
136 + (Class)layerClass {
137 return flutter::GetCoreAnimationLayerClassForRenderingAPI(
138 flutter::GetRenderingAPIForProcess(FlutterView.forceSoftwareRendering));
139 }
140
141 - (void)drawLayer:(CALayer*)layer inContext:(CGContextRef)context {
142 TRACE_EVENT0("flutter", "SnapshotFlutterView");
143
144 if (layer != self.layer || context == nullptr) {
145 return;
146 }
147
148 auto screenshot = [_delegate takeScreenshot:flutter::Rasterizer::ScreenshotType::UncompressedImage
149 asBase64Encoded:NO];
150
151 if (!screenshot.data || screenshot.data->isEmpty() || screenshot.frame_size.isEmpty()) {
152 return;
153 }
154
155 NSData* data = [NSData dataWithBytes:const_cast<void*>(screenshot.data->data())
156 length:screenshot.data->size()];
157
158 fml::CFRef<CGDataProviderRef> image_data_provider(
159 CGDataProviderCreateWithCFData(reinterpret_cast<CFDataRef>(data)));
160
161 fml::CFRef<CGColorSpaceRef> colorspace(CGColorSpaceCreateDeviceRGB());
162
163 fml::CFRef<CGImageRef> image(CGImageCreate(
164 screenshot.frame_size.width(), // size_t width
165 screenshot.frame_size.height(), // size_t height
166 8, // size_t bitsPerComponent
167 32, // size_t bitsPerPixel,
168 4 * screenshot.frame_size.width(), // size_t bytesPerRow
169 colorspace, // CGColorSpaceRef space
170 static_cast<CGBitmapInfo>(
171 static_cast<uint32_t>(kCGImageAlphaPremultipliedLast) |
172 static_cast<uint32_t>(kCGBitmapByteOrder32Big)), // CGBitmapInfo bitmapInfo
173 image_data_provider, // CGDataProviderRef provider
174 nullptr, // const CGFloat* decode
175 false, // bool shouldInterpolate
176 kCGRenderingIntentDefault // CGColorRenderingIntent intent
177 ));
178
179 const CGRect frame_rect =
180 CGRectMake(0.0, 0.0, screenshot.frame_size.width(), screenshot.frame_size.height());
181
182 CGContextSaveGState(context);
183 CGContextTranslateCTM(context, 0.0, CGBitmapContextGetHeight(context));
184 CGContextScaleCTM(context, 1.0, -1.0);
185 CGContextDrawImage(context, frame_rect, image);
186 CGContextRestoreGState(context);
187 }
188
189 - (BOOL)isAccessibilityElement {
190 // iOS does not provide an API to query whether the voice control
191 // is turned on or off. It is likely at least one of the assitive
192 // technologies is turned on if this method is called. If we do
193 // not catch it in notification center, we will catch it here.
194 //
195 // TODO(chunhtai): Remove this workaround once iOS provides an
196 // API to query whether voice control is enabled.
197 // https://github.com/flutter/flutter/issues/76808.
198 [_delegate flutterViewAccessibilityDidCall];
199 return NO;
200 }
201
202 @end
FlutterView::forceSoftwareRendering
BOOL forceSoftwareRendering
Definition: FlutterView.h:50
flutter::GetCoreAnimationLayerClassForRenderingAPI
Class GetCoreAnimationLayerClassForRenderingAPI(IOSRenderingAPI rendering_api)
Definition: rendering_api_selection.mm:61
FlutterViewEngineDelegate-p
Definition: FlutterView.h:18
initWithFrame
instancetype initWithFrame
Definition: FlutterTextInputPlugin.h:164
ios_surface_software.h
flutter::GetRenderingAPIForProcess
IOSRenderingAPI GetRenderingAPIForProcess(bool force_software)
Definition: rendering_api_selection.mm:31
initWithCoder
instancetype initWithCoder
Definition: FlutterTextInputPlugin.h:163
_isWideGamutEnabled
BOOL _isWideGamutEnabled
Definition: FlutterView.mm:19
FlutterViewController_Internal.h
FlutterView
Definition: FlutterView.h:38
FlutterView.h
_forceSoftwareRendering
static BOOL _forceSoftwareRendering
Definition: FlutterView.mm:126