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 
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"
17 #include "third_party/skia/include/utils/mac/SkCGUtils.h"
18 
19 @implementation FlutterView {
20  id<FlutterViewEngineDelegate> _delegate;
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 
127 
128 + (BOOL)forceSoftwareRendering {
130 }
131 
132 + (void)setForceSoftwareRendering:(BOOL)forceSoftwareRendering {
133  _forceSoftwareRendering = forceSoftwareRendering;
134 }
135 
136 + (Class)layerClass {
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