From 70f39e2f02492a688e2d24ebbe75bd16a19c4e1f Mon Sep 17 00:00:00 2001 From: Ali <> Date: Fri, 11 Jun 2021 19:01:12 +0400 Subject: [PATCH] Cache wallpaper --- .../Sources/DefaultDayPresentationTheme.swift | 2 + .../TelegramUI/Sources/ChatController.swift | 13 +- .../Sources/WallpaperBackgroundNode.swift | 165 ++++++++++++------ 3 files changed, 126 insertions(+), 54 deletions(-) diff --git a/submodules/TelegramPresentationData/Sources/DefaultDayPresentationTheme.swift b/submodules/TelegramPresentationData/Sources/DefaultDayPresentationTheme.swift index ee7a3fecdb..bfeb3b2b75 100644 --- a/submodules/TelegramPresentationData/Sources/DefaultDayPresentationTheme.swift +++ b/submodules/TelegramPresentationData/Sources/DefaultDayPresentationTheme.swift @@ -18,6 +18,8 @@ public func selectDateFillStaticColor(theme: PresentationTheme, wallpaper: Teleg public func dateFillNeedsBlur(theme: PresentationTheme, wallpaper: TelegramWallpaper) -> Bool { if case .builtin = wallpaper { return false + } else if case .color = wallpaper { + return false } else { return true } diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index 5c5a53f100..0612126b96 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -201,6 +201,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G private let cachedDataReady = Promise() private var didSetCachedDataReady = false + + private let wallpaperReady = Promise() private var presentationInterfaceState: ChatPresentationInterfaceState private var presentationInterfaceStatePromise: ValuePromise @@ -433,6 +435,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G self.peekData = peekData self.chatBackgroundNode = WallpaperBackgroundNode(context: context, useSharedAnimationPhase: true) + self.wallpaperReady.set(chatBackgroundNode.isReady) var locationBroadcastPanelSource: LocationBroadcastPanelSource var groupCallPanelSource: GroupCallPanelSource @@ -4451,11 +4454,13 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G self.chatDisplayNode.historyNode.historyState.get(), self._chatLocationInfoReady.get(), effectiveCachedDataReady, - initialData + initialData, + self.wallpaperReady.get() ) - |> map { _, chatLocationInfoReady, cachedDataReady, _ in - return chatLocationInfoReady && cachedDataReady - }) + |> map { _, chatLocationInfoReady, cachedDataReady, _, wallpaperReady in + return chatLocationInfoReady && cachedDataReady && wallpaperReady + } + |> distinctUntilChanged) if self.context.sharedContext.immediateExperimentalUISettings.crashOnLongQueries { let _ = (self.ready.get() diff --git a/submodules/WallpaperBackgroundNode/Sources/WallpaperBackgroundNode.swift b/submodules/WallpaperBackgroundNode/Sources/WallpaperBackgroundNode.swift index 5ba23bb317..efa9ecfd78 100644 --- a/submodules/WallpaperBackgroundNode/Sources/WallpaperBackgroundNode.swift +++ b/submodules/WallpaperBackgroundNode/Sources/WallpaperBackgroundNode.swift @@ -243,13 +243,33 @@ public final class WallpaperBackgroundNode: ASDisplayNode { private let contentNode: ASDisplayNode private var gradientBackgroundNode: GradientBackgroundNode? - private let patternImageNode: TransformImageNode - private var invertPattern: Bool = false - private var patternIsLight: Bool = false + private let patternImageNode: ASImageNode private var validLayout: CGSize? private var wallpaper: TelegramWallpaper? + private struct CachedValidPatternImage { + let generate: (TransformImageArguments) -> DrawingContext? + let generated: ValidPatternGeneratedImage + let image: UIImage + } + + private static var cachedValidPatternImage: CachedValidPatternImage? + + private struct ValidPatternImage { + let wallpaper: TelegramWallpaper + let generate: (TransformImageArguments) -> DrawingContext? + } + private var validPatternImage: ValidPatternImage? + + private struct ValidPatternGeneratedImage: Equatable { + let wallpaper: TelegramWallpaper + let size: CGSize + let patternColor: UInt32 + let backgroundColor: UInt32 + } + private var validPatternGeneratedImage: ValidPatternGeneratedImage? + private let patternImageDisposable = MetaDisposable() private var bubbleTheme: PresentationTheme? @@ -257,6 +277,8 @@ public final class WallpaperBackgroundNode: ASDisplayNode { private var bubbleBackgroundNodeReferences = SparseBag() private let wallpaperDisposable = MetaDisposable() + + private let imageDisposable = MetaDisposable() private var motionEnabled: Bool = false { didSet { @@ -330,7 +352,7 @@ public final class WallpaperBackgroundNode: ASDisplayNode { self.contentNode = ASDisplayNode() self.contentNode.contentMode = self.imageContentMode - self.patternImageNode = TransformImageNode() + self.patternImageNode = ASImageNode() super.init() @@ -343,10 +365,10 @@ public final class WallpaperBackgroundNode: ASDisplayNode { deinit { self.patternImageDisposable.dispose() self.wallpaperDisposable.dispose() + self.imageDisposable.dispose() } public func update(wallpaper: TelegramWallpaper) { - let previousWallpaper = self.wallpaper if self.wallpaper == wallpaper { return } @@ -430,11 +452,25 @@ public final class WallpaperBackgroundNode: ASDisplayNode { } } + if let size = self.validLayout { + self.updateLayout(size: size, transition: .immediate) + self.updateBubbles() + } + } + + private func loadPatternForSizeIfNeeded(size: CGSize, transition: ContainedViewLayoutTransition) { + guard let wallpaper = self.wallpaper else { + return + } + + var invertPattern: Bool = false + var patternIsLight: Bool = false + switch wallpaper { case let .file(_, _, _, _, isPattern, _, _, file, settings) where isPattern: var updated = true let isLight = UIColor.average(of: settings.colors.map(UIColor.init(rgb:))).hsb.b > 0.3 - if let previousWallpaper = previousWallpaper { + if let previousWallpaper = self.validPatternImage?.wallpaper { switch previousWallpaper { case let .file(_, _, _, _, _, _, _, previousFile, _): if file.id == previousFile.id { @@ -444,35 +480,43 @@ public final class WallpaperBackgroundNode: ASDisplayNode { break } } - self.patternIsLight = isLight + patternIsLight = isLight if updated { - /*let cacheKey = PatternKey(mediaId: file.id ?? MediaId(namespace: 0, id: 0), isLight: isLight) - if let (currentKey, currentImage) = WallpaperBackgroundNode.cachedSharedPattern, currentKey == cacheKey { - }*/ + self.validPatternGeneratedImage = nil + self.validPatternImage = nil - func reference(for resource: MediaResource, media: Media, message: Message?) -> MediaResourceReference { - if let message = message { - return .media(media: .message(message: MessageReference(message), media: media), resource: resource) + if let cachedValidPatternImage = WallpaperBackgroundNode.cachedValidPatternImage, cachedValidPatternImage.generated.wallpaper == wallpaper { + self.validPatternImage = ValidPatternImage(wallpaper: cachedValidPatternImage.generated.wallpaper, generate: cachedValidPatternImage.generate) + } else { + func reference(for resource: MediaResource, media: Media, message: Message?) -> MediaResourceReference { + if let message = message { + return .media(media: .message(message: MessageReference(message), media: media), resource: resource) + } + return .wallpaper(wallpaper: nil, resource: resource) } - return .wallpaper(wallpaper: nil, resource: resource) - } - var convertedRepresentations: [ImageRepresentationWithReference] = [] - for representation in file.previewRepresentations { - convertedRepresentations.append(ImageRepresentationWithReference(representation: representation, reference: reference(for: representation.resource, media: file, message: nil))) - } - let dimensions = file.dimensions ?? PixelDimensions(width: 2000, height: 4000) - convertedRepresentations.append(ImageRepresentationWithReference(representation: .init(dimensions: dimensions, resource: file.resource, progressiveSizes: [], immediateThumbnailData: nil), reference: reference(for: file.resource, media: file, message: nil))) - - let signal = patternWallpaperImage(account: self.context.account, accountManager: self.context.sharedContext.accountManager, representations: convertedRepresentations, mode: .screen, autoFetchFullSize: true) - self.patternImageNode.imageUpdated = { [weak self] _ in - guard let strongSelf = self else { - return + var convertedRepresentations: [ImageRepresentationWithReference] = [] + for representation in file.previewRepresentations { + convertedRepresentations.append(ImageRepresentationWithReference(representation: representation, reference: reference(for: representation.resource, media: file, message: nil))) } - strongSelf._isReady.set(true) + let dimensions = file.dimensions ?? PixelDimensions(width: 2000, height: 4000) + convertedRepresentations.append(ImageRepresentationWithReference(representation: .init(dimensions: dimensions, resource: file.resource, progressiveSizes: [], immediateThumbnailData: nil), reference: reference(for: file.resource, media: file, message: nil))) + + let signal = patternWallpaperImage(account: self.context.account, accountManager: self.context.sharedContext.accountManager, representations: convertedRepresentations, mode: .screen, autoFetchFullSize: true) + self.patternImageDisposable.set((signal + |> deliverOnMainQueue).start(next: { [weak self] generator in + guard let strongSelf = self else { + return + } + strongSelf.validPatternImage = ValidPatternImage(wallpaper: wallpaper, generate: generator) + if let size = strongSelf.validLayout { + strongSelf.loadPatternForSizeIfNeeded(size: size, transition: .immediate) + } else { + strongSelf._isReady.set(true) + } + })) } - self.patternImageNode.setSignal(signal) } let intensity = CGFloat(settings.intensity ?? 50) / 100.0 if intensity < 0 { @@ -483,8 +527,8 @@ public final class WallpaperBackgroundNode: ASDisplayNode { self.patternImageNode.layer.compositingFilter = "softLightBlendMode" } self.patternImageNode.isHidden = false - self.invertPattern = intensity < 0 - if self.invertPattern { + invertPattern = intensity < 0 + if invertPattern { self.backgroundColor = .black let contentAlpha = abs(intensity) self.gradientBackgroundNode?.contentView.alpha = contentAlpha @@ -495,6 +539,8 @@ public final class WallpaperBackgroundNode: ASDisplayNode { self.contentNode.alpha = 1.0 } default: + self.patternImageDisposable.set(nil) + self.validPatternImage = nil self.patternImageNode.isHidden = true self.backgroundColor = nil self.gradientBackgroundNode?.contentView.alpha = 1.0 @@ -502,11 +548,46 @@ public final class WallpaperBackgroundNode: ASDisplayNode { self._isReady.set(true) } - self.updateBubbles() + if let validPatternImage = self.validPatternImage { + let patternBackgroundColor: UIColor + let patternColor: UIColor + if invertPattern { + patternColor = .clear + patternBackgroundColor = .clear + } else { + if patternIsLight { + patternColor = .black + } else { + patternColor = .white + } + patternBackgroundColor = .clear + } - if let size = self.validLayout { - self.updateLayout(size: size, transition: .immediate) + let updatedGeneratedImage = ValidPatternGeneratedImage(wallpaper: validPatternImage.wallpaper, size: size, patternColor: patternColor.rgb, backgroundColor: patternBackgroundColor.rgb) + + if self.validPatternGeneratedImage != updatedGeneratedImage { + self.validPatternGeneratedImage = updatedGeneratedImage + + if let cachedValidPatternImage = WallpaperBackgroundNode.cachedValidPatternImage, cachedValidPatternImage.generated == updatedGeneratedImage { + self.patternImageNode.image = cachedValidPatternImage.image + } else { + let patternArguments = TransformImageArguments(corners: ImageCorners(), imageSize: size, boundingSize: size, intrinsicInsets: UIEdgeInsets(), custom: PatternWallpaperArguments(colors: [patternBackgroundColor], rotation: nil, customPatternColor: patternColor, preview: false), scale: min(2.0, UIScreenScale)) + if let drawingContext = validPatternImage.generate(patternArguments) { + if let image = drawingContext.generateImage() { + self.patternImageNode.image = image + + if self.useSharedAnimationPhase { + WallpaperBackgroundNode.cachedValidPatternImage = CachedValidPatternImage(generate: validPatternImage.generate, generated: updatedGeneratedImage, image: image) + } + } + } + } + + self._isReady.set(true) + } } + + transition.updateFrame(node: self.patternImageNode, frame: CGRect(origin: CGPoint(), size: size)) } public func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) { @@ -521,23 +602,7 @@ public final class WallpaperBackgroundNode: ASDisplayNode { gradientBackgroundNode.updateLayout(size: size, transition: transition) } - let makeImageLayout = self.patternImageNode.asyncLayout() - let patternBackgroundColor: UIColor - let patternColor: UIColor - if self.invertPattern { - patternColor = .clear - patternBackgroundColor = .clear - } else { - if self.patternIsLight { - patternColor = .black - } else { - patternColor = .white - } - patternBackgroundColor = .clear - } - let applyImage = makeImageLayout(TransformImageArguments(corners: ImageCorners(), imageSize: size, boundingSize: size, intrinsicInsets: UIEdgeInsets(), custom: PatternWallpaperArguments(colors: [patternBackgroundColor], rotation: nil, customPatternColor: patternColor, preview: false), scale: min(2.0, UIScreenScale))) - applyImage() - transition.updateFrame(node: self.patternImageNode, frame: CGRect(origin: CGPoint(), size: size)) + self.loadPatternForSizeIfNeeded(size: size, transition: transition) if isFirstLayout && !self.frame.isEmpty { self.updateScale()