mirror of
https://github.com/oxen-io/session-ios.git
synced 2023-12-13 21:30:14 +01:00
Respond to CR.
// FREEBIE
This commit is contained in:
parent
2aaa9155de
commit
c90ca331e3
1 changed files with 52 additions and 43 deletions
|
@ -6,7 +6,7 @@ import Foundation
|
|||
import MediaPlayer
|
||||
|
||||
class OWSLayerView: UIView {
|
||||
var layoutCallback : (() -> Void)?
|
||||
let layoutCallback : (() -> Void)
|
||||
|
||||
required init(frame: CGRect, layoutCallback : @escaping () -> Void) {
|
||||
self.layoutCallback = layoutCallback
|
||||
|
@ -21,23 +21,36 @@ class OWSLayerView: UIView {
|
|||
|
||||
override var bounds: CGRect {
|
||||
didSet {
|
||||
guard let layoutCallback = self.layoutCallback else {
|
||||
return
|
||||
}
|
||||
layoutCallback()
|
||||
}
|
||||
}
|
||||
|
||||
override var frame: CGRect {
|
||||
didSet {
|
||||
guard let layoutCallback = self.layoutCallback else {
|
||||
return
|
||||
}
|
||||
layoutCallback()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This kind of view is tricky. I've tried to organize things in the
|
||||
// simplest possible way.
|
||||
//
|
||||
// I've tried to avoid the following sources of confusion:
|
||||
//
|
||||
// * Points vs. pixels. All variables should have names that
|
||||
// reflect the units. Pretty much everything is done in points
|
||||
// except rendering of the output image which is done in pixels.
|
||||
// * Coordinate systems. You have a) the src image coordinates
|
||||
// b) the image view coordinates c) the output image coordinates.
|
||||
// Wherever possible, I've tried to use src image coordinates.
|
||||
// * Translation & scaling vs. crop region. The crop region is
|
||||
// implicit. We represent the crop state using the translation
|
||||
// and scaling of the "default" crop region (the largest possible
|
||||
// crop region, at the origin (upper left) of the source image.
|
||||
// Given the translation & scaling, we can determine a) the crop
|
||||
// region b) the rectangle at which the src image should be rendered
|
||||
// given a dst view or output context that will yield the
|
||||
// appropriate cropping.
|
||||
class CropScaleImageViewController: OWSViewController {
|
||||
|
||||
let TAG = "[CropScaleImageViewController]"
|
||||
|
@ -46,14 +59,14 @@ class CropScaleImageViewController: OWSViewController {
|
|||
|
||||
let srcImage: UIImage
|
||||
|
||||
var successCompletion: ((UIImage) -> Void)?
|
||||
let successCompletion: ((UIImage) -> Void)
|
||||
|
||||
var imageView: UIView?
|
||||
var imageView: UIView!
|
||||
|
||||
// We use a CALayer to render the image for performance reasons.
|
||||
var imageLayer: CALayer?
|
||||
var imageLayer: CALayer!
|
||||
|
||||
var dashedBorderLayer: CAShapeLayer?
|
||||
var dashedBorderLayer: CAShapeLayer!
|
||||
|
||||
// In width/height.
|
||||
//
|
||||
|
@ -87,9 +100,11 @@ class CropScaleImageViewController: OWSViewController {
|
|||
|
||||
// MARK: Initializers
|
||||
|
||||
@available(*, unavailable, message:"use attachment: constructor instead.")
|
||||
@available(*, unavailable, message:"use srcImage:successCompletion: constructor instead.")
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
self.srcImage = UIImage(named:"fail")!
|
||||
self.successCompletion = { _ in
|
||||
}
|
||||
super.init(coder: aDecoder)
|
||||
owsFail("\(self.TAG) invalid constructor")
|
||||
|
||||
|
@ -108,7 +123,9 @@ class CropScaleImageViewController: OWSViewController {
|
|||
// MARK: Cropping and Scaling
|
||||
|
||||
private func configureCropAndScale() {
|
||||
// Size of bounding box that reflects the target aspect ratio, whose longer side = 1.
|
||||
// We use a "unit" view size (long dimension of length 1, short dimension reflects
|
||||
// the dst aspect ratio) since we want to be able to perform this logic before we
|
||||
// know the actual size of the cropped image view.
|
||||
let unitSquareHeight: CGFloat = (dstAspectRatio >= 1.0 ? 1.0 : 1.0 / dstAspectRatio)
|
||||
let unitSquareWidth: CGFloat = (dstAspectRatio >= 1.0 ? dstAspectRatio * unitSquareHeight : 1.0)
|
||||
let unitSquareSize = CGSize(width: unitSquareWidth, height: unitSquareHeight)
|
||||
|
@ -207,10 +224,6 @@ class CropScaleImageViewController: OWSViewController {
|
|||
contentView.isUserInteractionEnabled = true
|
||||
contentView.addGestureRecognizer(UIPinchGestureRecognizer(target: self, action: #selector(handlePinch(sender:))))
|
||||
contentView.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(handlePan(sender:))))
|
||||
let doubleTap = UITapGestureRecognizer(target: self, action: #selector(handleDoubleTap(sender:)))
|
||||
doubleTap.numberOfTapsRequired = 2
|
||||
doubleTap.numberOfTouchesRequired = 1
|
||||
contentView.addGestureRecognizer(doubleTap)
|
||||
}
|
||||
|
||||
override func viewDidLayoutSubviews() {
|
||||
|
@ -232,6 +245,10 @@ class CropScaleImageViewController: OWSViewController {
|
|||
updateImageLayout()
|
||||
}
|
||||
|
||||
// Given a src image size and a dst view size, this finds the bounds
|
||||
// of the largest rectangular crop region with the correct dst aspect
|
||||
// ratio that fits in the src image's aspect ratio, in src image point
|
||||
// coordinates.
|
||||
private func defaultCropFramePoints(imageSizePoints: CGSize, viewSizePoints: CGSize) -> (CGRect) {
|
||||
let imageAspectRatio = imageSizePoints.width / imageSizePoints.height
|
||||
let viewAspectRatio = viewSizePoints.width / viewSizePoints.height
|
||||
|
@ -257,12 +274,6 @@ class CropScaleImageViewController: OWSViewController {
|
|||
guard let imageView = self.imageView else {
|
||||
return
|
||||
}
|
||||
guard let imageLayer = self.imageLayer else {
|
||||
return
|
||||
}
|
||||
guard let dashedBorderLayer = self.dashedBorderLayer else {
|
||||
return
|
||||
}
|
||||
guard srcImageSizePoints.width > 0 && srcImageSizePoints.height > 0 else {
|
||||
return
|
||||
}
|
||||
|
@ -297,10 +308,22 @@ class CropScaleImageViewController: OWSViewController {
|
|||
CATransaction.begin()
|
||||
CATransaction.setDisableActions(true)
|
||||
imageLayer.frame = imageViewFrame
|
||||
CATransaction.commit()
|
||||
|
||||
// Mask to circle.
|
||||
let maskLayer = CAShapeLayer()
|
||||
maskLayer.frame = imageViewFrame
|
||||
maskLayer.fillRule = kCAFillRuleEvenOdd
|
||||
let maskFrame = CGRect(origin:CGPoint(x:-imageViewFrame.origin.x * 2,
|
||||
y: -imageViewFrame.origin.y * 2),
|
||||
size:imageView.bounds.size)
|
||||
maskLayer.path =
|
||||
CGPath(ellipseIn: maskFrame, transform: nil)
|
||||
imageLayer.mask = maskLayer
|
||||
|
||||
dashedBorderLayer.frame = imageView.bounds
|
||||
dashedBorderLayer.path = UIBezierPath(rect: imageView.bounds).cgPath
|
||||
|
||||
CATransaction.commit()
|
||||
}
|
||||
|
||||
private func imageRenderRect(forDstSize dstSize: CGSize) -> CGRect {
|
||||
|
@ -335,10 +358,6 @@ class CropScaleImageViewController: OWSViewController {
|
|||
lastPinchScale = sender.scale
|
||||
break
|
||||
case .changed, .ended:
|
||||
guard let imageView = self.imageView else {
|
||||
return
|
||||
}
|
||||
|
||||
if sender.numberOfTouches > 1 {
|
||||
let location =
|
||||
sender.location(in: sender.view)
|
||||
|
@ -391,9 +410,6 @@ class CropScaleImageViewController: OWSViewController {
|
|||
srcTranslationAtPanStart = srcTranslation
|
||||
break
|
||||
case .changed, .ended:
|
||||
guard let imageView = self.imageView else {
|
||||
return
|
||||
}
|
||||
let viewSizePoints = imageView.frame.size
|
||||
let srcCropSizePoints = CGSize(width:srcDefaultCropSizePoints.width / imageScale,
|
||||
height:srcDefaultCropSizePoints.height / imageScale)
|
||||
|
@ -416,16 +432,6 @@ class CropScaleImageViewController: OWSViewController {
|
|||
updateImageLayout()
|
||||
}
|
||||
|
||||
func handleDoubleTap(sender: UIPanGestureRecognizer) {
|
||||
if (sender.state == .recognized) {
|
||||
if imageScale > 1.5 {
|
||||
imageScale = kMinImageScale
|
||||
}
|
||||
}
|
||||
|
||||
updateImageLayout()
|
||||
}
|
||||
|
||||
private func createButtonRow(contentView: UIView) {
|
||||
let buttonTopMargin = ScaleFromIPhone5To7Plus(30, 40)
|
||||
let buttonBottomMargin = ScaleFromIPhone5To7Plus(25, 40)
|
||||
|
@ -477,7 +483,7 @@ class CropScaleImageViewController: OWSViewController {
|
|||
guard let dstImage = self.generateDstImage() else {
|
||||
return
|
||||
}
|
||||
successCompletion?(dstImage)
|
||||
successCompletion(dstImage)
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -488,12 +494,15 @@ class CropScaleImageViewController: OWSViewController {
|
|||
let dstScale: CGFloat = 1.0 // The size is specified in pixels, not in points.
|
||||
UIGraphicsBeginImageContextWithOptions(dstSizePixels, !hasAlpha, dstScale)
|
||||
|
||||
let context = UIGraphicsGetCurrentContext()
|
||||
context!.interpolationQuality = .high
|
||||
|
||||
let imageViewFrame = imageRenderRect(forDstSize:dstSizePixels)
|
||||
srcImage.draw(in:imageViewFrame)
|
||||
|
||||
let scaledImage = UIGraphicsGetImageFromCurrentImageContext()
|
||||
if scaledImage == nil {
|
||||
Logger.error("\(TAG) could not generate dst image.")
|
||||
owsFail("\(TAG) could not generate dst image.")
|
||||
}
|
||||
UIGraphicsEndImageContext()
|
||||
return scaledImage
|
||||
|
|
Loading…
Reference in a new issue