From fbe52a1140775864be61d618a7a5503f6a8da102 Mon Sep 17 00:00:00 2001 From: Ali <> Date: Thu, 24 Jun 2021 01:59:06 +0400 Subject: [PATCH] Don't cache svg files --- .../CachedResourceRepresentations.swift | 62 ----- .../Sources/Themes/EditThemeController.swift | 9 +- .../Themes/ThemeAccentColorController.swift | 11 +- .../Themes/ThemeSettingsController.swift | 12 - .../Themes/WallpaperGalleryController.swift | 9 +- .../Sources/Themes/WallpaperGalleryItem.swift | 30 +-- .../Themes/WallpaperPatternPanelNode.swift | 2 +- submodules/SvgRendering/BUILD | 14 + .../SvgRendering/Sources/SvgParser.swift | 0 .../ChatControllerBackgroundNode.swift | 61 +---- .../ChatMessageInteractiveMediaNode.swift | 2 +- .../Sources/FetchCachedRepresentations.swift | 221 ---------------- .../TelegramUI/Sources/UpgradedAccounts.swift | 4 - .../Sources/WallpaperBackgroundNode.swift | 2 +- submodules/WallpaperResources/BUILD | 1 + .../Sources/WallpaperResources.swift | 246 +++++++++--------- 16 files changed, 154 insertions(+), 532 deletions(-) create mode 100644 submodules/SvgRendering/BUILD create mode 100644 submodules/SvgRendering/Sources/SvgParser.swift diff --git a/submodules/MediaResources/Sources/CachedResourceRepresentations.swift b/submodules/MediaResources/Sources/CachedResourceRepresentations.swift index 210abd57ca..54f11eb402 100644 --- a/submodules/MediaResources/Sources/CachedResourceRepresentations.swift +++ b/submodules/MediaResources/Sources/CachedResourceRepresentations.swift @@ -117,68 +117,6 @@ public final class CachedBlurredWallpaperRepresentation: CachedMediaResourceRepr } } -public final class CachedPatternWallpaperMaskRepresentation: CachedMediaResourceRepresentation { - public let keepDuration: CachedMediaRepresentationKeepDuration = .general - - public let size: CGSize? - - public var uniqueId: String { - if let size = self.size { - var result = "pattern-wallpaper-mask-\(Int(size.width))x\(Int(size.height))" - return result - } else { - return "pattern-wallpaper-mask" - } - } - - public init(size: CGSize?) { - self.size = size - } - - public func isEqual(to: CachedMediaResourceRepresentation) -> Bool { - if let to = to as? CachedPatternWallpaperMaskRepresentation { - return self.size == to.size - } else { - return false - } - } -} - - -public final class CachedPatternWallpaperRepresentation: CachedMediaResourceRepresentation { - public let keepDuration: CachedMediaRepresentationKeepDuration = .general - - public let colors: [UInt32] - public let intensity: Int32 - public let rotation: Int32? - - public var uniqueId: String { - var id: String = "pattern-wallpaper" - for color in self.colors { - id.append("-\(color)") - } - id.append("-\(self.intensity)") - if let rotation = self.rotation, rotation != 0 { - id += "-\(rotation)deg" - } - return id - } - - public init(colors: [UInt32], intensity: Int32, rotation: Int32?) { - self.colors = colors - self.intensity = intensity - self.rotation = rotation - } - - public func isEqual(to: CachedMediaResourceRepresentation) -> Bool { - if let to = to as? CachedPatternWallpaperRepresentation { - return self.colors == to.colors && self.intensity == intensity && self.rotation == to.rotation - } else { - return false - } - } -} - public final class CachedAlbumArtworkRepresentation: CachedMediaResourceRepresentation { public let keepDuration: CachedMediaRepresentationKeepDuration = .general diff --git a/submodules/SettingsUI/Sources/Themes/EditThemeController.swift b/submodules/SettingsUI/Sources/Themes/EditThemeController.swift index 20298a0251..fc6d82f5d3 100644 --- a/submodules/SettingsUI/Sources/Themes/EditThemeController.swift +++ b/submodules/SettingsUI/Sources/Themes/EditThemeController.swift @@ -514,7 +514,6 @@ public func editThemeController(context: AccountContext, mode: EditThemeControll let prepare: Signal if let resolvedWallpaper = resolvedWallpaper, case let .file(file) = resolvedWallpaper, resolvedWallpaper.isPattern { let resource = file.file.resource - let representation = CachedPatternWallpaperRepresentation(colors: file.settings.colors.count >= 1 ? file.settings.colors : [0xd6e2ee], intensity: file.settings.intensity ?? 50, rotation: file.settings.rotation) var data: Data? if let path = context.account.postbox.mediaBox.completedResourcePath(resource), let maybeData = try? Data(contentsOf: URL(fileURLWithPath: path), options: .mappedRead) { @@ -525,13 +524,7 @@ public func editThemeController(context: AccountContext, mode: EditThemeControll if let data = data { context.sharedContext.accountManager.mediaBox.storeResourceData(resource.id, data: data, synchronous: true) - prepare = (context.sharedContext.accountManager.mediaBox.cachedResourceRepresentation(resource, representation: representation, complete: true, fetch: true) - |> filter({ $0.complete }) - |> take(1) - |> castError(CreateThemeError.self) - |> mapToSignal { _ -> Signal in - return .complete() - }) + prepare = .complete() } else { prepare = .complete() } diff --git a/submodules/SettingsUI/Sources/Themes/ThemeAccentColorController.swift b/submodules/SettingsUI/Sources/Themes/ThemeAccentColorController.swift index 17c48c21bf..b193e7982a 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemeAccentColorController.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemeAccentColorController.swift @@ -176,8 +176,7 @@ final class ThemeAccentColorController: ViewController { let prepareWallpaper: Signal if let patternWallpaper = state.patternWallpaper, case let .file(file) = patternWallpaper, !state.backgroundColors.isEmpty { let resource = file.file.resource - let representation = CachedPatternWallpaperRepresentation(colors: state.backgroundColors.count >= 1 ? state.backgroundColors : [0], intensity: state.patternIntensity, rotation: state.rotation) - + var data: Data? if let path = strongSelf.context.account.postbox.mediaBox.completedResourcePath(resource), let maybeData = try? Data(contentsOf: URL(fileURLWithPath: path), options: .mappedRead) { data = maybeData @@ -187,13 +186,7 @@ final class ThemeAccentColorController: ViewController { if let data = data { strongSelf.context.sharedContext.accountManager.mediaBox.storeResourceData(resource.id, data: data, synchronous: true) - prepareWallpaper = (strongSelf.context.sharedContext.accountManager.mediaBox.cachedResourceRepresentation(resource, representation: representation, complete: true, fetch: true) - |> filter({ $0.complete }) - |> take(1) - |> castError(CreateThemeError.self) - |> mapToSignal { _ -> Signal in - return .complete() - }) + prepareWallpaper = .complete() } else { prepareWallpaper = .complete() } diff --git a/submodules/SettingsUI/Sources/Themes/ThemeSettingsController.swift b/submodules/SettingsUI/Sources/Themes/ThemeSettingsController.swift index a7777c5082..7f7047442f 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemeSettingsController.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemeSettingsController.swift @@ -1256,21 +1256,9 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The |> mapToSignal { cachedWallpaper in if let wallpaper = cachedWallpaper?.wallpaper, case let .file(file) = wallpaper { let resource = file.file.resource - let representation = CachedPatternWallpaperRepresentation(colors: file.settings.colors.count >= 1 ? file.settings.colors : [0xd6e2ee], intensity: file.settings.intensity ?? 50, rotation: file.settings.rotation) let _ = fetchedMediaResource(mediaBox: context.account.postbox.mediaBox, reference: .wallpaper(wallpaper: .slug(file.slug), resource: file.file.resource)).start() - let _ = (context.account.postbox.mediaBox.cachedResourceRepresentation(resource, representation: representation, complete: false, fetch: true) - |> filter({ $0.complete })).start(next: { data in - if data.complete, let path = context.account.postbox.mediaBox.completedResourcePath(resource) { - if let maybeData = try? Data(contentsOf: URL(fileURLWithPath: path), options: .mappedRead) { - context.sharedContext.accountManager.mediaBox.storeResourceData(resource.id, data: maybeData, synchronous: true) - } - if let maybeData = try? Data(contentsOf: URL(fileURLWithPath: data.path), options: .mappedRead) { - context.sharedContext.accountManager.mediaBox.storeCachedResourceRepresentation(resource, representation: representation, data: maybeData) - } - } - }) return .single(wallpaper) } else { diff --git a/submodules/SettingsUI/Sources/Themes/WallpaperGalleryController.swift b/submodules/SettingsUI/Sources/Themes/WallpaperGalleryController.swift index a44dd6145a..9973dfafe9 100644 --- a/submodules/SettingsUI/Sources/Themes/WallpaperGalleryController.swift +++ b/submodules/SettingsUI/Sources/Themes/WallpaperGalleryController.swift @@ -516,8 +516,6 @@ public class WallpaperGalleryController: ViewController { } } else if case let .file(file) = wallpaper, let resource = resource { if wallpaper.isPattern, !file.settings.colors.isEmpty, let intensity = file.settings.intensity { - let representation = CachedPatternWallpaperRepresentation(colors: file.settings.colors, intensity: intensity, rotation: file.settings.rotation) - var data: Data? var thumbnailData: Data? if let path = strongSelf.context.account.postbox.mediaBox.completedResourcePath(resource), let maybeData = try? Data(contentsOf: URL(fileURLWithPath: path), options: .mappedRead) { @@ -541,12 +539,7 @@ public class WallpaperGalleryController: ViewController { if let thumbnailResource = thumbnailResource, let thumbnailData = thumbnailData { strongSelf.context.sharedContext.accountManager.mediaBox.storeResourceData(thumbnailResource.id, data: thumbnailData, synchronous: true) } - let _ = (strongSelf.context.sharedContext.accountManager.mediaBox.cachedResourceRepresentation(resource, representation: representation, complete: true, fetch: true) - |> filter({ $0.complete }) - |> take(1) - |> deliverOnMainQueue).start(next: { _ in - completion(wallpaper) - }) + completion(wallpaper) } } else if let path = strongSelf.context.account.postbox.mediaBox.completedResourcePath(file.file.resource), let data = try? Data(contentsOf: URL(fileURLWithPath: path), options: .mappedRead) { strongSelf.context.sharedContext.accountManager.mediaBox.storeResourceData(file.file.resource.id, data: data) diff --git a/submodules/SettingsUI/Sources/Themes/WallpaperGalleryItem.swift b/submodules/SettingsUI/Sources/Themes/WallpaperGalleryItem.swift index fd23e6eb45..e9cc0bce08 100644 --- a/submodules/SettingsUI/Sources/Themes/WallpaperGalleryItem.swift +++ b/submodules/SettingsUI/Sources/Themes/WallpaperGalleryItem.swift @@ -407,32 +407,10 @@ final class WallpaperGalleryItemNode: GalleryItemNode { self.backgroundColor = patternColor.withAlphaComponent(1.0) - /*if let previousEntry = previousEntry, case let .wallpaper(wallpaper, _) = previousEntry, case let .file(previousFile) = wallpaper, file.id == previousFile.id && (file.settings.colors != previousFile.settings.colors || file.settings.intensity != previousFile.settings.intensity) && self.colorPreview == self.arguments.colorPreview { - - let makeImageLayout = self.imageNode.asyncLayout() - Queue.concurrentDefaultQueue().async { - let apply = makeImageLayout(TransformImageArguments(corners: ImageCorners(), imageSize: displaySize, boundingSize: displaySize, intrinsicInsets: UIEdgeInsets(), custom: patternArguments)) - Queue.mainQueue().async { - if self.colorPreview { - apply() - } - } - } - return - } else if let offset = self.validOffset, self.arguments.colorPreview && abs(offset) > 0.0 { - return - } else { - patternArguments = PatternWallpaperArguments(colors: patternColors, rotation: file.settings.rotation) - }*/ - self.colorPreview = self.arguments.colorPreview signal = .single({ _ in nil }) - /*if file.settings.colors.count >= 3 { - signal = .single({ _ in nil }) - } else { - signal = patternWallpaperImage(account: self.context.account, accountManager: self.context.sharedContext.accountManager, representations: convertedRepresentations, mode: .screen, autoFetchFullSize: true) - }*/ + colorSignal = chatServiceBackgroundColor(wallpaper: wallpaper, mediaBox: self.context.account.postbox.mediaBox) isBlurrable = false @@ -855,12 +833,6 @@ final class WallpaperGalleryItemNode: GalleryItemNode { } private func preparePatternEditing() { - if let entry = self.entry, case let .wallpaper(wallpaper, _) = entry, case let .file(file) = wallpaper { - let dimensions = file.file.dimensions ?? PixelDimensions(width: 1440, height: 2960) - - let size = dimensions.cgSize.fitted(CGSize(width: 1280.0, height: 1280.0)) - let _ = self.context.account.postbox.mediaBox.cachedResourceRepresentation(file.file.resource, representation: CachedPatternWallpaperMaskRepresentation(size: size), complete: false, fetch: true).start() - } } func setMotionEnabled(_ enabled: Bool, animated: Bool) { diff --git a/submodules/SettingsUI/Sources/Themes/WallpaperPatternPanelNode.swift b/submodules/SettingsUI/Sources/Themes/WallpaperPatternPanelNode.swift index c9f96b98d7..fa972c2292 100644 --- a/submodules/SettingsUI/Sources/Themes/WallpaperPatternPanelNode.swift +++ b/submodules/SettingsUI/Sources/Themes/WallpaperPatternPanelNode.swift @@ -29,7 +29,7 @@ private func intensityToSliderValue(_ value: Int32, allowDark: Bool) -> CGFloat private func sliderValueToIntensity(_ value: CGFloat, allowDark: Bool) -> Int32 { if allowDark { if value < 100.0 { - return -Int32(value) + return -Int32(max(1.0, value)) } else { return Int32(value - 100.0) } diff --git a/submodules/SvgRendering/BUILD b/submodules/SvgRendering/BUILD new file mode 100644 index 0000000000..20e1b4b487 --- /dev/null +++ b/submodules/SvgRendering/BUILD @@ -0,0 +1,14 @@ +load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") + +swift_library( + name = "SvgRendering", + module_name = "SvgRendering", + srcs = glob([ + "Sources/**/*.swift", + ]), + deps = [ + ], + visibility = [ + "//visibility:public", + ], +) diff --git a/submodules/SvgRendering/Sources/SvgParser.swift b/submodules/SvgRendering/Sources/SvgParser.swift new file mode 100644 index 0000000000..e69de29bb2 diff --git a/submodules/TelegramPresentationData/Sources/ChatControllerBackgroundNode.swift b/submodules/TelegramPresentationData/Sources/ChatControllerBackgroundNode.swift index cc5345c643..04a846ed1e 100644 --- a/submodules/TelegramPresentationData/Sources/ChatControllerBackgroundNode.swift +++ b/submodules/TelegramPresentationData/Sources/ChatControllerBackgroundNode.swift @@ -74,14 +74,8 @@ public func chatControllerBackgroundImage(theme: PresentationTheme?, wallpaper i } } case let .file(file): - if wallpaper.isPattern, !file.settings.colors.isEmpty, let intensity = file.settings.intensity { - var image: UIImage? - let _ = mediaBox.cachedResourceRepresentation(file.file.resource, representation: CachedPatternWallpaperRepresentation(colors: file.settings.colors, intensity: intensity, rotation: file.settings.rotation), complete: true, fetch: true, attemptSynchronously: true).start(next: { data in - if data.complete { - image = UIImage(contentsOfFile: data.path)?.precomposed() - } - }) - backgroundImage = image + if wallpaper.isPattern { + backgroundImage = nil } else { if file.settings.blur && composed { var image: UIImage? @@ -174,55 +168,8 @@ public func chatControllerBackgroundImageSignal(wallpaper: TelegramWallpaper, me } } case let .file(file): - if wallpaper.isPattern, !file.settings.colors.isEmpty, let intensity = file.settings.intensity { - let representation = CachedPatternWallpaperRepresentation(colors: file.settings.colors, intensity: intensity, rotation: file.settings.rotation) - - let effectiveMediaBox: MediaBox - if FileManager.default.fileExists(atPath: mediaBox.cachedRepresentationCompletePath(file.file.resource.id, representation: representation)) { - effectiveMediaBox = mediaBox - } else { - effectiveMediaBox = accountMediaBox - } - - return effectiveMediaBox.cachedResourceRepresentation(file.file.resource, representation: representation, complete: true, fetch: true, attemptSynchronously: true) - |> take(1) - |> mapToSignal { data -> Signal<(UIImage?, Bool)?, NoError> in - if data.complete { - return .single((UIImage(contentsOfFile: data.path)?.precomposed(), true)) - } else { - let interimWallpaper: TelegramWallpaper - if file.settings.colors.count >= 2 { - interimWallpaper = .gradient(nil, file.settings.colors, file.settings) - } else { - interimWallpaper = .color(file.settings.colors.count >= 1 ? file.settings.colors[0] : 0) - } - - let settings = file.settings - let interrimImage = generateImage(CGSize(width: 640.0, height: 1280.0), rotatedContext: { size, context in - if file.settings.colors.count >= 1 { - let gradientColors = [UIColor(argb: file.settings.colors[0]).cgColor, UIColor(argb: file.settings.colors.count >= 2 ? file.settings.colors[1] : file.settings.colors[0]).cgColor] as CFArray - - var locations: [CGFloat] = [0.0, 1.0] - let colorSpace = CGColorSpaceCreateDeviceRGB() - let gradient = CGGradient(colorsSpace: colorSpace, colors: gradientColors, locations: &locations)! - - context.translateBy(x: 320.0, y: 640.0) - context.rotate(by: CGFloat(settings.rotation ?? 0) * CGFloat.pi / 180.0) - context.translateBy(x: -320.0, y: -640.0) - - context.drawLinearGradient(gradient, start: CGPoint(x: 0.0, y: 0.0), end: CGPoint(x: 0.0, y: size.height), options: [.drawsBeforeStartLocation, .drawsAfterEndLocation]) - } - }) - - return .single((interrimImage, false)) |> then(effectiveMediaBox.cachedResourceRepresentation(file.file.resource, representation: CachedPatternWallpaperRepresentation(colors: file.settings.colors, intensity: intensity, rotation: file.settings.rotation), complete: true, fetch: true, attemptSynchronously: false) - |> map { data -> (UIImage?, Bool)? in - return (UIImage(contentsOfFile: data.path)?.precomposed(), true) - }) - } - } - |> afterNext { image in - cacheWallpaper(image?.0) - } + if wallpaper.isPattern { + return .single((nil, true)) } else { if file.settings.blur { let representation = CachedBlurredWallpaperRepresentation() diff --git a/submodules/TelegramUI/Sources/ChatMessageInteractiveMediaNode.swift b/submodules/TelegramUI/Sources/ChatMessageInteractiveMediaNode.swift index d5c7784b3e..37419dec04 100644 --- a/submodules/TelegramUI/Sources/ChatMessageInteractiveMediaNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageInteractiveMediaNode.swift @@ -738,7 +738,7 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTransitio representations.append(ImageRepresentationWithReference(representation: .init(dimensions: PixelDimensions(width: 1440, height: 2960), resource: file.resource, progressiveSizes: [], immediateThumbnailData: nil), reference: AnyMediaReference.message(message: MessageReference(message), media: file).resourceReference(file.resource))) } if ["image/png", "image/svg+xml", "application/x-tgwallpattern"].contains(file.mimeType) { - return patternWallpaperImage(account: context.account, accountManager: context.sharedContext.accountManager, representations: representations, mode: .thumbnail) + return patternWallpaperImage(account: context.account, accountManager: context.sharedContext.accountManager, representations: representations, mode: .screen) |> mapToSignal { value -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> in if let value = value { return .single(value) diff --git a/submodules/TelegramUI/Sources/FetchCachedRepresentations.swift b/submodules/TelegramUI/Sources/FetchCachedRepresentations.swift index 19073f6a28..3c67d897f4 100644 --- a/submodules/TelegramUI/Sources/FetchCachedRepresentations.swift +++ b/submodules/TelegramUI/Sources/FetchCachedRepresentations.swift @@ -16,7 +16,6 @@ import LocationResources import ImageBlur import TelegramAnimatedStickerNode import WallpaperResources -import Svg import GZip import TelegramUniversalVideoContent import GradientBackground @@ -68,22 +67,6 @@ public func fetchCachedResourceRepresentation(account: Account, resource: MediaR } return fetchCachedBlurredWallpaperRepresentation(resource: resource, resourceData: data, representation: representation) } - } else if let representation = representation as? CachedPatternWallpaperMaskRepresentation { - return account.postbox.mediaBox.resourceData(resource, option: .complete(waitUntilFetchStatus: false)) - |> mapToSignal { data -> Signal in - if !data.complete { - return .complete() - } - return fetchCachedPatternWallpaperMaskRepresentation(resource: resource, resourceData: data, representation: representation) - } - } else if let representation = representation as? CachedPatternWallpaperRepresentation { - return account.postbox.mediaBox.resourceData(resource, option: .complete(waitUntilFetchStatus: false)) - |> mapToSignal { data -> Signal in - if !data.complete { - return .complete() - } - return fetchCachedPatternWallpaperRepresentation(resource: resource, resourceData: data, representation: representation) - } } else if let representation = representation as? CachedAlbumArtworkRepresentation { return account.postbox.mediaBox.resourceData(resource, option: .complete(waitUntilFetchStatus: false)) |> mapToSignal { data -> Signal in @@ -417,194 +400,6 @@ private func fetchCachedBlurredWallpaperRepresentation(resource: MediaResource, }) |> runOn(Queue.concurrentDefaultQueue()) } -private func fetchCachedPatternWallpaperMaskRepresentation(resource: MediaResource, resourceData: MediaResourceData, representation: CachedPatternWallpaperMaskRepresentation) -> Signal { - return Signal({ subscriber in - if var data = try? Data(contentsOf: URL(fileURLWithPath: resourceData.path), options: [.mappedIfSafe]) { - let path = NSTemporaryDirectory() + "\(Int64.random(in: Int64.min ... Int64.max))" - let url = URL(fileURLWithPath: path) - - if let unzippedData = TGGUnzipData(data, 2 * 1024 * 1024) { - data = unzippedData - } - - if data.count > 5, let string = String(data: data.subdata(in: 0 ..< 5), encoding: .utf8), string == " runOn(Queue.concurrentDefaultQueue()) -} - -private func fetchCachedPatternWallpaperRepresentation(resource: MediaResource, resourceData: MediaResourceData, representation: CachedPatternWallpaperRepresentation) -> Signal { - return Signal({ subscriber in - if var data = try? Data(contentsOf: URL(fileURLWithPath: resourceData.path), options: [.mappedIfSafe]) { - if let unzippedData = TGGUnzipData(data, 2 * 1024 * 1024) { - data = unzippedData - } - - let path = NSTemporaryDirectory() + "\(Int64.random(in: Int64.min ... Int64.max))" - let url = URL(fileURLWithPath: path) - - let colors: [UIColor] = representation.colors.map(UIColor.init(rgb:)) - - let intensity = CGFloat(representation.intensity) / 100.0 - - var size: CGSize? - var maskImage: UIImage? - if data.count > 5, let string = String(data: data.subdata(in: 0 ..< 5), encoding: .utf8), string == "= 3 { - let drawingRect = rect - let image = GradientBackgroundNode.generatePreview(size: CGSize(width: 60.0, height: 60.0), colors: colors) - c.translateBy(x: drawingRect.midX, y: drawingRect.midY) - c.scaleBy(x: 1.0, y: 1.0) - c.translateBy(x: -drawingRect.midX, y: -drawingRect.midY) - c.draw(image.cgImage!, in: drawingRect) - c.translateBy(x: drawingRect.midX, y: drawingRect.midY) - c.scaleBy(x: 1.0, y: 1.0) - c.translateBy(x: -drawingRect.midX, y: -drawingRect.midY) - } else { - let gradientColors = colors.map { $0.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.saveGState() - c.translateBy(x: rect.width / 2.0, y: rect.height / 2.0) - c.rotate(by: CGFloat(representation.rotation ?? 0) * CGFloat.pi / -180.0) - c.translateBy(x: -rect.width / 2.0, y: -rect.height / 2.0) - - c.drawLinearGradient(gradient, start: CGPoint(x: 0.0, y: 0.0), end: CGPoint(x: 0.0, y: rect.height), options: [.drawsBeforeStartLocation, .drawsAfterEndLocation]) - c.restoreGState() - } - - c.setBlendMode(.normal) - if let cgImage = maskImage?.cgImage { - c.clip(to: rect, mask: cgImage) - } - - if colors.count >= 3 { - c.setBlendMode(.softLight) - } - - let isLight = averageBackgroundColor.hsb.b >= 0.3 - if isLight { - c.setFillColor(UIColor(white: 0.0, alpha: abs(intensity)).cgColor) - c.fill(rect) - } else { - c.setFillColor(UIColor(white: 1.0, alpha: abs(intensity)).cgColor) - c.fill(rect) - } - - /*if colors.count == 1, let color = colors.first { - c.setFillColor(patternColor(for: color, intensity: intensity).cgColor) - c.fill(rect) - } else { - let gradientColors = colors.map { patternColor(for: $0, intensity: intensity).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: rect.width / 2.0, y: rect.height / 2.0) - c.rotate(by: CGFloat(representation.rotation ?? 0) * CGFloat.pi / -180.0) - c.translateBy(x: -rect.width / 2.0, y: -rect.height / 2.0) - - c.drawLinearGradient(gradient, start: CGPoint(x: 0.0, y: 0.0), end: CGPoint(x: 0.0, y: rect.height), options: [.drawsBeforeStartLocation, .drawsAfterEndLocation]) - }*/ - }, scale: 1.0) - } - - if let colorImage = colorImage, let colorDestination = CGImageDestinationCreateWithURL(url as CFURL, kUTTypeJPEG, 1, nil) { - CGImageDestinationSetProperties(colorDestination, [:] as CFDictionary) - - let colorQuality: Float = 0.9 - - let options = NSMutableDictionary() - options.setObject(colorQuality as NSNumber, forKey: kCGImageDestinationLossyCompressionQuality as NSString) - - CGImageDestinationAddImage(colorDestination, colorImage.cgImage!, options as CFDictionary) - if CGImageDestinationFinalize(colorDestination) { - subscriber.putNext(.temporaryPath(path)) - subscriber.putCompletion() - } - } - } - return EmptyDisposable - }) |> runOn(Queue.concurrentDefaultQueue()) -} - public func fetchCachedSharedResourceRepresentation(accountManager: AccountManager, resource: MediaResource, representation: CachedMediaResourceRepresentation) -> Signal { if let representation = representation as? CachedScaledImageRepresentation { return accountManager.mediaBox.resourceData(resource, option: .complete(waitUntilFetchStatus: false)) @@ -622,22 +417,6 @@ public func fetchCachedSharedResourceRepresentation(accountManager: AccountManag } return fetchCachedBlurredWallpaperRepresentation(resource: resource, resourceData: data, representation: representation) } - } else if let representation = representation as? CachedPatternWallpaperMaskRepresentation { - return accountManager.mediaBox.resourceData(resource, option: .complete(waitUntilFetchStatus: false)) - |> mapToSignal { data -> Signal in - if !data.complete { - return .complete() - } - return fetchCachedPatternWallpaperMaskRepresentation(resource: resource, resourceData: data, representation: representation) - } - } else if let representation = representation as? CachedPatternWallpaperRepresentation { - return accountManager.mediaBox.resourceData(resource, option: .complete(waitUntilFetchStatus: false)) - |> mapToSignal { data -> Signal in - if !data.complete { - return .complete() - } - return fetchCachedPatternWallpaperRepresentation(resource: resource, resourceData: data, representation: representation) - } } else { return .never() } diff --git a/submodules/TelegramUI/Sources/UpgradedAccounts.swift b/submodules/TelegramUI/Sources/UpgradedAccounts.swift index bc733d53e0..765e2f7d7a 100644 --- a/submodules/TelegramUI/Sources/UpgradedAccounts.swift +++ b/submodules/TelegramUI/Sources/UpgradedAccounts.swift @@ -122,7 +122,6 @@ public func upgradedAccounts(accountManager: AccountManager, rootPath: String, e } |> ignoreValues |> mapToSignal { _ -> Signal in - return .complete() } } var signal: Signal = .complete() @@ -166,9 +165,6 @@ public func upgradedAccounts(accountManager: AccountManager, rootPath: String, e accountManager.mediaBox.storeResourceData(file.file.resource.id, data: data) let _ = accountManager.mediaBox.cachedResourceRepresentation(file.file.resource, representation: CachedScaledImageRepresentation(size: CGSize(width: 720.0, height: 720.0), mode: .aspectFit), complete: true, fetch: true).start() if wallpaper.isPattern { - if !file.settings.colors.isEmpty, let intensity = file.settings.intensity { - let _ = accountManager.mediaBox.cachedResourceRepresentation(file.file.resource, representation: CachedPatternWallpaperRepresentation(colors: file.settings.colors, intensity: intensity, rotation: file.settings.rotation), complete: true, fetch: true).start() - } } else { if file.settings.blur { let _ = accountManager.mediaBox.cachedResourceRepresentation(file.file.resource, representation: CachedBlurredWallpaperRepresentation(), complete: true, fetch: true).start() diff --git a/submodules/WallpaperBackgroundNode/Sources/WallpaperBackgroundNode.swift b/submodules/WallpaperBackgroundNode/Sources/WallpaperBackgroundNode.swift index 1450eba4b7..830dd0505a 100644 --- a/submodules/WallpaperBackgroundNode/Sources/WallpaperBackgroundNode.swift +++ b/submodules/WallpaperBackgroundNode/Sources/WallpaperBackgroundNode.swift @@ -617,7 +617,7 @@ public final class WallpaperBackgroundNode: ASDisplayNode { 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, onlyFullSize: true) + 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 { diff --git a/submodules/WallpaperResources/BUILD b/submodules/WallpaperResources/BUILD index 45408d2b8f..4a369c7786 100644 --- a/submodules/WallpaperResources/BUILD +++ b/submodules/WallpaperResources/BUILD @@ -20,6 +20,7 @@ swift_library( "//submodules/PersistentStringHash:PersistentStringHash", "//submodules/AppBundle:AppBundle", "//submodules/Svg:Svg", + "//submodules/GZip:GZip", "//submodules/GradientBackground:GradientBackground", ], visibility = [ diff --git a/submodules/WallpaperResources/Sources/WallpaperResources.swift b/submodules/WallpaperResources/Sources/WallpaperResources.swift index aa706483d8..d654e55376 100644 --- a/submodules/WallpaperResources/Sources/WallpaperResources.swift +++ b/submodules/WallpaperResources/Sources/WallpaperResources.swift @@ -16,6 +16,7 @@ import TelegramUIPreferences import AppBundle import Svg import GradientBackground +import GZip public func wallpaperDatas(account: Account, accountManager: AccountManager, fileReference: FileMediaReference? = nil, representations: [ImageRepresentationWithReference], alwaysShowThumbnailFirst: Bool = false, thumbnail: Bool = false, onlyFullSize: Bool = false, autoFetchFullSize: Bool = false, synchronousLoad: Bool = false) -> Signal<(Data?, Data?, Bool), NoError> { if let smallestRepresentation = smallestImageRepresentation(representations.map({ $0.representation })), let largestRepresentation = largestImageRepresentation(representations.map({ $0.representation })), let smallestIndex = representations.firstIndex(where: { $0.representation == smallestRepresentation }), let largestIndex = representations.firstIndex(where: { $0.representation == largestRepresentation }) { @@ -358,88 +359,47 @@ public struct PatternWallpaperArguments: TransformImageCustomArguments { } } -private func patternWallpaperDatas(account: Account, accountManager: AccountManager, representations: [ImageRepresentationWithReference], mode: PatternWallpaperDrawMode, autoFetchFullSize: Bool = false) -> Signal<(Data?, Data?, Bool), NoError> { - if let smallestRepresentation = smallestImageRepresentation(representations.map({ $0.representation })), let largestRepresentation = largestImageRepresentation(representations.map({ $0.representation })), let smallestIndex = representations.firstIndex(where: { $0.representation == smallestRepresentation }), let largestIndex = representations.firstIndex(where: { $0.representation == largestRepresentation }) { - - let size: CGSize? - switch mode { - case .thumbnail: - size = largestRepresentation.dimensions.cgSize.fitted(CGSize(width: 640.0, height: 640.0)) - default: - size = nil +private func patternWallpaperDatas(account: Account, accountManager: AccountManager, representations: [ImageRepresentationWithReference], mode: PatternWallpaperDrawMode, autoFetchFullSize: Bool = false) -> Signal<(Data?, Bool), NoError> { + var targetRepresentation: ImageRepresentationWithReference? + switch mode { + case .thumbnail: + if let representation = smallestImageRepresentation(representations.map({ $0.representation })) { + targetRepresentation = representations[representations.firstIndex(where: { $0.representation == representation })!] } - let maybeFullSize = combineLatest(accountManager.mediaBox.cachedResourceRepresentation(largestRepresentation.resource, representation: CachedPatternWallpaperMaskRepresentation(size: size), complete: false, fetch: false), account.postbox.mediaBox.cachedResourceRepresentation(largestRepresentation.resource, representation: CachedPatternWallpaperMaskRepresentation(size: size), complete: false, fetch: false)) + case .screen: + if let representation = largestImageRepresentation(representations.map({ $0.representation })) { + targetRepresentation = representations[representations.firstIndex(where: { $0.representation == representation })!] + } + } + + if let targetRepresentation = targetRepresentation { + let maybeFullSize = combineLatest( + accountManager.mediaBox.resourceData(targetRepresentation.representation.resource), + account.postbox.mediaBox.resourceData(targetRepresentation.representation.resource) + ) let signal = maybeFullSize |> take(1) - |> mapToSignal { maybeSharedData, maybeData -> Signal<(Data?, Data?, Bool), NoError> in + |> mapToSignal { maybeSharedData, maybeData -> Signal<(Data?, Bool), NoError> in if maybeSharedData.complete { if let loadedData = try? Data(contentsOf: URL(fileURLWithPath: maybeSharedData.path), options: [.mappedRead]) { - return .single((nil, loadedData, true)) + return .single((loadedData, true)) } else { - return .single((nil, nil, true)) + return .single(( nil, true)) } } else if maybeData.complete { let loadedData: Data? = try? Data(contentsOf: URL(fileURLWithPath: maybeData.path), options: []) - return .single((nil, loadedData, true)) + return .single((loadedData, true)) } else { - let fetchedThumbnail = fetchedMediaResource(mediaBox: account.postbox.mediaBox, reference: representations[smallestIndex].reference) - let fetchedFullSize = fetchedMediaResource(mediaBox: account.postbox.mediaBox, reference: representations[largestIndex].reference) - - let accountThumbnailData = Signal { subscriber in - let fetchedDisposable = fetchedThumbnail.start() - let thumbnailDisposable = account.postbox.mediaBox.cachedResourceRepresentation(representations[smallestIndex].representation.resource, representation: CachedPatternWallpaperMaskRepresentation(size: size), complete: false, fetch: true).start(next: { next in - subscriber.putNext(next.size == 0 ? nil : try? Data(contentsOf: URL(fileURLWithPath: next.path), options: [])) - - if next.complete, let data = try? Data(contentsOf: URL(fileURLWithPath: next.path), options: .mappedRead) { - accountManager.mediaBox.storeResourceData(representations[smallestIndex].representation.resource.id, data: data) - let _ = accountManager.mediaBox.cachedResourceRepresentation(representations[smallestIndex].representation.resource, representation: CachedPatternWallpaperMaskRepresentation(size: size), complete: false, fetch: true).start() - } - }, error: subscriber.putError, completed: subscriber.putCompletion) - - return ActionDisposable { - fetchedDisposable.dispose() - thumbnailDisposable.dispose() - } - } + let fetchedFullSize = fetchedMediaResource(mediaBox: account.postbox.mediaBox, reference: targetRepresentation.reference) - let sharedThumbnailData = Signal { subscriber in - let thumbnailDisposable = accountManager.mediaBox.cachedResourceRepresentation(representations[smallestIndex].representation.resource, representation: CachedPatternWallpaperMaskRepresentation(size: size), complete: false, fetch: true).start(next: { next in - subscriber.putNext(next.size == 0 ? nil : try? Data(contentsOf: URL(fileURLWithPath: next.path), options: [])) - }, error: subscriber.putError, completed: subscriber.putCompletion) - - return ActionDisposable { - thumbnailDisposable.dispose() - } - } - - let thumbnailData = combineLatest(accountThumbnailData, sharedThumbnailData) - |> map { thumbnailData, sharedThumbnailData -> Data? in - return thumbnailData ?? sharedThumbnailData - } - |> distinctUntilChanged(isEqual: { lhs, rhs in - if lhs == nil && rhs == nil { - return true - } else { - return false - } - }) - |> take(until: { value in - if value != nil { - return SignalTakeAction(passthrough: true, complete: true) - } else { - return SignalTakeAction(passthrough: true, complete: false) - } - }) - let accountFullSizeData = Signal<(Data?, Bool), NoError> { subscriber in let fetchedFullSizeDisposable = fetchedFullSize.start() - let fullSizeDisposable = account.postbox.mediaBox.cachedResourceRepresentation(representations[largestIndex].representation.resource, representation: CachedPatternWallpaperMaskRepresentation(size: size), complete: false, fetch: true).start(next: { next in + let fullSizeDisposable = account.postbox.mediaBox.resourceData(targetRepresentation.representation.resource).start(next: { next in subscriber.putNext((next.size == 0 ? nil : try? Data(contentsOf: URL(fileURLWithPath: next.path), options: []), next.complete)) if next.complete, let data = try? Data(contentsOf: URL(fileURLWithPath: next.path), options: .mappedRead) { - accountManager.mediaBox.storeResourceData(representations[largestIndex].representation.resource.id, data: data) - let _ = accountManager.mediaBox.cachedResourceRepresentation(representations[largestIndex].representation.resource, representation: CachedPatternWallpaperMaskRepresentation(size: size), complete: false, fetch: true).start() + accountManager.mediaBox.storeResourceData(targetRepresentation.representation.resource.id, data: data) } }, error: subscriber.putError, completed: subscriber.putCompletion) @@ -450,7 +410,7 @@ private func patternWallpaperDatas(account: Account, accountManager: AccountMana } let sharedFullSizeData = Signal<(Data?, Bool), NoError> { subscriber in - let fullSizeDisposable = accountManager.mediaBox.cachedResourceRepresentation(representations[largestIndex].representation.resource, representation: CachedPatternWallpaperMaskRepresentation(size: size), complete: false, fetch: true).start(next: { next in + let fullSizeDisposable = accountManager.mediaBox.resourceData(targetRepresentation.representation.resource).start(next: { next in subscriber.putNext((next.size == 0 ? nil : try? Data(contentsOf: URL(fileURLWithPath: next.path), options: []), next.complete)) }, error: subscriber.putError, completed: subscriber.putCompletion) @@ -481,12 +441,8 @@ private func patternWallpaperDatas(account: Account, accountManager: AccountMana return SignalTakeAction(passthrough: true, complete: false) } }) - - return thumbnailData |> mapToSignal { thumbnailData in - return fullSizeData |> map { (fullSizeData, complete) in - return (thumbnailData, fullSizeData, complete) - } - } + + return fullSizeData } } @@ -496,14 +452,14 @@ private func patternWallpaperDatas(account: Account, accountManager: AccountMana } } -public func patternWallpaperImage(account: Account, accountManager: AccountManager, representations: [ImageRepresentationWithReference], mode: PatternWallpaperDrawMode, autoFetchFullSize: Bool = false, onlyFullSize: Bool = false) -> Signal<((TransformImageArguments) -> DrawingContext?)?, NoError> { +public func patternWallpaperImage(account: Account, accountManager: AccountManager, representations: [ImageRepresentationWithReference], mode: PatternWallpaperDrawMode, autoFetchFullSize: Bool = false) -> Signal<((TransformImageArguments) -> DrawingContext?)?, NoError> { return patternWallpaperDatas(account: account, accountManager: accountManager, representations: representations, mode: mode, autoFetchFullSize: autoFetchFullSize) - |> mapToSignal { (thumbnailData, fullSizeData, fullSizeComplete) in - return patternWallpaperImageInternal(thumbnailData: thumbnailData, fullSizeData: fullSizeData, fullSizeComplete: fullSizeComplete, mode: mode, onlyFullSize: onlyFullSize) + |> mapToSignal { fullSizeData, fullSizeComplete in + return patternWallpaperImageInternal(fullSizeData: fullSizeData, fullSizeComplete: fullSizeComplete, mode: mode) } } -public func patternWallpaperImageInternal(thumbnailData: Data?, fullSizeData: Data?, fullSizeComplete: Bool, mode: PatternWallpaperDrawMode, onlyFullSize: Bool = false) -> Signal<((TransformImageArguments) -> DrawingContext?)?, NoError> { +private func patternWallpaperImageInternal(fullSizeData: Data?, fullSizeComplete: Bool, mode: PatternWallpaperDrawMode) -> Signal<((TransformImageArguments) -> DrawingContext?)?, NoError> { var prominent = false if case .thumbnail = mode { prominent = true @@ -511,31 +467,8 @@ public func patternWallpaperImageInternal(thumbnailData: Data?, fullSizeData: Da let scale: CGFloat = 0.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 - } - } - } - - if onlyFullSize { - if fullSizeData == nil { - return nil - } - } - + return .single((fullSizeData, fullSizeComplete)) + |> map { fullSizeData, fullSizeComplete in return { arguments in var scale = scale if scale.isZero { @@ -554,7 +487,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: !arguments.corners.isEmpty) + let context = DrawingContext(size: arguments.drawingSize, scale: scale, clear: !arguments.corners.isEmpty) context.withFlippedContext { c in c.setBlendMode(.copy) @@ -596,7 +529,12 @@ public func patternWallpaperImageInternal(thumbnailData: Data?, fullSizeData: Da let overlayImage = generateImage(arguments.drawingRect.size, rotatedContext: { size, c in c.clear(CGRect(origin: CGPoint(), size: size)) - let image = customArguments.preview ? (scaledSizeImage ?? fullSizeImage) : fullSizeImage + var image: UIImage? + if let fullSizeData = fullSizeData, let unpackedData = TGGUnzipData(fullSizeData, 2 * 1024 * 1024) { + image = drawSvgImage(unpackedData, CGSize(width: size.width * context.scale, height: size.height * context.scale), .black, .white) + } else if let fullSizeData = fullSizeData { + image = UIImage(data: fullSizeData) + } if let customPatternColor = customArguments.customPatternColor, customPatternColor.alpha < 1.0 { c.setBlendMode(.copy) @@ -607,7 +545,7 @@ public func patternWallpaperImageInternal(thumbnailData: Data?, fullSizeData: Da } if let image = image { - var fittedSize = CGSize(width: image.width, height: image.height) + var fittedSize = image.size if abs(fittedSize.width - arguments.boundingSize.width).isLessThanOrEqualTo(CGFloat(1.0)) { fittedSize.width = arguments.boundingSize.width } @@ -619,7 +557,7 @@ public func patternWallpaperImageInternal(thumbnailData: Data?, fullSizeData: Da 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) + c.clip(to: fittedRect, mask: image.cgImage!) if let customPatternColor = customArguments.customPatternColor { c.setFillColor(customPatternColor.cgColor) @@ -713,6 +651,55 @@ public func solidColorImage(_ color: UIColor) -> Signal<(TransformImageArguments }) } +public func drawWallpaperGradientImage(_ colors: [UIColor], rotation: Int32? = nil, context: CGContext, size: CGSize) { + guard !colors.isEmpty else { + return + } + guard colors.count > 1 else { + context.setFillColor(colors[0].cgColor) + context.fill(CGRect(origin: CGPoint(), size: size)) + return + } + + let drawingRect = CGRect(origin: CGPoint(), size: size) + + let c = context + + if colors.count >= 3 { + let image = GradientBackgroundNode.generatePreview(size: CGSize(width: 60.0, height: 60.0), colors: colors) + c.translateBy(x: drawingRect.midX, y: drawingRect.midY) + c.scaleBy(x: 1.0, y: -1.0) + c.translateBy(x: -drawingRect.midX, y: -drawingRect.midY) + c.draw(image.cgImage!, in: drawingRect) + c.translateBy(x: drawingRect.midX, y: drawingRect.midY) + c.scaleBy(x: 1.0, y: -1.0) + c.translateBy(x: -drawingRect.midX, y: -drawingRect.midY) + } else { + let gradientColors = colors.map { $0.withAlphaComponent(1.0).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)! + + if let rotation = rotation { + c.saveGState() + c.translateBy(x: drawingRect.width / 2.0, y: drawingRect.height / 2.0) + c.rotate(by: CGFloat(rotation) * CGFloat.pi / 180.0) + c.translateBy(x: -drawingRect.width / 2.0, y: -drawingRect.height / 2.0) + } + + c.drawLinearGradient(gradient, start: CGPoint(x: 0.0, y: 0.0), end: CGPoint(x: 0.0, y: drawingRect.height), options: [.drawsBeforeStartLocation, .drawsAfterEndLocation]) + + if rotation != nil { + c.restoreGState() + } + } +} + public func gradientImage(_ colors: [UIColor], rotation: Int32? = nil) -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> { guard !colors.isEmpty else { return .complete() @@ -1157,18 +1144,23 @@ public func themeImage(account: Account, accountManager: AccountManager, source: case let .settings(settings): theme = .single((makePresentationTheme(mediaBox: accountManager.mediaBox, themeReference: .builtin(PresentationBuiltinThemeReference(baseTheme: settings.baseTheme)), accentColor: UIColor(argb: settings.accentColor), backgroundColors: [], bubbleColors: settings.messageColors.flatMap { (UIColor(argb: $0.top), UIColor(argb: $0.bottom)) }, wallpaper: settings.wallpaper, serviceBackgroundColor: nil, preview: false), nil)) } + + enum WallpaperImage { + case image(UIImage) + case pattern(data: Data, colors: [UInt32], intensity: Int32) + } let data = theme - |> mapToSignal { (theme, thumbnailData) -> Signal<(PresentationTheme?, UIImage?, Data?), NoError> in + |> mapToSignal { (theme, thumbnailData) -> Signal<(PresentationTheme?, WallpaperImage?, Data?), NoError> in if let theme = theme { if case let .file(file) = theme.chat.defaultWallpaper { return cachedWallpaper(account: account, slug: file.slug, settings: file.settings) - |> mapToSignal { wallpaper -> Signal<(PresentationTheme?, UIImage?, Data?), NoError> in + |> mapToSignal { wallpaper -> Signal<(PresentationTheme?, WallpaperImage?, Data?), NoError> in if let wallpaper = wallpaper, case let .file(file) = wallpaper.wallpaper { var convertedRepresentations: [ImageRepresentationWithReference] = [] convertedRepresentations.append(ImageRepresentationWithReference(representation: TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: 100, height: 100), resource: file.file.resource, progressiveSizes: [], immediateThumbnailData: nil), reference: .wallpaper(wallpaper: .slug(file.slug), resource: file.file.resource))) return wallpaperDatas(account: account, accountManager: accountManager, fileReference: .standalone(media: file.file), representations: convertedRepresentations, alwaysShowThumbnailFirst: false, thumbnail: false, onlyFullSize: true, autoFetchFullSize: true, synchronousLoad: false) - |> mapToSignal { _, fullSizeData, complete -> Signal<(PresentationTheme?, UIImage?, Data?), NoError> in + |> mapToSignal { _, fullSizeData, complete -> Signal<(PresentationTheme?, WallpaperImage?, Data?), NoError> in guard complete, let fullSizeData = fullSizeData else { return .complete() } @@ -1176,10 +1168,10 @@ public func themeImage(account: Account, accountManager: AccountManager, source: let _ = accountManager.mediaBox.cachedResourceRepresentation(file.file.resource, representation: CachedScaledImageRepresentation(size: CGSize(width: 720.0, height: 720.0), mode: .aspectFit), complete: true, fetch: true).start() if wallpaper.wallpaper.isPattern, !file.settings.colors.isEmpty, let intensity = file.settings.intensity { - return accountManager.mediaBox.cachedResourceRepresentation(file.file.resource, representation: CachedPatternWallpaperRepresentation(colors: file.settings.colors, intensity: intensity, rotation: file.settings.rotation), complete: true, fetch: true) + return accountManager.mediaBox.resourceData(file.file.resource) |> mapToSignal { data in - if data.complete, let imageData = try? Data(contentsOf: URL(fileURLWithPath: data.path)), let image = UIImage(data: imageData) { - return .single((theme, image, thumbnailData)) + if data.complete, let imageData = try? Data(contentsOf: URL(fileURLWithPath: data.path)) { + return .single((theme, .pattern(data: imageData, colors: file.settings.colors, intensity: intensity), thumbnailData)) } else { return .complete() } @@ -1188,13 +1180,13 @@ public func themeImage(account: Account, accountManager: AccountManager, source: return accountManager.mediaBox.cachedResourceRepresentation(file.file.resource, representation: CachedBlurredWallpaperRepresentation(), complete: true, fetch: true) |> mapToSignal { data in if data.complete, let data = try? Data(contentsOf: URL(fileURLWithPath: data.path)), let image = UIImage(data: data) { - return .single((theme, image, thumbnailData)) + return .single((theme, .image(image), thumbnailData)) } else { return .complete() } } } else if let image = UIImage(data: fullSizeData) { - return .single((theme, image, thumbnailData)) + return .single((theme, .image(image), thumbnailData)) } else { return .complete() } @@ -1262,8 +1254,27 @@ public func themeImage(account: Account, accountManager: AccountManager, source: if let theme = theme { context.withFlippedContext { c in c.setBlendMode(.normal) - - drawThemeImage(context: c, theme: theme, wallpaperImage: wallpaperImage, size: arguments.drawingSize) + + switch wallpaperImage { + case let .image(image): + drawThemeImage(context: c, theme: theme, wallpaperImage: image, size: arguments.drawingSize) + case let .pattern(data, colors, intensity): + let wallpaperImage = generateImage(arguments.drawingSize, rotatedContext: { size, context in + drawWallpaperGradientImage(colors.map(UIColor.init(rgb:)), context: context, size: size) + if let unpackedData = TGGUnzipData(data, 2 * 1024 * 1024), let image = drawSvgImage(unpackedData, arguments.drawingSize, .clear, .black) { + context.setBlendMode(.softLight) + context.setAlpha(abs(CGFloat(intensity)) / 100.0) + context.draw(image.cgImage!, in: CGRect(origin: CGPoint(), size: arguments.drawingSize)) + } else if let image = UIImage(data: data) { + context.setBlendMode(.softLight) + context.setAlpha(abs(CGFloat(intensity)) / 100.0) + context.draw(image.cgImage!, in: CGRect(origin: CGPoint(), size: arguments.drawingSize)) + } + }) + drawThemeImage(context: c, theme: theme, wallpaperImage: wallpaperImage, size: arguments.drawingSize) + case .none: + drawThemeImage(context: c, theme: theme, wallpaperImage: nil, size: arguments.drawingSize) + } c.setStrokeColor(theme.rootController.navigationBar.separatorColor.cgColor) c.setLineWidth(2.0) @@ -1486,10 +1497,7 @@ public func themeIconImage(account: Account, accountManager: AccountManager, the if intensity < 0 { return .single(((.black, nil, []), incomingColor, outgoingColor, nil, rotation)) } else { - return accountManager.mediaBox.cachedResourceRepresentation(file.file.resource, representation: CachedPatternWallpaperRepresentation(colors: file.settings.colors, intensity: intensity, rotation: file.settings.rotation), complete: true, fetch: true) - |> mapToSignal { _ in - return .single((effectiveBackgroundColor, incomingColor, outgoingColor, nil, rotation)) - } + return .single((effectiveBackgroundColor, incomingColor, outgoingColor, nil, rotation)) } } else { return .complete()