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