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/fml/platform/darwin/cf_utils.h"
8 
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  // This line is necessary. CoreAnimation(or UIKit) may take this to do
81  // something to compute the final frame presented on screen, if we don't set this,
82  // it will make it take long time for us to take next CAMetalDrawable and will
83  // cause constant junk during rendering.
84  self.backgroundColor = UIColor.clearColor;
85  }
86 
87  return self;
88 }
89 
90 static void PrintWideGamutWarningOnce() {
91  static BOOL did_print = NO;
92  if (did_print) {
93  return;
94  }
95  FML_DLOG(WARNING) << "Rendering wide gamut colors is turned on but isn't "
96  "supported, downgrading the color gamut to sRGB.";
97  did_print = YES;
98 }
99 
100 - (void)layoutSubviews {
101  if ([self.layer isKindOfClass:NSClassFromString(@"CAMetalLayer")]) {
102 // It is a known Apple bug that CAMetalLayer incorrectly reports its supported
103 // SDKs. It is, in fact, available since iOS 8.
104 #pragma clang diagnostic push
105 #pragma clang diagnostic ignored "-Wunguarded-availability-new"
106  CAMetalLayer* layer = (CAMetalLayer*)self.layer;
107 #pragma clang diagnostic pop
108  CGFloat screenScale = self.screen.scale;
109  layer.allowsGroupOpacity = YES;
110  layer.contentsScale = screenScale;
111  layer.rasterizationScale = screenScale;
112  layer.framebufferOnly = flutter::Settings::kSurfaceDataAccessible ? NO : YES;
113  BOOL isWideGamutSupported = self.isWideGamutSupported;
114  if (_isWideGamutEnabled && isWideGamutSupported) {
115  CGColorSpaceRef srgb = CGColorSpaceCreateWithName(kCGColorSpaceExtendedSRGB);
116  layer.colorspace = srgb;
117  CFRelease(srgb);
118  layer.pixelFormat = MTLPixelFormatBGRA10_XR;
119  } else if (_isWideGamutEnabled && !isWideGamutSupported) {
120  PrintWideGamutWarningOnce();
121  }
122  }
123 
124  [super layoutSubviews];
125 }
126 
128 
129 + (BOOL)forceSoftwareRendering {
131 }
132 
133 + (void)setForceSoftwareRendering:(BOOL)forceSoftwareRendering {
134  _forceSoftwareRendering = forceSoftwareRendering;
135 }
136 
137 + (Class)layerClass {
140 }
141 
142 - (void)drawLayer:(CALayer*)layer inContext:(CGContextRef)context {
143  TRACE_EVENT0("flutter", "SnapshotFlutterView");
144 
145  if (layer != self.layer || context == nullptr) {
146  return;
147  }
148 
149  auto screenshot = [_delegate takeScreenshot:flutter::Rasterizer::ScreenshotType::UncompressedImage
150  asBase64Encoded:NO];
151 
152  if (!screenshot.data || screenshot.data->isEmpty() || screenshot.frame_size.isEmpty()) {
153  return;
154  }
155 
156  NSData* data = [NSData dataWithBytes:const_cast<void*>(screenshot.data->data())
157  length:screenshot.data->size()];
158 
159  fml::CFRef<CGDataProviderRef> image_data_provider(
160  CGDataProviderCreateWithCFData(reinterpret_cast<CFDataRef>(data)));
161 
162  fml::CFRef<CGColorSpaceRef> colorspace(CGColorSpaceCreateDeviceRGB());
163 
164  // Defaults for RGBA8888.
165  size_t bits_per_component = 8u;
166  size_t bits_per_pixel = 32u;
167  size_t bytes_per_row_multiplier = 4u;
168  CGBitmapInfo bitmap_info =
169  static_cast<CGBitmapInfo>(static_cast<uint32_t>(kCGImageAlphaPremultipliedLast) |
170  static_cast<uint32_t>(kCGBitmapByteOrder32Big));
171 
172  switch (screenshot.pixel_format) {
173  case flutter::Rasterizer::ScreenshotFormat::kUnknown:
174  case flutter::Rasterizer::ScreenshotFormat::kR8G8B8A8UNormInt:
175  // Assume unknown is Skia and is RGBA8888. Keep defaults.
176  break;
177  case flutter::Rasterizer::ScreenshotFormat::kB8G8R8A8UNormInt:
178  // Treat this as little endian with the alpha first so that it's read backwards.
179  bitmap_info =
180  static_cast<CGBitmapInfo>(static_cast<uint32_t>(kCGImageAlphaPremultipliedFirst) |
181  static_cast<uint32_t>(kCGBitmapByteOrder32Little));
182  break;
183  case flutter::Rasterizer::ScreenshotFormat::kR16G16B16A16Float:
184  bits_per_component = 16u;
185  bits_per_pixel = 64u;
186  bytes_per_row_multiplier = 8u;
187  bitmap_info =
188  static_cast<CGBitmapInfo>(static_cast<uint32_t>(kCGImageAlphaPremultipliedLast) |
189  static_cast<uint32_t>(kCGBitmapFloatComponents) |
190  static_cast<uint32_t>(kCGBitmapByteOrder16Little));
191  break;
192  }
193 
194  fml::CFRef<CGImageRef> image(CGImageCreate(
195  screenshot.frame_size.width(), // size_t width
196  screenshot.frame_size.height(), // size_t height
197  bits_per_component, // size_t bitsPerComponent
198  bits_per_pixel, // size_t bitsPerPixel,
199  bytes_per_row_multiplier * screenshot.frame_size.width(), // size_t bytesPerRow
200  colorspace, // CGColorSpaceRef space
201  bitmap_info, // CGBitmapInfo bitmapInfo
202  image_data_provider, // CGDataProviderRef provider
203  nullptr, // const CGFloat* decode
204  false, // bool shouldInterpolate
205  kCGRenderingIntentDefault // CGColorRenderingIntent intent
206  ));
207 
208  const CGRect frame_rect =
209  CGRectMake(0.0, 0.0, screenshot.frame_size.width(), screenshot.frame_size.height());
210  CGContextSaveGState(context);
211  // If the CGContext is not a bitmap based context, this returns zero.
212  CGFloat height = CGBitmapContextGetHeight(context);
213  if (height == 0) {
214  height = CGFloat(screenshot.frame_size.height());
215  }
216  CGContextTranslateCTM(context, 0.0, height);
217  CGContextScaleCTM(context, 1.0, -1.0);
218  CGContextDrawImage(context, frame_rect, image);
219  CGContextRestoreGState(context);
220 }
221 
222 - (BOOL)isAccessibilityElement {
223  // iOS does not provide an API to query whether the voice control
224  // is turned on or off. It is likely at least one of the assitive
225  // technologies is turned on if this method is called. If we do
226  // not catch it in notification center, we will catch it here.
227  //
228  // TODO(chunhtai): Remove this workaround once iOS provides an
229  // API to query whether voice control is enabled.
230  // https://github.com/flutter/flutter/issues/76808.
231  [self.delegate flutterViewAccessibilityDidCall];
232  return NO;
233 }
234 
235 @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:127