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