Animated stickers playback improvements

This commit is contained in:
Peter 2019-07-01 19:50:39 +03:00
parent ce9aefcd53
commit afb14ea953
10 changed files with 188 additions and 41 deletions

View File

@ -225,8 +225,9 @@ final class ChatMediaInputStickerGridItemNode: GridItemNode {
self.animationNode = animationNode
self.addSubnode(animationNode)
}
self.animationNode?.setup(account: item.account, fileReference: FileMediaReference.standalone(media: item.stickerItem.file), width: 140, height: 140)
self.animationNode?.setup(account: item.account, resource: item.stickerItem.file.resource, width: 160, height: 160, mode: .cached)
self.animationNode?.visibility = self.isVisibleInGrid
self.stickerFetchedDisposable.set(freeMediaFileResourceInteractiveFetched(account: item.account, fileReference: stickerPackFileReference(item.stickerItem.file), resource: item.stickerItem.file.resource).start())
} else {
if let animationNode = self.animationNode {
animationNode.visibility = false
@ -234,8 +235,8 @@ final class ChatMediaInputStickerGridItemNode: GridItemNode {
animationNode.removeFromSupernode()
}
self.imageNode.setSignal(chatMessageSticker(account: item.account, file: item.stickerItem.file, small: true, synchronousLoad: synchronousLoads && isVisible))
self.stickerFetchedDisposable.set(freeMediaFileResourceInteractiveFetched(account: item.account, fileReference: stickerPackFileReference(item.stickerItem.file), resource: chatMessageStickerResource(file: item.stickerItem.file, small: true)).start())
}
self.stickerFetchedDisposable.set(freeMediaFileResourceInteractiveFetched(account: item.account, fileReference: stickerPackFileReference(item.stickerItem.file), resource: chatMessageStickerResource(file: item.stickerItem.file, small: true)).start())
self.currentState = (item.account, item.stickerItem, dimensions)
self.setNeedsLayout()

View File

@ -67,17 +67,54 @@ private let boundingImageSize = CGSize(width: 28.0, height: 28.0)
private let highlightSize = CGSize(width: 35.0, height: 35.0)
private let verticalOffset: CGFloat = 3.0
private enum StickerPackThumbnailItem: Equatable {
case still(TelegramMediaImageRepresentation)
case animated(MediaResource)
static func ==(lhs: StickerPackThumbnailItem, rhs: StickerPackThumbnailItem) -> Bool {
switch lhs {
case let .still(representation):
if case .still(representation) = rhs {
return true
} else {
return false
}
case let .animated(lhsResource):
if case let .animated(rhsResource) = rhs, lhsResource.isEqual(to: rhsResource) {
return true
} else {
return false
}
}
}
}
final class ChatMediaInputStickerPackItemNode: ListViewItemNode {
private let imageNode: TransformImageNode
private var animatedStickerNode: AnimatedStickerNode?
private let highlightNode: ASImageNode
var inputNodeInteraction: ChatMediaInputNodeInteraction?
var currentCollectionId: ItemCollectionId?
private var currentThumbnailItem: TelegramMediaImageRepresentation?
private var currentThumbnailItem: StickerPackThumbnailItem?
private var theme: PresentationTheme?
private let stickerFetchedDisposable = MetaDisposable()
override var visibility: ListViewItemNodeVisibility {
didSet {
self.visibilityStatus = self.visibility != .none
}
}
private var visibilityStatus: Bool = false {
didSet {
if self.visibilityStatus != oldValue {
self.animatedStickerNode?.visibility = self.visibilityStatus
}
}
}
init() {
self.highlightNode = ASImageNode()
self.highlightNode.isLayerBacked = true
@ -110,28 +147,58 @@ final class ChatMediaInputStickerPackItemNode: ListViewItemNode {
self.highlightNode.image = PresentationResourcesChat.chatMediaInputPanelHighlightedIconImage(theme)
}
var thumbnailItem: TelegramMediaImageRepresentation?
var thumbnailItem: StickerPackThumbnailItem?
var resourceReference: MediaResourceReference?
if let thumbnail = info.thumbnail {
thumbnailItem = thumbnail
resourceReference = MediaResourceReference.stickerPackThumbnail(stickerPack: .id(id: info.id.id, accessHash: info.accessHash), resource: thumbnail.resource)
} else if let item = item, let dimensions = item.file.dimensions, let resource = chatMessageStickerResource(file: item.file, small: true) as? TelegramMediaResource {
thumbnailItem = TelegramMediaImageRepresentation(dimensions: dimensions, resource: resource)
resourceReference = MediaResourceReference.media(media: .standalone(media: item.file), resource: resource)
if info.flags.contains(.isAnimated) {
thumbnailItem = .animated(thumbnail.resource)
resourceReference = MediaResourceReference.stickerPackThumbnail(stickerPack: .id(id: info.id.id, accessHash: info.accessHash), resource: thumbnail.resource)
} else {
thumbnailItem = .still(thumbnail)
resourceReference = MediaResourceReference.stickerPackThumbnail(stickerPack: .id(id: info.id.id, accessHash: info.accessHash), resource: thumbnail.resource)
}
} else if let item = item {
if item.file.isAnimatedSticker {
thumbnailItem = .animated(item.file.resource)
resourceReference = MediaResourceReference.media(media: .standalone(media: item.file), resource: item.file.resource)
} else if let dimensions = item.file.dimensions, let resource = chatMessageStickerResource(file: item.file, small: true) as? TelegramMediaResource {
thumbnailItem = .still(TelegramMediaImageRepresentation(dimensions: dimensions, resource: resource))
resourceReference = MediaResourceReference.media(media: .standalone(media: item.file), resource: resource)
}
}
if self.currentThumbnailItem != thumbnailItem {
self.currentThumbnailItem = thumbnailItem
if let thumbnailItem = thumbnailItem {
let imageSize = thumbnailItem.dimensions.aspectFitted(boundingImageSize)
let imageApply = self.imageNode.asyncLayout()(TransformImageArguments(corners: ImageCorners(), imageSize: imageSize, boundingSize: imageSize, intrinsicInsets: UIEdgeInsets()))
imageApply()
self.imageNode.setSignal(chatMessageStickerPackThumbnail(postbox: account.postbox, representation: thumbnailItem))
if let resourceReference = resourceReference {
self.stickerFetchedDisposable.set(fetchedMediaResource(postbox: account.postbox, reference: resourceReference).start())
switch thumbnailItem {
case let .still(representation):
let imageSize = representation.dimensions.aspectFitted(boundingImageSize)
let imageApply = self.imageNode.asyncLayout()(TransformImageArguments(corners: ImageCorners(), imageSize: imageSize, boundingSize: imageSize, intrinsicInsets: UIEdgeInsets()))
imageApply()
self.imageNode.setSignal(chatMessageStickerPackThumbnail(postbox: account.postbox, representation: representation))
self.imageNode.frame = CGRect(origin: CGPoint(x: floor((boundingSize.width - imageSize.width) / 2.0) + verticalOffset, y: floor((boundingSize.height - imageSize.height) / 2.0)), size: imageSize)
case let .animated(resource):
let imageSize = boundingImageSize
let animatedStickerNode: AnimatedStickerNode
if let current = self.animatedStickerNode {
animatedStickerNode = current
} else {
animatedStickerNode = AnimatedStickerNode()
self.animatedStickerNode = animatedStickerNode
animatedStickerNode.transform = CATransform3DMakeRotation(CGFloat.pi / 2.0, 0.0, 0.0, 1.0)
self.addSubnode(animatedStickerNode)
animatedStickerNode.setup(account: account, resource: resource, width: 80, height: 80, mode: .cached)
animatedStickerNode.visibility = self.visibilityStatus
}
if let animatedStickerNode = self.animatedStickerNode {
animatedStickerNode.frame = CGRect(origin: CGPoint(x: floor((boundingSize.width - imageSize.width) / 2.0) + verticalOffset, y: floor((boundingSize.height - imageSize.height) / 2.0)), size: imageSize)
}
}
if let resourceReference = resourceReference {
self.stickerFetchedDisposable.set(fetchedMediaResource(postbox: account.postbox, reference: resourceReference).start())
}
self.imageNode.frame = CGRect(origin: CGPoint(x: floor((boundingSize.width - imageSize.width) / 2.0) + verticalOffset, y: floor((boundingSize.height - imageSize.height) / 2.0)), size: imageSize)
}
self.updateIsHighlighted()

View File

@ -107,8 +107,9 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
if let telegramFile = media as? TelegramMediaFile {
if self.telegramFile?.id != telegramFile.id {
self.telegramFile = telegramFile
self.imageNode.setSignal(chatMessageAnimatedSticker(postbox: item.context.account.postbox, file: telegramFile, small: false, size: CGSize(width: 360.0, height: 360.0), thumbnail: false))
self.animationNode.setup(account: item.context.account, fileReference: .message(message: MessageReference(item.message), media: telegramFile), width: 360, height: 360)
self.imageNode.setSignal(chatMessageAnimatedSticker(postbox: item.context.account.postbox, file: telegramFile, small: false, size: CGSize(width: 400.0, height: 400.0), thumbnail: false))
self.animationNode.setup(account: item.context.account, resource: telegramFile.resource, width: 400, height: 400, mode: .cached)
self.disposable.set(freeMediaFileInteractiveFetched(account: item.context.account, fileReference: .standalone(media: telegramFile)).start())
}
break
}

View File

@ -418,7 +418,7 @@ final class ChatMessageAttachedContentNode: ASDisplayNode {
let (_, initialImageWidth, refineLayout) = contentImageLayout(context, presentationData.theme.theme, presentationData.strings, presentationData.dateTimeFormat, message, file, automaticDownload, associatedData.automaticDownloadPeerType, .constrained(CGSize(width: constrainedSize.width - horizontalInsets.left - horizontalInsets.right, height: constrainedSize.height)), layoutConstants, contentMode)
initialWidth = initialImageWidth + horizontalInsets.left + horizontalInsets.right
refineContentImageLayout = refineLayout
} else if file.isSticker, let _ = file.dimensions {
} else if file.isSticker || file.isAnimatedSticker {
let automaticDownload = shouldDownloadMediaAutomatically(settings: automaticDownloadSettings, peerType: associatedData.automaticDownloadPeerType, networkType: associatedData.automaticDownloadNetworkType, authorPeerId: message.author?.id, contactsPeerIds: associatedData.contactsPeerIds, media: file)
let (_, initialImageWidth, refineLayout) = contentImageLayout(context, presentationData.theme.theme, presentationData.strings, presentationData.dateTimeFormat, message, file, automaticDownload ? .full : .none, associatedData.automaticDownloadPeerType, .constrained(CGSize(width: constrainedSize.width - horizontalInsets.left - horizontalInsets.right, height: constrainedSize.height)), layoutConstants, contentMode)
initialWidth = initialImageWidth + horizontalInsets.left + horizontalInsets.right

View File

@ -46,6 +46,7 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode {
private var currentImageArguments: TransformImageArguments?
private var videoNode: UniversalVideoNode?
private var videoContent: NativeVideoContent?
private var animatedStickerNode: AnimatedStickerNode?
private var statusNode: RadialStatusNode?
var videoNodeDecoration: ChatBubbleVideoDecoration?
private var badgeNode: ChatMessageInteractiveMediaBadge?
@ -100,6 +101,7 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode {
videoNode.canAttachContent = false
}
}
self.animatedStickerNode?.visibility = self.visibility
self.visibilityPromise.set(self.visibility)
}
}
@ -203,7 +205,10 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode {
let imageLayout = self.imageNode.asyncLayout()
let currentVideoNode = self.videoNode
let currentAnimatedStickerNode = self.animatedStickerNode
let hasCurrentVideoNode = currentVideoNode != nil
let hasCurrentAnimatedStickerNode = currentAnimatedStickerNode != nil
let currentAutomaticDownload = self.automaticDownload
let currentAutomaticPlayback = self.automaticPlayback
@ -231,6 +236,7 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode {
}
var isInlinePlayableVideo = false
var isSticker = false
var maxDimensions = layoutConstants.image.maxDimensions
var maxHeight = layoutConstants.image.maxDimensions.height
@ -255,8 +261,9 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode {
maxDimensions = CGSize(width: constrainedSize.width, height: layoutConstants.video.maxVerticalHeight)
}
maxHeight = maxDimensions.height
} else if file.isSticker {
} else if file.isSticker || file.isAnimatedSticker {
unboundSize = unboundSize.aspectFilled(CGSize(width: 162.0, height: 162.0))
isSticker = true
}
isInlinePlayableVideo = file.isVideo && !isSecretMedia
} else if let image = media as? TelegramMediaWebFile, let dimensions = image.dimensions {
@ -283,10 +290,14 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode {
switch sizeCalculation {
case let .constrained(constrainedSize):
if unboundSize.width > unboundSize.height {
nativeSize = unboundSize.aspectFitted(constrainedSize)
if isSticker {
nativeSize = unboundSize.aspectFittedOrSmaller(constrainedSize)
} else {
nativeSize = unboundSize.aspectFitted(CGSize(width: constrainedSize.height, height: constrainedSize.width))
if unboundSize.width > unboundSize.height {
nativeSize = unboundSize.aspectFitted(constrainedSize)
} else {
nativeSize = unboundSize.aspectFitted(CGSize(width: constrainedSize.height, height: constrainedSize.width))
}
}
case .unconstrained:
nativeSize = unboundSize
@ -375,10 +386,17 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode {
}
var replaceVideoNode: Bool?
var replaceAnimatedStickerNode: Bool?
var updateVideoFile: TelegramMediaFile?
var updateAnimatedStickerFile: TelegramMediaFile?
var onlyFullSizeVideoThumbnail: Bool?
var emptyColor: UIColor = message.effectivelyIncoming(context.account.peerId) ? theme.chat.bubble.incomingMediaPlaceholderColor : theme.chat.bubble.outgoingMediaPlaceholderColor
var emptyColor: UIColor
if isSticker {
emptyColor = .clear
} else {
emptyColor = message.effectivelyIncoming(context.account.peerId) ? theme.chat.bubble.incomingMediaPlaceholderColor : theme.chat.bubble.outgoingMediaPlaceholderColor
}
if let wallpaper = media as? WallpaperPreviewMedia, case let .file(_, patternColor) = wallpaper.content {
emptyColor = patternColor ?? UIColor(rgb: 0xd6e2ee, alpha: 0.5)
}
@ -388,6 +406,9 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode {
if hasCurrentVideoNode {
replaceVideoNode = true
}
if hasCurrentAnimatedStickerNode {
replaceAnimatedStickerNode = true
}
if isSecretMedia {
updateImageSignal = { synchronousLoad in
return chatSecretPhoto(account: context.account, photoReference: .message(message: MessageReference(message), media: image))
@ -416,6 +437,9 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode {
if hasCurrentVideoNode {
replaceVideoNode = true
}
if hasCurrentAnimatedStickerNode {
replaceAnimatedStickerNode = true
}
updateImageSignal = { synchronousLoad in
return chatWebFileImage(account: context.account, file: image)
}
@ -433,7 +457,11 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode {
return chatSecretMessageVideo(account: context.account, videoReference: .message(message: MessageReference(message), media: file))
}
} else {
if file.isSticker {
if file.isAnimatedSticker {
updateImageSignal = { synchronousLoad in
return chatMessageAnimatedSticker(postbox: context.account.postbox, file: file, small: false, size: CGSize(width: 400.0, height: 400.0))
}
} else if file.isSticker {
updateImageSignal = { synchronousLoad in
return chatMessageSticker(account: context.account, file: file, small: false)
}
@ -463,6 +491,21 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode {
if hasCurrentVideoNode {
replaceVideoNode = false
}
if file.isAnimatedSticker {
updateAnimatedStickerFile = file
if hasCurrentAnimatedStickerNode {
if let currentMedia = currentMedia {
if !currentMedia.isSemanticallyEqual(to: file) {
replaceAnimatedStickerNode = true
}
} else {
replaceAnimatedStickerNode = true
}
} else {
replaceAnimatedStickerNode = true
}
}
}
updatedFetchControls = FetchControls(fetch: { manual in
@ -625,6 +668,29 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode {
}
}
if let currentReplaceAnimatedStickerNode = replaceAnimatedStickerNode {
replaceAnimatedStickerNode = nil
if currentReplaceAnimatedStickerNode, let animatedStickerNode = strongSelf.animatedStickerNode {
animatedStickerNode.removeFromSupernode()
strongSelf.animatedStickerNode = nil
}
if currentReplaceAnimatedStickerNode, let updatedAnimatedStickerFile = updateAnimatedStickerFile {
let animatedStickerNode = AnimatedStickerNode()
animatedStickerNode.isUserInteractionEnabled = false
animatedStickerNode.started = {
guard let strongSelf = self else {
return
}
strongSelf.imageNode.isHidden = true
}
strongSelf.animatedStickerNode = animatedStickerNode
animatedStickerNode.setup(account: context.account, resource: updatedAnimatedStickerFile.resource, width: 400, height: 400, mode: .cached)
strongSelf.insertSubnode(animatedStickerNode, aboveSubnode: strongSelf.imageNode)
animatedStickerNode.visibility = strongSelf.visibility
}
}
if let videoNode = strongSelf.videoNode {
if !(replaceVideoNode ?? false), let decoration = videoNode.decoration as? ChatBubbleVideoDecoration, decoration.corners != corners {
decoration.updateCorners(corners)
@ -645,6 +711,11 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode {
}
}
if let animatedStickerNode = strongSelf.animatedStickerNode {
animatedStickerNode.frame = imageFrame
animatedStickerNode.updateLayout(size: imageFrame.size)
}
if let updateImageSignal = updateImageSignal {
strongSelf.imageNode.setSignal(updateImageSignal(synchronousLoads), attemptSynchronously: synchronousLoads)
}

View File

@ -353,7 +353,7 @@ public final class ChatMessageItem: ListViewItem, CustomStringConvertible {
loop: for media in self.message.media {
if let telegramFile = media as? TelegramMediaFile {
if telegramFile.isAnimatedSticker, let size = telegramFile.size, size > 0 && size <= 64 * 1024, !telegramFile.previewRepresentations.isEmpty {
if telegramFile.isAnimatedSticker, let size = telegramFile.size, size > 0 && size <= 64 * 1024 {
viewClassName = ChatMessageAnimatedStickerItemNode.self
break loop
}

View File

@ -6,11 +6,18 @@ import SwiftSignalKit
import TelegramUIPrivateModule
final class SoftwareAnimationRenderer: ASDisplayNode, AnimationRenderer {
func render(queue: Queue, width: Int, height: Int, data: Data, completion: @escaping () -> Void) {
func render(queue: Queue, width: Int, height: Int, data: Data, type: AnimationRendererFrameType, completion: @escaping () -> Void) {
queue.async { [weak self] in
let image = generateImagePixel(CGSize(width: CGFloat(width), height: CGFloat(height)), scale: 1.0, pixelGenerator: { _, pixelData in
data.withUnsafeBytes { (bytes: UnsafePointer<UInt8>) -> Void in
decodeYUVAToRGBA(bytes, pixelData, Int32(width), Int32(height))
switch type {
case .yuva:
data.withUnsafeBytes { (bytes: UnsafePointer<UInt8>) -> Void in
decodeYUVAToRGBA(bytes, pixelData, Int32(width), Int32(height))
}
case .argb:
data.withUnsafeBytes { (bytes: UnsafePointer<UInt8>) -> Void in
memcpy(pixelData, bytes, data.count)
}
}
})

View File

@ -83,7 +83,7 @@ final class StickerPackPreviewGridItemNode: GridItemNode {
super.init()
self.addSubnode(self.imageNode)
self.addSubnode(self.textNode)
//self.addSubnode(self.textNode)
}
deinit {
@ -115,8 +115,9 @@ final class StickerPackPreviewGridItemNode: GridItemNode {
self.animationNode = animationNode
self.addSubnode(animationNode)
}
self.animationNode?.setup(account: account, fileReference: FileMediaReference.standalone(media: stickerItem.file), width: 140, height: 140)
self.animationNode?.setup(account: account, resource: stickerItem.file.resource, width: 160, height: 160, mode: .cached)
self.animationNode?.visibility = self.isVisibleInGrid
self.stickerFetchedDisposable.set(freeMediaFileResourceInteractiveFetched(account: account, fileReference: stickerPackFileReference(stickerItem.file), resource: stickerItem.file.resource).start())
} else {
if let animationNode = self.animationNode {
animationNode.visibility = false
@ -124,10 +125,9 @@ final class StickerPackPreviewGridItemNode: GridItemNode {
animationNode.removeFromSupernode()
}
self.imageNode.setSignal(chatMessageSticker(account: account, file: stickerItem.file, small: true))
self.stickerFetchedDisposable.set(freeMediaFileResourceInteractiveFetched(account: account, fileReference: stickerPackFileReference(stickerItem.file), resource: chatMessageStickerResource(file: stickerItem.file, small: true)).start())
}
self.stickerFetchedDisposable.set(freeMediaFileResourceInteractiveFetched(account: account, fileReference: stickerPackFileReference(stickerItem.file), resource: chatMessageStickerResource(file: stickerItem.file, small: true)).start())
self.currentState = (account, stickerItem, dimensions)
self.setNeedsLayout()
}

View File

@ -76,7 +76,6 @@ private final class StickerPreviewPeekContentNode: ASDisplayNode, PeekController
self.textNode = ASTextNode()
self.imageNode = TransformImageNode()
self.imageNode.addSubnode(self.textNode)
for case let .Sticker(text, _, _) in item.file.attributes {
self.textNode.attributedText = NSAttributedString(string: text, font: Font.regular(32.0), textColor: .black)
@ -87,9 +86,11 @@ private final class StickerPreviewPeekContentNode: ASDisplayNode, PeekController
let animationNode = AnimatedStickerNode()
self.animationNode = animationNode
self.animationNode?.setup(account: account, fileReference: FileMediaReference.standalone(media: item.file), width: 320, height: 320)
self.animationNode?.setup(account: account, resource: item.file.resource, width: 400, height: 400, mode: .direct)
self.animationNode?.visibility = true
self.animationNode?.addSubnode(self.textNode)
} else {
self.imageNode.addSubnode(self.textNode)
self.animationNode = nil
}

View File

@ -219,8 +219,8 @@ private func chatMessageStickerPackThumbnailData(postbox: Postbox, representatio
}
}
func chatMessageAnimationData(postbox: Postbox, fileReference: FileMediaReference, width: Int, height: Int, synchronousLoad: Bool) -> Signal<MediaResourceData, NoError> {
let maybeFetched = postbox.mediaBox.cachedResourceRepresentation(fileReference.media.resource, representation: CachedAnimatedStickerRepresentation(width: Int32(width), height: Int32(height)), pathExtension: "mp4", complete: false, fetch: false, attemptSynchronously: synchronousLoad)
func chatMessageAnimationData(postbox: Postbox, resource: MediaResource, width: Int, height: Int, synchronousLoad: Bool) -> Signal<MediaResourceData, NoError> {
let maybeFetched = postbox.mediaBox.cachedResourceRepresentation(resource, representation: CachedAnimatedStickerRepresentation(width: Int32(width), height: Int32(height)), complete: false, fetch: false, attemptSynchronously: synchronousLoad)
return maybeFetched
|> take(1)
@ -228,7 +228,7 @@ func chatMessageAnimationData(postbox: Postbox, fileReference: FileMediaReferenc
if maybeData.complete {
return .single(maybeData)
} else {
return postbox.mediaBox.cachedResourceRepresentation(fileReference.media.resource, representation: CachedAnimatedStickerRepresentation(width: Int32(width), height: Int32(height)), pathExtension: "mp4", complete: false)
return postbox.mediaBox.cachedResourceRepresentation(resource, representation: CachedAnimatedStickerRepresentation(width: Int32(width), height: Int32(height)), complete: false)
}
}
}
@ -474,12 +474,11 @@ public func chatMessageAnimatedSticker(postbox: Postbox, file: TelegramMediaFile
let fullSizeData = value._1
let fullSizeComplete = value._2
return { arguments in
let context = DrawingContext(size: arguments.drawingSize, scale: arguments.scale ?? 0.0, clear: arguments.emptyColor == nil)
let context = DrawingContext(size: arguments.drawingSize, scale: arguments.scale ?? 0.0, clear: true)
let drawingRect = arguments.drawingRect
let fittedSize = arguments.imageSize
let fittedRect = CGRect(origin: CGPoint(x: drawingRect.origin.x + (drawingRect.size.width - fittedSize.width) / 2.0, y: drawingRect.origin.y + (drawingRect.size.height - fittedSize.height) / 2.0), size: fittedSize)
//let fittedRect = arguments.drawingRect
var fullSizeImage: (UIImage, UIImage)?
if let fullSizeData = fullSizeData, fullSizeComplete {