diff --git a/submodules/DrawingUI/Sources/PenTool.swift b/submodules/DrawingUI/Sources/PenTool.swift index e458cf143a..763cc25f4a 100644 --- a/submodules/DrawingUI/Sources/PenTool.swift +++ b/submodules/DrawingUI/Sources/PenTool.swift @@ -173,8 +173,8 @@ final class PenTool: DrawingElement { self.lineWidth = lineWidth self.arrow = arrow - let minLineWidth = max(1.0, min(drawingSize.width, drawingSize.height) * 0.003) - let maxLineWidth = max(10.0, min(drawingSize.width, drawingSize.height) * 0.09) + let minLineWidth = max(1.0, min(drawingSize.width, drawingSize.height) * 0.0015) + let maxLineWidth = max(10.0, min(drawingSize.width, drawingSize.height) * 0.05) let lineWidth = minLineWidth + (maxLineWidth - minLineWidth) * lineWidth self.renderLineWidth = lineWidth diff --git a/submodules/InvisibleInkDustNode/Sources/InvisibleInkDustNode.swift b/submodules/InvisibleInkDustNode/Sources/InvisibleInkDustNode.swift index b9ccb4f813..661a90ca71 100644 --- a/submodules/InvisibleInkDustNode/Sources/InvisibleInkDustNode.swift +++ b/submodules/InvisibleInkDustNode/Sources/InvisibleInkDustNode.swift @@ -233,59 +233,6 @@ public class InvisibleInkDustNode: ASDisplayNode { self.emitterSpotNode.layer.removeAllAnimations() self.emitterMaskFillNode.layer.removeAllAnimations() } - - var spoilersLength: Int = 0 - if let spoilers = textNode.cachedLayout?.spoilers { - for spoiler in spoilers { - spoilersLength += spoiler.0.length - } - } - - let timeToRead = min(45.0, ceil(max(4.0, Double(spoilersLength) * 0.04))) - Queue.mainQueue().after(timeToRead * UIView.animationDurationFactor()) { - if let (_, color, _, _, _) = self.currentParams { - let colorSpace = CGColorSpaceCreateDeviceRGB() - let animation = POPBasicAnimation() - animation.property = (POPAnimatableProperty.property(withName: "color", initializer: { property in - property?.readBlock = { node, values in - if let color = (node as! InvisibleInkDustNode).emitter?.color { - if let a = color.components { - values?[0] = a[0] - values?[1] = a[1] - values?[2] = a[2] - values?[3] = a[3] - } - } - } - property?.writeBlock = { node, values in - if let values = values, let color = CGColor(colorSpace: colorSpace, components: values) { - (node as! InvisibleInkDustNode).animColor = color - (node as! InvisibleInkDustNode).updateEmitter() - } - } - property?.threshold = 0.4 - }) as! POPAnimatableProperty) - animation.fromValue = self.emitter?.color - animation.toValue = color - animation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.linear) - animation.duration = 0.1 - animation.completionBlock = { [weak self] _, _ in - if let strongSelf = self { - strongSelf.animColor = nil - strongSelf.updateEmitter() - } - } - self.pop_add(animation, forKey: "color") - } - - Queue.mainQueue().after(0.15) { - let transition = ContainedViewLayoutTransition.animated(duration: 0.4, curve: .linear) - transition.updateAlpha(node: self, alpha: 1.0) - transition.updateAlpha(node: textNode, alpha: 0.0, completion: { [weak self] _ in - self?.isRevealed = false - }) - } - } } private func updateEmitter() { diff --git a/submodules/InvisibleInkDustNode/Sources/MediaDustNode.swift b/submodules/InvisibleInkDustNode/Sources/MediaDustNode.swift index cd6ef24d29..42c53e4159 100644 --- a/submodules/InvisibleInkDustNode/Sources/MediaDustNode.swift +++ b/submodules/InvisibleInkDustNode/Sources/MediaDustNode.swift @@ -257,12 +257,14 @@ public class MediaDustNode: ASDisplayNode { } - public func update(size: CGSize, color: UIColor) { + public func update(size: CGSize, color: UIColor, transition: ContainedViewLayoutTransition) { self.currentParams = (size, color) - - self.emitterNode.frame = CGRect(origin: CGPoint(), size: size) - self.emitterMaskNode.frame = self.emitterNode.bounds - self.emitterMaskFillNode.frame = self.emitterNode.bounds + + let bounds = CGRect(origin: .zero, size: size) + transition.updateFrame(node: self.emitterNode, frame: bounds) + + self.emitterMaskNode.frame = bounds + self.emitterMaskFillNode.frame = bounds if self.isNodeLoaded { self.updateEmitter() diff --git a/submodules/MediaPickerUI/Sources/MediaPickerGridItem.swift b/submodules/MediaPickerUI/Sources/MediaPickerGridItem.swift index 0e1cfff5cb..f4862c3bd8 100644 --- a/submodules/MediaPickerUI/Sources/MediaPickerGridItem.swift +++ b/submodules/MediaPickerUI/Sources/MediaPickerGridItem.swift @@ -13,6 +13,8 @@ import CheckNode import LegacyComponents import PhotoResources import InvisibleInkDustNode +import ImageBlur +import FastBlur enum MediaPickerGridItemContent: Equatable { case asset(PHFetchResult, Int) @@ -116,6 +118,10 @@ final class MediaPickerGridItemNode: GridItemNode { super.init() self.addSubnode(self.imageNode) + + self.imageNode.contentUpdated = { [weak self] image in + self?.spoilerNode?.setImage(image) + } } deinit { @@ -367,14 +373,25 @@ final class MediaPickerGridItemNode: GridItemNode { self.updateHiddenMedia() } + private var didSetupSpoiler = false private func updateHasSpoiler(_ hasSpoiler: Bool) { + var animated = true + if !self.didSetupSpoiler { + animated = false + self.didSetupSpoiler = true + } + if hasSpoiler { if self.spoilerNode == nil { let spoilerNode = SpoilerOverlayNode() self.insertSubnode(spoilerNode, aboveSubnode: self.imageNode) self.spoilerNode = spoilerNode - spoilerNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) + spoilerNode.setImage(self.imageNode.image) + + if animated { + spoilerNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) + } } self.spoilerNode?.update(size: self.bounds.size, transition: .immediate) self.spoilerNode?.frame = CGRect(origin: .zero, size: self.bounds.size) @@ -422,14 +439,15 @@ final class MediaPickerGridItemNode: GridItemNode { } class SpoilerOverlayNode: ASDisplayNode { - private let blurNode: NavigationBackgroundNode + private let blurNode: ASImageNode private let dustNode: MediaDustNode private var maskView: UIView? private var maskLayer: CAShapeLayer? override init() { - self.blurNode = NavigationBackgroundNode(color: UIColor(rgb: 0x000000, alpha: 0.1), enableBlur: true) + self.blurNode = ASImageNode() + self.blurNode.displaysAsynchronously = false self.dustNode = MediaDustNode() @@ -444,7 +462,6 @@ class SpoilerOverlayNode: ASDisplayNode { override func didLoad() { super.didLoad() - let maskView = UIView() self.maskView = maskView // self.dustNode.view.mask = maskView @@ -456,26 +473,44 @@ class SpoilerOverlayNode: ASDisplayNode { self.maskLayer = maskLayer } + func setImage(_ image: UIImage?) { + self.blurNode.image = image.flatMap { blurredImage($0) } + } + func update(size: CGSize, transition: ContainedViewLayoutTransition) { - self.blurNode.update(size: size, transition: transition) - self.blurNode.frame = CGRect(origin: .zero, size: size) + transition.updateFrame(node: self.blurNode, frame: CGRect(origin: .zero, size: size)) - self.dustNode.frame = CGRect(origin: .zero, size: size) - self.dustNode.update(size: size, color: .white) - -// var leftOffset: CGFloat = 0.0 -// var rightOffset: CGFloat = 0.0 -// let corners = corners ?? ImageCorners(radius: 16.0) -// if case .Tail = corners.bottomLeft { -// leftOffset = 4.0 -// } else if case .Tail = corners.bottomRight { -// rightOffset = 4.0 -// } -// let rect = CGRect(origin: CGPoint(x: leftOffset, y: 0.0), size: CGSize(width: size.width - leftOffset - rightOffset, height: size.height)) -// let path = UIBezierPath(roundRect: rect, topLeftRadius: corners.topLeft.radius, topRightRadius: corners.topRight.radius, bottomLeftRadius: corners.bottomLeft.radius, bottomRightRadius: corners.bottomRight.radius) -// let buttonPath = UIBezierPath(roundedRect: self.buttonNode.frame, cornerRadius: 16.0) -// path.append(buttonPath) -// path.usesEvenOddFillRule = true -// self.maskLayer?.path = path.cgPath + transition.updateFrame(node: self.dustNode, frame: CGRect(origin: .zero, size: size)) + self.dustNode.update(size: size, color: .white, transition: transition) } } + +private func blurredImage(_ image: UIImage) -> UIImage? { + guard let image = image.cgImage else { + return nil + } + + let thumbnailSize = CGSize(width: image.width, height: image.height) + let thumbnailContextSize = thumbnailSize.aspectFilled(CGSize(width: 20.0, height: 20.0)) + if let thumbnailContext = DrawingContext(size: thumbnailContextSize, scale: 1.0) { + thumbnailContext.withFlippedContext { c in + c.interpolationQuality = .none + c.draw(image, in: CGRect(origin: CGPoint(), size: thumbnailContextSize)) + } + imageFastBlur(Int32(thumbnailContextSize.width), Int32(thumbnailContextSize.height), Int32(thumbnailContext.bytesPerRow), thumbnailContext.bytes) + + let thumbnailContext2Size = thumbnailSize.aspectFitted(CGSize(width: 100.0, height: 100.0)) + if let thumbnailContext2 = DrawingContext(size: thumbnailContext2Size, scale: 1.0) { + thumbnailContext2.withFlippedContext { c in + c.interpolationQuality = .none + if let image = thumbnailContext.generateImage()?.cgImage { + c.draw(image, in: CGRect(origin: CGPoint(), size: thumbnailContext2Size)) + } + } + imageFastBlur(Int32(thumbnailContext2Size.width), Int32(thumbnailContext2Size.height), Int32(thumbnailContext2.bytesPerRow), thumbnailContext2.bytes) + adjustSaturationInContext(context: thumbnailContext2, saturation: 1.7) + return thumbnailContext2.generateImage() + } + } + return nil +} diff --git a/submodules/MediaPickerUI/Sources/MediaPickerSelectedListNode.swift b/submodules/MediaPickerUI/Sources/MediaPickerSelectedListNode.swift index d53e10384e..7eb23af421 100644 --- a/submodules/MediaPickerUI/Sources/MediaPickerSelectedListNode.swift +++ b/submodules/MediaPickerUI/Sources/MediaPickerSelectedListNode.swift @@ -131,6 +131,10 @@ private class MediaPickerSelectedItemNode: ASDisplayNode { strongSelf.updateHasSpoiler(hasSpoiler) })) } + + self.imageNode.contentUpdated = { [weak self] image in + self?.spoilerNode?.setImage(image) + } } deinit { @@ -151,14 +155,25 @@ private class MediaPickerSelectedItemNode: ASDisplayNode { self.interaction?.openSelectedMedia(asset, self.imageNode.image) } + private var didSetupSpoiler = false private func updateHasSpoiler(_ hasSpoiler: Bool) { + var animated = true + if !self.didSetupSpoiler { + animated = false + self.didSetupSpoiler = true + } + if hasSpoiler { if self.spoilerNode == nil { let spoilerNode = SpoilerOverlayNode() self.insertSubnode(spoilerNode, aboveSubnode: self.imageNode) self.spoilerNode = spoilerNode - spoilerNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) + spoilerNode.setImage(self.imageNode.image) + + if animated { + spoilerNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) + } } self.spoilerNode?.update(size: self.bounds.size, transition: .immediate) self.spoilerNode?.frame = CGRect(origin: .zero, size: self.bounds.size) @@ -616,11 +631,15 @@ final class MediaPickerSelectedListNode: ASDisplayNode, UIScrollViewDelegate, UI } } - self.messageNodes?.first?.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false) - self.messageNodes?.first?.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: -30.0), duration: 0.4, delay: 0.0, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, additive: true) + if let topNode = self.messageNodes?.first { + topNode.layer.animateAlpha(from: topNode.alpha, to: 0.0, duration: 0.15, removeOnCompletion: false) + topNode.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: -30.0), duration: 0.4, delay: 0.0, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, additive: true) + } - self.messageNodes?.last?.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false) - self.messageNodes?.last?.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: 30.0), duration: 0.4, delay: 0.0, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, additive: true) + if let bottomNode = self.messageNodes?.last { + bottomNode.layer.animateAlpha(from: bottomNode.alpha, to: 0.0, duration: 0.15, removeOnCompletion: false) + bottomNode.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: 30.0), duration: 0.4, delay: 0.0, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, additive: true) + } } func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool { diff --git a/submodules/PhotoResources/Sources/PhotoResources.swift b/submodules/PhotoResources/Sources/PhotoResources.swift index 9dacb156b0..4644c26452 100644 --- a/submodules/PhotoResources/Sources/PhotoResources.swift +++ b/submodules/PhotoResources/Sources/PhotoResources.swift @@ -1157,7 +1157,7 @@ public func chatSecretPhoto(account: Account, photoReference: ImageMediaReferenc } } -private func adjustSaturationInContext(context: DrawingContext, saturation: CGFloat) { +public func adjustSaturationInContext(context: DrawingContext, saturation: CGFloat) { var buffer = vImage_Buffer() buffer.data = context.bytes buffer.width = UInt(context.size.width * context.scale) diff --git a/submodules/TelegramUI/Sources/ChatMessageInteractiveMediaNode.swift b/submodules/TelegramUI/Sources/ChatMessageInteractiveMediaNode.swift index bb59fc32c0..80b73fc822 100644 --- a/submodules/TelegramUI/Sources/ChatMessageInteractiveMediaNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageInteractiveMediaNode.swift @@ -251,15 +251,15 @@ private class ExtendedMediaOverlayNode: ASDisplayNode { self.maskLayer = maskLayer } - func update(size: CGSize, text: String, imageSignal: (Signal<(TransformImageArguments) -> DrawingContext?, NoError>, CGSize)?, imageFrame: CGRect, corners: ImageCorners?) { + func update(size: CGSize, text: String, imageSignal: (Signal<(TransformImageArguments) -> DrawingContext?, NoError>, CGSize, CGSize)?, imageFrame: CGRect, corners: ImageCorners?) { let spacing: CGFloat = 2.0 let padding: CGFloat = 10.0 - if let (imageSignal, drawingSize) = imageSignal { + if let (imageSignal, drawingSize, boundingSize) = imageSignal { self.blurredImageNode.setSignal(imageSignal) let imageLayout = self.blurredImageNode.asyncLayout() - let arguments = TransformImageArguments(corners: corners ?? ImageCorners(), imageSize: drawingSize, boundingSize: imageFrame.size, intrinsicInsets: UIEdgeInsets(), resizeMode: .blurBackground, emptyColor: .clear, custom: nil) + let arguments = TransformImageArguments(corners: corners ?? ImageCorners(), imageSize: drawingSize, boundingSize: boundingSize, intrinsicInsets: UIEdgeInsets(), resizeMode: .blurBackground, emptyColor: .clear, custom: nil) let apply = imageLayout(arguments) apply() @@ -277,7 +277,7 @@ private class ExtendedMediaOverlayNode: ASDisplayNode { self.blurredImageNode.frame = imageFrame self.dustNode.frame = CGRect(origin: .zero, size: size) - self.dustNode.update(size: size, color: .white) + self.dustNode.update(size: size, color: .white, transition: .immediate) if text.isEmpty { self.buttonNode.isHidden = true @@ -320,7 +320,7 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTransitio private let imageNode: TransformImageNode private var currentImageArguments: TransformImageArguments? private var currentHighQualityImageSignal: (Signal<(TransformImageArguments) -> DrawingContext?, NoError>, CGSize)? - private var currentBlurredImageSignal: (Signal<(TransformImageArguments) -> DrawingContext?, NoError>, CGSize)? + private var currentBlurredImageSignal: (Signal<(TransformImageArguments) -> DrawingContext?, NoError>, CGSize, CGSize)? private var highQualityImageNode: TransformImageNode? private var videoNode: UniversalVideoNode? @@ -1360,13 +1360,11 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTransitio strongSelf.currentHighQualityImageSignal = (updateImageSignal(false, true), imageDimensions) if let updateBlurredImageSignal = updateBlurredImageSignal { - strongSelf.currentBlurredImageSignal = (updateBlurredImageSignal(false, true), imageDimensions) + strongSelf.currentBlurredImageSignal = (updateBlurredImageSignal(false, true), drawingSize, boundingSize) } } } - - - + if let _ = secretBeginTimeAndTimeout { if updatedStatusSignal == nil, let fetchStatus = strongSelf.fetchStatus, case .Local = fetchStatus { if let statusNode = strongSelf.statusNode, case .secretTimeout = statusNode.state { diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift index 56e68c189d..a680bf711d 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift @@ -4789,9 +4789,6 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate } var canReport = true - if channel.isVerified { - canReport = false - } if channel.adminRights != nil { canReport = false }