@ -66,8 +66,6 @@ class CropScaleImageViewController: OWSViewController {
// W e u s e a C A L a y e r t o r e n d e r t h e i m a g e f o r p e r f o r m a n c e r e a s o n s .
// W e u s e a C A L a y e r t o r e n d e r t h e i m a g e f o r p e r f o r m a n c e r e a s o n s .
var imageLayer : CALayer !
var imageLayer : CALayer !
var dashedBorderLayer : CAShapeLayer !
// I n w i d t h / h e i g h t .
// I n w i d t h / h e i g h t .
//
//
// TODO: W e c o u l d m a k e t h i s a p a r a m e t e r .
// TODO: W e c o u l d m a k e t h i s a p a r a m e t e r .
@ -98,6 +96,9 @@ class CropScaleImageViewController: OWSViewController {
// c o r n e r o f t h e c r o p r e g i o n i n s r c i m a g e p o i n t c o o r d i n a t e s .
// c o r n e r o f t h e c r o p r e g i o n i n s r c i m a g e p o i n t c o o r d i n a t e s .
var srcTranslation : CGPoint = CGPoint . zero
var srcTranslation : CGPoint = CGPoint . zero
// s p a c e b e t w e e n t h e c r o p p i n g c i r c l e a n d t h e o u t s i d e e d g e o f t h e v i e w
let maskMargin = CGFloat ( 20 )
// MARK: I n i t i a l i z e r s
// MARK: I n i t i a l i z e r s
@ available ( * , unavailable , message : " use srcImage:successCompletion: constructor instead. " )
@ available ( * , unavailable , message : " use srcImage:successCompletion: constructor instead. " )
@ -174,29 +175,18 @@ class CropScaleImageViewController: OWSViewController {
view . backgroundColor = UIColor . white
view . backgroundColor = UIColor . white
self . navigationItem . leftBarButtonItem = UIBarButtonItem ( barButtonSystemItem : . stop ,
target : self ,
action : #selector ( cancelPressed ) )
self . navigationItem . title = NSLocalizedString ( " CROP_SCALE_IMAGE_VIEW_TITLE " ,
comment : " Title for the 'crop/scale image' dialog. " )
createViews ( )
createViews ( )
}
}
// MARK: - C r e a t e V i e w s
// MARK: - C r e a t e V i e w s
private func createViews ( ) {
private func createViews ( ) {
let previewTopMargin : CGFloat = 30
let previewHMargin : CGFloat = 20
let contentView = UIView ( )
let contentView = UIView ( )
contentView . backgroundColor = UIColor . black
self . view . addSubview ( contentView )
self . view . addSubview ( contentView )
contentView . autoPinWidthToSuperview ( withMargin : previewHMargin )
contentView . autoPinEdgesToSuperviewEdges ( )
contentView . autoPin ( toTopLayoutGuideOf : self , withInset : previewTopMargin )
createButtonRow ( contentView : contentView )
let imageHMargin : CGFloat = 0
let imageView = OWSLayerView ( frame : CGRect . zero , layoutCallback : { [ weak self ] _ in
let imageView = OWSLayerView ( frame : CGRect . zero , layoutCallback : { [ weak self ] _ in
guard let strongSelf = self else { return }
guard let strongSelf = self else { return }
strongSelf . updateImageLayout ( )
strongSelf . updateImageLayout ( )
@ -204,22 +194,45 @@ class CropScaleImageViewController: OWSViewController {
imageView . clipsToBounds = true
imageView . clipsToBounds = true
self . imageView = imageView
self . imageView = imageView
contentView . addSubview ( imageView )
contentView . addSubview ( imageView )
imageView . autoPinWidthToSuperview ( withMargin : imageHMargin )
imageView . autoPinEdgesToSuperviewEdges ( )
imageView . autoVCenterInSuperview ( )
imageView . autoPinToSquareAspectRatio ( )
let imageLayer = CALayer ( )
let imageLayer = CALayer ( )
self . imageLayer = imageLayer
self . imageLayer = imageLayer
imageLayer . contents = srcImage . cgImage
imageLayer . contents = srcImage . cgImage
imageView . layer . addSublayer ( imageLayer )
imageView . layer . addSublayer ( imageLayer )
let dashedBorderLayer = CAShapeLayer ( )
let maskingView = OWSBezierPathView ( )
self . dashedBorderLayer = dashedBorderLayer
contentView . addSubview ( maskingView )
dashedBorderLayer . strokeColor = UIColor . ows_materialBlue ( ) . cgColor
dashedBorderLayer . lineDashPattern = [ 10 , 10 ]
maskingView . configureShapeLayerBlock = { layer , bounds in
dashedBorderLayer . lineWidth = 4
let path = UIBezierPath ( rect : bounds )
dashedBorderLayer . fillColor = nil
imageView . layer . addSublayer ( dashedBorderLayer )
let radius = min ( bounds . size . width , bounds . size . height ) * 0.5 - self . maskMargin
// C e n t e r t h e c i r c l e ' s b o u n d i n g r e c t a n g l e
let circleRect = CGRect ( x : bounds . size . width * 0.5 - radius , y : bounds . size . height * 0.5 - radius , width : radius * 2 , height : radius * 2 )
let circlePath = UIBezierPath ( roundedRect : circleRect , cornerRadius : radius )
path . append ( circlePath )
path . usesEvenOddFillRule = true
layer . path = path . cgPath
layer . fillRule = kCAFillRuleEvenOdd
layer . fillColor = UIColor . black . cgColor
layer . opacity = 0.7
}
maskingView . autoPinEdgesToSuperviewEdges ( )
let titleLabel = UILabel ( )
titleLabel . textColor = UIColor . white
titleLabel . textAlignment = . center
titleLabel . font = UIFont . ows_mediumFont ( withSize : ScaleFromIPhone5 ( 16 ) )
titleLabel . text = NSLocalizedString ( " CROP_SCALE_IMAGE_VIEW_TITLE " ,
comment : " Title for the 'crop/scale image' dialog. " )
contentView . addSubview ( titleLabel )
titleLabel . autoPinWidthToSuperview ( )
let titleLabelMargin = ScaleFromIPhone5 ( 16 )
titleLabel . autoPin ( toTopLayoutGuideOf : self , withInset : titleLabelMargin )
createButtonRow ( contentView : contentView )
contentView . isUserInteractionEnabled = true
contentView . isUserInteractionEnabled = true
contentView . addGestureRecognizer ( UIPinchGestureRecognizer ( target : self , action : #selector ( handlePinch ( sender : ) ) ) )
contentView . addGestureRecognizer ( UIPinchGestureRecognizer ( target : self , action : #selector ( handlePinch ( sender : ) ) ) )
@ -294,34 +307,35 @@ class CropScaleImageViewController: OWSViewController {
height : srcDefaultCropSizePoints . height / imageScale )
height : srcDefaultCropSizePoints . height / imageScale )
let minSrcTranslationPoints = CGPoint . zero
let minSrcTranslationPoints = CGPoint . zero
// P r e v e n t p a n n i n g o u t s i d e o f i m a g e a r e a .
let maxSrcTranslationPoints = CGPoint ( x : srcImageSizePoints . width - srcCropSizePoints . width ,
let maxSrcTranslationPoints = CGPoint ( x : srcImageSizePoints . width - srcCropSizePoints . width ,
y : srcImageSizePoints . height - srcCropSizePoints . height
y : srcImageSizePoints . height - srcCropSizePoints . height
)
)
// N o r m a l i z e t h e t r a n s l a t i o n p r o p e r t y .
// N o r m a l i z e t h e t r a n s l a t i o n p r o p e r t y
srcTranslation = CGPoint ( x : max ( minSrcTranslationPoints . x , min ( maxSrcTranslationPoints . x , srcTranslation . x ) ) ,
srcTranslation = CGPoint ( x : max ( minSrcTranslationPoints . x , min ( maxSrcTranslationPoints . x , srcTranslation . x ) ) ,
y : max ( minSrcTranslationPoints . y , min ( maxSrcTranslationPoints . y , srcTranslation . y ) ) )
y : max ( minSrcTranslationPoints . y , min ( maxSrcTranslationPoints . y , srcTranslation . y ) ) )
let imageViewFrame = imageRenderRect ( forDstSize : viewSizePoints )
var imageViewFrame = imageRenderRect ( forDstSize : viewSizePoints )
// o f f s e t t o v e r t i c a l l y c e n t e r i m a g e i n v i e w ( a s p e c t r a t i o ? )
let srcToViewRatio = viewSizePoints . width / srcCropSizePoints . width / imageScale
let heightOfImageLayer = srcDefaultCropSizePoints . height * srcToViewRatio
let yOffset = imageView . frame . height * 0.5 - heightOfImageLayer * 0.5
imageViewFrame = imageViewFrame . offsetBy ( dx : 0 , dy : yOffset )
// i n s e t f r a m e b y t h e w i t h o f t h e m a s k i n g m a r g i n s o t h e u s e r c a n p a n t o t h e v e r y e d g e o f t h e i m a g e
imageViewFrame = CGRect ( x : imageViewFrame . origin . x + self . maskMargin ,
y : imageViewFrame . origin . y + self . maskMargin ,
width : imageViewFrame . width - maskMargin * 2 ,
height : imageViewFrame . height - maskMargin * 2 )
// D i s a b l e i m p l i c i t a n i m a t i o n s .
// D i s a b l e i m p l i c i t a n i m a t i o n s f o r s n a p p i e r p a n n i n g / z o o m i n g .
CATransaction . begin ( )
CATransaction . begin ( )
CATransaction . setDisableActions ( true )
CATransaction . setDisableActions ( true )
imageLayer . frame = imageViewFrame
// M a s k t o c i r c l e .
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
imageLayer . frame = imageViewFrame
dashedBorderLayer . path = UIBezierPath ( rect : imageView . bounds ) . cgPath
CATransaction . commit ( )
CATransaction . commit ( )
}
}
@ -333,10 +347,10 @@ class CropScaleImageViewController: OWSViewController {
let srcToViewRatio = dstSize . width / srcCropSizePoints . width
let srcToViewRatio = dstSize . width / srcCropSizePoints . width
return CGRect ( origin : CGPoint ( x : srcTranslation . x * - srcToViewRatio ,
return CGRect ( origin : CGPoint ( x : srcTranslation . x * - srcToViewRatio ,
y : srcTranslation . y * - srcToViewRatio ) ,
y : srcTranslation . y * - srcToViewRatio ) ,
size : CGSize ( width : srcImageSizePoints . width * + srcToViewRatio ,
size : CGSize ( width : srcImageSizePoints . width * + srcToViewRatio ,
height : srcImageSizePoints . height * + srcToViewRatio
height : srcImageSizePoints . height * + srcToViewRatio
) )
) )
}
}
@ -442,19 +456,24 @@ class CropScaleImageViewController: OWSViewController {
buttonRow . autoPinEdge ( toSuperviewEdge : . bottom , withInset : buttonBottomMargin )
buttonRow . autoPinEdge ( toSuperviewEdge : . bottom , withInset : buttonBottomMargin )
buttonRow . autoPinEdge ( . top , to : . bottom , of : contentView , withOffset : buttonTopMargin )
buttonRow . autoPinEdge ( . top , to : . bottom , of : contentView , withOffset : buttonTopMargin )
let cancelButton = createButton ( title : CommonStrings . cancelButton ,
action : #selector ( cancelPressed ) )
buttonRow . addSubview ( cancelButton )
cancelButton . autoPinEdge ( toSuperviewEdge : . top )
cancelButton . autoPinEdge ( toSuperviewEdge : . bottom )
cancelButton . autoPinEdge ( toSuperviewEdge : . left )
let doneButton = createButton ( title : NSLocalizedString ( " BUTTON_DONE " ,
let doneButton = createButton ( title : NSLocalizedString ( " BUTTON_DONE " ,
comment : " Label for generic done button. " ) ,
comment : " Label for generic done button. " ) ,
color : UIColor . ows_materialBlue ( ) ,
action : #selector ( donePressed ) )
action : #selector ( donePressed ) )
buttonRow . addSubview ( doneButton )
buttonRow . addSubview ( doneButton )
doneButton . autoPinEdge ( toSuperviewEdge : . top )
doneButton . autoPinEdge ( toSuperviewEdge : . top )
doneButton . autoPinEdge ( toSuperviewEdge : . bottom )
doneButton . autoPinEdge ( toSuperviewEdge : . bottom )
doneButton . auto HCenterInSuperview( )
doneButton . auto PinEdge( toSuperviewEdge : . right )
}
}
private func createButton ( title : String , color: UIColor , action: Selector ) -> UIButton {
private func createButton ( title : String , action: Selector ) -> UIButton {
let buttonFont = UIFont . ows_mediumFont ( withSize : ScaleFromIPhone5To7Plus ( 18 , 22 ) )
let buttonFont = UIFont . ows_mediumFont ( withSize : ScaleFromIPhone5To7Plus ( 18 , 22 ) )
let buttonCornerRadius = ScaleFromIPhone5To7Plus ( 4 , 5 )
let buttonWidth = ScaleFromIPhone5To7Plus ( 110 , 140 )
let buttonWidth = ScaleFromIPhone5To7Plus ( 110 , 140 )
let buttonHeight = ScaleFromIPhone5To7Plus ( 35 , 45 )
let buttonHeight = ScaleFromIPhone5To7Plus ( 35 , 45 )
@ -462,9 +481,6 @@ class CropScaleImageViewController: OWSViewController {
button . setTitle ( title , for : . normal )
button . setTitle ( title , for : . normal )
button . setTitleColor ( UIColor . white , for : . normal )
button . setTitleColor ( UIColor . white , for : . normal )
button . titleLabel ! . font = buttonFont
button . titleLabel ! . font = buttonFont
button . backgroundColor = color
button . layer . cornerRadius = buttonCornerRadius
button . clipsToBounds = true
button . addTarget ( self , action : action , for : . touchUpInside )
button . addTarget ( self , action : action , for : . touchUpInside )
button . autoSetDimension ( . width , toSize : buttonWidth )
button . autoSetDimension ( . width , toSize : buttonWidth )
button . autoSetDimension ( . height , toSize : buttonHeight )
button . autoSetDimension ( . height , toSize : buttonHeight )
@ -497,8 +513,8 @@ class CropScaleImageViewController: OWSViewController {
let context = UIGraphicsGetCurrentContext ( )
let context = UIGraphicsGetCurrentContext ( )
context ! . interpolationQuality = . high
context ! . interpolationQuality = . high
let imageViewFrame = imageRenderRect ( forDstSize : dstSizePixels )
let imageViewFrame = imageRenderRect ( forDstSize : dstSizePixels )
srcImage . draw ( in : imageViewFrame )
srcImage . draw ( in : imageViewFrame )
let scaledImage = UIGraphicsGetImageFromCurrentImageContext ( )
let scaledImage = UIGraphicsGetImageFromCurrentImageContext ( )
if scaledImage = = nil {
if scaledImage = = nil {