Swiftgram/submodules/TelegramUniversalVideoContent/Sources/ChatBubbleVideoDecoration.swift

151 lines
6.9 KiB
Swift

import Foundation
import UIKit
import AsyncDisplayKit
import Display
import SwiftSignalKit
import UniversalMediaPlayer
import AccountContext
import PhotoResources
public enum ChatBubbleVideoDecorationContentMode {
case aspectFit
case aspectFill
}
public final class ChatBubbleVideoDecoration: UniversalVideoDecoration {
private let nativeSize: CGSize
private let contentMode: ChatBubbleVideoDecorationContentMode
public let corners: ImageCorners
public let backgroundNode: ASDisplayNode? = nil
public let contentContainerNode: ASDisplayNode
public let foregroundNode: ASDisplayNode? = nil
private var contentNode: (ASDisplayNode & UniversalVideoContentNode)?
private var validLayoutSize: CGSize?
public init(corners: ImageCorners, nativeSize: CGSize, contentMode: ChatBubbleVideoDecorationContentMode, backgroundColor: UIColor) {
self.corners = corners
self.nativeSize = nativeSize
self.contentMode = contentMode
self.contentContainerNode = ASDisplayNode()
self.contentContainerNode.backgroundColor = backgroundColor
self.contentContainerNode.clipsToBounds = true
self.updateCorners(corners)
}
public func updateCorners(_ corners: ImageCorners) {
if isRoundEqualCorners(corners) {
self.contentContainerNode.cornerRadius = corners.topLeft.radius
self.contentContainerNode.layer.mask = nil
} else {
self.contentContainerNode.cornerRadius = 0
let boundingSize: CGSize = CGSize(width: max(corners.topLeft.radius, corners.bottomLeft.radius) + max(corners.topRight.radius, corners.bottomRight.radius), height: max(corners.topLeft.radius, corners.topRight.radius) + max(corners.bottomLeft.radius, corners.bottomRight.radius))
let size: CGSize = CGSize(width: boundingSize.width + corners.extendedEdges.left + corners.extendedEdges.right, height: boundingSize.height + corners.extendedEdges.top + corners.extendedEdges.bottom)
let arguments = TransformImageArguments(corners: corners, imageSize: size, boundingSize: boundingSize, intrinsicInsets: UIEdgeInsets())
guard let context = DrawingContext(size: size, clear: true) else {
return
}
context.withContext { ctx in
ctx.setFillColor(UIColor.black.cgColor)
ctx.fill(arguments.drawingRect)
}
addCorners(context, arguments: arguments)
if let maskImage = context.generateImage() {
let mask = CALayer()
mask.contents = maskImage.cgImage
mask.contentsScale = maskImage.scale
mask.contentsCenter = CGRect(x: max(corners.topLeft.radius, corners.bottomLeft.radius) / maskImage.size.width, y: max(corners.topLeft.radius, corners.topRight.radius) / maskImage.size.height, width: (maskImage.size.width - max(corners.topLeft.radius, corners.bottomLeft.radius) - max(corners.topRight.radius, corners.bottomRight.radius)) / maskImage.size.width, height: (maskImage.size.height - max(corners.topLeft.radius, corners.topRight.radius) - max(corners.bottomLeft.radius, corners.bottomRight.radius)) / maskImage.size.height)
self.contentContainerNode.layer.mask = mask
self.contentContainerNode.layer.mask?.frame = self.contentContainerNode.bounds
}
}
}
public func updateContentNode(_ contentNode: (UniversalVideoContentNode & ASDisplayNode)?) {
if self.contentNode !== contentNode {
let previous = self.contentNode
self.contentNode = contentNode
if let previous = previous {
if previous.supernode === self.contentContainerNode {
previous.removeFromSupernode()
}
}
if let contentNode = contentNode {
if contentNode.supernode !== self.contentContainerNode {
self.contentContainerNode.addSubnode(contentNode)
if let size = self.validLayoutSize {
var scaledSize: CGSize
switch self.contentMode {
case .aspectFit:
scaledSize = self.nativeSize.aspectFitted(size)
case .aspectFill:
scaledSize = self.nativeSize.aspectFilled(size)
}
if abs(scaledSize.width - size.width) < 2.0 {
scaledSize.width = size.width
}
if abs(scaledSize.height - size.height) < 2.0 {
scaledSize.height = size.height
}
contentNode.frame = CGRect(origin: CGPoint(x: floor((size.width - scaledSize.width) / 2.0), y: floor((size.height - scaledSize.height) / 2.0)), size: scaledSize)
contentNode.updateLayout(size: scaledSize, transition: .immediate)
}
}
}
}
}
public func updateContentNodeSnapshot(_ snapshot: UIView?) {
}
public func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) {
self.validLayoutSize = size
let bounds = CGRect(origin: CGPoint(), size: size)
if let backgroundNode = self.backgroundNode {
transition.updateFrame(node: backgroundNode, frame: bounds)
}
if let foregroundNode = self.foregroundNode {
transition.updateFrame(node: foregroundNode, frame: bounds)
}
transition.updateFrame(node: self.contentContainerNode, frame: bounds)
if let maskLayer = self.contentContainerNode.layer.mask {
transition.updateFrame(layer: maskLayer, frame: bounds)
}
if let contentNode = self.contentNode {
var scaledSize: CGSize
switch self.contentMode {
case .aspectFit:
scaledSize = self.nativeSize.aspectFitted(size)
case .aspectFill:
scaledSize = self.nativeSize.aspectFilled(size)
}
if abs(scaledSize.width - size.width) < 2.0 {
scaledSize.width = size.width
}
if abs(scaledSize.height - size.height) < 2.0 {
scaledSize.height = size.height
}
transition.updateFrame(node: contentNode, frame: CGRect(origin: CGPoint(x: floor((size.width - scaledSize.width) / 2.0), y: floor((size.height - scaledSize.height) / 2.0)), size: scaledSize))
contentNode.updateLayout(size: scaledSize, transition: transition)
}
}
public func setStatus(_ status: Signal<MediaPlayerStatus?, NoError>) {
}
public func tap() {
}
}