From dd2b47bd76b1a51f6a75a12e7b377d2a1c3a97c1 Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Tue, 26 Feb 2019 13:48:25 -0500 Subject: [PATCH] Add "flip horizontal" feature. --- .../ImageEditor/ImageEditorCanvasView.swift | 9 +++++ .../ImageEditorCropViewController.swift | 34 ++++++++++++++----- .../Views/ImageEditor/ImageEditorModel.swift | 23 +++++++++---- 3 files changed, 51 insertions(+), 15 deletions(-) diff --git a/SignalMessaging/Views/ImageEditor/ImageEditorCanvasView.swift b/SignalMessaging/Views/ImageEditor/ImageEditorCanvasView.swift index 033b0ffec..0bf3a2703 100644 --- a/SignalMessaging/Views/ImageEditor/ImageEditorCanvasView.swift +++ b/SignalMessaging/Views/ImageEditor/ImageEditorCanvasView.swift @@ -265,6 +265,15 @@ public class ImageEditorCanvasView: UIView { public class func updateImageLayer(imageLayer: CALayer, viewSize: CGSize, imageSize: CGSize, transform: ImageEditorTransform) { imageLayer.frame = imageFrame(forViewSize: viewSize, imageSize: imageSize, transform: transform) + + // This is the only place the isFlipped flag is consulted. + // We deliberately do _not_ use it in the affine transforms, etc. + // so that: + // + // * It doesn't affect text content & brush strokes. + // * To not complicate the other "coordinate system math". + let transform = CGAffineTransform.identity.scaledBy(x: transform.isFlipped ? -1 : +1, y: 1) + imageLayer.setAffineTransform(transform) } public class func imageFrame(forViewSize viewSize: CGSize, imageSize: CGSize, transform: ImageEditorTransform) -> CGRect { diff --git a/SignalMessaging/Views/ImageEditor/ImageEditorCropViewController.swift b/SignalMessaging/Views/ImageEditor/ImageEditorCropViewController.swift index 3ee502fcb..51ee588c1 100644 --- a/SignalMessaging/Views/ImageEditor/ImageEditorCropViewController.swift +++ b/SignalMessaging/Views/ImageEditor/ImageEditorCropViewController.swift @@ -101,6 +101,9 @@ class ImageEditorCropViewController: OWSViewController { let zoom2xButton = OWSButton(title: "Zoom 2x") { [weak self] in self?.zoom2xButtonPressed() } + let flipButton = OWSButton(title: "Flip") { [weak self] in + self?.flipButtonPressed() + } // MARK: - Header @@ -149,6 +152,7 @@ class ImageEditorCropViewController: OWSViewController { // MARK: - Footer let footer = UIStackView(arrangedSubviews: [ + flipButton, rotate90Button, rotate45Button, UIView.hStretchingSpacer(), @@ -418,7 +422,8 @@ class ImageEditorCropViewController: OWSViewController { updateTransform(ImageEditorTransform(outputSizePixels: gestureStartTransform.outputSizePixels, unitTranslation: newUnitTranslation, rotationRadians: newRotationRadians, - scaling: newScaling).normalize(srcImageSizePixels: model.srcImageSizePixels)) + scaling: newScaling, + isFlipped: gestureStartTransform.isFlipped).normalize(srcImageSizePixels: model.srcImageSizePixels)) default: break } @@ -552,7 +557,8 @@ class ImageEditorCropViewController: OWSViewController { let naiveTransform = ImageEditorTransform(outputSizePixels: croppedOutputSizePixels, unitTranslation: transform.unitTranslation, rotationRadians: transform.rotationRadians, - scaling: transform.scaling) + scaling: transform.scaling, + isFlipped: transform.isFlipped) let naiveImageFrameOld = ImageEditorCanvasView.imageFrame(forViewSize: transform.outputSizePixels, imageSize: model.srcImageSizePixels, transform: naiveTransform) let naiveImageFrameNew = ImageEditorCanvasView.imageFrame(forViewSize: croppedOutputSizePixels, imageSize: model.srcImageSizePixels, transform: naiveTransform) let scalingDeltaX = naiveImageFrameNew.width / naiveImageFrameOld.width @@ -590,7 +596,8 @@ class ImageEditorCropViewController: OWSViewController { updateTransform(ImageEditorTransform(outputSizePixels: croppedOutputSizePixels, unitTranslation: unitTranslation, rotationRadians: transform.rotationRadians, - scaling: scaling).normalize(srcImageSizePixels: model.srcImageSizePixels)) + scaling: scaling, + isFlipped: transform.isFlipped).normalize(srcImageSizePixels: model.srcImageSizePixels)) } private func handleNormalPanGesture(_ gestureRecognizer: ImageEditorPanGestureRecognizer) { @@ -614,7 +621,8 @@ class ImageEditorCropViewController: OWSViewController { updateTransform(ImageEditorTransform(outputSizePixels: gestureStartTransform.outputSizePixels, unitTranslation: newUnitTranslation, rotationRadians: gestureStartTransform.rotationRadians, - scaling: gestureStartTransform.scaling).normalize(srcImageSizePixels: model.srcImageSizePixels)) + scaling: gestureStartTransform.scaling, + isFlipped: gestureStartTransform.isFlipped).normalize(srcImageSizePixels: model.srcImageSizePixels)) } private func cropRegion(forGestureRecognizer gestureRecognizer: ImageEditorPanGestureRecognizer) -> CropRegion? { @@ -691,7 +699,8 @@ class ImageEditorCropViewController: OWSViewController { updateTransform(ImageEditorTransform(outputSizePixels: outputSizePixels, unitTranslation: unitTranslation, rotationRadians: rotationRadians, - scaling: scaling).normalize(srcImageSizePixels: model.srcImageSizePixels)) + scaling: scaling, + isFlipped: transform.isFlipped).normalize(srcImageSizePixels: model.srcImageSizePixels)) } @objc public func zoom2xButtonPressed() { @@ -700,9 +709,18 @@ class ImageEditorCropViewController: OWSViewController { let rotationRadians = transform.rotationRadians let scaling = transform.scaling * 2.0 updateTransform(ImageEditorTransform(outputSizePixels: outputSizePixels, - unitTranslation: unitTranslation, - rotationRadians: rotationRadians, - scaling: scaling).normalize(srcImageSizePixels: model.srcImageSizePixels)) + unitTranslation: unitTranslation, + rotationRadians: rotationRadians, + scaling: scaling, + isFlipped: transform.isFlipped).normalize(srcImageSizePixels: model.srcImageSizePixels)) + } + + @objc public func flipButtonPressed() { + updateTransform(ImageEditorTransform(outputSizePixels: transform.outputSizePixels, + unitTranslation: transform.unitTranslation, + rotationRadians: transform.rotationRadians, + scaling: transform.scaling, + isFlipped: !transform.isFlipped).normalize(srcImageSizePixels: model.srcImageSizePixels)) } @objc public func resetButtonPressed() { diff --git a/SignalMessaging/Views/ImageEditor/ImageEditorModel.swift b/SignalMessaging/Views/ImageEditor/ImageEditorModel.swift index e4bee2cdd..4711c010b 100644 --- a/SignalMessaging/Views/ImageEditor/ImageEditorModel.swift +++ b/SignalMessaging/Views/ImageEditor/ImageEditorModel.swift @@ -62,15 +62,19 @@ public class ImageEditorTransform: NSObject { public let rotationRadians: CGFloat // x >= 1.0. public let scaling: CGFloat + // Flipping is horizontal. + public let isFlipped: Bool public init(outputSizePixels: CGSize, unitTranslation: CGPoint, rotationRadians: CGFloat, - scaling: CGFloat) { + scaling: CGFloat, + isFlipped: Bool) { self.outputSizePixels = outputSizePixels self.unitTranslation = unitTranslation self.rotationRadians = rotationRadians self.scaling = scaling + self.isFlipped = isFlipped } public class func defaultTransform(srcImageSizePixels: CGSize) -> ImageEditorTransform { @@ -78,7 +82,8 @@ public class ImageEditorTransform: NSObject { return ImageEditorTransform(outputSizePixels: srcImageSizePixels, unitTranslation: .zero, rotationRadians: 0.0, - scaling: 1.0).normalize(srcImageSizePixels: srcImageSizePixels) + scaling: 1.0, + isFlipped: false).normalize(srcImageSizePixels: srcImageSizePixels) } public func affineTransform(viewSize: CGSize) -> CGAffineTransform { @@ -133,7 +138,8 @@ public class ImageEditorTransform: NSObject { let naiveTransform = ImageEditorTransform(outputSizePixels: outputSizePixels, unitTranslation: .zero, rotationRadians: rotationRadians, - scaling: scaling) + scaling: scaling, + isFlipped: self.isFlipped) let naiveAffineTransform = naiveTransform.affineTransform(viewSize: viewBounds.size) var naiveViewportMinCanvas = CGPoint.zero var naiveViewportMaxCanvas = CGPoint.zero @@ -192,7 +198,8 @@ public class ImageEditorTransform: NSObject { return ImageEditorTransform(outputSizePixels: outputSizePixels, unitTranslation: unitTranslation, rotationRadians: rotationRadians, - scaling: scaling) + scaling: scaling, + isFlipped: self.isFlipped) } public override func isEqual(_ object: Any?) -> Bool { @@ -202,7 +209,8 @@ public class ImageEditorTransform: NSObject { return (outputSizePixels == other.outputSizePixels && unitTranslation == other.unitTranslation && rotationRadians == other.rotationRadians && - scaling == other.scaling) + scaling == other.scaling && + isFlipped == other.isFlipped) } public override var hash: Int { @@ -211,11 +219,12 @@ public class ImageEditorTransform: NSObject { unitTranslation.x.hashValue ^ unitTranslation.y.hashValue ^ rotationRadians.hashValue ^ - scaling.hashValue) + scaling.hashValue ^ + isFlipped.hashValue) } open override var description: String { - return "[outputSizePixels: \(outputSizePixels), unitTranslation: \(unitTranslation), rotationRadians: \(rotationRadians), scaling: \(scaling)]" + return "[outputSizePixels: \(outputSizePixels), unitTranslation: \(unitTranslation), rotationRadians: \(rotationRadians), scaling: \(scaling), isFlipped: \(isFlipped)]" } }