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