Optimize pattern rendering

This commit is contained in:
Ilya Laktyushin 2019-12-16 21:55:57 +04:00
parent 165c334854
commit 06522652f9
8 changed files with 210 additions and 181 deletions

View File

@ -537,11 +537,11 @@ final class LocationPickerControllerNode: ViewControllerTracingNode {
}
if address != nil {
if foundVenues == nil {
if foundVenues == nil && !state.searchingVenuesAround {
displayingPlacesButton = true
} else if let previousLocation = foundVenuesLocation {
let currentLocation = CLLocation(latitude: coordinate.latitude, longitude: coordinate.longitude)
if currentLocation.distance(from: previousLocation) > 500 {
if currentLocation.distance(from: previousLocation) > 300 {
displayingPlacesButton = true
}
}

View File

@ -11,6 +11,7 @@ import TelegramUIPreferences
import ChatListUI
import AccountContext
import WallpaperResources
import PresentationDataUtils
private func generateMaskImage(color: UIColor) -> UIImage? {
return generateImage(CGSize(width: 1.0, height: 80.0), opaque: false, rotatedContext: { size, context in
@ -157,6 +158,8 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
private let mode: ThemeAccentColorControllerMode
private var presentationData: PresentationData
private let queue = Queue()
private var state: ThemeColorState
private let referenceTimestamp: Int32
@ -398,8 +401,11 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
}
self.stateDisposable = (self.statePromise.get()
|> deliverOn(Queue.concurrentDefaultQueue())
|> map { state -> (PresentationTheme, (TelegramWallpaper, UIImage?, Signal<(TransformImageArguments) -> DrawingContext?, NoError>?), UIColor, (UIColor, UIColor?)?, [UIColor], Bool) in
|> deliverOn(self.queue)
|> mapToThrottled { next -> Signal<ThemeColorState, NoError> in
return .single(next) |> then(.complete() |> delay(0.0166667, queue: self.queue))
}
|> map { [weak self] state -> (PresentationTheme?, (TelegramWallpaper, UIImage?, Signal<(TransformImageArguments) -> DrawingContext?, NoError>?, (() -> Void)?), UIColor, (UIColor, UIColor?)?, PatternWallpaperArguments, Bool) in
let accentColor = state.accentColor
var backgroundColors = state.backgroundColors
let messagesColors = state.messagesColors
@ -410,6 +416,11 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
var singleBackgroundColor: UIColor?
var updateOnlyWallpaper = false
if state.section == .background && state.preview {
updateOnlyWallpaper = true
}
if let backgroundColors = backgroundColors {
if let patternWallpaper = state.patternWallpaper, case let .file(file) = patternWallpaper {
let color = Int32(bitPattern: backgroundColors.0.rgb)
@ -453,40 +464,51 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
}
let serviceBackgroundColor = serviceColor(for: (wallpaper, wallpaperImage))
let updatedTheme: PresentationTheme
if let themeReference = mode.themeReference {
updatedTheme = makePresentationTheme(mediaBox: context.sharedContext.accountManager.mediaBox, themeReference: themeReference, accentColor: accentColor, backgroundColors: backgroundColors, bubbleColors: messagesColors, serviceBackgroundColor: serviceBackgroundColor, preview: true) ?? defaultPresentationTheme
} else if case let .edit(theme, _, _, _, _) = mode {
updatedTheme = customizePresentationTheme(theme, editing: false, accentColor: accentColor, backgroundColors: backgroundColors, bubbleColors: messagesColors)
let updatedTheme: PresentationTheme?
if !updateOnlyWallpaper {
if let themeReference = mode.themeReference {
updatedTheme = makePresentationTheme(mediaBox: context.sharedContext.accountManager.mediaBox, themeReference: themeReference, accentColor: accentColor, backgroundColors: backgroundColors, bubbleColors: messagesColors, serviceBackgroundColor: serviceBackgroundColor, preview: true) ?? defaultPresentationTheme
} else if case let .edit(theme, _, _, _, _) = mode {
updatedTheme = customizePresentationTheme(theme, editing: false, accentColor: accentColor, backgroundColors: backgroundColors, bubbleColors: messagesColors)
} else {
updatedTheme = theme
}
let _ = PresentationResourcesChat.principalGraphics(mediaBox: context.account.postbox.mediaBox, knockoutWallpaper: context.sharedContext.immediateExperimentalUISettings.knockoutWallpaper, theme: updatedTheme!, wallpaper: wallpaper)
} else {
updatedTheme = theme
updatedTheme = nil
}
let _ = PresentationResourcesChat.principalGraphics(mediaBox: context.account.postbox.mediaBox, knockoutWallpaper: context.sharedContext.immediateExperimentalUISettings.knockoutWallpaper, theme: updatedTheme, wallpaper: wallpaper)
let patternColors = calcPatternColors(for: state)
let patternArguments = PatternWallpaperArguments(colors: calcPatternColors(for: state), rotation: wallpaper.settings?.rotation ?? 0, preview: state.preview)
return (updatedTheme, (wallpaper, wallpaperImage, wallpaperSignal), serviceBackgroundColor, backgroundColors, patternColors, state.preview)
var wallpaperApply: (() -> Void)?
if let strongSelf = self, case let .file(file) = wallpaper, file.isPattern, let (layout, _, _) = strongSelf.validLayout {
let makeImageLayout = strongSelf.signalBackgroundNode.asyncLayout()
wallpaperApply = makeImageLayout(TransformImageArguments(corners: ImageCorners(), imageSize: wallpaper.dimensions ?? layout.size, boundingSize: layout.size, intrinsicInsets: UIEdgeInsets(), custom: patternArguments))
}
return (updatedTheme, (wallpaper, wallpaperImage, wallpaperSignal, wallpaperApply), serviceBackgroundColor, backgroundColors, patternArguments, state.preview)
}
|> deliverOnMainQueue).start(next: { [weak self] theme, wallpaperImageAndSignal, serviceBackgroundColor, backgroundColors, patternColors, preview in
|> deliverOnMainQueue).start(next: { [weak self] theme, wallpaperImageAndSignal, serviceBackgroundColor, backgroundColors, patternArguments, preview in
guard let strongSelf = self else {
return
}
let (wallpaper, wallpaperImage, wallpaperSignal) = wallpaperImageAndSignal
let (wallpaper, wallpaperImage, wallpaperSignal, wallpaperApply) = wallpaperImageAndSignal
if let theme = theme {
strongSelf.theme = theme
strongSelf.themeUpdated?(theme)
strongSelf.themePromise.set(.single(theme))
strongSelf.colorPanelNode.updateTheme(theme)
strongSelf.toolbarNode.updateThemeAndStrings(theme: theme, strings: strongSelf.presentationData.strings)
strongSelf.chatListBackgroundNode.backgroundColor = theme.chatList.backgroundColor
strongSelf.maskNode.image = generateMaskImage(color: theme.chatList.backgroundColor)
}
strongSelf.theme = theme
strongSelf.themeUpdated?(theme)
strongSelf.themePromise.set(.single(theme))
strongSelf.serviceBackgroundColor = serviceBackgroundColor
strongSelf.serviceBackgroundColorPromise.set(.single(serviceBackgroundColor))
strongSelf.colorPanelNode.updateTheme(theme)
strongSelf.toolbarNode.updateThemeAndStrings(theme: theme, strings: strongSelf.presentationData.strings)
strongSelf.chatListBackgroundNode.backgroundColor = theme.chatList.backgroundColor
strongSelf.maskNode.image = generateMaskImage(color: theme.chatList.backgroundColor)
let patternArguments = PatternWallpaperArguments(colors: patternColors, rotation: wallpaper.settings?.rotation ?? 0, preview: preview)
if case let .color(value) = wallpaper {
strongSelf.backgroundColor = UIColor(rgb: UInt32(bitPattern: value))
strongSelf.immediateBackgroundNode.backgroundColor = UIColor(rgb: UInt32(bitPattern: value))
@ -506,41 +528,10 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
strongSelf.signalBackgroundNode.isHidden = false
if case let .file(file) = wallpaper, let (layout, _, _) = strongSelf.validLayout {
var dispatch = false
wallpaperApply?()
if let previousWallpaper = strongSelf.patternWallpaper, case let .file(previousFile) = previousWallpaper, file.id == previousFile.id {
dispatch = true
}
if dispatch {
if let _ = strongSelf.patternArgumentsDisposable {
} else {
strongSelf.signalBackgroundNode.contentAnimations = .subsequentUpdates
let throttledSignal = strongSelf.patternArgumentsPromise.get()
|> mapToThrottled { next -> Signal<TransformImageArguments, NoError> in
return .single(next) |> then(.complete() |> delay(0.033333, queue: Queue.concurrentDefaultQueue()))
}
strongSelf.patternArgumentsDisposable = (throttledSignal).start(next: { [weak self] arguments in
if let strongSelf = self {
let makeImageLayout = strongSelf.signalBackgroundNode.asyncLayout()
let imageApply = makeImageLayout(arguments)
let _ = imageApply()
}
})
}
strongSelf.patternArgumentsPromise.set(.single(TransformImageArguments(corners: ImageCorners(), imageSize: layout.size, boundingSize: layout.size, intrinsicInsets: UIEdgeInsets(), custom: patternArguments)))
} else {
strongSelf.patternArgumentsDisposable?.dispose()
strongSelf.patternArgumentsDisposable = nil
let makeImageLayout = strongSelf.signalBackgroundNode.asyncLayout()
let imageApply = makeImageLayout(TransformImageArguments(corners: ImageCorners(), imageSize: layout.size, boundingSize: layout.size, intrinsicInsets: UIEdgeInsets(), custom: patternArguments))
let _ = imageApply()
}
if !dispatch {
strongSelf.signalBackgroundNode.setSignal(wallpaperSignal)
strongSelf.patternWallpaper = wallpaper
}
@ -557,7 +548,7 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
strongSelf.patternPanelNode.backgroundColors = backgroundColors
}
if let (layout, navigationBarHeight, messagesBottomInset) = strongSelf.validLayout {
if let _ = theme, let (layout, navigationBarHeight, messagesBottomInset) = strongSelf.validLayout {
strongSelf.updateChatsLayout(layout: layout, topInset: navigationBarHeight, transition: .immediate)
strongSelf.updateMessagesLayout(layout: layout, bottomInset: messagesBottomInset, transition: .immediate)
}
@ -954,7 +945,7 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
transition.updateFrame(node: self.backgroundContainerNode, frame: CGRect(origin: CGPoint(), size: backgroundSize))
let makeImageLayout = self.signalBackgroundNode.asyncLayout()
let imageApply = makeImageLayout(TransformImageArguments(corners: ImageCorners(), imageSize: layout.size, boundingSize: layout.size, intrinsicInsets: UIEdgeInsets(), custom: self.patternArguments))
let imageApply = makeImageLayout(TransformImageArguments(corners: ImageCorners(), imageSize: self.patternWallpaper?.dimensions ?? layout.size, boundingSize: layout.size, intrinsicInsets: UIEdgeInsets(), custom: self.patternArguments))
let _ = imageApply()
transition.updatePosition(node: self.backgroundWrapperNode, position: CGPoint(x: backgroundSize.width / 2.0, y: backgroundSize.height / 2.0))

View File

@ -384,23 +384,24 @@ private enum ThemeSettingsControllerEntry: ItemListNodeEntry {
}
}
private func themeSettingsControllerEntries(presentationData: PresentationData, presentationThemeSettings: PresentationThemeSettings, theme: PresentationTheme, themeReference: PresentationThemeReference, themeSpecificAccentColors: [Int64: PresentationThemeAccentColor], availableThemes: [PresentationThemeReference], autoNightSettings: AutomaticThemeSwitchSetting, strings: PresentationStrings, wallpaper: TelegramWallpaper, fontSize: PresentationFontSize, dateTimeFormat: PresentationDateTimeFormat, largeEmoji: Bool, disableAnimations: Bool, availableAppIcons: [PresentationAppIcon], currentAppIconName: String?) -> [ThemeSettingsControllerEntry] {
private func themeSettingsControllerEntries(presentationData: PresentationData, presentationThemeSettings: PresentationThemeSettings, themeReference: PresentationThemeReference, availableThemes: [PresentationThemeReference], availableAppIcons: [PresentationAppIcon], currentAppIconName: String?) -> [ThemeSettingsControllerEntry] {
var entries: [ThemeSettingsControllerEntry] = []
let strings = presentationData.strings
let title = presentationData.autoNightModeTriggered ? strings.Appearance_ColorThemeNight.uppercased() : strings.Appearance_ColorTheme.uppercased()
entries.append(.themeListHeader(presentationData.theme, title))
entries.append(.chatPreview(presentationData.theme, theme, wallpaper, presentationData.fontSize, presentationData.strings, dateTimeFormat, presentationData.nameDisplayOrder, [ChatPreviewMessageItem(outgoing: false, reply: (presentationData.strings.Appearance_PreviewReplyAuthor, presentationData.strings.Appearance_PreviewReplyText), text: presentationData.strings.Appearance_PreviewIncomingText), ChatPreviewMessageItem(outgoing: true, reply: nil, text: presentationData.strings.Appearance_PreviewOutgoingText)]))
entries.append(.chatPreview(presentationData.theme, presentationData.theme, presentationData.chatWallpaper, presentationData.fontSize, presentationData.strings, presentationData.dateTimeFormat, presentationData.nameDisplayOrder, [ChatPreviewMessageItem(outgoing: false, reply: (presentationData.strings.Appearance_PreviewReplyAuthor, presentationData.strings.Appearance_PreviewReplyText), text: presentationData.strings.Appearance_PreviewIncomingText), ChatPreviewMessageItem(outgoing: true, reply: nil, text: presentationData.strings.Appearance_PreviewOutgoingText)]))
entries.append(.themeItem(presentationData.theme, presentationData.strings, availableThemes, themeReference, themeSpecificAccentColors, themeSpecificAccentColors[themeReference.index]))
entries.append(.themeItem(presentationData.theme, presentationData.strings, availableThemes, themeReference, presentationThemeSettings.themeSpecificAccentColors, presentationThemeSettings.themeSpecificAccentColors[themeReference.index]))
if case let .builtin(theme) = themeReference {
entries.append(.accentColor(presentationData.theme, themeReference, themeSpecificAccentColors[themeReference.index]))
entries.append(.accentColor(presentationData.theme, themeReference, presentationThemeSettings.themeSpecificAccentColors[themeReference.index]))
}
entries.append(.wallpaper(presentationData.theme, strings.Settings_ChatBackground))
let autoNightMode: String
switch autoNightSettings.trigger {
switch presentationThemeSettings.automaticThemeSwitchSetting.trigger {
case .system:
if #available(iOSApplicationExtension 13.0, iOS 13.0, *) {
autoNightMode = strings.AutoNightTheme_System
@ -430,8 +431,8 @@ private func themeSettingsControllerEntries(presentationData: PresentationData,
}
entries.append(.otherHeader(presentationData.theme, strings.Appearance_Other.uppercased()))
entries.append(.largeEmoji(presentationData.theme, strings.Appearance_LargeEmoji, largeEmoji))
entries.append(.animations(presentationData.theme, strings.Appearance_ReduceMotion, disableAnimations))
entries.append(.largeEmoji(presentationData.theme, strings.Appearance_LargeEmoji, presentationData.largeEmoji))
entries.append(.animations(presentationData.theme, strings.Appearance_ReduceMotion, presentationData.disableAnimations))
entries.append(.animationsInfo(presentationData.theme, strings.Appearance_ReduceMotionInfo))
return entries
@ -688,7 +689,7 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The
|> map { presentationData, sharedData, cloudThemes, availableAppIcons, currentAppIconName -> (ItemListControllerState, (ItemListNodeState, Any)) in
let settings = (sharedData.entries[ApplicationSpecificSharedDataKeys.presentationThemeSettings] as? PresentationThemeSettings) ?? PresentationThemeSettings.defaultSettings
let fontSize = settings.fontSize
let fontSize = presentationData.fontSize
let dateTimeFormat = presentationData.dateTimeFormat
let largeEmoji = presentationData.largeEmoji
let disableAnimations = presentationData.disableAnimations
@ -700,9 +701,7 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The
themeReference = settings.theme
}
let theme = presentationData.theme
let accentColor = settings.themeSpecificAccentColors[themeReference.index]
let wallpaper = presentationData.chatWallpaper
let rightNavigationButton = ItemListNavigationButton(content: .icon(.add), style: .regular, enabled: true, action: {
moreImpl?()
@ -724,7 +723,7 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The
availableThemes.append(contentsOf: cloudThemes)
let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(presentationData.strings.Appearance_Title), leftNavigationButton: nil, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back))
let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: themeSettingsControllerEntries(presentationData: presentationData, presentationThemeSettings: settings, theme: theme, themeReference: themeReference, themeSpecificAccentColors: settings.themeSpecificAccentColors, availableThemes: availableThemes, autoNightSettings: settings.automaticThemeSwitchSetting, strings: presentationData.strings, wallpaper: wallpaper, fontSize: fontSize, dateTimeFormat: dateTimeFormat, largeEmoji: largeEmoji, disableAnimations: disableAnimations, availableAppIcons: availableAppIcons, currentAppIconName: currentAppIconName), style: .blocks, ensureVisibleItemTag: focusOnItemTag, animateChanges: false)
let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: themeSettingsControllerEntries(presentationData: presentationData, presentationThemeSettings: settings, themeReference: themeReference, availableThemes: availableThemes, availableAppIcons: availableAppIcons, currentAppIconName: currentAppIconName), style: .blocks, ensureVisibleItemTag: focusOnItemTag, animateChanges: false)
let previousThemeIndex = previousThemeReference.swap(themeReference)?.index
let previousAccentColor = previousAccentColor.swap(accentColor)

View File

@ -31,36 +31,22 @@ private func textInputBackgroundImage(fieldColor: UIColor, strokeColor: UIColor,
}
}
private func generateSwatchImage(theme: PresentationTheme, color: UIColor) -> UIImage? {
private func generateSwatchBorderImage(theme: PresentationTheme) -> UIImage? {
return nil
return generateImage(CGSize(width: 21.0, height: 21.0), rotatedContext: { size, context in
let bounds = CGRect(origin: CGPoint(), size: size)
context.clear(bounds)
let fillColor = color
var strokeColor: UIColor?
let inputBackgroundColor = theme.chat.inputPanel.inputBackgroundColor
if fillColor.distance(to: inputBackgroundColor) < 200 {
strokeColor = theme.chat.inputPanel.inputStrokeColor
if strokeColor!.distance(to: inputBackgroundColor) < 200 {
strokeColor = theme.chat.inputPanel.inputControlColor
}
}
context.setFillColor(fillColor.cgColor)
context.setLineWidth(1.0)
context.fillEllipse(in: bounds)
if let strokeColor = strokeColor {
context.setStrokeColor(strokeColor.cgColor)
context.strokeEllipse(in: bounds.insetBy(dx: 1.0, dy: 1.0))
}
context.setStrokeColor(theme.chat.inputPanel.inputControlColor.cgColor)
context.strokeEllipse(in: bounds.insetBy(dx: 1.0, dy: 1.0))
})
}
private class ColorInputFieldNode: ASDisplayNode, UITextFieldDelegate {
private var theme: PresentationTheme
private let swatchNode: ASImageNode
private let swatchNode: ASDisplayNode
private let borderNode: ASImageNode
private let removeButton: HighlightableButtonNode
private let textBackgroundNode: ASImageNode
private let selectionNode: ASDisplayNode
@ -123,9 +109,13 @@ private class ColorInputFieldNode: ASDisplayNode, UITextFieldDelegate {
self.prefixNode = ASTextNode()
self.prefixNode.attributedText = NSAttributedString(string: "#", font: Font.regular(17.0), textColor: self.theme.chat.inputPanel.inputTextColor)
self.swatchNode = ASImageNode()
self.swatchNode.displaysAsynchronously = false
self.swatchNode.displayWithoutProcessing = true
self.swatchNode = ASDisplayNode()
self.swatchNode.cornerRadius = 10.5
self.borderNode = ASImageNode()
self.borderNode.displaysAsynchronously = false
self.borderNode.displayWithoutProcessing = true
self.borderNode.image = generateSwatchBorderImage(theme: theme)
self.removeButton = HighlightableButtonNode()
self.removeButton.setImage(generateTintedImage(image: UIImage(bundleImageName: "Settings/ThemeColorRemoveIcon"), color: theme.chat.inputPanel.inputControlColor), for: .normal)
@ -137,6 +127,7 @@ private class ColorInputFieldNode: ASDisplayNode, UITextFieldDelegate {
self.addSubnode(self.textFieldNode)
self.addSubnode(self.prefixNode)
self.addSubnode(self.swatchNode)
self.addSubnode(self.borderNode)
self.addSubnode(self.removeButton)
self.removeButton.addTarget(self, action: #selector(self.removePressed), forControlEvents: .touchUpInside)
@ -172,6 +163,8 @@ private class ColorInputFieldNode: ASDisplayNode, UITextFieldDelegate {
self.textFieldNode.textField.tintColor = self.theme.list.itemAccentColor
self.selectionNode.backgroundColor = theme.chat.inputPanel.panelControlAccentColor.withAlphaComponent(0.2)
self.borderNode.image = generateSwatchBorderImage(theme: theme)
self.updateBorderVisibility()
}
func setColor(_ color: UIColor, isDefault: Bool = false, update: Bool = true, ended: Bool = true) {
@ -186,7 +179,20 @@ private class ColorInputFieldNode: ASDisplayNode, UITextFieldDelegate {
if update {
self.colorChanged?(color, ended)
}
self.swatchNode.image = generateSwatchImage(theme: self.theme, color: color)
self.swatchNode.backgroundColor = color
self.updateBorderVisibility()
}
private func updateBorderVisibility() {
guard let color = self.swatchNode.backgroundColor else {
return
}
let inputBackgroundColor = self.theme.chat.inputPanel.inputBackgroundColor
if color.distance(to: inputBackgroundColor) < 200 {
self.borderNode.alpha = 1.0
} else {
self.borderNode.alpha = 0.0
}
}
@objc private func removePressed() {
@ -294,7 +300,9 @@ private class ColorInputFieldNode: ASDisplayNode, UITextFieldDelegate {
func updateLayout(size: CGSize, condensed: Bool, transition: ContainedViewLayoutTransition) {
self.validLayout = (size, condensed)
transition.updateFrame(node: self.swatchNode, frame: CGRect(origin: CGPoint(x: 6.0, y: 6.0), size: CGSize(width: 21.0, height: 21.0)))
let swatchFrame = CGRect(origin: CGPoint(x: 6.0, y: 6.0), size: CGSize(width: 21.0, height: 21.0))
transition.updateFrame(node: self.swatchNode, frame: swatchFrame)
transition.updateFrame(node: self.borderNode, frame: swatchFrame)
let textPadding: CGFloat = condensed ? 31.0 : 37.0

View File

@ -5,19 +5,22 @@ import SwiftSignalKit
import Display
import TelegramPresentationData
private let shadowImage: UIImage = {
return generateImage(CGSize(width: 45.0, height: 45.0), opaque: false, scale: nil, rotatedContext: { size, context in
context.setBlendMode(.clear)
context.setFillColor(UIColor.clear.cgColor)
context.fill(CGRect(origin: CGPoint(), size: size))
private let knobBackgroundImage: UIImage? = {
return generateImage(CGSize(width: 45.0, height: 45.0), contextGenerator: { size, context in
let bounds = CGRect(origin: CGPoint(), size: size)
context.clear(bounds)
context.setShadow(offset: CGSize(width: 0.0, height: -1.5), blur: 4.5, color: UIColor(rgb: 0x000000, alpha: 0.4).cgColor)
context.setFillColor(UIColor(rgb: 0x000000, alpha: 0.4).cgColor)
context.fillEllipse(in: bounds.insetBy(dx: 3.0 + UIScreenPixel, dy: 3.0 + UIScreenPixel))
context.setBlendMode(.normal)
context.setShadow(offset: CGSize(width: 0.0, height: 1.5), blur: 4.5, color: UIColor(rgb: 0x000000, alpha: 0.5).cgColor)
context.setFillColor(UIColor(rgb: 0x000000, alpha: 0.5).cgColor)
context.fillEllipse(in: CGRect(origin: CGPoint(), size: size).insetBy(dx: 3.0 + UIScreenPixel, dy: 3.0 + UIScreenPixel))
})!
context.setFillColor(UIColor.white.cgColor)
context.fillEllipse(in: bounds.insetBy(dx: 3.0, dy: 3.0))
}, opaque: false, scale: nil)
}()
private let pointerImage: UIImage = {
private let pointerImage: UIImage? = {
return generateImage(CGSize(width: 12.0, height: 55.0), opaque: false, scale: nil, rotatedContext: { size, context in
context.setBlendMode(.clear)
context.setFillColor(UIColor.clear.cgColor)
@ -43,7 +46,33 @@ private let pointerImage: UIImage = {
context.addLine(to: CGPoint(x: size.width - lineWidth / 2.0, y: size.height - lineWidth / 2.0))
context.closePath()
context.drawPath(using: .fillStroke)
})!
})
}()
private let brightnessMaskImage: UIImage? = {
return generateImage(CGSize(width: 36.0, height: 36.0), opaque: false, scale: nil, rotatedContext: { size, context in
let bounds = CGRect(origin: CGPoint(), size: size)
context.setFillColor(UIColor.white.cgColor)
context.fill(bounds)
context.setBlendMode(.clear)
context.setFillColor(UIColor.clear.cgColor)
context.fillEllipse(in: bounds)
})?.stretchableImage(withLeftCapWidth: 18, topCapHeight: 18)
}()
private let brightnessGradientImage: UIImage? = {
return generateImage(CGSize(width: 160.0, height: 1.0), opaque: false, scale: nil, rotatedContext: { size, context in
let bounds = CGRect(origin: CGPoint(), size: size)
context.clear(bounds)
let gradientColors = [UIColor.black.withAlphaComponent(0.0), UIColor.black].map { $0.cgColor } as CFArray
var locations: [CGFloat] = [0.0, 1.0]
let colorSpace = CGColorSpaceCreateDeviceRGB()
let gradient = CGGradient(colorsSpace: colorSpace, colors: gradientColors, locations: &locations)!
context.drawLinearGradient(gradient, start: CGPoint(x: 0.0, y: 0.0), end: CGPoint(x: size.width, y: 0.0), options: [.drawsBeforeStartLocation, .drawsAfterEndLocation])
})
}()
private final class HSBParameter: NSObject {
@ -63,53 +92,46 @@ private final class WallpaperColorKnobNode: ASDisplayNode {
var hsb: (CGFloat, CGFloat, CGFloat) = (0.0, 0.0, 1.0) {
didSet {
if self.hsb != oldValue {
self.setNeedsDisplay()
let color = UIColor(hue: hsb.0, saturation: hsb.1, brightness: hsb.2, alpha: 1.0)
self.colorNode.backgroundColor = color
}
}
}
private let backgroundNode: ASImageNode
private let colorNode: ASDisplayNode
override init() {
self.backgroundNode = ASImageNode()
self.backgroundNode.displaysAsynchronously = false
self.backgroundNode.displayWithoutProcessing = true
self.backgroundNode.image = knobBackgroundImage
self.colorNode = ASDisplayNode()
super.init()
self.isOpaque = false
self.displaysAsynchronously = false
self.isUserInteractionEnabled = false
self.addSubnode(self.backgroundNode)
self.addSubnode(self.colorNode)
}
override func drawParameters(forAsyncLayer layer: _ASDisplayLayer) -> NSObjectProtocol? {
return HSBParameter(hue: self.hsb.0, saturation: self.hsb.1, value: self.hsb.2)
}
@objc override class func draw(_ bounds: CGRect, withParameters parameters: Any?, isCancelled: () -> Bool, isRasterizing: Bool) {
guard let parameters = parameters as? HSBParameter else {
return
}
let context = UIGraphicsGetCurrentContext()!
override func layout() {
super.layout()
if !isRasterizing {
context.setBlendMode(.copy)
context.setFillColor(UIColor.clear.cgColor)
context.fill(bounds)
}
context.draw(shadowImage.cgImage!, in: bounds)
context.setBlendMode(.normal)
context.setFillColor(UIColor.white.cgColor)
context.fillEllipse(in: bounds.insetBy(dx: 3.0, dy: 3.0))
let color = UIColor(hue: parameters.hue, saturation: parameters.saturation, brightness: parameters.value, alpha: 1.0)
context.setFillColor(color.cgColor)
let borderWidth: CGFloat = 7.0
context.fillEllipse(in: bounds.insetBy(dx: borderWidth - UIScreenPixel, dy: borderWidth - UIScreenPixel))
self.backgroundNode.frame = self.bounds
self.colorNode.frame = self.bounds.insetBy(dx: 7.0 - UIScreenPixel, dy: 7.0 - UIScreenPixel)
self.colorNode.cornerRadius = self.colorNode.frame.width / 2.0
}
}
private final class WallpaperColorHueSaturationNode: ASDisplayNode {
var value: CGFloat = 1.0 {
didSet {
self.setNeedsDisplay()
if self.value != oldValue {
self.setNeedsDisplay()
}
}
}
@ -121,7 +143,7 @@ private final class WallpaperColorHueSaturationNode: ASDisplayNode {
}
override func drawParameters(forAsyncLayer layer: _ASDisplayLayer) -> NSObjectProtocol? {
return HSBParameter(hue: 1.0, saturation: 1.0, value: self.value)
return HSBParameter(hue: 1.0, saturation: 1.0, value: 1.0)
}
@objc override class func draw(_ bounds: CGRect, withParameters parameters: Any?, isCancelled: () -> Bool, isRasterizing: Bool) {
@ -147,47 +169,43 @@ private final class WallpaperColorHueSaturationNode: ASDisplayNode {
}
private final class WallpaperColorBrightnessNode: ASDisplayNode {
private let gradientNode: ASImageNode
private let maskNode: ASImageNode
var hsb: (CGFloat, CGFloat, CGFloat) = (0.0, 1.0, 1.0) {
didSet {
self.setNeedsDisplay()
if self.hsb.0 != oldValue.0 || self.hsb.1 != oldValue.1 {
let color = UIColor(hue: hsb.0, saturation: hsb.1, brightness: 1.0, alpha: 1.0)
self.backgroundColor = color
}
}
}
override init() {
self.gradientNode = ASImageNode()
self.gradientNode.displaysAsynchronously = false
self.gradientNode.displayWithoutProcessing = true
self.gradientNode.image = brightnessGradientImage
self.gradientNode.contentMode = .scaleToFill
self.maskNode = ASImageNode()
self.maskNode.displaysAsynchronously = false
self.maskNode.displayWithoutProcessing = true
self.maskNode.image = brightnessMaskImage
self.maskNode.contentMode = .scaleToFill
super.init()
self.isOpaque = true
self.displaysAsynchronously = false
self.addSubnode(self.gradientNode)
self.addSubnode(self.maskNode)
}
override func drawParameters(forAsyncLayer layer: _ASDisplayLayer) -> NSObjectProtocol? {
return HSBParameter(hue: self.hsb.0, saturation: self.hsb.1, value: self.hsb.2)
}
@objc override class func draw(_ bounds: CGRect, withParameters parameters: Any?, isCancelled: () -> Bool, isRasterizing: Bool) {
guard let parameters = parameters as? HSBParameter else {
return
}
let context = UIGraphicsGetCurrentContext()!
let colorSpace = CGColorSpaceCreateDeviceRGB()
override func layout() {
super.layout()
context.setFillColor(UIColor(white: parameters.value, alpha: 1.0).cgColor)
context.fill(bounds)
let path = UIBezierPath(roundedRect: bounds, cornerRadius: bounds.height / 2.0)
context.addPath(path.cgPath)
context.setFillColor(UIColor.white.cgColor)
context.fillPath()
let innerPath = UIBezierPath(roundedRect: bounds.insetBy(dx: 1.0, dy: 1.0), cornerRadius: bounds.height / 2.0)
context.addPath(innerPath.cgPath)
context.clip()
let color = UIColor(hue: parameters.hue, saturation: parameters.saturation, brightness: 1.0, alpha: 1.0)
let colors = [color.cgColor, UIColor.black.cgColor]
var locations: [CGFloat] = [0.0, 1.0]
let gradient = CGGradient(colorsSpace: colorSpace, colors: colors as CFArray, locations: &locations)!
context.drawLinearGradient(gradient, start: CGPoint(), end: CGPoint(x: bounds.width, y: 0.0), options: CGGradientDrawingOptions())
self.gradientNode.frame = self.bounds
self.maskNode.frame = self.bounds
}
}
@ -250,7 +268,7 @@ final class WallpaperColorPickerNode: ASDisplayNode {
}
private func update() {
self.backgroundColor = UIColor(white: self.colorHsb.2, alpha: 1.0)
self.backgroundColor = .white
self.colorNode.value = self.colorHsb.2
self.brightnessNode.hsb = self.colorHsb
self.colorKnobNode.hsb = self.colorHsb

View File

@ -271,7 +271,7 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
}
}
patternArguments = PatternWallpaperArguments(colors: patternColors, rotation: file.settings.rotation)
patternArguments = PatternWallpaperArguments(colors: patternColors, rotation: file.settings.rotation, preview: self.arguments.colorPreview)
self.backgroundColor = patternColor.withAlphaComponent(1.0)
@ -295,7 +295,7 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
self.colorPreview = self.arguments.colorPreview
signal = patternWallpaperImage(account: context.account, accountManager: context.sharedContext.accountManager, representations: convertedRepresentations, mode: self.arguments.colorPreview ? .fastScreen : .screen, autoFetchFullSize: true)
signal = patternWallpaperImage(account: context.account, accountManager: context.sharedContext.accountManager, representations: convertedRepresentations, mode: .screen, autoFetchFullSize: true)
colorSignal = chatServiceBackgroundColor(wallpaper: wallpaper, mediaBox: context.account.postbox.mediaBox)
isBlurrable = false

View File

@ -1,4 +1,5 @@
import Foundation
import UIKit
import TelegramCore
import SyncCore
@ -46,4 +47,12 @@ public extension TelegramWallpaper {
return false
}
}
var dimensions: CGSize? {
if case let .file(file) = self {
return file.file.dimensions?.cgSize
} else {
return nil
}
}
}

View File

@ -291,7 +291,6 @@ public func wallpaperImage(account: Account, accountManager: AccountManager, fil
public enum PatternWallpaperDrawMode {
case thumbnail
case fastScreen
case screen
}
@ -322,8 +321,6 @@ private func patternWallpaperDatas(account: Account, accountManager: AccountMana
switch mode {
case .thumbnail:
size = largestRepresentation.dimensions.cgSize.fitted(CGSize(width: 640.0, height: 640.0))
case .fastScreen:
size = largestRepresentation.dimensions.cgSize.fitted(CGSize(width: 1280.0, height: 1280.0))
default:
size = nil
}
@ -407,18 +404,23 @@ public func patternWallpaperImageInternal(thumbnailData: Data?, fullSizeData: Da
}
var scale: CGFloat = 0.0
if case .fastScreen = mode {
scale = max(1.0, UIScreenScale - 1.0)
}
return .single((thumbnailData, fullSizeData, fullSizeComplete))
|> map { (thumbnailData, fullSizeData, fullSizeComplete) in
var fullSizeImage: CGImage?
var scaledSizeImage: CGImage?
if let fullSizeData = fullSizeData, fullSizeComplete {
let options = NSMutableDictionary()
options[kCGImageSourceShouldCache as NSString] = false as NSNumber
if let imageSource = CGImageSourceCreateWithData(fullSizeData as CFData, nil), let image = CGImageSourceCreateImageAtIndex(imageSource, 0, options as CFDictionary) {
fullSizeImage = image
let options = NSMutableDictionary()
options.setValue(960 as NSNumber, forKey: kCGImageSourceThumbnailMaxPixelSize as String)
options.setValue(true as NSNumber, forKey: kCGImageSourceCreateThumbnailFromImageAlways as String)
if let imageSource = CGImageSourceCreateWithData(fullSizeData as CFData, nil), let image = CGImageSourceCreateThumbnailAtIndex(imageSource, 0, options) {
scaledSizeImage = image
}
}
}
@ -433,6 +435,7 @@ public func patternWallpaperImageInternal(thumbnailData: Data?, fullSizeData: Da
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)
@ -446,7 +449,7 @@ public func patternWallpaperImageInternal(thumbnailData: Data?, fullSizeData: Da
let color = combinedColor.withAlphaComponent(1.0)
let intensity = combinedColor.alpha
let context = DrawingContext(size: arguments.drawingSize, scale: fullSizeImage == nil ? 1.0 : scale, clear: true)
let context = DrawingContext(size: arguments.drawingSize, scale: fullSizeImage == nil ? 1.0 : scale, clear: !arguments.corners.isEmpty)
context.withFlippedContext { c in
c.setBlendMode(.copy)
@ -473,10 +476,11 @@ public func patternWallpaperImageInternal(thumbnailData: Data?, fullSizeData: Da
c.restoreGState()
}
if let fullSizeImage = fullSizeImage {
let image = customArguments.preview ? (scaledSizeImage ?? fullSizeImage) : fullSizeImage
if let image = image {
c.setBlendMode(.normal)
c.interpolationQuality = .medium
c.clip(to: fittedRect, mask: fullSizeImage)
c.interpolationQuality = customArguments.preview ? .low : .medium
c.clip(to: fittedRect, mask: image)
if colors.count == 1 {
c.setFillColor(patternColor(for: color, intensity: intensity, prominent: prominent).cgColor)