[WIP] Stickers editor

This commit is contained in:
Ilya Laktyushin 2024-04-10 19:40:10 +04:00
parent 094341b520
commit 78a30245e7
3 changed files with 66 additions and 68 deletions

View File

@ -192,7 +192,7 @@ public final class MediaEditor {
public private(set) var canCutout: Bool = false
public var canCutoutUpdated: (Bool, Bool) -> Void = { _, _ in }
public var maskUpdated: (UIImage) -> Void = { _ in }
public var maskUpdated: (UIImage, Bool) -> Void = { _, _ in }
public var classificationUpdated: ([(String, Float)]) -> Void = { _ in }
@ -731,12 +731,13 @@ public final class MediaEditor {
self.canCutout = canCutout
self.canCutoutUpdated(canCutout, false)
})
self.maskUpdated(image, false)
} else {
self.canCutout = false
self.canCutoutUpdated(false, true)
if let maskImage = generateTintedImage(image: image, color: .white, backgroundColor: .black) {
self.maskUpdated(maskImage)
self.maskUpdated(maskImage, true)
}
}
let _ = (classifyImage(image)

View File

@ -226,11 +226,8 @@ private final class MediaCutoutScreenComponent: Component {
}
controller.drawingView.isUserInteractionEnabled = false
let overlayView = controller.overlayView
let backgroundView = controller.backgroundView
let transition = Transition(animation: .curve(duration: 0.2, curve: .easeInOut))
transition.setAlpha(view: overlayView, alpha: 0.0)
transition.setAlpha(view: backgroundView, alpha: 1.0)
controller.overlayView.alpha = 0.0
controller.backgroundView.alpha = 1.0
}
public func playDissolveAnimation() {

View File

@ -2870,65 +2870,15 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
}
if let mediaEntityView = self.entitiesView.add(mediaEntity, announce: false) as? DrawingMediaEntityView {
var updateStickerMaskDrawing: (CGPoint, CGFloat, CGFloat) -> Void = { _, _, _ in }
if isStickerEditor {
let mediaEntitySize = mediaEntityView.bounds.size
let scaledDimensions = subject.dimensions.cgSize.aspectFittedOrSmaller(CGSize(width: 1920, height: 1920))
let maskDrawingSize = scaledDimensions.aspectFilled(mediaEntitySize)
let stickerMaskDrawingView = DrawingView(size: scaledDimensions, gestureView: self.previewContainerView)
stickerMaskDrawingView.stateUpdated = { [weak self] _ in
if let self {
self.requestLayout(forceUpdate: true, transition: .easeInOut(duration: 0.25))
}
}
stickerMaskDrawingView.emptyColor = .white
stickerMaskDrawingView.updateToolState(.pen(DrawingToolState.BrushState(color: DrawingColor(color: .black), size: 0.5)))
stickerMaskDrawingView.isUserInteractionEnabled = false
stickerMaskDrawingView.animationsEnabled = false
stickerMaskDrawingView.clearWithEmptyColor()
if let filter = makeLuminanceToAlphaFilter() {
self.stickerMaskWrapperView.layer.filters = [filter]
}
self.stickerMaskWrapperView.addSubview(stickerMaskDrawingView)
self.stickerMaskWrapperView.addSubview(self.stickerMaskPreviewView)
self.stickerMaskDrawingView = stickerMaskDrawingView
var initialMaskPosition = CGPoint()
var initialMaskScale: CGFloat = 1.0
updateStickerMaskDrawing = { [weak stickerMaskDrawingView] position, scale, rotation in
guard let stickerMaskDrawingView else {
return
}
let maskScale = initialMaskPosition.x * 2.0 / 1080.0
stickerMaskDrawingView.center = initialMaskPosition.offsetBy(dx: position.x * maskScale, dy: position.y * maskScale)
stickerMaskDrawingView.transform = CGAffineTransform(scaleX: initialMaskScale * scale, y: initialMaskScale * scale).rotated(by: rotation)
}
Queue.mainQueue().after(0.1) {
let previewSize = self.previewView.bounds.size
self.stickerMaskWrapperView.frame = CGRect(origin: .zero, size: previewSize)
self.stickerMaskPreviewView.frame = CGRect(origin: .zero, size: previewSize)
let maskScale = previewSize.width / min(maskDrawingSize.width, maskDrawingSize.height)
initialMaskScale = maskScale
initialMaskPosition = CGPoint(x: previewSize.width / 2.0, y: previewSize.height / 2.0)
stickerMaskDrawingView.bounds = CGRect(origin: .zero, size: maskDrawingSize)
updateStickerMaskDrawing(.zero, 1.0, 0.0)
}
}
self.entitiesView.sendSubviewToBack(mediaEntityView)
mediaEntityView.updated = { [weak self, weak mediaEntity] in
if let self, let mediaEntity {
let rotationDelta = mediaEntity.rotation - initialRotation
let positionDelta = CGPoint(x: mediaEntity.position.x - initialPosition.x, y: mediaEntity.position.y - initialPosition.y)
let scaleDelta = mediaEntity.scale / initialScale
self.mediaEditor?.setCrop(offset: positionDelta, scale: scaleDelta, rotation: rotationDelta, mirroring: false)
let rotation = mediaEntity.rotation - initialRotation
let position = CGPoint(x: mediaEntity.position.x - initialPosition.x, y: mediaEntity.position.y - initialPosition.y)
let scale = mediaEntity.scale / initialScale
self.mediaEditor?.setCrop(offset: position, scale: scale, rotation: rotation, mirroring: false)
updateStickerMaskDrawing(positionDelta, scaleDelta, rotationDelta)
self.updateMaskDrawingView(position: position, scale: scale, rotation: rotation)
}
}
@ -2969,11 +2919,14 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
self.hasTransparency = hasTransparency
self.requestLayout(forceUpdate: true, transition: .easeInOut(duration: 0.25))
}
mediaEditor.maskUpdated = { [weak self] mask in
mediaEditor.maskUpdated = { [weak self] mask, apply in
guard let self else {
return
}
if let maskData = mask.pngData() {
if self.stickerMaskDrawingView == nil {
self.setupMaskDrawingView(size: mask.size)
}
if apply, let maskData = mask.pngData() {
self.stickerMaskDrawingView?.setup(withDrawing: maskData, storeAsClear: true)
}
}
@ -3151,6 +3104,55 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
}
}
}
private var initialMaskScale: CGFloat = .zero
private var initialMaskPosition: CGPoint = .zero
private func setupMaskDrawingView(size: CGSize) {
guard let mediaEntityView = self.entitiesView.getView(where: { $0 is DrawingMediaEntityView }) as? DrawingMediaEntityView else {
return
}
let mediaEntitySize = mediaEntityView.bounds.size
let scaledDimensions = size
let maskDrawingSize = scaledDimensions.aspectFilled(mediaEntitySize)
let stickerMaskDrawingView = DrawingView(size: scaledDimensions, gestureView: self.previewContainerView)
stickerMaskDrawingView.stateUpdated = { [weak self] _ in
if let self {
self.requestLayout(forceUpdate: true, transition: .easeInOut(duration: 0.25))
}
}
stickerMaskDrawingView.emptyColor = .white
stickerMaskDrawingView.updateToolState(.pen(DrawingToolState.BrushState(color: DrawingColor(color: .black), size: 0.5)))
stickerMaskDrawingView.isUserInteractionEnabled = false
stickerMaskDrawingView.animationsEnabled = false
stickerMaskDrawingView.clearWithEmptyColor()
if let filter = makeLuminanceToAlphaFilter() {
self.stickerMaskWrapperView.layer.filters = [filter]
}
self.stickerMaskWrapperView.addSubview(stickerMaskDrawingView)
self.stickerMaskWrapperView.addSubview(self.stickerMaskPreviewView)
self.stickerMaskDrawingView = stickerMaskDrawingView
let previewSize = self.previewView.bounds.size
self.stickerMaskWrapperView.frame = CGRect(origin: .zero, size: previewSize)
self.stickerMaskPreviewView.frame = CGRect(origin: .zero, size: previewSize)
let maskScale = previewSize.width / min(maskDrawingSize.width, maskDrawingSize.height)
self.initialMaskScale = maskScale
self.initialMaskPosition = CGPoint(x: previewSize.width / 2.0, y: previewSize.height / 2.0)
stickerMaskDrawingView.bounds = CGRect(origin: .zero, size: maskDrawingSize)
self.updateMaskDrawingView(position: .zero, scale: 1.0, rotation: 0.0)
}
private func updateMaskDrawingView(position: CGPoint, scale: CGFloat, rotation: CGFloat) {
guard let stickerMaskDrawingView = self.stickerMaskDrawingView else {
return
}
let maskScale = self.initialMaskPosition.x * 2.0 / 1080.0
stickerMaskDrawingView.center = self.initialMaskPosition.offsetBy(dx: position.x * maskScale, dy: position.y * maskScale)
stickerMaskDrawingView.transform = CGAffineTransform(scaleX: self.initialMaskScale * scale, y: self.initialMaskScale * scale).rotated(by: rotation)
}
override func didLoad() {
super.didLoad()
@ -4928,10 +4930,8 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate
action()
}
if self.isDisplayingTool == .cutoutRestore {
self.cutoutScreen?.mode = .erase
self.isDisplayingTool = .cutoutErase
self.requestLayout(forceUpdate: true, transition: .easeInOut(duration: 0.25))
if let cutoutScreen = self.cutoutScreen {
cutoutScreen.requestDismiss(animated: true)
}
}
}