Camera and editor improvements

This commit is contained in:
Ilya Laktyushin 2023-06-03 11:51:33 +04:00
parent 3c39e2c0ce
commit ae32ccca68
5 changed files with 101 additions and 45 deletions

View File

@ -223,6 +223,10 @@ private final class CameraContext {
self.device.setZoomLevel(zoomLevel)
}
func setZoomDelta(_ zoomDelta: CGFloat) {
self.device.setZoomDelta(zoomDelta)
}
func takePhoto() -> Signal<PhotoCaptureResult, NoError> {
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) {
self.queue.async {
if let context = self.contextRef?.takeUnretainedValue() {

View File

@ -220,4 +220,13 @@ final class CameraDevice {
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))
}
}
}

View File

@ -873,11 +873,10 @@ public class CameraScreen: ViewController {
@objc private func handlePinch(_ gestureRecognizer: UIPinchGestureRecognizer) {
switch gestureRecognizer.state {
case .began:
gestureRecognizer.scale = 1.0
case .changed:
let scale = gestureRecognizer.scale
self.camera.setZoomLevel(scale)
self.camera.setZoomDelta(scale)
gestureRecognizer.scale = 1.0
default:
break
}

View File

@ -387,6 +387,7 @@ public final class MediaEditor {
self.setGradientColors([topColor, bottomColor])
if player == nil {
self.updateRenderChain()
self.maybeGeneratePersonSegmentation(image)
}
@ -425,30 +426,45 @@ public final class MediaEditor {
}
private var skipRendering = false
public func setCrop(offset: CGPoint, scale: CGFloat, rotation: CGFloat, mirroring: Bool) {
private func updateValues(skipRendering: Bool = false, _ f: (MediaEditorValues) -> MediaEditorValues) {
if skipRendering {
self.skipRendering = true
self.values = self.values.withUpdatedCrop(offset: offset, scale: scale, rotation: rotation, mirroring: mirroring)
}
self.values = f(self.values)
if skipRendering {
self.skipRendering = false
}
}
public func setCrop(offset: CGPoint, scale: CGFloat, rotation: CGFloat, mirroring: Bool) {
self.updateValues(skipRendering: true) { values in
return values.withUpdatedCrop(offset: offset, scale: scale, rotation: rotation, mirroring: mirroring)
}
}
public func getToolValue(_ key: EditorToolKey) -> Any? {
return self.values.toolValues[key]
}
public func setToolValue(_ key: EditorToolKey, value: Any) {
var updatedToolValues = self.values.toolValues
self.updateValues { values in
var updatedToolValues = values.toolValues
updatedToolValues[key] = value
self.values = self.values.withUpdatedToolValues(updatedToolValues)
self.updateRenderChain()
return values.withUpdatedToolValues(updatedToolValues)
}
}
public func setVideoIsMuted(_ videoIsMuted: Bool) {
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) {
self.values = self.values.withUpdatedVideoIsFullHd(videoIsFullHd)
self.updateValues(skipRendering: true) { values in
return values.withUpdatedVideoIsFullHd(videoIsFullHd)
}
}
private var targetTimePosition: (CMTime, Bool)?
@ -494,23 +510,29 @@ public final class MediaEditor {
})
}
public func setVideoTrimRange(_ trimRange: Range<Double>) {
self.skipRendering = true
self.values = self.values.withUpdatedVideoTrimRange(trimRange)
self.skipRendering = false
public func setVideoTrimRange(_ trimRange: Range<Double>, apply: Bool) {
self.updateValues(skipRendering: true) { values in
return values.withUpdatedVideoTrimRange(trimRange)
}
if apply {
self.player?.currentItem?.forwardPlaybackEndTime = CMTime(seconds: trimRange.upperBound, preferredTimescale: CMTimeScale(1000))
}
}
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]) {
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 func updateRenderChain() {
self.renderChain.update(values: self.values)
@ -519,10 +541,9 @@ public final class MediaEditor {
let currentTime = CACurrentMediaTime()
if !self.scheduledUpdate {
let delay = 0.03333
let delta = currentTime - self.previousUpdateTime
if delta < delay {
if let previousUpdateTime = self.previousUpdateTime, currentTime - previousUpdateTime < delay {
self.scheduledUpdate = true
Queue.mainQueue().after(delay - delta) {
Queue.mainQueue().after(delay - (currentTime - previousUpdateTime)) {
self.scheduledUpdate = false
self.previousUpdateTime = CACurrentMediaTime()
self.renderer.willRenderFrame()

View File

@ -200,6 +200,8 @@ final class MediaEditorScreenComponent: Component {
private let textDoneButton = ComponentView<Empty>()
private let textSize = ComponentView<Empty>()
private var isDismissed = false
private var component: MediaEditorScreenComponent?
private weak var state: State?
private var environment: ViewControllerComponentContainer.Environment?
@ -274,6 +276,7 @@ final class MediaEditorScreenComponent: Component {
}
func animateOut(to source: TransitionAnimationSource) {
self.isDismissed = true
let transition = Transition(animation: .curve(duration: 0.2, curve: .easeInOut))
if let view = self.cancelButton.view {
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 {
guard !self.isDismissed else {
return availableSize
}
let environment = environment[ViewControllerComponentContainer.Environment.self].value
self.environment = environment
@ -612,10 +618,9 @@ final class MediaEditorScreenComponent: Component {
framesUpdateTimestamp: playerState.framesUpdateTimestamp,
trimUpdated: { [weak mediaEditor] start, end, updatedEnd, done in
if let mediaEditor {
mediaEditor.setVideoTrimRange(start..<end, apply: done)
if done {
mediaEditor.seek(start, andPlay: true)
mediaEditor.setVideoTrimRange(start..<end)
} else {
mediaEditor.seek(updatedEnd ? end : start, andPlay: false)
}
@ -1106,9 +1111,10 @@ public final class MediaEditorScreen: ViewController {
private var isDisplayingTool = false
private var isInteractingWithEntities = false
private var isEnhacing = false
private var isDismissing = false
private var dismissOffset: CGFloat = 0.0
private var isEnhacing = false
private var isDismissed = false
private var presentationData: PresentationData
private var validLayout: ContainerViewLayout?
@ -1259,22 +1265,6 @@ public final class MediaEditorScreen: ViewController {
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 maxSide: CGFloat = 1920.0 / UIScreen.main.scale
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.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(_:)))
panGestureRecognizer.delegate = self
panGestureRecognizer.minimumNumberOfTouches = 2
@ -1429,11 +1425,20 @@ public final class MediaEditorScreen: ViewController {
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 velocity = gestureRecognizer.velocity(in: self.view)
switch gestureRecognizer.state {
case .changed:
if abs(translation.y) > 10.0 && !self.isEnhacing {
if abs(translation.y) > 10.0 && !self.isEnhacing && hasSwipeToDismiss {
if !self.isDismissing {
self.isDismissing = true
controller.requestLayout(transition: .animated(duration: 0.25, curve: .easeInOut))
@ -1449,7 +1454,7 @@ public final class MediaEditorScreen: ViewController {
} else if self.isEnhacing {
if let mediaEditor = self.mediaEditor {
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))
mediaEditor.setToolValue(.enhance, value: updatedValue)
}
@ -1573,6 +1578,7 @@ public final class MediaEditorScreen: ViewController {
guard let controller = self.controller else {
return
}
self.isDismissed = true
controller.statusBar.statusBarStyle = .Ignore
let previousDimAlpha = self.backgroundDimView.alpha
@ -1825,7 +1831,7 @@ public final class MediaEditorScreen: ViewController {
private var drawingScreen: DrawingScreen?
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
}
let isFirstTime = self.validLayout == nil
@ -2800,6 +2806,8 @@ private final class ToolValueComponent: Component {
private let title = ComponentView<Empty>()
private let value = ComponentView<Empty>()
private let hapticFeedback = HapticFeedback()
private var component: ToolValueComponent?
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 {
let previousValue = self.component?.value
self.component = component
self.state = state
@ -2861,6 +2871,10 @@ private final class ToolValueComponent: Component {
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
}
}