1// Copyright 2019 Google LLC. 2// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. 3 4// This is an example of a minimal iOS application that uses Skia to draw to 5// a Metal drawable. 6 7// Much of this code is copied from the default application created by XCode. 8 9#include "tools/skottie_ios_app/SkMetalViewBridge.h" 10 11#include "include/core/SkCanvas.h" 12#include "include/core/SkPaint.h" 13#include "include/core/SkSurface.h" 14#include "include/core/SkTime.h" 15#include "include/effects/SkGradientShader.h" 16#include "include/gpu/GrBackendSurface.h" 17#include "include/gpu/GrDirectContext.h" 18#include "include/gpu/mtl/GrMtlTypes.h" 19 20#import <Metal/Metal.h> 21#import <MetalKit/MetalKit.h> 22#import <UIKit/UIKit.h> 23 24//////////////////////////////////////////////////////////////////////////////// 25 26static void config_paint(SkPaint* paint) { 27 if (!paint->getShader()) { 28 const SkColor4f colors[2] = {SkColors::kBlack, SkColors::kWhite}; 29 const SkPoint points[2] = {{0, -1024}, {0, 1024}}; 30 paint->setShader(SkGradientShader::MakeLinear(points, colors, nullptr, nullptr, 2, 31 SkTileMode::kClamp, 0, nullptr)); 32 } 33} 34 35static void draw_example(SkSurface* surface, const SkPaint& paint, double rotation) { 36 SkCanvas* canvas = surface->getCanvas(); 37 canvas->translate(surface->width() * 0.5f, surface->height() * 0.5f); 38 canvas->rotate(rotation); 39 canvas->drawPaint(paint); 40} 41 42//////////////////////////////////////////////////////////////////////////////// 43 44@interface AppViewDelegate : NSObject <MTKViewDelegate> 45@property (assign, nonatomic) GrDirectContext* grContext; // non-owning pointer. 46@property (assign, nonatomic) id<MTLCommandQueue> metalQueue; 47@end 48 49@implementation AppViewDelegate { 50 SkPaint fPaint; 51} 52 53- (void)drawInMTKView:(nonnull MTKView *)view { 54 if (![self grContext] || !view) { 55 return; 56 } 57 // Do as much as possible before creating surface. 58 config_paint(&fPaint); 59 float rotation = (float)(180 * 1e-9 * SkTime::GetNSecs()); 60 61 // Create surface: 62 sk_sp<SkSurface> surface = SkMtkViewToSurface(view, [self grContext]); 63 if (!surface) { 64 NSLog(@"error: no sksurface"); 65 return; 66 } 67 68 draw_example(surface.get(), fPaint, rotation); 69 70 // Must flush *and* present for this to work! 71 surface->flushAndSubmit(); 72 surface = nullptr; 73 74 id<MTLCommandBuffer> commandBuffer = [[self metalQueue] commandBuffer]; 75 [commandBuffer presentDrawable:[view currentDrawable]]; 76 [commandBuffer commit]; 77} 78 79- (void)mtkView:(nonnull MTKView *)view drawableSizeWillChange:(CGSize)size { 80 // change anything on size change? 81} 82@end 83 84//////////////////////////////////////////////////////////////////////////////// 85 86@interface AppViewController : UIViewController 87@property (strong, nonatomic) id<MTLDevice> metalDevice; 88@property (strong, nonatomic) id<MTLCommandQueue> metalQueue; 89@end 90 91@implementation AppViewController { 92 GrContextHolder fGrContext; 93} 94 95- (void)loadView { 96 [self setView:[[MTKView alloc] initWithFrame:[[UIScreen mainScreen] bounds] device:nil]]; 97} 98 99- (void)viewDidLoad { 100 [super viewDidLoad]; 101 if (!fGrContext) { 102 [self setMetalDevice:MTLCreateSystemDefaultDevice()]; 103 [self setMetalQueue:[[self metalDevice] newCommandQueue]]; 104 fGrContext = SkMetalDeviceToGrContext([self metalDevice], [self metalQueue]); 105 } 106 if (![self view] || ![self metalDevice]) { 107 NSLog(@"Metal is not supported on this device"); 108 self.view = [[UIView alloc] initWithFrame:self.view.frame]; 109 return; 110 } 111 MTKView* mtkView = (MTKView*)[self view]; 112 [mtkView setDevice:[self metalDevice]]; 113 [mtkView setBackgroundColor:[UIColor blackColor]]; 114 SkMtkViewConfigForSkia(mtkView); 115 AppViewDelegate* viewDelegate = [[AppViewDelegate alloc] init]; 116 [viewDelegate setGrContext:fGrContext.get()]; 117 [viewDelegate setMetalQueue:[self metalQueue]]; 118 [viewDelegate mtkView:mtkView drawableSizeWillChange:[mtkView bounds].size]; 119 [mtkView setDelegate:viewDelegate]; 120} 121@end 122 123//////////////////////////////////////////////////////////////////////////////// 124 125@interface AppDelegate : UIResponder <UIApplicationDelegate> 126@property (strong, nonatomic) UIWindow *window; 127@end 128 129@implementation AppDelegate 130- (BOOL)application:(UIApplication *)app didFinishLaunchingWithOptions:(NSDictionary*)opts { 131 // Override point for customization after application launch. 132 [self setWindow:[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]]; 133 [[self window] setFrame:[[UIScreen mainScreen] bounds]]; 134 [[self window] setRootViewController:[[AppViewController alloc] init]]; 135 [[self window] makeKeyAndVisible]; 136 return YES; 137} 138@end 139 140//////////////////////////////////////////////////////////////////////////////// 141 142int main(int argc, char* argv[]) { 143 @autoreleasepool { 144 return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 145 } 146} 147