experimental/skottie_ios: Skottie iOS/Metal App
To use, see instructions in experimental/skottie_ios/README.md . No-Try: true Change-Id: I4fb71576c5e38c7776d14561930b8c2598cfb48f Reviewed-on: https://skia-review.googlesource.com/c/skia/+/240284 Commit-Queue: Hal Canary <halcanary@google.com> Reviewed-by: Jim Van Verth <jvanverth@google.com>
This commit is contained in:
parent
d3d13af110
commit
4119443532
45
experimental/skottie_ios/BUILD.gn
Normal file
45
experimental/skottie_ios/BUILD.gn
Normal file
@ -0,0 +1,45 @@
|
||||
# Copyright 2019 Google LLC.
|
||||
# Use of this source code is governed by a BSD-style license that can be
|
||||
# found in the LICENSE file.
|
||||
|
||||
import("../../gn/ios.gni")
|
||||
|
||||
if (is_ios && skia_use_metal) {
|
||||
ios_app_bundle("skottie_ios") {
|
||||
sources = [
|
||||
"SkMetalViewBridge.h",
|
||||
"SkMetalViewBridge.mm",
|
||||
"SkottieMtkView.h",
|
||||
"SkottieMtkView.mm",
|
||||
"main.mm",
|
||||
]
|
||||
data_sources = [
|
||||
"../../resources/skottie/skottie-3d-rotation-order.json",
|
||||
"../../resources/skottie/skottie-camera-parent-3.json",
|
||||
"../../resources/skottie/skottie-gradient-ramp.json",
|
||||
"../../resources/skottie/skottie-linear-wipe-effect.json",
|
||||
"../../resources/skottie/skottie-text-animator-1.json",
|
||||
"../../resources/skottie/skottie-text-animator-2.json",
|
||||
"../../resources/skottie/skottie-text-animator-3.json",
|
||||
"../../resources/skottie/skottie-text-animator-4.json",
|
||||
"../../resources/skottie/skottie-text-animator-5.json",
|
||||
"../../resources/skottie/skottie-text-animator-8.json",
|
||||
"../../resources/skottie/skottie-transform-effect.json",
|
||||
"../../resources/skottie/skottie_sample_2.json",
|
||||
]
|
||||
deps = [
|
||||
"../..:skia",
|
||||
"../../modules/skottie",
|
||||
]
|
||||
cflags_objcc = [
|
||||
"-std=c++14",
|
||||
"-w",
|
||||
]
|
||||
libs = [
|
||||
"Metal.framework",
|
||||
"MetalKit.framework",
|
||||
"UIKit.framework",
|
||||
]
|
||||
launchscreen = "../../platform_tools/ios/app/LaunchScreen.storyboard"
|
||||
}
|
||||
}
|
29
experimental/skottie_ios/README.md
Normal file
29
experimental/skottie_ios/README.md
Normal file
@ -0,0 +1,29 @@
|
||||
##`skottie_ios`
|
||||
|
||||
How to compile:
|
||||
|
||||
cd $SKIA_ROOT_DIRECTORY
|
||||
|
||||
cat >> BUILD.gn <<EOM
|
||||
if (is_ios && skia_use_metal) {
|
||||
group("skottie_ios") {
|
||||
deps = [ "experimental/skottie_ios" ]
|
||||
}
|
||||
}
|
||||
EOM
|
||||
|
||||
mkdir -p out/ios_arm64_mtl
|
||||
cat > out/ios_arm64_mtl/args.gn <<EOM
|
||||
target_os="ios"
|
||||
target_cpu="arm64"
|
||||
skia_use_metal=true
|
||||
skia_use_expat=false
|
||||
skia_enable_pdf=false
|
||||
EOM
|
||||
|
||||
tools/git-sync-deps
|
||||
bin/gn gen out/ios_arm64_mtl
|
||||
ninja -C out/ios_arm64_mtl skottie_ios
|
||||
|
||||
Then install the `out/ios_arm64_mtl/skottie_ios.app` bundle.
|
||||
|
19
experimental/skottie_ios/SkMetalViewBridge.h
Normal file
19
experimental/skottie_ios/SkMetalViewBridge.h
Normal file
@ -0,0 +1,19 @@
|
||||
// Copyright 2019 Google LLC.
|
||||
// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
|
||||
#ifndef SkMetalViewBridge_DEFINED
|
||||
#define SkMetalViewBridge_DEFINED
|
||||
|
||||
#import <MetalKit/MetalKit.h>
|
||||
|
||||
class SkSurface;
|
||||
class GrContext;
|
||||
class GrContextOptions;
|
||||
template <typename T> class sk_sp;
|
||||
|
||||
sk_sp<SkSurface> SkMtkViewToSurface(MTKView*, GrContext*);
|
||||
|
||||
sk_sp<GrContext> SkMetalDeviceToGrContext(id<MTLDevice>, const GrContextOptions&);
|
||||
|
||||
void SkMtkViewConfigForSkia(MTKView*);
|
||||
|
||||
#endif // SkMetalViewBridge_DEFINED
|
53
experimental/skottie_ios/SkMetalViewBridge.mm
Normal file
53
experimental/skottie_ios/SkMetalViewBridge.mm
Normal file
@ -0,0 +1,53 @@
|
||||
// Copyright 2019 Google LLC.
|
||||
// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
#include "experimental/skottie_ios/SkMetalViewBridge.h"
|
||||
|
||||
#include "include/core/SkSurface.h"
|
||||
#include "include/gpu/GrBackendSurface.h"
|
||||
#include "include/gpu/GrContext.h"
|
||||
#include "include/gpu/GrContextOptions.h"
|
||||
#include "include/gpu/mtl/GrMtlTypes.h"
|
||||
|
||||
#import <Metal/Metal.h>
|
||||
#import <MetalKit/MetalKit.h>
|
||||
|
||||
sk_sp<SkSurface> SkMtkViewToSurface(MTKView* mtkView, GrContext* grContext) {
|
||||
if (![[mtkView currentDrawable] texture] ||
|
||||
!grContext ||
|
||||
MTLPixelFormatDepth32Float_Stencil8 != [mtkView depthStencilPixelFormat] ||
|
||||
MTLPixelFormatBGRA8Unorm != [mtkView colorPixelFormat]) {
|
||||
return nullptr;
|
||||
}
|
||||
const SkColorType colorType = kBGRA_8888_SkColorType; // MTLPixelFormatBGRA8Unorm
|
||||
sk_sp<SkColorSpace> colorSpace = nullptr; // MTLPixelFormatBGRA8Unorm
|
||||
const GrSurfaceOrigin origin = kTopLeft_GrSurfaceOrigin;
|
||||
const SkSurfaceProps surfaceProps(SkSurfaceProps::kLegacyFontHost_InitType);
|
||||
int sampleCount = (int)[mtkView sampleCount];
|
||||
CGSize size = [mtkView drawableSize];
|
||||
int width = (int)size.width;
|
||||
int height = (int)size.height;
|
||||
|
||||
GrMtlTextureInfo fbInfo;
|
||||
fbInfo.fTexture.retain((__bridge const void*)([[mtkView currentDrawable] texture]));
|
||||
if (sampleCount == 1) {
|
||||
GrBackendRenderTarget backendRT(width, height, 1, fbInfo);
|
||||
return SkSurface::MakeFromBackendRenderTarget(grContext, backendRT, origin,
|
||||
colorType, colorSpace, &surfaceProps);
|
||||
} else {
|
||||
GrBackendTexture backendTexture(width, height, GrMipMapped::kNo, fbInfo);
|
||||
return SkSurface::MakeFromBackendTexture(grContext, backendTexture, origin, sampleCount,
|
||||
colorType, colorSpace, &surfaceProps);
|
||||
}
|
||||
}
|
||||
|
||||
sk_sp<GrContext> SkMetalDeviceToGrContext(id<MTLDevice> device, const GrContextOptions& opts) {
|
||||
return GrContext::MakeMetal((void*)device,
|
||||
(void*)[device newCommandQueue], opts);
|
||||
}
|
||||
|
||||
void SkMtkViewConfigForSkia(MTKView* mtkView) {
|
||||
[mtkView setDepthStencilPixelFormat:MTLPixelFormatDepth32Float_Stencil8];
|
||||
[mtkView setColorPixelFormat:MTLPixelFormatBGRA8Unorm];
|
||||
[mtkView setSampleCount:1];
|
||||
}
|
19
experimental/skottie_ios/SkottieMtkView.h
Normal file
19
experimental/skottie_ios/SkottieMtkView.h
Normal file
@ -0,0 +1,19 @@
|
||||
// Copyright 2019 Google LLC.
|
||||
// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
|
||||
#ifndef SkottieMtkView_DEFINED
|
||||
#define SkottieMtkView_DEFINED
|
||||
|
||||
#import <MetalKit/MetalKit.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
class GrContext;
|
||||
|
||||
@interface SkottieMtkView : MTKView
|
||||
@property (assign) GrContext* grContext; // non-owning pointer.
|
||||
- (void)drawRect:(CGRect)rect;
|
||||
- (BOOL)loadAnimation:(NSData*)d;
|
||||
- (CGSize)size;
|
||||
- (BOOL)togglePaused;
|
||||
@end
|
||||
|
||||
#endif // SkottieMtkView_DEFINED
|
95
experimental/skottie_ios/SkottieMtkView.mm
Normal file
95
experimental/skottie_ios/SkottieMtkView.mm
Normal file
@ -0,0 +1,95 @@
|
||||
//Copyright 2019 Google LLC.
|
||||
//Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
#include "experimental/skottie_ios/SkottieMtkView.h"
|
||||
|
||||
#include "include/core/SkCanvas.h"
|
||||
#include "include/core/SkPaint.h"
|
||||
#include "include/core/SkSurface.h"
|
||||
#include "include/core/SkTime.h"
|
||||
|
||||
#include "modules/skottie/include/Skottie.h"
|
||||
|
||||
#include "experimental/skottie_ios/SkMetalViewBridge.h"
|
||||
|
||||
@implementation SkottieMtkView {
|
||||
sk_sp<skottie::Animation> fAnimation; // owner
|
||||
CGSize fSize;
|
||||
double fStartTime;
|
||||
double fTime;
|
||||
SkMatrix fMatrix;
|
||||
SkRect fAnimRect;
|
||||
bool fPaused;
|
||||
}
|
||||
|
||||
-(void)dealloc {
|
||||
fAnimation = nullptr;
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (void)drawRect:(CGRect)rect {
|
||||
[super drawRect:rect];
|
||||
// TODO(halcanary): Use the rect and the InvalidationController to speed up rendering.
|
||||
if (!fAnimation || ![[self currentDrawable] texture] || ![self grContext]) {
|
||||
return;
|
||||
}
|
||||
CGSize size = [self drawableSize];
|
||||
if (size.width != fSize.width || size.height != fSize.height) {
|
||||
float aw = fAnimRect.right(),
|
||||
ah = fAnimRect.bottom();
|
||||
if (aw > 0 && ah > 0) {
|
||||
float scale = std::min(size.width / aw, size.height / ah);
|
||||
fMatrix.setScaleTranslate(scale,
|
||||
scale,
|
||||
((float)size.width - aw * scale) * 0.5f,
|
||||
((float)size.height - ah * scale) * 0.5f);
|
||||
} else {
|
||||
fMatrix = SkMatrix();
|
||||
}
|
||||
fSize = size;
|
||||
}
|
||||
SkPaint whitePaint(SkColors::kWhite);
|
||||
if (!fPaused) {
|
||||
fTime = SkTime::GetNSecs();
|
||||
fAnimation->seekFrameTime(std::fmod(1e-9 * (fTime - fStartTime),
|
||||
fAnimation->duration()), nullptr);
|
||||
}
|
||||
sk_sp<SkSurface> surface = SkMtkViewToSurface(self, [self grContext]);
|
||||
if (!surface) {
|
||||
NSLog(@"error: no sksurface");
|
||||
return;
|
||||
}
|
||||
SkCanvas* canvas = surface->getCanvas();
|
||||
canvas->concat(fMatrix);
|
||||
canvas->drawRect(fAnimRect, whitePaint);
|
||||
fAnimation->render(canvas);
|
||||
surface->flush();
|
||||
surface = nullptr;
|
||||
[[self currentDrawable] present];
|
||||
}
|
||||
|
||||
- (BOOL)loadAnimation:(NSData*) data {
|
||||
skottie::Animation::Builder builder;
|
||||
fAnimation = builder.make((const char*)[data bytes], (size_t)[data length]);
|
||||
fTime = fStartTime = SkTime::GetNSecs();
|
||||
fSize = {0, 0};
|
||||
fAnimRect = fAnimation ? SkRect::MakeSize(fAnimation->size()) : SkRect{0, 0, 0, 0};
|
||||
return fAnimation != nullptr;
|
||||
}
|
||||
|
||||
- (CGSize)size {
|
||||
if (fAnimation) {
|
||||
const SkSize& s = fAnimation->size();
|
||||
return {(CGFloat)s.width(), (CGFloat)s.height()};
|
||||
}
|
||||
return {0, 0};
|
||||
}
|
||||
|
||||
- (BOOL)togglePaused {
|
||||
fPaused = !fPaused;
|
||||
if (!fPaused) {
|
||||
fStartTime += (SkTime::GetNSecs() - fTime);
|
||||
}
|
||||
return fPaused;
|
||||
}
|
||||
@end
|
141
experimental/skottie_ios/main.mm
Normal file
141
experimental/skottie_ios/main.mm
Normal file
@ -0,0 +1,141 @@
|
||||
// Copyright 2019 Google LLC.
|
||||
// Use of this source cofcee is governed by a BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
#include "experimental/skottie_ios/SkMetalViewBridge.h"
|
||||
#include "experimental/skottie_ios/SkottieMtkView.h"
|
||||
|
||||
#include "include/gpu/GrContext.h"
|
||||
#include "include/gpu/GrContextOptions.h"
|
||||
|
||||
#import <Metal/Metal.h>
|
||||
#import <MetalKit/MetalKit.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
static UIStackView* make_skottie_stack(CGFloat width,
|
||||
id<MTLDevice> metalDevice,
|
||||
GrContext* grContext) {
|
||||
UIStackView* stack = [[UIStackView alloc] init];
|
||||
[stack setAxis:UILayoutConstraintAxisVertical];
|
||||
[stack setDistribution:UIStackViewDistributionEqualSpacing];
|
||||
|
||||
NSBundle* mainBundle = [NSBundle mainBundle];
|
||||
NSArray<NSString*>* paths = [mainBundle pathsForResourcesOfType:@"json"
|
||||
inDirectory:nil];
|
||||
constexpr CGFloat kSpacing = 2;
|
||||
CGFloat totalHeight = kSpacing;
|
||||
for (NSUInteger i = 0; i < [paths count]; ++i) {
|
||||
NSString* path = [paths objectAtIndex:i];
|
||||
NSData* content = [NSData dataWithContentsOfFile:path];
|
||||
if (!content) {
|
||||
NSLog(@"'%@' not found", path);
|
||||
continue;
|
||||
}
|
||||
SkottieMtkView* skottieView = [[SkottieMtkView alloc] init];
|
||||
if (![skottieView loadAnimation:content]) {
|
||||
continue;
|
||||
}
|
||||
[skottieView setDevice:metalDevice];
|
||||
[skottieView setGrContext:grContext];
|
||||
SkMtkViewConfigForSkia(skottieView);
|
||||
CGSize animSize = [skottieView size];
|
||||
CGFloat height = animSize.width ? (width * animSize.height / animSize.width) : 0;
|
||||
[skottieView setFrame:{{0, 0}, {width, height}}];
|
||||
[skottieView setPreferredFramesPerSecond:30];
|
||||
[[[skottieView heightAnchor] constraintEqualToConstant:height] setActive:true];
|
||||
[[[skottieView widthAnchor] constraintEqualToConstant:width] setActive:true];
|
||||
[stack addArrangedSubview:skottieView];
|
||||
totalHeight += height + kSpacing;
|
||||
}
|
||||
[stack setFrame:{{0, 0}, {width, totalHeight}}];
|
||||
return stack;
|
||||
}
|
||||
|
||||
@interface AppViewController : UIViewController
|
||||
@property (strong) id<MTLDevice> metalDevice;
|
||||
@property (strong) UIStackView* stackView;
|
||||
@end
|
||||
|
||||
@implementation AppViewController {
|
||||
sk_sp<GrContext> fGrContext;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
fGrContext = nullptr;
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (void)loadView {
|
||||
[self setView:[[UIView alloc] init]];
|
||||
}
|
||||
|
||||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
if (!fGrContext) {
|
||||
[self setMetalDevice:MTLCreateSystemDefaultDevice()];
|
||||
if(![self metalDevice]) {
|
||||
NSLog(@"Metal is not supported on this device");
|
||||
return;
|
||||
}
|
||||
GrContextOptions grContextOptions; // set different options here.
|
||||
fGrContext = SkMetalDeviceToGrContext([self metalDevice], grContextOptions);
|
||||
}
|
||||
|
||||
[self setStackView:make_skottie_stack([[UIScreen mainScreen] bounds].size.width,
|
||||
[self metalDevice], fGrContext.get())];
|
||||
|
||||
CGFloat statusBarHeight = [[UIApplication sharedApplication] statusBarFrame].size.height;
|
||||
CGSize mainScreenSize = [[UIScreen mainScreen] bounds].size;
|
||||
CGRect scrollViewBounds = {{0, statusBarHeight},
|
||||
{mainScreenSize.width, mainScreenSize.height - statusBarHeight}};
|
||||
UIScrollView* scrollView = [[UIScrollView alloc] initWithFrame:scrollViewBounds];
|
||||
[scrollView setContentSize:[[self stackView] frame].size];
|
||||
[scrollView addSubview:[self stackView]];
|
||||
[scrollView setBackgroundColor:[UIColor blackColor]];
|
||||
|
||||
UIView* mainView = [self view];
|
||||
[mainView setBounds:{{0, 0}, mainScreenSize}];
|
||||
[mainView setBackgroundColor:[UIColor whiteColor]];
|
||||
[mainView addSubview:scrollView];
|
||||
|
||||
UITapGestureRecognizer* tapGestureRecognizer = [[UITapGestureRecognizer alloc] init];
|
||||
[tapGestureRecognizer addTarget:self action:@selector(handleTap:)];
|
||||
[mainView addGestureRecognizer:tapGestureRecognizer];
|
||||
}
|
||||
|
||||
- (void)handleTap:(UIGestureRecognizer*)sender {
|
||||
if (![sender state] == UIGestureRecognizerStateEnded) {
|
||||
return;
|
||||
}
|
||||
NSArray<UIView*>* subviews = [[self stackView] subviews];
|
||||
for (NSUInteger i = 0; i < [subviews count]; ++i) {
|
||||
UIView* subview = [subviews objectAtIndex:i];
|
||||
if (![subview isKindOfClass:[SkottieMtkView class]]) {
|
||||
continue;
|
||||
}
|
||||
SkottieMtkView* skottieView = (SkottieMtkView*)subview;
|
||||
BOOL paused = [skottieView togglePaused];
|
||||
[skottieView setEnableSetNeedsDisplay:paused];
|
||||
[skottieView setPaused:paused];
|
||||
}
|
||||
}
|
||||
@end
|
||||
|
||||
@interface AppDelegate : UIResponder <UIApplicationDelegate>
|
||||
@property (strong, nonatomic) UIWindow* window;
|
||||
@end
|
||||
|
||||
@implementation AppDelegate
|
||||
|
||||
- (BOOL)application:(UIApplication*)app didFinishLaunchingWithOptions:(NSDictionary*)ops {
|
||||
[self setWindow:[[UIWindow alloc] initWithFrame:[[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]));
|
||||
}
|
||||
}
|
@ -17,15 +17,7 @@
|
||||
'../../dm',
|
||||
'../../docs/examples',
|
||||
'../../example',
|
||||
'../../experimental/Networking/SkSockets.cpp',
|
||||
'../../experimental/Networking/SkSockets.h',
|
||||
'../../experimental/c-api-example/skia-c-example.c',
|
||||
'../../experimental/ffmpeg',
|
||||
'../../experimental/minimal_ios_mtl_skia_app/BUILD.gn',
|
||||
'../../experimental/svg/model',
|
||||
'../../experimental/tools/coreGraphicsPdf2png.cpp',
|
||||
'../../experimental/wasm-skp-debugger/debugger_bindings.cpp',
|
||||
'../../experimental/xform',
|
||||
'../../experimental',
|
||||
'../../fuzz',
|
||||
'../../gm',
|
||||
'../../gn',
|
||||
|
Loading…
Reference in New Issue
Block a user