| 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/fml/platform/darwin/cf_utils.h"
8 #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterSharedApplication.h"
9 #import "flutter/shell/platform/darwin/ios/framework/Source/SemanticsObject.h"
10
11 FLUTTER_ASSERT_ARC
12
13 @interface FlutterView ()
14 @property(nonatomic, weak) id<FlutterViewEngineDelegate> delegate;
15 @end
16
17 @implementation FlutterView {
18 BOOL _isWideGamutEnabled;
19 }
20
21 - (instancetype)init {
22 NSAssert(NO, @"FlutterView must initWithDelegate");
23 return nil;
24 }
25
26 - (instancetype)initWithFrame:(CGRect)frame {
27 NSAssert(NO, @"FlutterView must initWithDelegate");
28 return nil;
29 }
30
31 - (instancetype)initWithCoder:(NSCoder*)aDecoder {
32 NSAssert(NO, @"FlutterView must initWithDelegate");
33 return nil;
34 }
35
36 - (UIScreen*)screen {
37 if (@available(iOS 13.0, *)) {
38 return self.window.windowScene.screen;
39 }
40 return UIScreen.mainScreen;
41 }
42
43 - (MTLPixelFormat)pixelFormat {
44 if ([self.layer isKindOfClass:[CAMetalLayer class]]) {
45 // It is a known Apple bug that CAMetalLayer incorrectly reports its supported
46 // SDKs. It is, in fact, available since iOS 8.
47 #pragma clang diagnostic push
48 #pragma clang diagnostic ignored "-Wunguarded-availability-new"
49 CAMetalLayer* layer = (CAMetalLayer*)self.layer;
50 return layer.pixelFormat;
51 }
52 return MTLPixelFormatBGRA8Unorm;
53 }
54 - (BOOL)isWideGamutSupported {
55 FML_DCHECK(self.screen);
56
57 // Wide Gamut is not supported for iOS Extensions due to memory limitations
58 // (see https://github.com/flutter/flutter/issues/165086).
59 if (FlutterSharedApplication.isAppExtension) {
60 return NO;
61 }
62
63 // This predicates the decision on the capabilities of the iOS device's
64 // display. This means external displays will not support wide gamut if the
65 // device's display doesn't support it. It practice that should be never.
66 return self.screen.traitCollection.displayGamut != UIDisplayGamutSRGB;
67 }
68
69 - (instancetype)initWithDelegate:(id<FlutterViewEngineDelegate>)delegate
70 opaque:(BOOL)opaque
71 enableWideGamut:(BOOL)isWideGamutEnabled {
72 if (delegate == nil) {
73 NSLog(@"FlutterView delegate was nil.");
74 return nil;
75 }
76
77 self = [super initWithFrame:CGRectNull];
78
79 if (self) {
80 _delegate = delegate;
81 _isWideGamutEnabled = isWideGamutEnabled;
82 self.layer.opaque = opaque;
83 }
84
85 return self;
86 }
87
88 static void PrintWideGamutWarningOnce() {
89 static BOOL did_print = NO;
90 if (did_print) {
91 return;
92 }
93 FML_DLOG(WARNING) << "Rendering wide gamut colors is turned on but isn't "
94 "supported, downgrading the color gamut to sRGB.";
95 did_print = YES;
96 }
97
98 - (void)layoutSubviews {
99 if ([self.layer isKindOfClass:[CAMetalLayer class]]) {
100 // It is a known Apple bug that CAMetalLayer incorrectly reports its supported
101 // SDKs. It is, in fact, available since iOS 8.
102 #pragma clang diagnostic push
103 #pragma clang diagnostic ignored "-Wunguarded-availability-new"
104 CAMetalLayer* layer = (CAMetalLayer*)self.layer;
105 #pragma clang diagnostic pop
106 CGFloat screenScale = self.screen.scale;
107 layer.allowsGroupOpacity = YES;
108 layer.contentsScale = screenScale;
109 layer.rasterizationScale = screenScale;
110 layer.framebufferOnly = flutter::Settings::kSurfaceDataAccessible ? NO : YES;
111 if (_isWideGamutEnabled && self.isWideGamutSupported) {
112 fml::CFRef<CGColorSpaceRef> srgb(CGColorSpaceCreateWithName(kCGColorSpaceExtendedSRGB));
113 layer.colorspace = srgb;
114 layer.pixelFormat = MTLPixelFormatBGRA10_XR;
115 } else if (_isWideGamutEnabled && !self.isWideGamutSupported) {
116 PrintWideGamutWarningOnce();
117 }
118 }
119
120 [super layoutSubviews];
121 }
122
123 + (Class)layerClass {
124 return flutter::GetCoreAnimationLayerClassForRenderingAPI(
125 flutter::GetRenderingAPIForProcess(/*force_software=*/false));
126 }
127
128 - (void)drawLayer:(CALayer*)layer inContext:(CGContextRef)context {
129 TRACE_EVENT0("flutter", "SnapshotFlutterView");
130
131 if (layer != self.layer || context == nullptr) {
132 return;
133 }
134
135 auto screenshot = [_delegate takeScreenshot:flutter::Rasterizer::ScreenshotType::UncompressedImage
136 asBase64Encoded:NO];
137
138 if (!screenshot.data || screenshot.data->isEmpty() || screenshot.frame_size.isEmpty()) {
139 return;
140 }
141
142 NSData* data = [NSData dataWithBytes:const_cast<void*>(screenshot.data->data())
143 length:screenshot.data->size()];
144
145 fml::CFRef<CGDataProviderRef> image_data_provider(
146 CGDataProviderCreateWithCFData(reinterpret_cast<CFDataRef>(data)));
147
148 fml::CFRef<CGColorSpaceRef> colorspace(CGColorSpaceCreateDeviceRGB());
149
150 // Defaults for RGBA8888.
151 size_t bits_per_component = 8u;
152 size_t bits_per_pixel = 32u;
153 size_t bytes_per_row_multiplier = 4u;
154 CGBitmapInfo bitmap_info =
155 static_cast<CGBitmapInfo>(static_cast<uint32_t>(kCGImageAlphaPremultipliedLast) |
156 static_cast<uint32_t>(kCGBitmapByteOrder32Big));
157
158 switch (screenshot.pixel_format) {
159 case flutter::Rasterizer::ScreenshotFormat::kUnknown:
160 case flutter::Rasterizer::ScreenshotFormat::kR8G8B8A8UNormInt:
161 // Assume unknown is Skia and is RGBA8888. Keep defaults.
162 break;
163 case flutter::Rasterizer::ScreenshotFormat::kB8G8R8A8UNormInt:
164 // Treat this as little endian with the alpha first so that it's read backwards.
165 bitmap_info =
166 static_cast<CGBitmapInfo>(static_cast<uint32_t>(kCGImageAlphaPremultipliedFirst) |
167 static_cast<uint32_t>(kCGBitmapByteOrder32Little));
168 break;
169 case flutter::Rasterizer::ScreenshotFormat::kR16G16B16A16Float:
170 bits_per_component = 16u;
171 bits_per_pixel = 64u;
172 bytes_per_row_multiplier = 8u;
173 bitmap_info =
174 static_cast<CGBitmapInfo>(static_cast<uint32_t>(kCGImageAlphaPremultipliedLast) |
175 static_cast<uint32_t>(kCGBitmapFloatComponents) |
176 static_cast<uint32_t>(kCGBitmapByteOrder16Little));
177 break;
178 }
179
180 fml::CFRef<CGImageRef> image(CGImageCreate(
181 screenshot.frame_size.width(), // size_t width
182 screenshot.frame_size.height(), // size_t height
183 bits_per_component, // size_t bitsPerComponent
184 bits_per_pixel, // size_t bitsPerPixel,
185 bytes_per_row_multiplier * screenshot.frame_size.width(), // size_t bytesPerRow
186 colorspace, // CGColorSpaceRef space
187 bitmap_info, // CGBitmapInfo bitmapInfo
188 image_data_provider, // CGDataProviderRef provider
189 nullptr, // const CGFloat* decode
190 false, // bool shouldInterpolate
191 kCGRenderingIntentDefault // CGColorRenderingIntent intent
192 ));
193
194 const CGRect frame_rect =
195 CGRectMake(0.0, 0.0, screenshot.frame_size.width(), screenshot.frame_size.height());
196 CGContextSaveGState(context);
197 // If the CGContext is not a bitmap based context, this returns zero.
198 CGFloat height = CGBitmapContextGetHeight(context);
199 if (height == 0) {
200 height = CGFloat(screenshot.frame_size.height());
201 }
202 CGContextTranslateCTM(context, 0.0, height);
203 CGContextScaleCTM(context, 1.0, -1.0);
204 CGContextDrawImage(context, frame_rect, image);
205 CGContextRestoreGState(context);
206 }
207
208 - (BOOL)isAccessibilityElement {
209 // iOS does not provide an API to query whether the voice control
210 // is turned on or off. It is likely at least one of the assitive
211 // technologies is turned on if this method is called. If we do
212 // not catch it in notification center, we will catch it here.
213 //
214 // TODO(chunhtai): Remove this workaround once iOS provides an
215 // API to query whether voice control is enabled.
216 // https://github.com/flutter/flutter/issues/76808.
217 [self.delegate flutterViewAccessibilityDidCall];
218 return NO;
219 }
220
221 // Enables keyboard-based navigation when the user turns on
222 // full keyboard access (FKA), using existing accessibility information.
223 //
224 // iOS does not provide any API for monitoring or querying whether FKA is on,
225 // but it does call isAccessibilityElement if FKA is on,
226 // so the isAccessibilityElement implementation above will be called
227 // when the view appears and the accessibility information will most likely
228 // be available by the time the user starts to interact with the app using FKA.
229 //
230 // See SemanticsObject+UIFocusSystem.mm for more details.
231 - (NSArray<id<UIFocusItem>>*)focusItemsInRect:(CGRect)rect {
232 NSObject* rootAccessibilityElement =
233 [self.accessibilityElements count] > 0 ? self.accessibilityElements[0] : nil;
234 return [rootAccessibilityElement isKindOfClass:[SemanticsObjectContainer class]]
235 ? @[ [rootAccessibilityElement accessibilityElementAtIndex:0] ]
236 : nil;
237 }
238
239 - (NSArray<id<UIFocusEnvironment>>*)preferredFocusEnvironments {
240 // Occasionally we add subviews to FlutterView (text fields for example).
241 // These views shouldn't be directly visible to the iOS focus engine, instead
242 // the focus engine should only interact with the designated focus items
243 // (SemanticsObjects).
244 return nil;
245 }
246
247 @end
FlutterSharedApplication.h
initWithFrame
instancetype initWithFrame
Definition: FlutterTextInputPlugin.h:172
initWithCoder
instancetype initWithCoder
Definition: FlutterTextInputPlugin.h:171
FlutterView.h
SemanticsObject.h
FlutterSharedApplication
Definition: FlutterSharedApplication.h:11
FlutterSharedApplication::isAppExtension
BOOL isAppExtension
Definition: FlutterSharedApplication.h:15
FlutterView
Definition: FlutterView.h:33
SemanticsObjectContainer
Definition: SemanticsObject.h:228
FLUTTER_ASSERT_ARC
Definition: FlutterChannelKeyResponder.mm:13
flutter::GetRenderingAPIForProcess
IOSRenderingAPI GetRenderingAPIForProcess(bool force_software)
Definition: rendering_api_selection.mm:31
flutter::GetCoreAnimationLayerClassForRenderingAPI
Class GetCoreAnimationLayerClassForRenderingAPI(IOSRenderingAPI rendering_api)
Definition: rendering_api_selection.mm:59
FlutterViewEngineDelegate-p
Definition: FlutterView.h:14