• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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#include "flutter/shell/platform/darwin/ios/framework/Source/FlutterView.h"
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"
15#include "flutter/shell/platform/darwin/ios/framework/Source/FlutterViewController_Internal.h"
16#include "flutter/shell/platform/darwin/ios/ios_surface_gl.h"
17#include "flutter/shell/platform/darwin/ios/ios_surface_software.h"
18#include "third_party/skia/include/utils/mac/SkCGUtils.h"
19
20#if FLUTTER_SHELL_ENABLE_METAL
21#include "flutter/shell/platform/darwin/ios/ios_surface_metal.h"
22#endif  //  FLUTTER_SHELL_ENABLE_METAL
23
24@implementation FlutterView
25
26id<FlutterViewEngineDelegate> _delegate;
27
28- (instancetype)init {
29  @throw([NSException exceptionWithName:@"FlutterView must initWithDelegate"
30                                 reason:nil
31                               userInfo:nil]);
32}
33
34- (instancetype)initWithFrame:(CGRect)frame {
35  @throw([NSException exceptionWithName:@"FlutterView must initWithDelegate"
36                                 reason:nil
37                               userInfo:nil]);
38}
39
40- (instancetype)initWithCoder:(NSCoder*)aDecoder {
41  @throw([NSException exceptionWithName:@"FlutterView must initWithDelegate"
42                                 reason:nil
43                               userInfo:nil]);
44}
45
46- (instancetype)initWithDelegate:(id<FlutterViewEngineDelegate>)delegate opaque:(BOOL)opaque {
47  FML_DCHECK(delegate) << "Delegate must not be nil.";
48  self = [super initWithFrame:CGRectNull];
49
50  if (self) {
51    _delegate = delegate;
52    self.layer.opaque = opaque;
53  }
54
55  return self;
56}
57
58- (void)layoutSubviews {
59  if ([self.layer isKindOfClass:[CAEAGLLayer class]]) {
60    CAEAGLLayer* layer = reinterpret_cast<CAEAGLLayer*>(self.layer);
61    layer.allowsGroupOpacity = YES;
62    CGFloat screenScale = [UIScreen mainScreen].scale;
63    layer.contentsScale = screenScale;
64    layer.rasterizationScale = screenScale;
65  }
66
67#if FLUTTER_SHELL_ENABLE_METAL
68  if ([self.layer isKindOfClass:[CAMetalLayer class]]) {
69    CGFloat screenScale = [UIScreen mainScreen].scale;
70    self.layer.contentsScale = screenScale;
71    self.layer.rasterizationScale = screenScale;
72  }
73
74#endif  //  FLUTTER_SHELL_ENABLE_METAL
75  [super layoutSubviews];
76}
77
78+ (Class)layerClass {
79#if TARGET_IPHONE_SIMULATOR
80  return [CALayer class];
81#else  // TARGET_IPHONE_SIMULATOR
82#if FLUTTER_SHELL_ENABLE_METAL
83  return [CAMetalLayer class];
84#else   // FLUTTER_SHELL_ENABLE_METAL
85  return [CAEAGLLayer class];
86#endif  //  FLUTTER_SHELL_ENABLE_METAL
87#endif  // TARGET_IPHONE_SIMULATOR
88}
89
90- (std::unique_ptr<flutter::IOSSurface>)createSurface:
91    (std::shared_ptr<flutter::IOSGLContext>)context {
92  if ([self.layer isKindOfClass:[CAEAGLLayer class]]) {
93    fml::scoped_nsobject<CAEAGLLayer> eagl_layer(
94        reinterpret_cast<CAEAGLLayer*>([self.layer retain]));
95    if (flutter::IsIosEmbeddedViewsPreviewEnabled()) {
96      // TODO(amirh): We can lower this to iOS 8.0 once we have a Metal rendering backend.
97      // https://github.com/flutter/flutter/issues/24132
98      if (@available(iOS 9.0, *)) {
99        // TODO(amirh): only do this if there's an embedded view.
100        // https://github.com/flutter/flutter/issues/24133
101        eagl_layer.get().presentsWithTransaction = YES;
102      }
103    }
104    return std::make_unique<flutter::IOSSurfaceGL>(context, std::move(eagl_layer),
105                                                   [_delegate platformViewsController]);
106  }
107#if FLUTTER_SHELL_ENABLE_METAL
108  else if ([self.layer isKindOfClass:[CAMetalLayer class]]) {
109    return std::make_unique<flutter::IOSSurfaceMetal>(
110        fml::scoped_nsobject<CAMetalLayer>(reinterpret_cast<CAMetalLayer*>([self.layer retain])),
111        [_delegate platformViewsController]);
112  }
113#endif  //  FLUTTER_SHELL_ENABLE_METAL
114
115  else {
116    fml::scoped_nsobject<CALayer> layer(reinterpret_cast<CALayer*>([self.layer retain]));
117    return std::make_unique<flutter::IOSSurfaceSoftware>(std::move(layer),
118                                                         [_delegate platformViewsController]);
119  }
120}
121
122- (void)drawLayer:(CALayer*)layer inContext:(CGContextRef)context {
123  TRACE_EVENT0("flutter", "SnapshotFlutterView");
124
125  if (layer != self.layer || context == nullptr) {
126    return;
127  }
128
129  auto screenshot = [_delegate takeScreenshot:flutter::Rasterizer::ScreenshotType::UncompressedImage
130                              asBase64Encoded:NO];
131
132  if (!screenshot.data || screenshot.data->isEmpty() || screenshot.frame_size.isEmpty()) {
133    return;
134  }
135
136  NSData* data = [NSData dataWithBytes:const_cast<void*>(screenshot.data->data())
137                                length:screenshot.data->size()];
138
139  fml::CFRef<CGDataProviderRef> image_data_provider(
140      CGDataProviderCreateWithCFData(reinterpret_cast<CFDataRef>(data)));
141
142  fml::CFRef<CGColorSpaceRef> colorspace(CGColorSpaceCreateDeviceRGB());
143
144  fml::CFRef<CGImageRef> image(CGImageCreate(
145      screenshot.frame_size.width(),      // size_t width
146      screenshot.frame_size.height(),     // size_t height
147      8,                                  // size_t bitsPerComponent
148      32,                                 // size_t bitsPerPixel,
149      4 * screenshot.frame_size.width(),  // size_t bytesPerRow
150      colorspace,                         // CGColorSpaceRef space
151      static_cast<CGBitmapInfo>(kCGImageAlphaPremultipliedLast |
152                                kCGBitmapByteOrder32Big),  // CGBitmapInfo bitmapInfo
153      image_data_provider,                                 // CGDataProviderRef provider
154      nullptr,                                             // const CGFloat* decode
155      false,                                               // bool shouldInterpolate
156      kCGRenderingIntentDefault                            // CGColorRenderingIntent intent
157      ));
158
159  const CGRect frame_rect =
160      CGRectMake(0.0, 0.0, screenshot.frame_size.width(), screenshot.frame_size.height());
161
162  CGContextSaveGState(context);
163  CGContextTranslateCTM(context, 0.0, CGBitmapContextGetHeight(context));
164  CGContextScaleCTM(context, 1.0, -1.0);
165  CGContextDrawImage(context, frame_rect, image);
166  CGContextRestoreGState(context);
167}
168
169@end
170