1 // Copyright 2020 Google LLC.
2 // Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
4 #include "tools/skottie_ios_app/SkiaContext.h"
6 #include "include/core/SkColorSpace.h"
7 #include "include/core/SkSurface.h"
8 #include "include/core/SkTime.h"
9 #include "include/gpu/GrBackendSurface.h"
10 #include "include/gpu/GrDirectContext.h"
11 #include "include/gpu/gl/GrGLInterface.h"
12 #include "include/gpu/gl/GrGLTypes.h"
14 #import <GLKit/GLKit.h>
15 #import <UIKit/UIKit.h>
16 #import <OpenGLES/ES3/gl.h>
18 #include <CoreFoundation/CoreFoundation.h>
20 static void configure_glkview_for_skia(GLKView* view) {
21 [view setDrawableColorFormat:GLKViewDrawableColorFormatRGBA8888];
22 [view setDrawableDepthFormat:GLKViewDrawableDepthFormat24];
23 [view setDrawableStencilFormat:GLKViewDrawableStencilFormat8];
26 static sk_sp<SkSurface> make_gl_surface(GrDirectContext* dContext, int width, int height) {
27 static constexpr int kStencilBits = 8;
28 static constexpr int kSampleCount = 1;
29 static const SkSurfaceProps surfaceProps;
30 if (!dContext || width <= 0 || height <= 0) {
34 glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &fboid);
35 return SkSurface::MakeFromBackendRenderTarget(
37 GrBackendRenderTarget(width,
41 GrGLFramebufferInfo{(GrGLuint)fboid, GL_RGBA8}),
42 kBottomLeft_GrSurfaceOrigin,
43 kRGBA_8888_SkColorType,
48 // A UIView that uses a GL-backed SkSurface to draw.
49 @interface SkiaGLView : GLKView
50 @property (strong) SkiaViewController* controller;
52 // Override of the UIView interface.
53 - (void)drawRect:(CGRect)rect;
55 // Required initializer.
56 - (instancetype)initWithFrame:(CGRect)frame
57 withEAGLContext:(EAGLContext*)eaglContext
58 withDirectContext:(GrDirectContext*)dContext;
61 @implementation SkiaGLView {
62 GrDirectContext* fDContext;
65 - (instancetype)initWithFrame:(CGRect)frame
66 withEAGLContext:(EAGLContext*)eaglContext
67 withDirectContext:(GrDirectContext*)dContext {
68 self = [super initWithFrame:frame context:eaglContext];
70 configure_glkview_for_skia(self);
74 - (void)drawRect:(CGRect)rect {
75 SkiaViewController* viewController = [self controller];
76 static constexpr double kFrameRate = 1.0 / 30.0;
77 double next = [viewController isPaused] ? 0 : kFrameRate + SkTime::GetNSecs() * 1e-9;
79 [super drawRect:rect];
81 int width = (int)[self drawableWidth],
82 height = (int)[self drawableHeight];
84 NSLog(@"Error: GrDirectContext missing.\n");
87 if (sk_sp<SkSurface> surface = make_gl_surface(fDContext, width, height)) {
88 [viewController draw:rect
89 toCanvas:(surface->getCanvas())
90 atSize:CGSize{(CGFloat)width, (CGFloat)height}];
91 surface->flushAndSubmit();
94 [NSTimer scheduledTimerWithTimeInterval:std::max(0.0, next - SkTime::GetNSecs() * 1e-9)
96 selector:@selector(setNeedsDisplay)
103 @interface SkiaGLContext : SkiaContext
104 @property (strong) EAGLContext* eaglContext;
105 - (instancetype) init;
106 - (UIView*) makeViewWithController:(SkiaViewController*)vc withFrame:(CGRect)frame;
107 - (SkiaViewController*) getViewController:(UIView*)view;
110 @implementation SkiaGLContext {
111 sk_sp<GrDirectContext> fDContext;
113 - (instancetype) init {
115 [self setEaglContext:[[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3]];
116 if (![self eaglContext]) {
117 NSLog(@"Falling back to GLES2.\n");
118 [self setEaglContext:[[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2]];
120 if (![self eaglContext]) {
121 NSLog(@"[[EAGLContext alloc] initWithAPI:...] failed");
124 EAGLContext* oldContext = [EAGLContext currentContext];
125 [EAGLContext setCurrentContext:[self eaglContext]];
126 fDContext = GrDirectContext::MakeGL(nullptr, GrContextOptions());
127 [EAGLContext setCurrentContext:oldContext];
129 NSLog(@"GrDirectContext::MakeGL failed");
135 - (UIView*) makeViewWithController:(SkiaViewController*)vc withFrame:(CGRect)frame {
136 SkiaGLView* skiaView = [[SkiaGLView alloc] initWithFrame:frame
137 withEAGLContext:[self eaglContext]
138 withDirectContext:fDContext.get()];
139 [skiaView setController:vc];
142 - (SkiaViewController*) getViewController:(UIView*)view {
143 return [view isKindOfClass:[SkiaGLView class]] ? [(SkiaGLView*)view controller] : nil;
147 SkiaContext* MakeSkiaGLContext() { return [[SkiaGLContext alloc] init]; }