mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Demo
This commit is contained in:
parent
097f012155
commit
c2a357a9d6
@ -18,6 +18,7 @@ swift_library(
|
||||
"//submodules/Emoji:Emoji",
|
||||
"//submodules/ImageCompression:ImageCompression",
|
||||
"//submodules/TinyThumbnail:TinyThumbnail",
|
||||
"//submodules/FastBlur:FastBlur",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
@ -224,7 +224,7 @@ public final class AvatarNode: ASDisplayNode {
|
||||
|
||||
public init(font: UIFont) {
|
||||
self.font = font
|
||||
self.imageNode = ImageNode(enableHasImage: true)
|
||||
self.imageNode = ImageNode(enableHasImage: true, enableAnimatedTransition: true)
|
||||
|
||||
super.init()
|
||||
|
||||
|
@ -8,6 +8,7 @@ import TelegramCore
|
||||
import SyncCore
|
||||
import ImageCompression
|
||||
import TinyThumbnail
|
||||
import FastBlur
|
||||
|
||||
private let roundCorners = { () -> UIImage in
|
||||
let diameter: CGFloat = 60.0
|
||||
@ -23,19 +24,32 @@ private let roundCorners = { () -> UIImage in
|
||||
return image
|
||||
}()
|
||||
|
||||
public func peerAvatarImageData(account: Account, peerReference: PeerReference?, authorOfMessage: MessageReference?, representation: TelegramMediaImageRepresentation?, synchronousLoad: Bool) -> Signal<Data?, NoError>? {
|
||||
public enum PeerAvatarImageType {
|
||||
case blurred
|
||||
case complete
|
||||
}
|
||||
|
||||
public func peerAvatarImageData(account: Account, peerReference: PeerReference?, authorOfMessage: MessageReference?, representation: TelegramMediaImageRepresentation?, synchronousLoad: Bool) -> Signal<(Data, PeerAvatarImageType)?, NoError>? {
|
||||
if let smallProfileImage = representation {
|
||||
let resourceData = account.postbox.mediaBox.resourceData(smallProfileImage.resource, attemptSynchronously: synchronousLoad)
|
||||
let imageData = resourceData
|
||||
|> take(1)
|
||||
|> mapToSignal { maybeData -> Signal<Data?, NoError> in
|
||||
|> mapToSignal { maybeData -> Signal<(Data, PeerAvatarImageType)?, NoError> in
|
||||
if maybeData.complete {
|
||||
return .single(try? Data(contentsOf: URL(fileURLWithPath: maybeData.path)))
|
||||
if let data = try? Data(contentsOf: URL(fileURLWithPath: maybeData.path)) {
|
||||
return .single((data, .complete))
|
||||
} else {
|
||||
return .single(nil)
|
||||
}
|
||||
} else {
|
||||
return Signal { subscriber in
|
||||
let resourceDataDisposable = resourceData.start(next: { data in
|
||||
if data.complete {
|
||||
subscriber.putNext(try? Data(contentsOf: URL(fileURLWithPath: maybeData.path)))
|
||||
if let dataValue = try? Data(contentsOf: URL(fileURLWithPath: maybeData.path)) {
|
||||
subscriber.putNext((dataValue, .complete))
|
||||
} else {
|
||||
subscriber.putNext(nil)
|
||||
}
|
||||
subscriber.putCompletion()
|
||||
} else {
|
||||
subscriber.putNext(nil)
|
||||
@ -61,6 +75,24 @@ public func peerAvatarImageData(account: Account, peerReference: PeerReference?,
|
||||
}
|
||||
}
|
||||
return imageData
|
||||
|> mapToSignal { data -> Signal<(Data, PeerAvatarImageType)?, NoError> in
|
||||
guard let (dataValue, type) = data, case .complete = type else {
|
||||
return .single(data)
|
||||
}
|
||||
|
||||
if let mappedImage = UIImage(data: dataValue), let miniData = compressImageMiniThumbnail(mappedImage, type: .avatar) {
|
||||
//print("Demo avatar size: \(miniData.count) bytes")
|
||||
if let decodedData = decodeTinyThumbnail(data: miniData) {
|
||||
return Signal<(Data, PeerAvatarImageType)?, NoError>.single((decodedData, .blurred))
|
||||
|> then(
|
||||
Signal<(Data, PeerAvatarImageType)?, NoError>.single((dataValue, .complete))
|
||||
|> delay(1.0, queue: .concurrentDefaultQueue())
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return .single(data)
|
||||
}
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
@ -102,7 +134,7 @@ public func peerAvatarImage(account: Account, peerReference: PeerReference?, aut
|
||||
return .single(nil)
|
||||
}
|
||||
let roundedImage = generateImage(displayDimensions, contextGenerator: { size, context -> Void in
|
||||
if let data = data {
|
||||
if let (data, dataType) = data {
|
||||
if let imageSource = CGImageSourceCreateWithData(data as CFData, nil), var dataImage = CGImageSourceCreateImageAtIndex(imageSource, 0, nil) {
|
||||
context.clear(CGRect(origin: CGPoint(), size: displayDimensions))
|
||||
context.setBlendMode(.copy)
|
||||
@ -112,13 +144,16 @@ public func peerAvatarImage(account: Account, peerReference: PeerReference?, aut
|
||||
context.clip()
|
||||
}
|
||||
|
||||
let mappedImage = UIImage(cgImage: dataImage)
|
||||
if let miniData = compressImageMiniThumbnail(mappedImage, type: .avatar) {
|
||||
if let decodedData = decodeTinyThumbnail(data: miniData) {
|
||||
if let decodedImage = UIImage(data: decodedData) {
|
||||
dataImage = decodedImage.cgImage!
|
||||
}
|
||||
if case .blurred = dataType {
|
||||
let imageContextSize = CGSize(width: 64.0, height: 64.0)
|
||||
let imageContext = DrawingContext(size: imageContextSize, scale: 1.0, premultiplied: true, clear: true)
|
||||
imageContext.withFlippedContext { c in
|
||||
c.draw(dataImage, in: CGRect(origin: CGPoint(), size: imageContextSize).insetBy(dx: inset, dy: inset))
|
||||
}
|
||||
|
||||
telegramFastBlurMore(Int32(imageContext.size.width * imageContext.scale), Int32(imageContext.size.height * imageContext.scale), Int32(imageContext.bytesPerRow), imageContext.bytes)
|
||||
|
||||
dataImage = imageContext.generateImage()!.cgImage!
|
||||
}
|
||||
|
||||
context.draw(dataImage, in: CGRect(origin: CGPoint(), size: displayDimensions).insetBy(dx: inset, dy: inset))
|
||||
@ -152,7 +187,7 @@ public func peerAvatarImage(account: Account, peerReference: PeerReference?, aut
|
||||
let unroundedImage: UIImage?
|
||||
if provideUnrounded {
|
||||
unroundedImage = generateImage(displayDimensions, contextGenerator: { size, context -> Void in
|
||||
if let data = data {
|
||||
if let (data, _) = data {
|
||||
if let imageSource = CGImageSourceCreateWithData(data as CFData, nil), let dataImage = CGImageSourceCreateImageAtIndex(imageSource, 0, nil) {
|
||||
context.clear(CGRect(origin: CGPoint(), size: displayDimensions))
|
||||
context.setBlendMode(.copy)
|
||||
|
@ -126,6 +126,7 @@ public class ImageNode: ASDisplayNode {
|
||||
private let hasImage: ValuePromise<Bool>?
|
||||
private var first = true
|
||||
private let enableEmpty: Bool
|
||||
private let enableAnimatedTransition: Bool
|
||||
|
||||
private let _contentReady = Promise<Bool>()
|
||||
private var didSetReady: Bool = false
|
||||
@ -141,13 +142,14 @@ public class ImageNode: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
|
||||
public init(enableHasImage: Bool = false, enableEmpty: Bool = false) {
|
||||
public init(enableHasImage: Bool = false, enableEmpty: Bool = false, enableAnimatedTransition: Bool = false) {
|
||||
if enableHasImage {
|
||||
self.hasImage = ValuePromise(false, ignoreRepeated: true)
|
||||
} else {
|
||||
self.hasImage = nil
|
||||
}
|
||||
self.enableEmpty = enableEmpty
|
||||
self.enableAnimatedTransition = enableAnimatedTransition
|
||||
super.init()
|
||||
}
|
||||
|
||||
@ -160,17 +162,33 @@ public class ImageNode: ASDisplayNode {
|
||||
self.disposable.set((signal |> deliverOnMainQueue).start(next: {[weak self] next in
|
||||
dispatcher.dispatch {
|
||||
if let strongSelf = self {
|
||||
if let image = next?.cgImage {
|
||||
strongSelf.contents = image
|
||||
} else if strongSelf.enableEmpty {
|
||||
strongSelf.contents = nil
|
||||
}
|
||||
var animate = strongSelf.enableAnimatedTransition
|
||||
if strongSelf.first && next != nil {
|
||||
strongSelf.first = false
|
||||
animate = false
|
||||
if strongSelf.isNodeLoaded {
|
||||
strongSelf.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.18)
|
||||
}
|
||||
}
|
||||
if let image = next?.cgImage {
|
||||
if animate, let previousContents = strongSelf.contents {
|
||||
strongSelf.contents = image
|
||||
let tempLayer = CALayer()
|
||||
tempLayer.contents = previousContents
|
||||
tempLayer.frame = strongSelf.layer.bounds
|
||||
strongSelf.layer.addSublayer(tempLayer)
|
||||
tempLayer.opacity = 0.0
|
||||
tempLayer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue, removeOnCompletion: true, completion: { [weak tempLayer] _ in
|
||||
tempLayer?.removeFromSuperlayer()
|
||||
})
|
||||
|
||||
//strongSelf.layer.animate(from: previousContents as! CGImage, to: image, keyPath: "contents", timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue, duration: 0.2)
|
||||
} else {
|
||||
strongSelf.contents = image
|
||||
}
|
||||
} else if strongSelf.enableEmpty {
|
||||
strongSelf.contents = nil
|
||||
}
|
||||
if !reportedHasImage {
|
||||
if let hasImage = strongSelf.hasImage {
|
||||
reportedHasImage = true
|
||||
|
@ -64,5 +64,25 @@ public enum MiniThumbnailType {
|
||||
}
|
||||
|
||||
public func compressImageMiniThumbnail(_ image: UIImage, type: MiniThumbnailType = .image) -> Data? {
|
||||
return compressMiniThumbnail(image, type == .avatar)
|
||||
switch type {
|
||||
case .image:
|
||||
return compressMiniThumbnail(image, CGSize(width: 40.0, height: 40.0))
|
||||
case .avatar:
|
||||
var size: CGFloat = 8.0
|
||||
var data = compressMiniThumbnail(image, CGSize(width: size, height: size))
|
||||
while true {
|
||||
size += 1.0
|
||||
if let candidateData = compressMiniThumbnail(image, CGSize(width: size, height: size)) {
|
||||
if candidateData.count >= 32 {
|
||||
break
|
||||
} else {
|
||||
data = candidateData
|
||||
}
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return data
|
||||
}
|
||||
}
|
||||
|
@ -2,4 +2,4 @@
|
||||
|
||||
NSData * _Nullable compressJPEGData(UIImage * _Nonnull sourceImage);
|
||||
NSArray<NSNumber *> * _Nonnull extractJPEGDataScans(NSData * _Nonnull data);
|
||||
NSData * _Nullable compressMiniThumbnail(UIImage * _Nonnull image, bool smaller);
|
||||
NSData * _Nullable compressMiniThumbnail(UIImage * _Nonnull image, CGSize size);
|
||||
|
@ -146,12 +146,7 @@ NSData * _Nullable compressJPEGData(UIImage * _Nonnull sourceImage) {
|
||||
return result;
|
||||
}
|
||||
|
||||
NSData * _Nullable compressMiniThumbnail(UIImage * _Nonnull image, bool smaller) {
|
||||
CGSize size = CGSizeMake(40.0f, 40.0f);
|
||||
if (smaller) {
|
||||
size.width = 8.0f;
|
||||
size.height = 8.0f;
|
||||
}
|
||||
NSData * _Nullable compressMiniThumbnail(UIImage * _Nonnull image, CGSize size) {
|
||||
CGSize fittedSize = image.size;
|
||||
if (fittedSize.width > size.width) {
|
||||
fittedSize = CGSizeMake(size.width, (int)((fittedSize.height * size.width / MAX(fittedSize.width, 1.0f))));
|
||||
|
@ -404,7 +404,7 @@ final class WatchMediaHandler: WatchRequestHandler {
|
||||
if let imageData = imageData {
|
||||
return imageData
|
||||
|> map { data -> UIImage? in
|
||||
if let data = data, let image = generateImage(targetSize, contextGenerator: { size, context -> Void in
|
||||
if let (data, _) = data, let image = generateImage(targetSize, contextGenerator: { size, context -> Void in
|
||||
if let imageSource = CGImageSourceCreateWithData(data as CFData, nil), let dataImage = CGImageSourceCreateImageAtIndex(imageSource, 0, nil) {
|
||||
context.setBlendMode(.copy)
|
||||
context.draw(dataImage, in: CGRect(origin: CGPoint(), size: targetSize))
|
||||
|
Loading…
x
Reference in New Issue
Block a user