2024-05-07 20:05:50 +04:00

491 lines
16 KiB
Plaintext

#include "LottieRenderTree.h"
#include "LottieRenderTreeInternal.h"
#include "Lottie/Public/Primitives/CGPath.hpp"
#include "Lottie/Public/Primitives/CGPathCocoa.h"
#include "Lottie/Public/Primitives/Color.hpp"
#include "Lottie/Public/Primitives/CALayer.hpp"
#include "Lottie/Public/Primitives/VectorsCocoa.h"
#include "RenderNode.hpp"
namespace {
}
@interface LottiePath () {
std::vector<lottie::BezierPath> _paths;
NSData *_customData;
}
@end
@implementation LottiePath
- (instancetype)initWithPaths:(std::vector<lottie::BezierPath>)paths __attribute__((objc_direct)) {
self = [super init];
if (self != nil) {
_paths = paths;
}
return self;
}
- (instancetype _Nonnull)initWithCustomData:(NSData * _Nonnull)customData __attribute__((objc_direct)) {
self = [super init];
if (self != nil) {
_customData = customData;
}
return self;
}
- (void)enumerateItems:(void (^ _Nonnull)(LottiePathItem * _Nonnull))iterate {
LottiePathItem item;
if (_customData != nil) {
int dataOffset = 0;
int dataLength = (int)_customData.length;
uint8_t const *dataBytes = (uint8_t const *)_customData.bytes;
while (dataOffset < dataLength) {
uint8_t itemType = dataBytes[dataOffset];
dataOffset += 1;
switch (itemType) {
case 0: {
Float32 px;
memcpy(&px, dataBytes + dataOffset, 4);
dataOffset += 4;
Float32 py;
memcpy(&py, dataBytes + dataOffset, 4);
dataOffset += 4;
item.type = LottiePathItemTypeMoveTo;
item.points[0] = CGPointMake(px, py);
iterate(&item);
break;
}
case 1: {
Float32 px;
memcpy(&px, dataBytes + dataOffset, 4);
dataOffset += 4;
Float32 py;
memcpy(&py, dataBytes + dataOffset, 4);
dataOffset += 4;
item.type = LottiePathItemTypeLineTo;
item.points[0] = CGPointMake(px, py);
iterate(&item);
break;
}
case 2: {
Float32 p1x;
memcpy(&p1x, dataBytes + dataOffset, 4);
dataOffset += 4;
Float32 p1y;
memcpy(&p1y, dataBytes + dataOffset, 4);
dataOffset += 4;
Float32 p2x;
memcpy(&p2x, dataBytes + dataOffset, 4);
dataOffset += 4;
Float32 p2y;
memcpy(&p2y, dataBytes + dataOffset, 4);
dataOffset += 4;
Float32 px;
memcpy(&px, dataBytes + dataOffset, 4);
dataOffset += 4;
Float32 py;
memcpy(&py, dataBytes + dataOffset, 4);
dataOffset += 4;
item.type = LottiePathItemTypeCurveTo;
item.points[0] = CGPointMake(p1x, p1y);
item.points[1] = CGPointMake(p2x, p2y);
item.points[2] = CGPointMake(px, py);
iterate(&item);
break;
}
case 3: {
item.type = LottiePathItemTypeClose;
iterate(&item);
break;
}
default: {
break;
}
}
}
} else {
for (const auto &path : _paths) {
std::optional<lottie::PathElement> previousElement;
for (const auto &element : path.elements()) {
if (previousElement.has_value()) {
if (previousElement->vertex.outTangentRelative().isZero() && element.vertex.inTangentRelative().isZero()) {
item.type = LottiePathItemTypeLineTo;
item.points[0] = CGPointMake(element.vertex.point.x, element.vertex.point.y);
iterate(&item);
} else {
item.type = LottiePathItemTypeCurveTo;
item.points[2] = CGPointMake(element.vertex.point.x, element.vertex.point.y);
item.points[1] = CGPointMake(element.vertex.inTangent.x, element.vertex.inTangent.y);
item.points[0] = CGPointMake(previousElement->vertex.outTangent.x, previousElement->vertex.outTangent.y);
iterate(&item);
}
} else {
item.type = LottiePathItemTypeMoveTo;
item.points[0] = CGPointMake(element.vertex.point.x, element.vertex.point.y);
iterate(&item);
}
previousElement = element;
}
if (path.closed().value_or(true)) {
item.type = LottiePathItemTypeClose;
iterate(&item);
}
}
}
}
@end
@implementation LottieColorStop : NSObject
- (instancetype _Nonnull)initWithColor:(LottieColor)color location:(CGFloat)location __attribute__((objc_direct)) {
self = [super init];
if (self != nil) {
_color = color;
_location = location;
}
return self;
}
@end
@implementation LottieRenderContentShading
- (instancetype _Nonnull)init {
self = [super init];
if (self != nil) {
}
return self;
}
@end
static LottieColor lottieColorFromColor(lottie::Color color) {
LottieColor result;
result.r = color.r;
result.g = color.g;
result.b = color.b;
result.a = color.a;
return result;
}
@implementation LottieRenderContentSolidShading
- (instancetype _Nonnull)initWithSolidShading:(lottie::RenderTreeNodeContent::SolidShading *)solidShading __attribute__((objc_direct)) {
self = [super init];
if (self != nil) {
_color = lottieColorFromColor(solidShading->color);
_opacity = solidShading->opacity;
}
return self;
}
- (instancetype _Nonnull)initWithColor:(LottieColor)color opacity:(CGFloat)opacity {
self = [super init];
if (self != nil) {
_color = color;
_opacity = opacity;
}
return self;
}
@end
@implementation LottieRenderContentGradientShading
- (instancetype _Nonnull)initWithGradientShading:(lottie::RenderTreeNodeContent::GradientShading *)gradientShading __attribute__((objc_direct)) {
self = [super init];
if (self != nil) {
_opacity = gradientShading->opacity;
switch (gradientShading->gradientType) {
case lottie::GradientType::Radial: {
_gradientType = LottieGradientTypeRadial;
break;
}
default: {
_gradientType = LottieGradientTypeLinear;
break;
}
}
NSMutableArray<LottieColorStop *> *colorStops = [[NSMutableArray alloc] initWithCapacity:gradientShading->colors.size()];
for (size_t i = 0; i < gradientShading->colors.size(); i++) {
[colorStops addObject:[[LottieColorStop alloc] initWithColor:lottieColorFromColor(gradientShading->colors[i]) location:gradientShading->locations[i]]];
}
_colorStops = colorStops;
_start = CGPointMake(gradientShading->start.x, gradientShading->start.y);
_end = CGPointMake(gradientShading->end.x, gradientShading->end.y);
}
return self;
}
- (instancetype _Nonnull)initWithOpacity:(CGFloat)opacity gradientType:(LottieGradientType)gradientType colorStops:(NSArray<LottieColorStop *> * _Nonnull)colorStops start:(CGPoint)start end:(CGPoint)end __attribute__((objc_direct)) {
self = [super init];
if (self != nil) {
_opacity = opacity;
_gradientType = gradientType;
_colorStops = colorStops;
_start = start;
_end = end;
}
return self;
}
@end
@implementation LottieRenderContentFill
- (instancetype _Nonnull)initWithFill:(std::shared_ptr<lottie::RenderTreeNodeContent::Fill> const &)fill __attribute__((objc_direct)) {
self = [super init];
if (self != nil) {
switch (fill->shading->type()) {
case lottie::RenderTreeNodeContent::ShadingType::Solid: {
_shading = [[LottieRenderContentSolidShading alloc] initWithSolidShading:(lottie::RenderTreeNodeContent::SolidShading *)fill->shading.get()];
break;
}
case lottie::RenderTreeNodeContent::ShadingType::Gradient: {
_shading = [[LottieRenderContentGradientShading alloc] initWithGradientShading:(lottie::RenderTreeNodeContent::GradientShading *)fill->shading.get()];
break;
}
default: {
abort();
}
}
switch (fill->rule) {
case lottie::FillRule::EvenOdd: {
_fillRule = LottieFillRuleEvenOdd;
break;
}
default: {
_fillRule = LottieFillRuleWinding;
break;
}
}
}
return self;
}
- (instancetype _Nonnull)initWithShading:(LottieRenderContentShading * _Nonnull)shading fillRule:(LottieFillRule)fillRule __attribute__((objc_direct)) {
self = [super init];
if (self != nil) {
_shading = shading;
_fillRule = fillRule;
}
return self;
}
@end
@implementation LottieRenderContentStroke
- (instancetype _Nonnull)initWithStroke:(std::shared_ptr<lottie::RenderTreeNodeContent::Stroke> const &)stroke __attribute__((objc_direct)) {
self = [super init];
if (self != nil) {
switch (stroke->shading->type()) {
case lottie::RenderTreeNodeContent::ShadingType::Solid: {
_shading = [[LottieRenderContentSolidShading alloc] initWithSolidShading:(lottie::RenderTreeNodeContent::SolidShading *)stroke->shading.get()];
break;
}
case lottie::RenderTreeNodeContent::ShadingType::Gradient: {
_shading = [[LottieRenderContentGradientShading alloc] initWithGradientShading:(lottie::RenderTreeNodeContent::GradientShading *)stroke->shading.get()];
break;
}
default: {
abort();
}
}
_lineWidth = stroke->lineWidth;
switch (stroke->lineJoin) {
case lottie::LineJoin::Miter: {
_lineJoin = kCGLineJoinMiter;
break;
}
case lottie::LineJoin::Round: {
_lineJoin = kCGLineJoinRound;
break;
}
case lottie::LineJoin::Bevel: {
_lineJoin = kCGLineJoinBevel;
break;
}
default: {
_lineJoin = kCGLineJoinBevel;
break;
}
}
switch (stroke->lineCap) {
case lottie::LineCap::Butt: {
_lineCap = kCGLineCapButt;
break;
}
case lottie::LineCap::Round: {
_lineCap = kCGLineCapRound;
break;
}
case lottie::LineCap::Square: {
_lineCap = kCGLineCapSquare;
break;
}
default: {
_lineCap = kCGLineCapSquare;
break;
}
}
_miterLimit = stroke->miterLimit;
_dashPhase = stroke->dashPhase;
if (!stroke->dashPattern.empty()) {
NSMutableArray *dashPattern = [[NSMutableArray alloc] initWithCapacity:stroke->dashPattern.size()];
for (auto value : stroke->dashPattern) {
[dashPattern addObject:@(value)];
}
_dashPattern = dashPattern;
}
}
return self;
}
- (instancetype _Nonnull)initWithShading:(LottieRenderContentShading * _Nonnull)shading lineWidth:(CGFloat)lineWidth lineJoin:(CGLineJoin)lineJoin lineCap:(CGLineCap)lineCap miterLimit:(CGFloat)miterLimit dashPhase:(CGFloat)dashPhase dashPattern:(NSArray<NSNumber *> * _Nullable)dashPattern __attribute__((objc_direct)) {
self = [super init];
if (self != nil) {
_shading = shading;
_lineWidth = lineWidth;
_lineJoin = lineJoin;
_lineCap = lineCap;
_miterLimit = miterLimit;
_dashPhase = dashPhase;
_dashPattern = dashPattern;
}
return self;
}
@end
@implementation LottieRenderContent
- (instancetype _Nonnull)initWithRenderContent:(std::shared_ptr<lottie::RenderTreeNodeContent> const &)content __attribute__((objc_direct)) {
self = [super init];
if (self != nil) {
_path = [[LottiePath alloc] initWithPaths:content->paths];
if (content->stroke) {
_stroke = [[LottieRenderContentStroke alloc] initWithStroke:content->stroke];
}
if (content->fill) {
_fill = [[LottieRenderContentFill alloc] initWithFill:content->fill];
}
}
return self;
}
- (instancetype _Nonnull)initWithPath:(LottiePath * _Nonnull)path stroke:(LottieRenderContentStroke * _Nullable)stroke fill:(LottieRenderContentFill * _Nullable)fill __attribute__((objc_direct)) {
self = [super init];
if (self != nil) {
_path = path;
_stroke = stroke;
_fill = fill;
}
return self;
}
@end
@implementation LottieRenderNode
- (instancetype _Nonnull)initWithPosition:(CGPoint)position bounds:(CGRect)bounds transform:(CATransform3D)transform opacity:(CGFloat)opacity masksToBounds:(bool)masksToBounds isHidden:(bool)isHidden globalRect:(CGRect)globalRect globalTransform:(CATransform3D)globalTransform renderContent:(LottieRenderContent * _Nullable)renderContent hasSimpleContents:(bool)hasSimpleContents isInvertedMatte:(bool)isInvertedMatte subnodes:(NSArray<LottieRenderNode *> * _Nonnull)subnodes mask:(LottieRenderNode * _Nullable)mask __attribute__((objc_direct)) {
self = [super init];
if (self != nil) {
_position = position;
_bounds = bounds;
_transform = transform;
_opacity = opacity;
_masksToBounds = masksToBounds;
_isHidden = isHidden;
_globalRect = globalRect;
_globalTransform= globalTransform;
_renderContent = renderContent;
_hasSimpleContents = hasSimpleContents;
_isInvertedMatte = isInvertedMatte;
_subnodes = subnodes;
_mask = mask;
}
return self;
}
@end
@implementation LottieRenderNode (Internal)
- (instancetype _Nonnull)initWithRenderNode:(std::shared_ptr<lottie::OutputRenderNode> const &)renderNode __attribute__((objc_direct)) {
self = [super init];
if (self != nil) {
auto position = renderNode->layer.position();
_position = CGPointMake(position.x, position.y);
auto bounds = renderNode->layer.bounds();
_bounds = CGRectMake(bounds.x, bounds.y, bounds.width, bounds.height);
_transform = lottie::nativeTransform(renderNode->layer.transform());
_opacity = renderNode->layer.opacity();
_masksToBounds = renderNode->layer.masksToBounds();
_isHidden = renderNode->layer.isHidden();
auto globalRect = renderNode->globalRect;
_globalRect = CGRectMake(globalRect.x, globalRect.y, globalRect.width, globalRect.height);
_globalTransform = lottie::nativeTransform(renderNode->globalTransform);
if (renderNode->renderContent) {
_renderContent = [[LottieRenderContent alloc] initWithRenderContent:renderNode->renderContent];
}
_hasSimpleContents = renderNode->drawContentDescendants <= 1;
_isInvertedMatte = renderNode->isInvertedMatte;
if (!renderNode->subnodes.empty()) {
NSMutableArray<LottieRenderNode *> *subnodes = [[NSMutableArray alloc] init];
for (const auto &subnode : renderNode->subnodes) {
[subnodes addObject:[[LottieRenderNode alloc] initWithRenderNode:subnode]];
}
_subnodes = subnodes;
} else {
_subnodes = [[NSArray alloc] init];
}
if (renderNode->mask) {
_mask = [[LottieRenderNode alloc] initWithRenderNode:renderNode->mask];
}
}
return self;
}
@end