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