Merge commit '7621e2f8dec938cf48181c8b10afc9b01f444e68' into beta

This commit is contained in:
Ilya Laktyushin
2025-12-06 02:17:48 +04:00
commit 8344b97e03
28070 changed files with 7995182 additions and 0 deletions
@@ -0,0 +1,42 @@
//
// ImageLayer.swift
// lottie-swift
//
// Created by Brandon Withrow on 1/8/19.
//
import Foundation
/// A layer that holds an image.
final class ImageLayerModel: LayerModel {
// MARK: Lifecycle
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: ImageLayerModel.CodingKeys.self)
referenceID = try container.decode(String.self, forKey: .referenceID)
try super.init(from: decoder)
}
required init(dictionary: [String: Any]) throws {
referenceID = try dictionary.value(for: CodingKeys.referenceID)
try super.init(dictionary: dictionary)
}
// MARK: Internal
/// The reference ID of the image.
let referenceID: String
override func encode(to encoder: Encoder) throws {
try super.encode(to: encoder)
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(referenceID, forKey: .referenceID)
}
// MARK: Private
private enum CodingKeys: String, CodingKey {
case referenceID = "refId"
}
}
@@ -0,0 +1,228 @@
//
// Layer.swift
// lottie-swift
//
// Created by Brandon Withrow on 1/7/19.
//
import Foundation
// MARK: - LayerType + ClassFamily
/// Used for mapping a heterogeneous list to classes for parsing.
extension LayerType: ClassFamily {
static var discriminator: Discriminator = .type
func getType() -> AnyObject.Type {
switch self {
case .precomp:
return PreCompLayerModel.self
case .solid:
return SolidLayerModel.self
case .image:
return ImageLayerModel.self
case .null:
return LayerModel.self
case .shape:
return ShapeLayerModel.self
case .text:
return TextLayerModel.self
}
}
}
// MARK: - LayerType
public enum LayerType: Int, Codable {
case precomp
case solid
case image
case null
case shape
case text
public init(from decoder: Decoder) throws {
self = try LayerType(rawValue: decoder.singleValueContainer().decode(RawValue.self)) ?? .null
}
}
// MARK: - MatteType
public enum MatteType: Int, Codable {
case none
case add
case invert
case unknown
}
// MARK: - BlendMode
public enum BlendMode: Int, Codable {
case normal
case multiply
case screen
case overlay
case darken
case lighten
case colorDodge
case colorBurn
case hardLight
case softLight
case difference
case exclusion
case hue
case saturation
case color
case luminosity
}
// MARK: - LayerModel
/// A base top container for shapes, images, and other view objects.
class LayerModel: Codable, DictionaryInitializable {
// MARK: Lifecycle
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: LayerModel.CodingKeys.self)
name = try container.decodeIfPresent(String.self, forKey: .name) ?? "Layer"
index = try container.decodeIfPresent(Int.self, forKey: .index) ?? .random(in: Int.min...Int.max)
type = try container.decode(LayerType.self, forKey: .type)
coordinateSpace = try container.decodeIfPresent(CoordinateSpace.self, forKey: .coordinateSpace) ?? .type2d
inFrame = try container.decode(Double.self, forKey: .inFrame)
outFrame = try container.decode(Double.self, forKey: .outFrame)
startTime = try container.decode(Double.self, forKey: .startTime)
transform = try container.decode(Transform.self, forKey: .transform)
parent = try container.decodeIfPresent(Int.self, forKey: .parent)
blendMode = try container.decodeIfPresent(BlendMode.self, forKey: .blendMode) ?? .normal
masks = try container.decodeIfPresent([Mask].self, forKey: .masks)
timeStretch = try container.decodeIfPresent(Double.self, forKey: .timeStretch) ?? 1
matte = try container.decodeIfPresent(MatteType.self, forKey: .matte)
hidden = try container.decodeIfPresent(Bool.self, forKey: .hidden) ?? false
}
required init(dictionary: [String: Any]) throws {
name = (try? dictionary.value(for: CodingKeys.name)) ?? "Layer"
index = try dictionary.value(for: CodingKeys.index) ?? .random(in: Int.min...Int.max)
type = LayerType(rawValue: try dictionary.value(for: CodingKeys.type)) ?? .null
if
let coordinateSpaceRawValue = dictionary[CodingKeys.coordinateSpace.rawValue] as? Int,
let coordinateSpace = CoordinateSpace(rawValue: coordinateSpaceRawValue)
{
self.coordinateSpace = coordinateSpace
} else {
coordinateSpace = .type2d
}
inFrame = try dictionary.value(for: CodingKeys.inFrame)
outFrame = try dictionary.value(for: CodingKeys.outFrame)
startTime = try dictionary.value(for: CodingKeys.startTime)
transform = try Transform(dictionary: try dictionary.value(for: CodingKeys.transform))
parent = try? dictionary.value(for: CodingKeys.parent)
if
let blendModeRawValue = dictionary[CodingKeys.blendMode.rawValue] as? Int,
let blendMode = BlendMode(rawValue: blendModeRawValue)
{
self.blendMode = blendMode
} else {
blendMode = .normal
}
if let maskDictionaries = dictionary[CodingKeys.masks.rawValue] as? [[String: Any]] {
masks = try maskDictionaries.map({ try Mask(dictionary: $0) })
} else {
masks = nil
}
timeStretch = (try? dictionary.value(for: CodingKeys.timeStretch)) ?? 1
if let matteRawValue = dictionary[CodingKeys.matte.rawValue] as? Int {
matte = MatteType(rawValue: matteRawValue)
} else {
matte = nil
}
hidden = (try? dictionary.value(for: CodingKeys.hidden)) ?? false
}
// MARK: Internal
/// The readable name of the layer
let name: String
/// The index of the layer
let index: Int
/// The type of the layer.
let type: LayerType
/// The coordinate space
let coordinateSpace: CoordinateSpace
/// The in time of the layer in frames.
let inFrame: Double
/// The out time of the layer in frames.
let outFrame: Double
/// The start time of the layer in frames.
let startTime: Double
/// The transform of the layer
let transform: Transform
/// The index of the parent layer, if applicable.
let parent: Int?
/// The blending mode for the layer
let blendMode: BlendMode
/// An array of masks for the layer.
let masks: [Mask]?
/// A number that stretches time by a multiplier
let timeStretch: Double
/// The type of matte if any.
let matte: MatteType?
let hidden: Bool
// MARK: Fileprivate
fileprivate enum CodingKeys: String, CodingKey {
case name = "nm"
case index = "ind"
case type = "ty"
case coordinateSpace = "ddd"
case inFrame = "ip"
case outFrame = "op"
case startTime = "st"
case transform = "ks"
case parent
case blendMode = "bm"
case masks = "masksProperties"
case timeStretch = "sr"
case matte = "tt"
case hidden = "hd"
}
}
extension Array where Element == LayerModel {
static func fromDictionaries(_ dictionaries: [[String: Any]]) throws -> [LayerModel] {
try dictionaries.compactMap { dictionary in
let layerType = dictionary[LayerModel.CodingKeys.type.rawValue] as? Int
switch LayerType(rawValue: layerType ?? LayerType.null.rawValue) {
case .precomp:
return try PreCompLayerModel(dictionary: dictionary)
case .solid:
return try SolidLayerModel(dictionary: dictionary)
case .image:
return try ImageLayerModel(dictionary: dictionary)
case .null:
return try LayerModel(dictionary: dictionary)
case .shape:
return try ShapeLayerModel(dictionary: dictionary)
case .text:
return try TextLayerModel(dictionary: dictionary)
case .none:
return nil
}
}
}
}
@@ -0,0 +1,67 @@
//
// PreCompLayer.swift
// lottie-swift
//
// Created by Brandon Withrow on 1/8/19.
//
import Foundation
/// A layer that holds another animation composition.
final class PreCompLayerModel: LayerModel {
// MARK: Lifecycle
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: PreCompLayerModel.CodingKeys.self)
referenceID = try container.decode(String.self, forKey: .referenceID)
timeRemapping = try container.decodeIfPresent(KeyframeGroup<Vector1D>.self, forKey: .timeRemapping)
width = try container.decode(Double.self, forKey: .width)
height = try container.decode(Double.self, forKey: .height)
try super.init(from: decoder)
}
required init(dictionary: [String: Any]) throws {
referenceID = try dictionary.value(for: CodingKeys.referenceID)
if let timeRemappingDictionary = dictionary[CodingKeys.timeRemapping.rawValue] as? [String: Any] {
timeRemapping = try KeyframeGroup<Vector1D>(dictionary: timeRemappingDictionary)
} else {
timeRemapping = nil
}
width = try dictionary.value(for: CodingKeys.width)
height = try dictionary.value(for: CodingKeys.height)
try super.init(dictionary: dictionary)
}
// MARK: Internal
/// The reference ID of the precomp.
let referenceID: String
/// A value that remaps time over time.
let timeRemapping: KeyframeGroup<Vector1D>?
/// Precomp Width
let width: Double
/// Precomp Height
let height: Double
override func encode(to encoder: Encoder) throws {
try super.encode(to: encoder)
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(referenceID, forKey: .referenceID)
try container.encode(timeRemapping, forKey: .timeRemapping)
try container.encode(width, forKey: .width)
try container.encode(height, forKey: .height)
}
// MARK: Private
private enum CodingKeys: String, CodingKey {
case referenceID = "refId"
case timeRemapping = "tm"
case width = "w"
case height = "h"
}
}
@@ -0,0 +1,43 @@
//
// ShapeLayer.swift
// lottie-swift
//
// Created by Brandon Withrow on 1/8/19.
//
import Foundation
/// A layer that holds vector shape objects.
final class ShapeLayerModel: LayerModel {
// MARK: Lifecycle
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: ShapeLayerModel.CodingKeys.self)
items = try container.decode([ShapeItem].self, ofFamily: ShapeType.self, forKey: .items)
try super.init(from: decoder)
}
required init(dictionary: [String: Any]) throws {
let itemDictionaries: [[String: Any]] = try dictionary.value(for: CodingKeys.items)
items = try [ShapeItem].fromDictionaries(itemDictionaries)
try super.init(dictionary: dictionary)
}
// MARK: Internal
/// A list of shape items.
let items: [ShapeItem]
override func encode(to encoder: Encoder) throws {
try super.encode(to: encoder)
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(items, forKey: .items)
}
// MARK: Private
private enum CodingKeys: String, CodingKey {
case items = "shapes"
}
}
@@ -0,0 +1,56 @@
//
// SolidLayer.swift
// lottie-swift
//
// Created by Brandon Withrow on 1/8/19.
//
import Foundation
/// A layer that holds a solid color.
final class SolidLayerModel: LayerModel {
// MARK: Lifecycle
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: SolidLayerModel.CodingKeys.self)
colorHex = try container.decode(String.self, forKey: .colorHex)
width = try container.decode(Double.self, forKey: .width)
height = try container.decode(Double.self, forKey: .height)
try super.init(from: decoder)
}
required init(dictionary: [String: Any]) throws {
colorHex = try dictionary.value(for: CodingKeys.colorHex)
width = try dictionary.value(for: CodingKeys.width)
height = try dictionary.value(for: CodingKeys.height)
try super.init(dictionary: dictionary)
}
// MARK: Internal
/// The color of the solid in Hex // Change to value provider.
let colorHex: String
/// The Width of the color layer
let width: Double
/// The height of the color layer
let height: Double
override func encode(to encoder: Encoder) throws {
try super.encode(to: encoder)
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(colorHex, forKey: .colorHex)
try container.encode(width, forKey: .width)
try container.encode(height, forKey: .height)
}
// MARK: Private
private enum CodingKeys: String, CodingKey {
case colorHex = "sc"
case width = "sw"
case height = "sh"
}
}
@@ -0,0 +1,58 @@
//
// TextLayer.swift
// lottie-swift
//
// Created by Brandon Withrow on 1/8/19.
//
import Foundation
/// A layer that holds text.
final class TextLayerModel: LayerModel {
// MARK: Lifecycle
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: TextLayerModel.CodingKeys.self)
let textContainer = try container.nestedContainer(keyedBy: TextCodingKeys.self, forKey: .textGroup)
text = try textContainer.decode(KeyframeGroup<TextDocument>.self, forKey: .text)
animators = try textContainer.decode([TextAnimator].self, forKey: .animators)
try super.init(from: decoder)
}
required init(dictionary: [String: Any]) throws {
let containerDictionary: [String: Any] = try dictionary.value(for: CodingKeys.textGroup)
let textDictionary: [String: Any] = try containerDictionary.value(for: TextCodingKeys.text)
text = try KeyframeGroup<TextDocument>(dictionary: textDictionary)
let animatorDictionaries: [[String: Any]] = try containerDictionary.value(for: TextCodingKeys.animators)
animators = try animatorDictionaries.map({ try TextAnimator(dictionary: $0) })
try super.init(dictionary: dictionary)
}
// MARK: Internal
/// The text for the layer
let text: KeyframeGroup<TextDocument>
/// Text animators
let animators: [TextAnimator]
override func encode(to encoder: Encoder) throws {
try super.encode(to: encoder)
var container = encoder.container(keyedBy: CodingKeys.self)
var textContainer = container.nestedContainer(keyedBy: TextCodingKeys.self, forKey: .textGroup)
try textContainer.encode(text, forKey: .text)
try textContainer.encode(animators, forKey: .animators)
}
// MARK: Private
private enum CodingKeys: String, CodingKey {
case textGroup = "t"
}
private enum TextCodingKeys: String, CodingKey {
case text = "d"
case animators = "a"
}
}