Files
yuyin_ios/Pods/SVGAPlayer/Source/SVGAVectorLayer.m
2025-08-08 11:05:33 +08:00

392 lines
18 KiB
Objective-C
Executable File

//
// SVGAVectorLayer.m
// SVGAPlayer
//
// Created by 崔明辉 on 2017/2/20.
// Copyright © 2017年 UED Center. All rights reserved.
//
#import "SVGAVectorLayer.h"
#import "SVGABezierPath.h"
#import "SVGAVideoSpriteFrameEntity.h"
#import "Svga.pbobjc.h"
@interface SVGAVectorLayer ()
@property (nonatomic, strong) NSArray<SVGAVideoSpriteFrameEntity *> *frames;
@property (nonatomic, assign) NSInteger drawedFrame;
@property (nonatomic, strong) NSDictionary *keepFrameCache;
@end
@implementation SVGAVectorLayer
- (instancetype)initWithFrames:(NSArray *)frames {
self = [super init];
if (self) {
self.backgroundColor = [UIColor clearColor].CGColor;
self.masksToBounds = NO;
_frames = frames;
_keepFrameCache = [NSMutableDictionary dictionary];
[self resetKeepFrameCache];
[self stepToFrame:0];
}
return self;
}
- (void)resetKeepFrameCache {
__block NSInteger lastKeep = 0;
__block NSMutableDictionary *keepFrameCache = [NSMutableDictionary dictionary];
[self.frames enumerateObjectsUsingBlock:^(SVGAVideoSpriteFrameEntity * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
if (![self isKeepFrame:obj]) {
lastKeep = idx;
}
else {
[keepFrameCache setObject:@(lastKeep) forKey:@(idx)];
}
}];
self.keepFrameCache = [keepFrameCache copy];
}
- (void)stepToFrame:(NSInteger)frame {
if (frame < self.frames.count) {
[self drawFrame:frame];
}
}
- (BOOL)isKeepFrame:(SVGAVideoSpriteFrameEntity *)frameItem {
if (frameItem.shapes.count == 0) {
return NO;
}
else if ([frameItem.shapes.firstObject isKindOfClass:[NSDictionary class]]) {
return [frameItem.shapes.firstObject[@"type"] isKindOfClass:[NSString class]] &&
[frameItem.shapes.firstObject[@"type"] isEqualToString:@"keep"];
}
else if ([frameItem.shapes.firstObject isKindOfClass:[SVGAProtoShapeEntity class]]) {
return [(SVGAProtoShapeEntity *)frameItem.shapes.firstObject type] == SVGAProtoShapeEntity_ShapeType_Keep;
}
else {
return NO;
}
}
- (NSInteger)requestKeepFrame:(NSInteger)frame {
if ([self.keepFrameCache objectForKey:@(frame)] != nil) {
return [[self.keepFrameCache objectForKey:@(frame)] integerValue];
}
return NSNotFound;
}
- (void)drawFrame:(NSInteger)frame {
if (frame < self.frames.count) {
SVGAVideoSpriteFrameEntity *frameItem = self.frames[frame];
if ([self isKeepFrame:frameItem]) {
if (self.drawedFrame == [self requestKeepFrame:frame]) {
return;
}
}
while(self.sublayers.count) [self.sublayers.firstObject removeFromSuperlayer];
for (NSDictionary *shape in frameItem.shapes) {
if ([shape isKindOfClass:[NSDictionary class]]) {
if ([shape[@"type"] isKindOfClass:[NSString class]]) {
if ([shape[@"type"] isEqualToString:@"shape"]) {
[self addSublayer:[self createCurveLayer:shape]];
}
else if ([shape[@"type"] isEqualToString:@"ellipse"]) {
[self addSublayer:[self createEllipseLayer:shape]];
}
else if ([shape[@"type"] isEqualToString:@"rect"]) {
[self addSublayer:[self createRectLayer:shape]];
}
}
}
else if ([shape isKindOfClass:[SVGAProtoShapeEntity class]]) {
SVGAProtoShapeEntity *shapeItem = (id)shape;
if (shapeItem.type == SVGAProtoShapeEntity_ShapeType_Shape) {
[self addSublayer:[self createCurveLayerWithProto:shapeItem]];
}
else if (shapeItem.type == SVGAProtoShapeEntity_ShapeType_Ellipse) {
[self addSublayer:[self createEllipseLayerWithProto:shapeItem]];
}
else if (shapeItem.type == SVGAProtoShapeEntity_ShapeType_Rect) {
[self addSublayer:[self createRectLayerWithProto:shapeItem]];
}
}
}
self.drawedFrame = frame;
}
}
- (CALayer *)createCurveLayer:(NSDictionary *)shape {
SVGABezierPath *bezierPath = [SVGABezierPath new];
if ([shape[@"args"] isKindOfClass:[NSDictionary class]]) {
if ([shape[@"args"][@"d"] isKindOfClass:[NSString class]]) {
[bezierPath setValues:shape[@"args"][@"d"]];
}
}
CAShapeLayer *shapeLayer = [bezierPath createLayer];
[self resetStyles:shapeLayer shape:shape];
[self resetTransform:shapeLayer shape:shape];
return shapeLayer;
}
- (CALayer *)createCurveLayerWithProto:(SVGAProtoShapeEntity *)shape {
SVGABezierPath *bezierPath = [SVGABezierPath new];
if (shape.argsOneOfCase == SVGAProtoShapeEntity_Args_OneOfCase_Shape) {
if ([shape.shape.d isKindOfClass:[NSString class]] && shape.shape.d.length > 0) {
[bezierPath setValues:shape.shape.d];
}
}
CAShapeLayer *shapeLayer = [bezierPath createLayer];
[self resetStyles:shapeLayer protoShape:shape];
[self resetTransform:shapeLayer protoShape:shape];
return shapeLayer;
}
- (CALayer *)createEllipseLayer:(NSDictionary *)shape {
UIBezierPath *bezierPath;
if ([shape[@"args"] isKindOfClass:[NSDictionary class]]) {
if ([shape[@"args"][@"x"] isKindOfClass:[NSNumber class]] &&
[shape[@"args"][@"y"] isKindOfClass:[NSNumber class]] &&
[shape[@"args"][@"radiusX"] isKindOfClass:[NSNumber class]] &&
[shape[@"args"][@"radiusY"] isKindOfClass:[NSNumber class]]) {
CGFloat x = [shape[@"args"][@"x"] floatValue];
CGFloat y = [shape[@"args"][@"y"] floatValue];
CGFloat rx = [shape[@"args"][@"radiusX"] floatValue];
CGFloat ry = [shape[@"args"][@"radiusY"] floatValue];
bezierPath = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(x - rx, y - ry, rx * 2, ry * 2)];
}
}
if (bezierPath != nil) {
CAShapeLayer *shapeLayer = [CAShapeLayer layer];
[shapeLayer setPath:[bezierPath CGPath]];
[self resetStyles:shapeLayer shape:shape];
[self resetTransform:shapeLayer shape:shape];
return shapeLayer;
}
else {
return [CALayer layer];
}
}
- (CALayer *)createEllipseLayerWithProto:(SVGAProtoShapeEntity *)shape {
UIBezierPath *bezierPath;
if (shape.argsOneOfCase == SVGAProtoShapeEntity_Args_OneOfCase_Ellipse) {
bezierPath = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(shape.ellipse.x - shape.ellipse.radiusX,
shape.ellipse.y - shape.ellipse.radiusY,
shape.ellipse.radiusX * 2,
shape.ellipse.radiusY * 2)];
}
if (bezierPath != nil) {
CAShapeLayer *shapeLayer = [CAShapeLayer layer];
[shapeLayer setPath:[bezierPath CGPath]];
[self resetStyles:shapeLayer protoShape:shape];
[self resetTransform:shapeLayer protoShape:shape];
return shapeLayer;
}
else {
return [CALayer layer];
}
}
- (CALayer *)createRectLayer:(NSDictionary *)shape {
UIBezierPath *bezierPath;
if ([shape[@"args"] isKindOfClass:[NSDictionary class]]) {
if ([shape[@"args"][@"x"] isKindOfClass:[NSNumber class]] &&
[shape[@"args"][@"y"] isKindOfClass:[NSNumber class]] &&
[shape[@"args"][@"width"] isKindOfClass:[NSNumber class]] &&
[shape[@"args"][@"height"] isKindOfClass:[NSNumber class]] &&
[shape[@"args"][@"cornerRadius"] isKindOfClass:[NSNumber class]]) {
CGFloat x = [shape[@"args"][@"x"] floatValue];
CGFloat y = [shape[@"args"][@"y"] floatValue];
CGFloat width = [shape[@"args"][@"width"] floatValue];
CGFloat height = [shape[@"args"][@"height"] floatValue];
CGFloat cornerRadius = [shape[@"args"][@"cornerRadius"] floatValue];
bezierPath = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(x, y, width, height) cornerRadius:cornerRadius];
}
}
if (bezierPath != nil) {
CAShapeLayer *shapeLayer = [CAShapeLayer layer];
[shapeLayer setPath:[bezierPath CGPath]];
[self resetStyles:shapeLayer shape:shape];
[self resetTransform:shapeLayer shape:shape];
return shapeLayer;
}
else {
return [CALayer layer];
}
}
- (CALayer *)createRectLayerWithProto:(SVGAProtoShapeEntity *)shape {
UIBezierPath *bezierPath;
if (shape.argsOneOfCase == SVGAProtoShapeEntity_Args_OneOfCase_Rect) {
bezierPath = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(shape.rect.x, shape.rect.y, shape.rect.width, shape.rect.height)
cornerRadius:shape.rect.cornerRadius];
}
if (bezierPath != nil) {
CAShapeLayer *shapeLayer = [CAShapeLayer layer];
[shapeLayer setPath:[bezierPath CGPath]];
[self resetStyles:shapeLayer protoShape:shape];
[self resetTransform:shapeLayer protoShape:shape];
return shapeLayer;
}
else {
return [CALayer layer];
}
}
- (void)resetStyles:(CAShapeLayer *)shapeLayer shape:(NSDictionary *)shape {
shapeLayer.masksToBounds = NO;
shapeLayer.backgroundColor = [UIColor clearColor].CGColor;
if ([shape[@"styles"] isKindOfClass:[NSDictionary class]]) {
if ([shape[@"styles"][@"fill"] isKindOfClass:[NSArray class]]) {
NSArray *colorArray = shape[@"styles"][@"fill"];
if ([colorArray count] == 4 &&
[colorArray[0] isKindOfClass:[NSNumber class]] &&
[colorArray[1] isKindOfClass:[NSNumber class]] &&
[colorArray[2] isKindOfClass:[NSNumber class]] &&
[colorArray[3] isKindOfClass:[NSNumber class]]) {
shapeLayer.fillColor = [UIColor colorWithRed:[colorArray[0] floatValue]
green:[colorArray[1] floatValue]
blue:[colorArray[2] floatValue]
alpha:[colorArray[3] floatValue]].CGColor;
}
}
else {
shapeLayer.fillColor = [UIColor clearColor].CGColor;
}
if ([shape[@"styles"][@"stroke"] isKindOfClass:[NSArray class]]) {
NSArray *colorArray = shape[@"styles"][@"stroke"];
if ([colorArray count] == 4 &&
[colorArray[0] isKindOfClass:[NSNumber class]] &&
[colorArray[1] isKindOfClass:[NSNumber class]] &&
[colorArray[2] isKindOfClass:[NSNumber class]] &&
[colorArray[3] isKindOfClass:[NSNumber class]]) {
shapeLayer.strokeColor = [UIColor colorWithRed:[colorArray[0] floatValue]
green:[colorArray[1] floatValue]
blue:[colorArray[2] floatValue]
alpha:[colorArray[3] floatValue]].CGColor;
}
}
if ([shape[@"styles"][@"strokeWidth"] isKindOfClass:[NSNumber class]]) {
shapeLayer.lineWidth = [shape[@"styles"][@"strokeWidth"] floatValue];
}
if ([shape[@"styles"][@"lineCap"] isKindOfClass:[NSString class]]) {
shapeLayer.lineCap = shape[@"styles"][@"lineCap"];
}
if ([shape[@"styles"][@"lineJoin"] isKindOfClass:[NSString class]]) {
shapeLayer.lineJoin = shape[@"styles"][@"lineJoin"];
}
if ([shape[@"styles"][@"lineDash"] isKindOfClass:[NSArray class]]) {
BOOL accept = YES;
for (id obj in shape[@"styles"][@"lineDash"]) {
if (![obj isKindOfClass:[NSNumber class]]) {
accept = NO;
}
}
if (accept) {
if ([shape[@"styles"][@"lineDash"] count] == 3) {
shapeLayer.lineDashPhase = [shape[@"styles"][@"lineDash"][2] floatValue];
shapeLayer.lineDashPattern = @[
([shape[@"styles"][@"lineDash"][0] floatValue] < 1.0 ? @(1.0) : shape[@"styles"][@"lineDash"][0]),
([shape[@"styles"][@"lineDash"][1] floatValue] < 0.1 ? @(0.1) : shape[@"styles"][@"lineDash"][1])
];
}
}
}
if ([shape[@"styles"][@"miterLimit"] isKindOfClass:[NSNumber class]]) {
shapeLayer.miterLimit = [shape[@"styles"][@"miterLimit"] floatValue];
}
}
}
- (void)resetStyles:(CAShapeLayer *)shapeLayer protoShape:(SVGAProtoShapeEntity *)protoShape {
shapeLayer.masksToBounds = NO;
shapeLayer.backgroundColor = [UIColor clearColor].CGColor;
if (protoShape.hasStyles) {
if (protoShape.styles.hasFill) {
shapeLayer.fillColor = [UIColor colorWithRed:protoShape.styles.fill.r
green:protoShape.styles.fill.g
blue:protoShape.styles.fill.b
alpha:protoShape.styles.fill.a].CGColor;
}
else {
shapeLayer.fillColor = [UIColor clearColor].CGColor;
}
if (protoShape.styles.hasStroke) {
shapeLayer.strokeColor = [UIColor colorWithRed:protoShape.styles.stroke.r
green:protoShape.styles.stroke.g
blue:protoShape.styles.stroke.b
alpha:protoShape.styles.stroke.a].CGColor;
}
shapeLayer.lineWidth = protoShape.styles.strokeWidth;
switch (protoShape.styles.lineCap) {
case SVGAProtoShapeEntity_ShapeStyle_LineCap_LineCapButt:
shapeLayer.lineCap = @"butt";
break;
case SVGAProtoShapeEntity_ShapeStyle_LineCap_LineCapRound:
shapeLayer.lineCap = @"round";
break;
case SVGAProtoShapeEntity_ShapeStyle_LineCap_LineCapSquare:
shapeLayer.lineCap = @"square";
break;
default:
break;
}
switch (protoShape.styles.lineJoin) {
case SVGAProtoShapeEntity_ShapeStyle_LineJoin_LineJoinRound:
shapeLayer.lineJoin = @"round";
break;
case SVGAProtoShapeEntity_ShapeStyle_LineJoin_LineJoinMiter:
shapeLayer.lineJoin = @"miter";
break;
case SVGAProtoShapeEntity_ShapeStyle_LineJoin_LineJoinBevel:
shapeLayer.lineJoin = @"bevel";
break;
default:
break;
}
shapeLayer.lineDashPhase = protoShape.styles.lineDashIii;
if (protoShape.styles.lineDashI > 0.0 || protoShape.styles.lineDashIi > 0.0) {
shapeLayer.lineDashPattern = @[
(protoShape.styles.lineDashI < 1.0 ? @(1.0) : @(protoShape.styles.lineDashI)),
(protoShape.styles.lineDashIi < 0.1 ? @(0.1) : @(protoShape.styles.lineDashIi))
];
}
shapeLayer.miterLimit = protoShape.styles.miterLimit;
}
}
- (void)resetTransform:(CAShapeLayer *)shapeLayer shape:(NSDictionary *)shape {
if ([shape[@"transform"] isKindOfClass:[NSDictionary class]]) {
if ([shape[@"transform"][@"a"] isKindOfClass:[NSNumber class]] &&
[shape[@"transform"][@"b"] isKindOfClass:[NSNumber class]] &&
[shape[@"transform"][@"c"] isKindOfClass:[NSNumber class]] &&
[shape[@"transform"][@"d"] isKindOfClass:[NSNumber class]] &&
[shape[@"transform"][@"tx"] isKindOfClass:[NSNumber class]] &&
[shape[@"transform"][@"ty"] isKindOfClass:[NSNumber class]]) {
shapeLayer.transform = CATransform3DMakeAffineTransform(CGAffineTransformMake([shape[@"transform"][@"a"] floatValue],
[shape[@"transform"][@"b"] floatValue],
[shape[@"transform"][@"c"] floatValue],
[shape[@"transform"][@"d"] floatValue],
[shape[@"transform"][@"tx"] floatValue],
[shape[@"transform"][@"ty"] floatValue])
);
}
}
}
- (void)resetTransform:(CAShapeLayer *)shapeLayer protoShape:(SVGAProtoShapeEntity *)protoShape {
if (protoShape.hasTransform) {
shapeLayer.transform = CATransform3DMakeAffineTransform(CGAffineTransformMake((CGFloat)protoShape.transform.a,
(CGFloat)protoShape.transform.b,
(CGFloat)protoShape.transform.c,
(CGFloat)protoShape.transform.d,
(CGFloat)protoShape.transform.tx,
(CGFloat)protoShape.transform.ty)
);
}
}
@end