// Copyright 2019 Google LLC. // Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. // This is an example of a minimal iOS application that uses Skia to draw to // a Metal drawable. // Much of this code is copied from the default application created by XCode. #include "tools/skottie_ios_app/SkMetalViewBridge.h" #include "include/core/SkCanvas.h" #include "include/core/SkPaint.h" #include "include/core/SkSurface.h" #include "include/core/SkTime.h" #include "include/effects/SkGradientShader.h" #include "include/gpu/GrBackendSurface.h" #include "include/gpu/GrDirectContext.h" #include "include/gpu/mtl/GrMtlTypes.h" #import #import #import //////////////////////////////////////////////////////////////////////////////// static void config_paint(SkPaint* paint) { if (!paint->getShader()) { const SkColor4f colors[2] = {SkColors::kBlack, SkColors::kWhite}; const SkPoint points[2] = {{0, -1024}, {0, 1024}}; paint->setShader(SkGradientShader::MakeLinear(points, colors, nullptr, nullptr, 2, SkTileMode::kClamp, 0, nullptr)); } } static void draw_example(SkSurface* surface, const SkPaint& paint, double rotation) { SkCanvas* canvas = surface->getCanvas(); canvas->translate(surface->width() * 0.5f, surface->height() * 0.5f); canvas->rotate(rotation); canvas->drawPaint(paint); } //////////////////////////////////////////////////////////////////////////////// @interface AppViewDelegate : NSObject @property (assign, nonatomic) GrDirectContext* grContext; // non-owning pointer. @property (assign, nonatomic) id metalQueue; @end @implementation AppViewDelegate { SkPaint fPaint; } - (void)drawInMTKView:(nonnull MTKView *)view { if (![self grContext] || !view) { return; } // Do as much as possible before creating surface. config_paint(&fPaint); float rotation = (float)(180 * 1e-9 * SkTime::GetNSecs()); // Create surface: sk_sp surface = SkMtkViewToSurface(view, [self grContext]); if (!surface) { NSLog(@"error: no sksurface"); return; } draw_example(surface.get(), fPaint, rotation); // Must flush *and* present for this to work! surface->flushAndSubmit(); surface = nullptr; id commandBuffer = [[self metalQueue] commandBuffer]; [commandBuffer presentDrawable:[view currentDrawable]]; [commandBuffer commit]; } - (void)mtkView:(nonnull MTKView *)view drawableSizeWillChange:(CGSize)size { // change anything on size change? } @end //////////////////////////////////////////////////////////////////////////////// @interface AppViewController : UIViewController @property (strong, nonatomic) id metalDevice; @property (strong, nonatomic) id metalQueue; @end @implementation AppViewController { GrContextHolder fGrContext; } - (void)loadView { [self setView:[[MTKView alloc] initWithFrame:[[UIScreen mainScreen] bounds] device:nil]]; } - (void)viewDidLoad { [super viewDidLoad]; if (!fGrContext) { [self setMetalDevice:MTLCreateSystemDefaultDevice()]; [self setMetalQueue:[[self metalDevice] newCommandQueue]]; fGrContext = SkMetalDeviceToGrContext([self metalDevice], [self metalQueue]); } if (![self view] || ![self metalDevice]) { NSLog(@"Metal is not supported on this device"); self.view = [[UIView alloc] initWithFrame:self.view.frame]; return; } MTKView* mtkView = (MTKView*)[self view]; [mtkView setDevice:[self metalDevice]]; [mtkView setBackgroundColor:[UIColor blackColor]]; SkMtkViewConfigForSkia(mtkView); AppViewDelegate* viewDelegate = [[AppViewDelegate alloc] init]; [viewDelegate setGrContext:fGrContext.get()]; [viewDelegate setMetalQueue:[self metalQueue]]; [viewDelegate mtkView:mtkView drawableSizeWillChange:[mtkView bounds].size]; [mtkView setDelegate:viewDelegate]; } @end //////////////////////////////////////////////////////////////////////////////// @interface AppDelegate : UIResponder @property (strong, nonatomic) UIWindow *window; @end @implementation AppDelegate - (BOOL)application:(UIApplication *)app didFinishLaunchingWithOptions:(NSDictionary*)opts { // Override point for customization after application launch. [self setWindow:[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]]; [[self window] setFrame:[[UIScreen mainScreen] bounds]]; [[self window] setRootViewController:[[AppViewController alloc] init]]; [[self window] makeKeyAndVisible]; return YES; } @end //////////////////////////////////////////////////////////////////////////////// int main(int argc, char* argv[]) { @autoreleasepool { return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); } }