mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-16 11:20:18 +00:00
Camera and editor improvements
This commit is contained in:
parent
3c39e2c0ce
commit
ae32ccca68
@ -223,6 +223,10 @@ private final class CameraContext {
|
|||||||
self.device.setZoomLevel(zoomLevel)
|
self.device.setZoomLevel(zoomLevel)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func setZoomDelta(_ zoomDelta: CGFloat) {
|
||||||
|
self.device.setZoomDelta(zoomDelta)
|
||||||
|
}
|
||||||
|
|
||||||
func takePhoto() -> Signal<PhotoCaptureResult, NoError> {
|
func takePhoto() -> Signal<PhotoCaptureResult, NoError> {
|
||||||
return self.output.takePhoto(orientation: self.videoOrientation ?? .portrait, flashMode: self._flashMode)
|
return self.output.takePhoto(orientation: self.videoOrientation ?? .portrait, flashMode: self._flashMode)
|
||||||
}
|
}
|
||||||
@ -414,6 +418,15 @@ public final class Camera {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public func setZoomDelta(_ zoomDelta: CGFloat) {
|
||||||
|
self.queue.async {
|
||||||
|
if let context = self.contextRef?.takeUnretainedValue() {
|
||||||
|
context.setZoomDelta(zoomDelta)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public func setTorchActive(_ active: Bool) {
|
public func setTorchActive(_ active: Bool) {
|
||||||
self.queue.async {
|
self.queue.async {
|
||||||
if let context = self.contextRef?.takeUnretainedValue() {
|
if let context = self.contextRef?.takeUnretainedValue() {
|
||||||
|
|||||||
@ -220,4 +220,13 @@ final class CameraDevice {
|
|||||||
device.videoZoomFactor = max(1.0, min(10.0, zoomLevel))
|
device.videoZoomFactor = max(1.0, min(10.0, zoomLevel))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func setZoomDelta(_ zoomDelta: CGFloat) {
|
||||||
|
guard let device = self.videoDevice else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self.transaction(device) { device in
|
||||||
|
device.videoZoomFactor = max(1.0, min(10.0, device.videoZoomFactor * zoomDelta))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -873,11 +873,10 @@ public class CameraScreen: ViewController {
|
|||||||
|
|
||||||
@objc private func handlePinch(_ gestureRecognizer: UIPinchGestureRecognizer) {
|
@objc private func handlePinch(_ gestureRecognizer: UIPinchGestureRecognizer) {
|
||||||
switch gestureRecognizer.state {
|
switch gestureRecognizer.state {
|
||||||
case .began:
|
|
||||||
gestureRecognizer.scale = 1.0
|
|
||||||
case .changed:
|
case .changed:
|
||||||
let scale = gestureRecognizer.scale
|
let scale = gestureRecognizer.scale
|
||||||
self.camera.setZoomLevel(scale)
|
self.camera.setZoomDelta(scale)
|
||||||
|
gestureRecognizer.scale = 1.0
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|||||||
@ -387,6 +387,7 @@ public final class MediaEditor {
|
|||||||
self.setGradientColors([topColor, bottomColor])
|
self.setGradientColors([topColor, bottomColor])
|
||||||
|
|
||||||
if player == nil {
|
if player == nil {
|
||||||
|
self.updateRenderChain()
|
||||||
self.maybeGeneratePersonSegmentation(image)
|
self.maybeGeneratePersonSegmentation(image)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -425,10 +426,20 @@ public final class MediaEditor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private var skipRendering = false
|
private var skipRendering = false
|
||||||
|
private func updateValues(skipRendering: Bool = false, _ f: (MediaEditorValues) -> MediaEditorValues) {
|
||||||
|
if skipRendering {
|
||||||
|
self.skipRendering = true
|
||||||
|
}
|
||||||
|
self.values = f(self.values)
|
||||||
|
if skipRendering {
|
||||||
|
self.skipRendering = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public func setCrop(offset: CGPoint, scale: CGFloat, rotation: CGFloat, mirroring: Bool) {
|
public func setCrop(offset: CGPoint, scale: CGFloat, rotation: CGFloat, mirroring: Bool) {
|
||||||
self.skipRendering = true
|
self.updateValues(skipRendering: true) { values in
|
||||||
self.values = self.values.withUpdatedCrop(offset: offset, scale: scale, rotation: rotation, mirroring: mirroring)
|
return values.withUpdatedCrop(offset: offset, scale: scale, rotation: rotation, mirroring: mirroring)
|
||||||
self.skipRendering = false
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func getToolValue(_ key: EditorToolKey) -> Any? {
|
public func getToolValue(_ key: EditorToolKey) -> Any? {
|
||||||
@ -436,19 +447,24 @@ public final class MediaEditor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public func setToolValue(_ key: EditorToolKey, value: Any) {
|
public func setToolValue(_ key: EditorToolKey, value: Any) {
|
||||||
var updatedToolValues = self.values.toolValues
|
self.updateValues { values in
|
||||||
updatedToolValues[key] = value
|
var updatedToolValues = values.toolValues
|
||||||
self.values = self.values.withUpdatedToolValues(updatedToolValues)
|
updatedToolValues[key] = value
|
||||||
self.updateRenderChain()
|
return values.withUpdatedToolValues(updatedToolValues)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func setVideoIsMuted(_ videoIsMuted: Bool) {
|
public func setVideoIsMuted(_ videoIsMuted: Bool) {
|
||||||
self.player?.isMuted = videoIsMuted
|
self.player?.isMuted = videoIsMuted
|
||||||
self.values = self.values.withUpdatedVideoIsMuted(videoIsMuted)
|
self.updateValues(skipRendering: true) { values in
|
||||||
|
return values.withUpdatedVideoIsMuted(videoIsMuted)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func setVideoIsFullHd(_ videoIsFullHd: Bool) {
|
public func setVideoIsFullHd(_ videoIsFullHd: Bool) {
|
||||||
self.values = self.values.withUpdatedVideoIsFullHd(videoIsFullHd)
|
self.updateValues(skipRendering: true) { values in
|
||||||
|
return values.withUpdatedVideoIsFullHd(videoIsFullHd)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private var targetTimePosition: (CMTime, Bool)?
|
private var targetTimePosition: (CMTime, Bool)?
|
||||||
@ -494,23 +510,29 @@ public final class MediaEditor {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
public func setVideoTrimRange(_ trimRange: Range<Double>) {
|
public func setVideoTrimRange(_ trimRange: Range<Double>, apply: Bool) {
|
||||||
self.skipRendering = true
|
self.updateValues(skipRendering: true) { values in
|
||||||
self.values = self.values.withUpdatedVideoTrimRange(trimRange)
|
return values.withUpdatedVideoTrimRange(trimRange)
|
||||||
self.skipRendering = false
|
}
|
||||||
|
|
||||||
self.player?.currentItem?.forwardPlaybackEndTime = CMTime(seconds: trimRange.upperBound, preferredTimescale: CMTimeScale(1000))
|
if apply {
|
||||||
|
self.player?.currentItem?.forwardPlaybackEndTime = CMTime(seconds: trimRange.upperBound, preferredTimescale: CMTimeScale(1000))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func setDrawingAndEntities(data: Data?, image: UIImage?, entities: [CodableDrawingEntity]) {
|
public func setDrawingAndEntities(data: Data?, image: UIImage?, entities: [CodableDrawingEntity]) {
|
||||||
self.values = self.values.withUpdatedDrawingAndEntities(drawing: image, entities: entities)
|
self.updateValues(skipRendering: true) { values in
|
||||||
|
return values.withUpdatedDrawingAndEntities(drawing: image, entities: entities)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func setGradientColors(_ gradientColors: [UIColor]) {
|
public func setGradientColors(_ gradientColors: [UIColor]) {
|
||||||
self.values = self.values.withUpdatedGradientColors(gradientColors: gradientColors)
|
self.updateValues(skipRendering: true) { values in
|
||||||
|
return values.withUpdatedGradientColors(gradientColors: gradientColors)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private var previousUpdateTime = CACurrentMediaTime()
|
private var previousUpdateTime: Double?
|
||||||
private var scheduledUpdate = false
|
private var scheduledUpdate = false
|
||||||
private func updateRenderChain() {
|
private func updateRenderChain() {
|
||||||
self.renderChain.update(values: self.values)
|
self.renderChain.update(values: self.values)
|
||||||
@ -519,10 +541,9 @@ public final class MediaEditor {
|
|||||||
let currentTime = CACurrentMediaTime()
|
let currentTime = CACurrentMediaTime()
|
||||||
if !self.scheduledUpdate {
|
if !self.scheduledUpdate {
|
||||||
let delay = 0.03333
|
let delay = 0.03333
|
||||||
let delta = currentTime - self.previousUpdateTime
|
if let previousUpdateTime = self.previousUpdateTime, currentTime - previousUpdateTime < delay {
|
||||||
if delta < delay {
|
|
||||||
self.scheduledUpdate = true
|
self.scheduledUpdate = true
|
||||||
Queue.mainQueue().after(delay - delta) {
|
Queue.mainQueue().after(delay - (currentTime - previousUpdateTime)) {
|
||||||
self.scheduledUpdate = false
|
self.scheduledUpdate = false
|
||||||
self.previousUpdateTime = CACurrentMediaTime()
|
self.previousUpdateTime = CACurrentMediaTime()
|
||||||
self.renderer.willRenderFrame()
|
self.renderer.willRenderFrame()
|
||||||
|
|||||||
@ -200,6 +200,8 @@ final class MediaEditorScreenComponent: Component {
|
|||||||
private let textDoneButton = ComponentView<Empty>()
|
private let textDoneButton = ComponentView<Empty>()
|
||||||
private let textSize = ComponentView<Empty>()
|
private let textSize = ComponentView<Empty>()
|
||||||
|
|
||||||
|
private var isDismissed = false
|
||||||
|
|
||||||
private var component: MediaEditorScreenComponent?
|
private var component: MediaEditorScreenComponent?
|
||||||
private weak var state: State?
|
private weak var state: State?
|
||||||
private var environment: ViewControllerComponentContainer.Environment?
|
private var environment: ViewControllerComponentContainer.Environment?
|
||||||
@ -274,6 +276,7 @@ final class MediaEditorScreenComponent: Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func animateOut(to source: TransitionAnimationSource) {
|
func animateOut(to source: TransitionAnimationSource) {
|
||||||
|
self.isDismissed = true
|
||||||
let transition = Transition(animation: .curve(duration: 0.2, curve: .easeInOut))
|
let transition = Transition(animation: .curve(duration: 0.2, curve: .easeInOut))
|
||||||
if let view = self.cancelButton.view {
|
if let view = self.cancelButton.view {
|
||||||
transition.setAlpha(view: view, alpha: 0.0)
|
transition.setAlpha(view: view, alpha: 0.0)
|
||||||
@ -398,6 +401,9 @@ final class MediaEditorScreenComponent: Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func update(component: MediaEditorScreenComponent, availableSize: CGSize, state: State, environment: Environment<ViewControllerComponentContainer.Environment>, transition: Transition) -> CGSize {
|
func update(component: MediaEditorScreenComponent, availableSize: CGSize, state: State, environment: Environment<ViewControllerComponentContainer.Environment>, transition: Transition) -> CGSize {
|
||||||
|
guard !self.isDismissed else {
|
||||||
|
return availableSize
|
||||||
|
}
|
||||||
let environment = environment[ViewControllerComponentContainer.Environment.self].value
|
let environment = environment[ViewControllerComponentContainer.Environment.self].value
|
||||||
self.environment = environment
|
self.environment = environment
|
||||||
|
|
||||||
@ -612,10 +618,9 @@ final class MediaEditorScreenComponent: Component {
|
|||||||
framesUpdateTimestamp: playerState.framesUpdateTimestamp,
|
framesUpdateTimestamp: playerState.framesUpdateTimestamp,
|
||||||
trimUpdated: { [weak mediaEditor] start, end, updatedEnd, done in
|
trimUpdated: { [weak mediaEditor] start, end, updatedEnd, done in
|
||||||
if let mediaEditor {
|
if let mediaEditor {
|
||||||
|
mediaEditor.setVideoTrimRange(start..<end, apply: done)
|
||||||
if done {
|
if done {
|
||||||
mediaEditor.seek(start, andPlay: true)
|
mediaEditor.seek(start, andPlay: true)
|
||||||
|
|
||||||
mediaEditor.setVideoTrimRange(start..<end)
|
|
||||||
} else {
|
} else {
|
||||||
mediaEditor.seek(updatedEnd ? end : start, andPlay: false)
|
mediaEditor.seek(updatedEnd ? end : start, andPlay: false)
|
||||||
}
|
}
|
||||||
@ -1106,9 +1111,10 @@ public final class MediaEditorScreen: ViewController {
|
|||||||
|
|
||||||
private var isDisplayingTool = false
|
private var isDisplayingTool = false
|
||||||
private var isInteractingWithEntities = false
|
private var isInteractingWithEntities = false
|
||||||
|
private var isEnhacing = false
|
||||||
private var isDismissing = false
|
private var isDismissing = false
|
||||||
private var dismissOffset: CGFloat = 0.0
|
private var dismissOffset: CGFloat = 0.0
|
||||||
private var isEnhacing = false
|
private var isDismissed = false
|
||||||
|
|
||||||
private var presentationData: PresentationData
|
private var presentationData: PresentationData
|
||||||
private var validLayout: ContainerViewLayout?
|
private var validLayout: ContainerViewLayout?
|
||||||
@ -1259,22 +1265,6 @@ public final class MediaEditorScreen: ViewController {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var hasSwipeToDismiss = false
|
|
||||||
if let subject = self.subject {
|
|
||||||
if case .asset = subject {
|
|
||||||
hasSwipeToDismiss = true
|
|
||||||
} else if case .draft = subject {
|
|
||||||
hasSwipeToDismiss = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if hasSwipeToDismiss {
|
|
||||||
let dismissPanGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(self.handleDismissPan(_:)))
|
|
||||||
dismissPanGestureRecognizer.delegate = self
|
|
||||||
dismissPanGestureRecognizer.maximumNumberOfTouches = 1
|
|
||||||
self.previewContainerView.addGestureRecognizer(dismissPanGestureRecognizer)
|
|
||||||
self.dismissPanGestureRecognizer = dismissPanGestureRecognizer
|
|
||||||
}
|
|
||||||
|
|
||||||
let mediaDimensions = subject.dimensions
|
let mediaDimensions = subject.dimensions
|
||||||
let maxSide: CGFloat = 1920.0 / UIScreen.main.scale
|
let maxSide: CGFloat = 1920.0 / UIScreen.main.scale
|
||||||
let fittedSize = mediaDimensions.cgSize.fitted(CGSize(width: maxSide, height: maxSide))
|
let fittedSize = mediaDimensions.cgSize.fitted(CGSize(width: maxSide, height: maxSide))
|
||||||
@ -1346,6 +1336,12 @@ public final class MediaEditorScreen: ViewController {
|
|||||||
self.view.disablesInteractiveModalDismiss = true
|
self.view.disablesInteractiveModalDismiss = true
|
||||||
self.view.disablesInteractiveKeyboardGestureRecognizer = true
|
self.view.disablesInteractiveKeyboardGestureRecognizer = true
|
||||||
|
|
||||||
|
let dismissPanGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(self.handleDismissPan(_:)))
|
||||||
|
dismissPanGestureRecognizer.delegate = self
|
||||||
|
dismissPanGestureRecognizer.maximumNumberOfTouches = 1
|
||||||
|
self.previewContainerView.addGestureRecognizer(dismissPanGestureRecognizer)
|
||||||
|
self.dismissPanGestureRecognizer = dismissPanGestureRecognizer
|
||||||
|
|
||||||
let panGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(self.handlePan(_:)))
|
let panGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(self.handlePan(_:)))
|
||||||
panGestureRecognizer.delegate = self
|
panGestureRecognizer.delegate = self
|
||||||
panGestureRecognizer.minimumNumberOfTouches = 2
|
panGestureRecognizer.minimumNumberOfTouches = 2
|
||||||
@ -1429,11 +1425,20 @@ public final class MediaEditorScreen: ViewController {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var hasSwipeToDismiss = false
|
||||||
|
if let subject = self.subject {
|
||||||
|
if case .asset = subject {
|
||||||
|
hasSwipeToDismiss = true
|
||||||
|
} else if case .draft = subject {
|
||||||
|
hasSwipeToDismiss = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let translation = gestureRecognizer.translation(in: self.view)
|
let translation = gestureRecognizer.translation(in: self.view)
|
||||||
let velocity = gestureRecognizer.velocity(in: self.view)
|
let velocity = gestureRecognizer.velocity(in: self.view)
|
||||||
switch gestureRecognizer.state {
|
switch gestureRecognizer.state {
|
||||||
case .changed:
|
case .changed:
|
||||||
if abs(translation.y) > 10.0 && !self.isEnhacing {
|
if abs(translation.y) > 10.0 && !self.isEnhacing && hasSwipeToDismiss {
|
||||||
if !self.isDismissing {
|
if !self.isDismissing {
|
||||||
self.isDismissing = true
|
self.isDismissing = true
|
||||||
controller.requestLayout(transition: .animated(duration: 0.25, curve: .easeInOut))
|
controller.requestLayout(transition: .animated(duration: 0.25, curve: .easeInOut))
|
||||||
@ -1449,7 +1454,7 @@ public final class MediaEditorScreen: ViewController {
|
|||||||
} else if self.isEnhacing {
|
} else if self.isEnhacing {
|
||||||
if let mediaEditor = self.mediaEditor {
|
if let mediaEditor = self.mediaEditor {
|
||||||
let value = mediaEditor.getToolValue(.enhance) as? Float ?? 0.0
|
let value = mediaEditor.getToolValue(.enhance) as? Float ?? 0.0
|
||||||
let delta = Float((translation.x / self.frame.width) * 2.0)
|
let delta = Float((translation.x / self.frame.width) * 1.5)
|
||||||
let updatedValue = max(0.0, min(1.0, value + delta))
|
let updatedValue = max(0.0, min(1.0, value + delta))
|
||||||
mediaEditor.setToolValue(.enhance, value: updatedValue)
|
mediaEditor.setToolValue(.enhance, value: updatedValue)
|
||||||
}
|
}
|
||||||
@ -1573,6 +1578,7 @@ public final class MediaEditorScreen: ViewController {
|
|||||||
guard let controller = self.controller else {
|
guard let controller = self.controller else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
self.isDismissed = true
|
||||||
controller.statusBar.statusBarStyle = .Ignore
|
controller.statusBar.statusBarStyle = .Ignore
|
||||||
|
|
||||||
let previousDimAlpha = self.backgroundDimView.alpha
|
let previousDimAlpha = self.backgroundDimView.alpha
|
||||||
@ -1825,7 +1831,7 @@ public final class MediaEditorScreen: ViewController {
|
|||||||
|
|
||||||
private var drawingScreen: DrawingScreen?
|
private var drawingScreen: DrawingScreen?
|
||||||
func containerLayoutUpdated(layout: ContainerViewLayout, forceUpdate: Bool = false, animateOut: Bool = false, transition: Transition) {
|
func containerLayoutUpdated(layout: ContainerViewLayout, forceUpdate: Bool = false, animateOut: Bool = false, transition: Transition) {
|
||||||
guard let controller = self.controller else {
|
guard let controller = self.controller, !self.isDismissed else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let isFirstTime = self.validLayout == nil
|
let isFirstTime = self.validLayout == nil
|
||||||
@ -2800,6 +2806,8 @@ private final class ToolValueComponent: Component {
|
|||||||
private let title = ComponentView<Empty>()
|
private let title = ComponentView<Empty>()
|
||||||
private let value = ComponentView<Empty>()
|
private let value = ComponentView<Empty>()
|
||||||
|
|
||||||
|
private let hapticFeedback = HapticFeedback()
|
||||||
|
|
||||||
private var component: ToolValueComponent?
|
private var component: ToolValueComponent?
|
||||||
private weak var state: EmptyComponentState?
|
private weak var state: EmptyComponentState?
|
||||||
|
|
||||||
@ -2814,6 +2822,8 @@ private final class ToolValueComponent: Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func update(component: ToolValueComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: Transition) -> CGSize {
|
func update(component: ToolValueComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: Transition) -> CGSize {
|
||||||
|
|
||||||
|
let previousValue = self.component?.value
|
||||||
self.component = component
|
self.component = component
|
||||||
self.state = state
|
self.state = state
|
||||||
|
|
||||||
@ -2860,6 +2870,10 @@ private final class ToolValueComponent: Component {
|
|||||||
transition.setPosition(view: valueView, position: valueFrame.center)
|
transition.setPosition(view: valueView, position: valueFrame.center)
|
||||||
transition.setBounds(view: valueView, bounds: CGRect(origin: .zero, size: valueFrame.size))
|
transition.setBounds(view: valueView, bounds: CGRect(origin: .zero, size: valueFrame.size))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let previousValue, component.value != previousValue, self.alpha > 0.0 {
|
||||||
|
self.hapticFeedback.impact(.click05)
|
||||||
|
}
|
||||||
|
|
||||||
return availableSize
|
return availableSize
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user