mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-15 13:35:19 +00:00
Don't cache svg files
This commit is contained in:
parent
23d8cc1657
commit
fbe52a1140
@ -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
|
||||
|
||||
|
@ -514,7 +514,6 @@ public func editThemeController(context: AccountContext, mode: EditThemeControll
|
||||
let prepare: Signal<CreateThemeResult, CreateThemeError>
|
||||
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<CreateThemeResult, CreateThemeError> in
|
||||
return .complete()
|
||||
})
|
||||
prepare = .complete()
|
||||
} else {
|
||||
prepare = .complete()
|
||||
}
|
||||
|
@ -176,8 +176,7 @@ final class ThemeAccentColorController: ViewController {
|
||||
let prepareWallpaper: Signal<CreateThemeResult, CreateThemeError>
|
||||
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<CreateThemeResult, CreateThemeError> in
|
||||
return .complete()
|
||||
})
|
||||
prepareWallpaper = .complete()
|
||||
} else {
|
||||
prepareWallpaper = .complete()
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
|
@ -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) {
|
||||
|
@ -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)
|
||||
}
|
||||
|
14
submodules/SvgRendering/BUILD
Normal file
14
submodules/SvgRendering/BUILD
Normal file
@ -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",
|
||||
],
|
||||
)
|
0
submodules/SvgRendering/Sources/SvgParser.swift
Normal file
0
submodules/SvgRendering/Sources/SvgParser.swift
Normal file
@ -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()
|
||||
|
@ -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)
|
||||
|
@ -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<CachedMediaResourceRepresentationResult, NoError> 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<CachedMediaResourceRepresentationResult, NoError> 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<CachedMediaResourceRepresentationResult, NoError> in
|
||||
@ -417,194 +400,6 @@ private func fetchCachedBlurredWallpaperRepresentation(resource: MediaResource,
|
||||
}) |> runOn(Queue.concurrentDefaultQueue())
|
||||
}
|
||||
|
||||
private func fetchCachedPatternWallpaperMaskRepresentation(resource: MediaResource, resourceData: MediaResourceData, representation: CachedPatternWallpaperMaskRepresentation) -> Signal<CachedMediaResourceRepresentationResult, NoError> {
|
||||
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 == "<?xml" {
|
||||
let size = representation.size ?? CGSize(width: 1440.0, height: 2960.0)
|
||||
|
||||
if let image = drawSvgImage(data, size, .black, .white) {
|
||||
if let alphaDestination = CGImageDestinationCreateWithURL(url as CFURL, kUTTypeJPEG, 1, nil) {
|
||||
CGImageDestinationSetProperties(alphaDestination, [:] as CFDictionary)
|
||||
|
||||
let colorQuality: Float = 0.87
|
||||
|
||||
let options = NSMutableDictionary()
|
||||
options.setObject(colorQuality as NSNumber, forKey: kCGImageDestinationLossyCompressionQuality as NSString)
|
||||
|
||||
CGImageDestinationAddImage(alphaDestination, image.cgImage!, options as CFDictionary)
|
||||
if CGImageDestinationFinalize(alphaDestination) {
|
||||
subscriber.putNext(.temporaryPath(path))
|
||||
subscriber.putCompletion()
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if let image = UIImage(data: data) {
|
||||
let size = representation.size.flatMap { image.size.aspectFitted($0) } ?? CGSize(width: image.size.width * image.scale, height: image.size.height * image.scale)
|
||||
|
||||
let alphaImage = generateImage(size, contextGenerator: { size, context in
|
||||
context.setFillColor(UIColor.black.cgColor)
|
||||
context.fill(CGRect(origin: CGPoint(), size: size))
|
||||
context.clip(to: CGRect(origin: CGPoint(), size: size), mask: image.cgImage!)
|
||||
context.setFillColor(UIColor.white.cgColor)
|
||||
context.fill(CGRect(origin: CGPoint(), size: size))
|
||||
}, scale: 1.0)
|
||||
|
||||
if let alphaImage = alphaImage, let alphaDestination = CGImageDestinationCreateWithURL(url as CFURL, kUTTypeJPEG, 1, nil) {
|
||||
CGImageDestinationSetProperties(alphaDestination, [:] as CFDictionary)
|
||||
|
||||
let colorQuality: Float = 0.87
|
||||
|
||||
let options = NSMutableDictionary()
|
||||
options.setObject(colorQuality as NSNumber, forKey: kCGImageDestinationLossyCompressionQuality as NSString)
|
||||
|
||||
CGImageDestinationAddImage(alphaDestination, alphaImage.cgImage!, options as CFDictionary)
|
||||
if CGImageDestinationFinalize(alphaDestination) {
|
||||
subscriber.putNext(.temporaryPath(path))
|
||||
subscriber.putCompletion()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return EmptyDisposable
|
||||
}) |> runOn(Queue.concurrentDefaultQueue())
|
||||
}
|
||||
|
||||
private func fetchCachedPatternWallpaperRepresentation(resource: MediaResource, resourceData: MediaResourceData, representation: CachedPatternWallpaperRepresentation) -> Signal<CachedMediaResourceRepresentationResult, NoError> {
|
||||
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 == "<?xml" {
|
||||
let defaultSize = CGSize(width: 1440.0, height: 2960.0)
|
||||
size = defaultSize
|
||||
if let image = drawSvgImage(data, defaultSize, .black, .white) {
|
||||
maskImage = image
|
||||
}
|
||||
} else if let image = UIImage(data: data) {
|
||||
size = CGSize(width: image.size.width * image.scale, height: image.size.height * image.scale)
|
||||
maskImage = image
|
||||
}
|
||||
|
||||
var colorImage: UIImage?
|
||||
if let size = size {
|
||||
colorImage = generateImage(size, contextGenerator: { size, c in
|
||||
let rect = CGRect(origin: CGPoint(), size: size)
|
||||
c.setBlendMode(.copy)
|
||||
|
||||
let averageBackgroundColor = UIColor.average(of: colors)
|
||||
|
||||
if colors.count == 1, let color = colors.first {
|
||||
c.setFillColor(color.cgColor)
|
||||
c.fill(rect)
|
||||
} else if colors.count >= 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<CachedMediaResourceRepresentationResult, NoError> {
|
||||
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<CachedMediaResourceRepresentationResult, NoError> 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<CachedMediaResourceRepresentationResult, NoError> in
|
||||
if !data.complete {
|
||||
return .complete()
|
||||
}
|
||||
return fetchCachedPatternWallpaperRepresentation(resource: resource, resourceData: data, representation: representation)
|
||||
}
|
||||
} else {
|
||||
return .never()
|
||||
}
|
||||
|
@ -122,7 +122,6 @@ public func upgradedAccounts(accountManager: AccountManager, rootPath: String, e
|
||||
}
|
||||
|> ignoreValues
|
||||
|> mapToSignal { _ -> Signal<Float, NoError> in
|
||||
return .complete()
|
||||
}
|
||||
}
|
||||
var signal: Signal<Float, NoError> = .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()
|
||||
|
@ -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 {
|
||||
|
@ -20,6 +20,7 @@ swift_library(
|
||||
"//submodules/PersistentStringHash:PersistentStringHash",
|
||||
"//submodules/AppBundle:AppBundle",
|
||||
"//submodules/Svg:Svg",
|
||||
"//submodules/GZip:GZip",
|
||||
"//submodules/GradientBackground:GradientBackground",
|
||||
],
|
||||
visibility = [
|
||||
|
@ -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<Data?, NoError> { 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<Data?, NoError> { 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()
|
||||
|
Loading…
x
Reference in New Issue
Block a user