From 8733c482c71c34e69587aaffe5800f7029441c55 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Tue, 28 Feb 2023 22:26:01 +0400 Subject: [PATCH] Wallpaper pattern tiling in landscape --- submodules/Display/Source/DeviceMetrics.swift | 2 +- .../Sources/MediaPickerSelectedListNode.swift | 2 +- .../Sources/PhotoResources.swift | 2 +- .../BubbleSettingsController.swift | 2 +- .../ForwardPrivacyChatPreviewItem.swift | 2 +- .../Reactions/ReactionChatPreviewItem.swift | 2 +- .../TextSizeSelectionController.swift | 2 +- .../ThemeAccentColorControllerNode.swift | 2 +- .../Themes/ThemePreviewControllerNode.swift | 4 +- .../Themes/ThemeSettingsChatPreviewItem.swift | 2 +- .../Sources/Themes/WallpaperGalleryItem.swift | 2 +- .../Sources/ShareController.swift | 2 +- submodules/Svg/PublicHeaders/Svg/Svg.h | 2 +- submodules/Svg/Sources/Svg.m | 19 +++- .../Sources/ChatControllerNode.swift | 7 +- .../TelegramUI/Sources/ChatQrCodeScreen.swift | 4 +- .../ChatRecentActionsControllerNode.swift | 2 +- .../MetalWallpaperBackgroundNode.swift | 2 +- .../Sources/WallpaperBackgroundNode.swift | 26 +++--- .../Sources/WallpaperResources.swift | 88 ++++++++++++------- 20 files changed, 108 insertions(+), 68 deletions(-) diff --git a/submodules/Display/Source/DeviceMetrics.swift b/submodules/Display/Source/DeviceMetrics.swift index 283aa0f63c..6db399385f 100644 --- a/submodules/Display/Source/DeviceMetrics.swift +++ b/submodules/Display/Source/DeviceMetrics.swift @@ -125,7 +125,7 @@ public enum DeviceMetrics: CaseIterable, Equatable { } } - var screenSize: CGSize { + public var screenSize: CGSize { switch self { case .iPhone4: return CGSize(width: 320.0, height: 480.0) diff --git a/submodules/MediaPickerUI/Sources/MediaPickerSelectedListNode.swift b/submodules/MediaPickerUI/Sources/MediaPickerSelectedListNode.swift index caa9b88849..3daf7cbe3b 100644 --- a/submodules/MediaPickerUI/Sources/MediaPickerSelectedListNode.swift +++ b/submodules/MediaPickerUI/Sources/MediaPickerSelectedListNode.swift @@ -1070,7 +1070,7 @@ final class MediaPickerSelectedListNode: ASDisplayNode, UIScrollViewDelegate, UI self.wallpaperBackgroundNode.update(wallpaper: wallpaper) self.wallpaperBackgroundNode.updateBubbleTheme(bubbleTheme: theme, bubbleCorners: bubbleCorners) transition.updateFrame(node: self.wallpaperBackgroundNode, frame: CGRect(origin: CGPoint(x: inset, y: 0.0), size: CGSize(width: size.width - inset * 2.0, height: size.height))) - self.wallpaperBackgroundNode.updateLayout(size: CGSize(width: size.width - inset * 2.0, height: size.height), transition: transition) + self.wallpaperBackgroundNode.updateLayout(size: CGSize(width: size.width - inset * 2.0, height: size.height), tile: false, transition: transition) self.updateItems(transition: itemsTransition) diff --git a/submodules/PhotoResources/Sources/PhotoResources.swift b/submodules/PhotoResources/Sources/PhotoResources.swift index 8f0243b8f3..c830fe9deb 100644 --- a/submodules/PhotoResources/Sources/PhotoResources.swift +++ b/submodules/PhotoResources/Sources/PhotoResources.swift @@ -2469,7 +2469,7 @@ public func svgIconImageFile(account: Account, fileReference: FileMediaReference } else { renderSize = CGSize(width: 90.0, height: 90.0) } - fullSizeImage = renderPreparedImage(data, renderSize, .clear, UIScreenScale) + fullSizeImage = renderPreparedImage(data, renderSize, .clear, UIScreenScale, false) if let image = fullSizeImage { fittedSize = image.size.aspectFitted(arguments.boundingSize) } diff --git a/submodules/SettingsUI/Sources/BubbleSettings/BubbleSettingsController.swift b/submodules/SettingsUI/Sources/BubbleSettings/BubbleSettingsController.swift index 4f30925641..569f73d169 100644 --- a/submodules/SettingsUI/Sources/BubbleSettings/BubbleSettingsController.swift +++ b/submodules/SettingsUI/Sources/BubbleSettings/BubbleSettingsController.swift @@ -285,7 +285,7 @@ private final class BubbleSettingsControllerNode: ASDisplayNode, UIScrollViewDel bottomInset = 37.0 self.chatBackgroundNode.frame = chatFrame - self.chatBackgroundNode.updateLayout(size: chatFrame.size, transition: transition) + self.chatBackgroundNode.updateLayout(size: chatFrame.size, tile: false, transition: transition) self.messagesContainerNode.frame = chatFrame transition.updateFrame(node: self.toolbarNode, frame: CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - toolbarHeight), size: CGSize(width: layout.size.width, height: toolbarHeight + layout.intrinsicInsets.bottom))) diff --git a/submodules/SettingsUI/Sources/Privacy and Security/ForwardPrivacyChatPreviewItem.swift b/submodules/SettingsUI/Sources/Privacy and Security/ForwardPrivacyChatPreviewItem.swift index be9f265359..97099c175b 100644 --- a/submodules/SettingsUI/Sources/Privacy and Security/ForwardPrivacyChatPreviewItem.swift +++ b/submodules/SettingsUI/Sources/Privacy and Security/ForwardPrivacyChatPreviewItem.swift @@ -268,7 +268,7 @@ class ForwardPrivacyChatPreviewItemNode: ListViewItemNode { backgroundNode.frame = backgroundFrame.insetBy(dx: 0.0, dy: -100.0) backgroundNode.update(wallpaper: item.wallpaper) backgroundNode.updateBubbleTheme(bubbleTheme: item.theme, bubbleCorners: item.chatBubbleCorners) - backgroundNode.updateLayout(size: backgroundNode.bounds.size, transition: .immediate) + backgroundNode.updateLayout(size: backgroundNode.bounds.size, tile: false, transition: .immediate) } strongSelf.maskNode.frame = backgroundFrame.insetBy(dx: params.leftInset, dy: 0.0) diff --git a/submodules/SettingsUI/Sources/Reactions/ReactionChatPreviewItem.swift b/submodules/SettingsUI/Sources/Reactions/ReactionChatPreviewItem.swift index eab5fdc9fc..ea4301421f 100644 --- a/submodules/SettingsUI/Sources/Reactions/ReactionChatPreviewItem.swift +++ b/submodules/SettingsUI/Sources/Reactions/ReactionChatPreviewItem.swift @@ -393,7 +393,7 @@ class ReactionChatPreviewItemNode: ListViewItemNode { backgroundNode.frame = backgroundFrame.insetBy(dx: 0.0, dy: -100.0) backgroundNode.update(wallpaper: item.wallpaper) backgroundNode.updateBubbleTheme(bubbleTheme: item.theme, bubbleCorners: item.chatBubbleCorners) - backgroundNode.updateLayout(size: backgroundNode.bounds.size, transition: .immediate) + backgroundNode.updateLayout(size: backgroundNode.bounds.size, tile: false, transition: .immediate) } strongSelf.maskNode.frame = backgroundFrame.insetBy(dx: params.leftInset, dy: 0.0) diff --git a/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift b/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift index fca1b66abb..357bc28fdb 100644 --- a/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift +++ b/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift @@ -564,7 +564,7 @@ private final class TextSizeSelectionControllerNode: ASDisplayNode, UIScrollView } self.chatBackgroundNode.frame = chatFrame - self.chatBackgroundNode.updateLayout(size: chatFrame.size, transition: transition) + self.chatBackgroundNode.updateLayout(size: chatFrame.size, tile: false, transition: transition) self.messagesContainerNode.frame = chatFrame transition.updateFrame(node: self.toolbarNode, frame: CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - toolbarHeight), size: CGSize(width: layout.size.width, height: toolbarHeight + layout.intrinsicInsets.bottom))) diff --git a/submodules/SettingsUI/Sources/Themes/ThemeAccentColorControllerNode.swift b/submodules/SettingsUI/Sources/Themes/ThemeAccentColorControllerNode.swift index 312468a5b4..29704b9d12 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemeAccentColorControllerNode.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemeAccentColorControllerNode.swift @@ -1198,7 +1198,7 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate transition.updateFrame(node: self.backgroundContainerNode, frame: CGRect(origin: CGPoint(), size: backgroundSize)) transition.updateFrame(node: self.backgroundNode, frame: CGRect(origin: CGPoint(), size: layout.size)) - self.backgroundNode.updateLayout(size: self.backgroundNode.bounds.size, transition: transition) + self.backgroundNode.updateLayout(size: self.backgroundNode.bounds.size, tile: false, transition: transition) transition.updatePosition(node: self.backgroundWrapperNode, position: CGPoint(x: backgroundSize.width / 2.0, y: backgroundSize.height / 2.0)) diff --git a/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift b/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift index 4e6314a2d4..487eac0b8a 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift @@ -712,11 +712,11 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate { self.messagesContainerNode.frame = self.chatContainerNode.bounds self.instantChatBackgroundNode.frame = self.chatContainerNode.bounds - self.instantChatBackgroundNode.updateLayout(size: self.instantChatBackgroundNode.bounds.size, transition: .immediate) + self.instantChatBackgroundNode.updateLayout(size: self.instantChatBackgroundNode.bounds.size, tile: false, transition: .immediate) self.remoteChatBackgroundNode.frame = self.chatContainerNode.bounds self.blurredNode.frame = self.chatContainerNode.bounds self.wallpaperNode.frame = self.chatContainerNode.bounds - self.wallpaperNode.updateLayout(size: self.wallpaperNode.bounds.size, transition: .immediate) + self.wallpaperNode.updateLayout(size: self.wallpaperNode.bounds.size, tile: false, transition: .immediate) transition.updateFrame(node: self.toolbarNode, frame: CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - toolbarHeight), size: CGSize(width: layout.size.width, height: toolbarHeight))) self.toolbarNode.updateLayout(size: CGSize(width: layout.size.width, height: 49.0), layout: layout, transition: transition) diff --git a/submodules/SettingsUI/Sources/Themes/ThemeSettingsChatPreviewItem.swift b/submodules/SettingsUI/Sources/Themes/ThemeSettingsChatPreviewItem.swift index 0991a31855..5aa5b1274d 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemeSettingsChatPreviewItem.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemeSettingsChatPreviewItem.swift @@ -267,7 +267,7 @@ class ThemeSettingsChatPreviewItemNode: ListViewItemNode { let backgroundFrame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: params.width, height: contentSize.height + min(insets.top, separatorHeight) + min(insets.bottom, separatorHeight))) if let backgroundNode = strongSelf.backgroundNode { backgroundNode.frame = backgroundFrame.insetBy(dx: 0.0, dy: -100.0) - backgroundNode.updateLayout(size: backgroundNode.bounds.size, transition: .immediate) + backgroundNode.updateLayout(size: backgroundNode.bounds.size, tile: false, transition: .immediate) } strongSelf.maskNode.frame = backgroundFrame.insetBy(dx: params.leftInset, dy: 0.0) strongSelf.topStripeNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: layoutSize.width, height: separatorHeight)) diff --git a/submodules/SettingsUI/Sources/Themes/WallpaperGalleryItem.swift b/submodules/SettingsUI/Sources/Themes/WallpaperGalleryItem.swift index dc19067e95..886b241fe1 100644 --- a/submodules/SettingsUI/Sources/Themes/WallpaperGalleryItem.swift +++ b/submodules/SettingsUI/Sources/Themes/WallpaperGalleryItem.swift @@ -1169,7 +1169,7 @@ final class WallpaperGalleryItemNode: GalleryItemNode { if self.cropNode.supernode == nil { self.imageNode.frame = self.wrapperNode.bounds self.nativeNode.frame = self.wrapperNode.bounds - self.nativeNode.updateLayout(size: self.nativeNode.bounds.size, transition: .immediate) + self.nativeNode.updateLayout(size: self.nativeNode.bounds.size, tile: false, transition: .immediate) self.blurredNode.frame = self.imageNode.frame } else { self.cropNode.frame = self.wrapperNode.bounds diff --git a/submodules/ShareController/Sources/ShareController.swift b/submodules/ShareController/Sources/ShareController.swift index d07f93839c..1f3d7b8050 100644 --- a/submodules/ShareController/Sources/ShareController.swift +++ b/submodules/ShareController/Sources/ShareController.swift @@ -1625,7 +1625,7 @@ final class MessageStoryRenderer { let size = layout.size self.containerNode.frame = CGRect(origin: CGPoint(), size: layout.size) self.instantChatBackgroundNode.frame = CGRect(origin: CGPoint(), size: layout.size) - self.instantChatBackgroundNode.updateLayout(size: size, transition: .immediate) + self.instantChatBackgroundNode.updateLayout(size: size, tile: false, transition: .immediate) self.messagesContainerNode.frame = CGRect(origin: CGPoint(), size: layout.size) let addressLayout = self.addressNode.updateLayout(size) diff --git a/submodules/Svg/PublicHeaders/Svg/Svg.h b/submodules/Svg/PublicHeaders/Svg/Svg.h index 0c05521c13..51e30030be 100755 --- a/submodules/Svg/PublicHeaders/Svg/Svg.h +++ b/submodules/Svg/PublicHeaders/Svg/Svg.h @@ -5,7 +5,7 @@ #import NSData * _Nullable prepareSvgImage(NSData * _Nonnull data); -UIImage * _Nullable renderPreparedImage(NSData * _Nonnull data, CGSize size, UIColor * _Nonnull backgroundColor, CGFloat scale); +UIImage * _Nullable renderPreparedImage(NSData * _Nonnull data, CGSize size, UIColor * _Nonnull backgroundColor, CGFloat scale, bool fit); UIImage * _Nullable drawSvgImage(NSData * _Nonnull data, CGSize size, UIColor * _Nullable backgroundColor, UIColor * _Nullable foregroundColor, bool opaque); diff --git a/submodules/Svg/Sources/Svg.m b/submodules/Svg/Sources/Svg.m index 06835a760d..e86b96599c 100755 --- a/submodules/Svg/Sources/Svg.m +++ b/submodules/Svg/Sources/Svg.m @@ -9,6 +9,11 @@ CGSize aspectFillSize(CGSize size, CGSize bounds) { return CGSizeMake(floor(size.width * scale), floor(size.height * scale)); } +CGSize aspectFitSize(CGSize size, CGSize bounds) { + CGFloat scale = MIN(bounds.width / MAX(1.0, size.width), bounds.height / MAX(1.0, size.height)); + return CGSizeMake(floor(size.width * scale), floor(size.height * scale)); +} + @interface SvgXMLParsingDelegate : NSObject { NSString *_elementName; NSString *_currentStyleString; @@ -358,7 +363,7 @@ UIImage * _Nullable drawSvgImage(NSData * _Nonnull data, CGSize size, UIColor *b @end -UIImage * _Nullable renderPreparedImage(NSData * _Nonnull data, CGSize size, UIColor *backgroundColor, CGFloat scale) { +UIImage * _Nullable renderPreparedImage(NSData * _Nonnull data, CGSize size, UIColor *backgroundColor, CGFloat scale, bool fit) { NSDate *startTime = [NSDate date]; UIColor *foregroundColor = [UIColor whiteColor]; @@ -383,6 +388,15 @@ UIImage * _Nullable renderPreparedImage(NSData * _Nonnull data, CGSize size, UIC bool isTransparent = [backgroundColor isEqual:[UIColor clearColor]]; + CGSize svgSize = CGSizeMake(width, height); + CGSize drawingSize; + if (fit) { + drawingSize = aspectFitSize(svgSize, size); + size = drawingSize; + } else { + drawingSize = aspectFillSize(svgSize, size); + } + UIGraphicsBeginImageContextWithOptions(size, !isTransparent, scale); CGContextRef context = UIGraphicsGetCurrentContext(); if (isTransparent) { @@ -392,8 +406,7 @@ UIImage * _Nullable renderPreparedImage(NSData * _Nonnull data, CGSize size, UIC CGContextFillRect(context, CGRectMake(0.0f, 0.0f, size.width, size.height)); } - CGSize svgSize = CGSizeMake(width, height); - CGSize drawingSize = aspectFillSize(svgSize, size); + CGFloat renderScale = MAX(size.width / MAX(1.0, svgSize.width), size.height / MAX(1.0, svgSize.height)); diff --git a/submodules/TelegramUI/Sources/ChatControllerNode.swift b/submodules/TelegramUI/Sources/ChatControllerNode.swift index 3dcc0b19b8..b783cbd8a7 100644 --- a/submodules/TelegramUI/Sources/ChatControllerNode.swift +++ b/submodules/TelegramUI/Sources/ChatControllerNode.swift @@ -1468,7 +1468,12 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { } transition.updateFrame(node: self.backgroundNode, frame: contentBounds) - self.backgroundNode.updateLayout(size: contentBounds.size, transition: transition) + + var shouldTile = false + if case .regular = layout.metrics.widthClass, layout.size.height == layout.deviceMetrics.screenSize.width { + shouldTile = true + } + self.backgroundNode.updateLayout(size: contentBounds.size, tile: shouldTile, transition: transition) transition.updateBounds(node: self.historyNodeContainer, bounds: contentBounds) transition.updatePosition(node: self.historyNodeContainer, position: contentBounds.center) diff --git a/submodules/TelegramUI/Sources/ChatQrCodeScreen.swift b/submodules/TelegramUI/Sources/ChatQrCodeScreen.swift index 0966ca7f17..2a29f17f0b 100644 --- a/submodules/TelegramUI/Sources/ChatQrCodeScreen.swift +++ b/submodules/TelegramUI/Sources/ChatQrCodeScreen.swift @@ -1852,7 +1852,7 @@ private class QrContentNode: ASDisplayNode, ContentNode { transition.updateFrame(node: self.containerNode, frame: CGRect(origin: CGPoint(), size: size)) transition.updateFrame(node: self.wallpaperBackgroundNode, frame: CGRect(origin: CGPoint(), size: size)) - self.wallpaperBackgroundNode.updateLayout(size: size, transition: transition) + self.wallpaperBackgroundNode.updateLayout(size: size, tile: false, transition: transition) let textLength = self.codeTextNode.attributedText?.string.count ?? 0 @@ -2195,7 +2195,7 @@ private class MessageContentNode: ASDisplayNode, ContentNode { transition.updateFrame(node: self.containerNode, frame: CGRect(origin: CGPoint(), size: size)) transition.updateFrame(node: self.wallpaperBackgroundNode, frame: CGRect(origin: CGPoint(), size: size)) - self.wallpaperBackgroundNode.updateLayout(size: size, transition: transition) + self.wallpaperBackgroundNode.updateLayout(size: size, tile: false, transition: transition) let inset: CGFloat = 24.0 let contentInset: CGFloat = 16.0 diff --git a/submodules/TelegramUI/Sources/ChatRecentActionsControllerNode.swift b/submodules/TelegramUI/Sources/ChatRecentActionsControllerNode.swift index 27172dcff2..88e425e243 100644 --- a/submodules/TelegramUI/Sources/ChatRecentActionsControllerNode.swift +++ b/submodules/TelegramUI/Sources/ChatRecentActionsControllerNode.swift @@ -666,7 +666,7 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode { let cleanInsets = layout.insets(options: []) transition.updateFrame(node: self.backgroundNode, frame: CGRect(origin: CGPoint(), size: layout.size)) - self.backgroundNode.updateLayout(size: self.backgroundNode.bounds.size, transition: transition) + self.backgroundNode.updateLayout(size: self.backgroundNode.bounds.size, tile: false, transition: transition) let intrinsicPanelHeight: CGFloat = 47.0 let panelHeight = intrinsicPanelHeight + cleanInsets.bottom diff --git a/submodules/WallpaperBackgroundNode/Sources/MetalWallpaperBackgroundNode.swift b/submodules/WallpaperBackgroundNode/Sources/MetalWallpaperBackgroundNode.swift index 77881a4693..20394de160 100644 --- a/submodules/WallpaperBackgroundNode/Sources/MetalWallpaperBackgroundNode.swift +++ b/submodules/WallpaperBackgroundNode/Sources/MetalWallpaperBackgroundNode.swift @@ -138,7 +138,7 @@ final class MetalWallpaperBackgroundNode: ASDisplayNode, WallpaperBackgroundNode } - func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) { + func updateLayout(size: CGSize, tile: Bool, transition: ContainedViewLayoutTransition) { if self.metalLayer.drawableSize != size { self.metalLayer.drawableSize = size diff --git a/submodules/WallpaperBackgroundNode/Sources/WallpaperBackgroundNode.swift b/submodules/WallpaperBackgroundNode/Sources/WallpaperBackgroundNode.swift index 7aa3e948ae..b580c8d23e 100644 --- a/submodules/WallpaperBackgroundNode/Sources/WallpaperBackgroundNode.swift +++ b/submodules/WallpaperBackgroundNode/Sources/WallpaperBackgroundNode.swift @@ -60,7 +60,7 @@ public protocol WallpaperBackgroundNode: ASDisplayNode { func update(wallpaper: TelegramWallpaper) func _internalUpdateIsSettingUpWallpaper() - func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) + func updateLayout(size: CGSize, tile: Bool, transition: ContainedViewLayoutTransition) func updateIsLooping(_ isLooping: Bool) func animateEvent(transition: ContainedViewLayoutTransition, extendAnimation: Bool) func updateBubbleTheme(bubbleTheme: PresentationTheme, bubbleCorners: PresentationChatBubbleCorners) @@ -599,7 +599,7 @@ final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgroundNode private let bakedBackgroundView: UIImageView - private var validLayout: CGSize? + private var validLayout: (CGSize, Bool)? private var wallpaper: TelegramWallpaper? private var isSettingUpWallpaper: Bool = false @@ -884,8 +884,8 @@ final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgroundNode } } - if let size = self.validLayout { - self.updateLayout(size: size, transition: .immediate) + if let (size, tile) = self.validLayout { + self.updateLayout(size: size, tile: tile, transition: .immediate) self.updateBubbles() if scheduleLoopingEvent { @@ -953,7 +953,7 @@ final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgroundNode } } - private func loadPatternForSizeIfNeeded(size: CGSize, transition: ContainedViewLayoutTransition) { + private func loadPatternForSizeIfNeeded(size: CGSize, tile: Bool, transition: ContainedViewLayoutTransition) { guard let wallpaper = self.wallpaper else { return } @@ -1024,8 +1024,8 @@ final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgroundNode strongSelf.validPatternImage = ValidPatternImage(wallpaper: wallpaper, invertPattern: invertPattern, generate: generator) strongSelf.validPatternGeneratedImage = nil - if let size = strongSelf.validLayout { - strongSelf.loadPatternForSizeIfNeeded(size: size, transition: .immediate) + if let (size, tile) = strongSelf.validLayout { + strongSelf.loadPatternForSizeIfNeeded(size: size, tile: tile, transition: .immediate) } else { strongSelf._isReady.set(true) } @@ -1067,7 +1067,7 @@ final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgroundNode self.patternImageLayer.suspendCompositionUpdates = false self.patternImageLayer.updateCompositionIfNeeded() } 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)) + let patternArguments = TransformImageArguments(corners: ImageCorners(), imageSize: size, boundingSize: size, intrinsicInsets: UIEdgeInsets(), custom: PatternWallpaperArguments(colors: [patternBackgroundColor], rotation: nil, customPatternColor: patternColor, preview: false, tile: tile), scale: min(2.0, UIScreenScale)) if self.useSharedAnimationPhase || self.patternImageLayer.contents == nil { if let drawingContext = validPatternImage.generate(patternArguments) { if let image = drawingContext.generateImage() { @@ -1121,9 +1121,9 @@ final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgroundNode transition.updateFrame(layer: self.patternImageLayer, frame: CGRect(origin: CGPoint(), size: size)) } - func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) { + func updateLayout(size: CGSize, tile: Bool, transition: ContainedViewLayoutTransition) { let isFirstLayout = self.validLayout == nil - self.validLayout = size + self.validLayout = (size, tile) if let blurredBackgroundPortalSourceView = self.blurredBackgroundPortalSourceView { transition.updateFrame(view: blurredBackgroundPortalSourceView, frame: CGRect(origin: CGPoint(), size: size)) @@ -1151,7 +1151,7 @@ final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgroundNode outgoingBubbleGradientBackgroundNode.updateLayout(size: size, transition: transition, extendAnimation: false, backwards: false, completion: {}) } - self.loadPatternForSizeIfNeeded(size: size, transition: transition) + self.loadPatternForSizeIfNeeded(size: size, tile: tile, transition: transition) /*for (animationNode, relativePosition) in self.inlineAnimationNodes { let sizeNorm = CGSize(width: 1440, height: 2960) @@ -1201,7 +1201,7 @@ final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgroundNode if bubbleTheme.chat.message.outgoing.bubble.withoutWallpaper.fill.count >= 3 && bubbleTheme.chat.animateMessageColors { if self.outgoingBubbleGradientBackgroundNode == nil { let outgoingBubbleGradientBackgroundNode = GradientBackgroundNode(adjustSaturation: false) - if let size = self.validLayout { + if let (size, _) = self.validLayout { outgoingBubbleGradientBackgroundNode.frame = CGRect(origin: CGPoint(), size: size) outgoingBubbleGradientBackgroundNode.updateLayout(size: size, transition: .immediate, extendAnimation: false, backwards: false, completion: {}) } @@ -2021,7 +2021,7 @@ final class WallpaperBackgroundNodeMergedImpl: ASDisplayNode, WallpaperBackgroun self.isSettingUpWallpaper = true } - func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) { + func updateLayout(size: CGSize, tile: Bool, transition: ContainedViewLayoutTransition) { self.validLayout = size self.staticView.frame = CGRect(origin: CGPoint(), size: size) diff --git a/submodules/WallpaperResources/Sources/WallpaperResources.swift b/submodules/WallpaperResources/Sources/WallpaperResources.swift index e48af8b463..4f103d3aab 100644 --- a/submodules/WallpaperResources/Sources/WallpaperResources.swift +++ b/submodules/WallpaperResources/Sources/WallpaperResources.swift @@ -341,13 +341,15 @@ public struct PatternWallpaperArguments: TransformImageCustomArguments { let preview: Bool let customPatternColor: UIColor? let bakePatternAlpha: CGFloat + let tile: Bool - public init(colors: [UIColor], rotation: Int32?, customPatternColor: UIColor? = nil, preview: Bool = false, bakePatternAlpha: CGFloat = 1.0) { + public init(colors: [UIColor], rotation: Int32?, customPatternColor: UIColor? = nil, preview: Bool = false, bakePatternAlpha: CGFloat = 1.0, tile: Bool = false) { self.colors = colors self.rotation = rotation self.customPatternColor = customPatternColor self.preview = preview self.bakePatternAlpha = bakePatternAlpha + self.tile = tile } public func serialized() -> NSArray { @@ -359,6 +361,7 @@ public struct PatternWallpaperArguments: TransformImageCustomArguments { } array.add(NSNumber(value: self.preview)) array.add(NSNumber(value: Double(self.bakePatternAlpha))) + array.add(NSNumber(value: self.tile)) return array } } @@ -539,12 +542,13 @@ private func patternWallpaperImageInternal(fullSizeData: Data?, fullSizeComplete c.restoreGState() } + let tile = customArguments.tile let overlayImage = generateImage(arguments.drawingRect.size, rotatedContext: { size, c in c.clear(CGRect(origin: CGPoint(), size: size)) var image: UIImage? if let fullSizeData = fullSizeData { if mode == .screen { - image = renderPreparedImage(fullSizeData, CGSize(width: size.width * context.scale, height: size.height * context.scale), .black, 1.0) + image = renderPreparedImage(fullSizeData, CGSize(width: size.width * context.scale, height: size.height * context.scale), .black, 1.0, tile) } else { image = UIImage(data: fullSizeData) } @@ -566,39 +570,57 @@ private func patternWallpaperImageInternal(fullSizeData: Data?, fullSizeComplete if abs(fittedSize.height - arguments.boundingSize.height).isLessThanOrEqualTo(CGFloat(1.0)) { fittedSize.height = arguments.boundingSize.height } - fittedSize = fittedSize.aspectFilled(arguments.drawingRect.size) - - 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) - - c.interpolationQuality = customArguments.preview ? .low : .medium - c.clip(to: fittedRect, mask: image.cgImage!) - - if let customPatternColor = customArguments.customPatternColor { - c.setFillColor(customPatternColor.cgColor) - c.fill(CGRect(origin: CGPoint(), size: arguments.drawingRect.size)) - } else if colors.count >= 3 && customArguments.customPatternColor == nil { - c.setFillColor(UIColor(white: 0.0, alpha: 0.5).cgColor) - c.fill(CGRect(origin: CGPoint(), size: arguments.drawingRect.size)) - } else if colors.count == 1 { - c.setFillColor(customArguments.customPatternColor?.cgColor ?? patternColor(for: color, intensity: intensity, prominent: prominent).cgColor) - c.fill(CGRect(origin: CGPoint(), size: arguments.drawingRect.size)) + if tile { + fittedSize = fittedSize.aspectFitted(arguments.drawingRect.size) } else { - let gradientColors = colors.map { patternColor(for: $0, intensity: intensity, prominent: prominent).cgColor } as CFArray - let delta: CGFloat = 1.0 / (CGFloat(colors.count) - 1.0) - - var locations: [CGFloat] = [] - for i in 0 ..< colors.count { - locations.append(delta * CGFloat(i)) - } - let colorSpace = CGColorSpaceCreateDeviceRGB() - let gradient = CGGradient(colorsSpace: colorSpace, colors: gradientColors, locations: &locations)! - - c.translateBy(x: arguments.drawingSize.width / 2.0, y: arguments.drawingSize.height / 2.0) - c.rotate(by: CGFloat(customArguments.rotation ?? 0) * CGFloat.pi / -180.0) - c.translateBy(x: -arguments.drawingSize.width / 2.0, y: -arguments.drawingSize.height / 2.0) - - c.drawLinearGradient(gradient, start: CGPoint(x: 0.0, y: 0.0), end: CGPoint(x: 0.0, y: arguments.drawingSize.height), options: [.drawsBeforeStartLocation, .drawsAfterEndLocation]) + fittedSize = fittedSize.aspectFilled(arguments.drawingRect.size) } + + c.interpolationQuality = customArguments.preview ? .low : .medium + + let drawTile: (CGRect) -> Void = { fittedRect in + c.saveGState() + c.clip(to: fittedRect, mask: image.cgImage!) + + if let customPatternColor = customArguments.customPatternColor { + c.setFillColor(customPatternColor.cgColor) + c.fill(CGRect(origin: CGPoint(), size: arguments.drawingRect.size)) + } else if colors.count >= 3 && customArguments.customPatternColor == nil { + c.setFillColor(UIColor(white: 0.0, alpha: 0.5).cgColor) + c.fill(CGRect(origin: CGPoint(), size: arguments.drawingRect.size)) + } else if colors.count == 1 { + c.setFillColor(customArguments.customPatternColor?.cgColor ?? patternColor(for: color, intensity: intensity, prominent: prominent).cgColor) + c.fill(CGRect(origin: CGPoint(), size: arguments.drawingRect.size)) + } else { + let gradientColors = colors.map { patternColor(for: $0, intensity: intensity, prominent: prominent).cgColor } as CFArray + let delta: CGFloat = 1.0 / (CGFloat(colors.count) - 1.0) + + var locations: [CGFloat] = [] + for i in 0 ..< colors.count { + locations.append(delta * CGFloat(i)) + } + let colorSpace = CGColorSpaceCreateDeviceRGB() + let gradient = CGGradient(colorsSpace: colorSpace, colors: gradientColors, locations: &locations)! + + c.translateBy(x: arguments.drawingSize.width / 2.0, y: arguments.drawingSize.height / 2.0) + c.rotate(by: CGFloat(customArguments.rotation ?? 0) * CGFloat.pi / -180.0) + c.translateBy(x: -arguments.drawingSize.width / 2.0, y: -arguments.drawingSize.height / 2.0) + + c.drawLinearGradient(gradient, start: CGPoint(x: 0.0, y: 0.0), end: CGPoint(x: 0.0, y: arguments.drawingSize.height), options: [.drawsBeforeStartLocation, .drawsAfterEndLocation]) + } + c.restoreGState() + } + + if tile { + var fittedRect = CGRect(origin: CGPoint(x: drawingRect.origin.x, y: drawingRect.origin.y + (drawingRect.size.height - fittedSize.height) / 2.0), size: fittedSize) + drawTile(fittedRect) + fittedRect = fittedRect.offsetBy(dx: fittedSize.width, dy: 0.0) + drawTile(fittedRect) + } else { + 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) + drawTile(fittedRect) + } + } }) if let customPatternColor = customArguments.customPatternColor, customPatternColor.alpha < 1.0 {