Improve palette interactions.
This commit is contained in:
parent
1a159d4d70
commit
871dceac3a
|
@ -59,7 +59,7 @@ public class ImageEditorBrushViewController: OWSViewController {
|
|||
paletteView.delegate = self
|
||||
self.view.addSubview(paletteView)
|
||||
paletteView.autoVCenterInSuperview()
|
||||
paletteView.autoPinEdge(toSuperviewEdge: .trailing, withInset: 20)
|
||||
paletteView.autoPinEdge(toSuperviewEdge: .trailing, withInset: 0)
|
||||
|
||||
self.view.isUserInteractionEnabled = true
|
||||
|
||||
|
|
|
@ -97,9 +97,8 @@ public class ImageEditorPaletteView: UIView {
|
|||
owsFailDebug("Missing image.")
|
||||
}
|
||||
addSubview(imageView)
|
||||
// We use an invisible margin to expand the hot area of
|
||||
// this control.
|
||||
let margin: CGFloat = 8
|
||||
// We use an invisible margin to expand the hot area of this control.
|
||||
let margin: CGFloat = 20
|
||||
imageView.autoPinEdgesToSuperviewEdges(with: UIEdgeInsets(top: margin, left: margin, bottom: margin, right: margin))
|
||||
|
||||
selectionWrapper.layoutCallback = { [weak self] (view) in
|
||||
|
@ -144,14 +143,49 @@ public class ImageEditorPaletteView: UIView {
|
|||
}
|
||||
|
||||
private func value(for palettePhase: CGFloat) -> ImageEditorColor {
|
||||
guard let image = imageView.image else {
|
||||
owsFailDebug("Missing image.")
|
||||
// We find the color in the palette's gradient that corresponds
|
||||
// to the "phase".
|
||||
//
|
||||
// 0 = top of gradient, first color.
|
||||
// 1 = bottom of gradient, last color.
|
||||
struct GradientSegment {
|
||||
let color0: UIColor
|
||||
let color1: UIColor
|
||||
let palettePhase0: CGFloat
|
||||
let palettePhase1: CGFloat
|
||||
}
|
||||
var segments = [GradientSegment]()
|
||||
let segmentCount = ImageEditorColor.gradientUIColors.count - 1
|
||||
var prevColor: UIColor?
|
||||
for color in ImageEditorColor.gradientUIColors {
|
||||
if let color0 = prevColor {
|
||||
let index = CGFloat(segments.count)
|
||||
let color1 = color
|
||||
let palettePhase0: CGFloat = index / CGFloat(segmentCount)
|
||||
let palettePhase1: CGFloat = (index + 1) / CGFloat(segmentCount)
|
||||
segments.append(GradientSegment(color0: color0, color1: color1, palettePhase0: palettePhase0, palettePhase1: palettePhase1))
|
||||
}
|
||||
prevColor = color
|
||||
}
|
||||
var bestSegment = segments.first
|
||||
for segment in segments {
|
||||
if palettePhase >= segment.palettePhase0 {
|
||||
bestSegment = segment
|
||||
}
|
||||
}
|
||||
guard let segment = bestSegment else {
|
||||
owsFailDebug("Couldn't find matching segment.")
|
||||
return ImageEditorColor.defaultColor()
|
||||
}
|
||||
guard let color = image.color(atLocation: CGPoint(x: CGFloat(image.size.width) * 0.5, y: CGFloat(image.size.height) * palettePhase)) else {
|
||||
owsFailDebug("Missing color.")
|
||||
guard palettePhase >= segment.palettePhase0,
|
||||
palettePhase <= segment.palettePhase1 else {
|
||||
owsFailDebug("Invalid segment.")
|
||||
return ImageEditorColor.defaultColor()
|
||||
}
|
||||
let segmentPhase = palettePhase.inverseLerp(segment.palettePhase0, segment.palettePhase1).clamp01()
|
||||
// If CAGradientLayer doesn't do naive RGB color interpolation,
|
||||
// this won't be WYSIWYG.
|
||||
let color = segment.color0.blend(with: segment.color1, alpha: segmentPhase)
|
||||
return ImageEditorColor(color: color, palettePhase: palettePhase)
|
||||
}
|
||||
|
||||
|
@ -170,7 +204,6 @@ public class ImageEditorPaletteView: UIView {
|
|||
|
||||
@objc
|
||||
func didTouch(gesture: UIGestureRecognizer) {
|
||||
Logger.verbose("gesture: \(NSStringForUIGestureRecognizerState(gesture.state))")
|
||||
switch gesture.state {
|
||||
case .began, .changed, .ended:
|
||||
break
|
||||
|
@ -201,55 +234,6 @@ public class ImageEditorPaletteView: UIView {
|
|||
|
||||
// MARK: -
|
||||
|
||||
extension UIImage {
|
||||
func color(atLocation locationPoints: CGPoint) -> UIColor? {
|
||||
guard let cgImage = cgImage else {
|
||||
owsFailDebug("Missing cgImage.")
|
||||
return nil
|
||||
}
|
||||
guard let dataProvider = cgImage.dataProvider else {
|
||||
owsFailDebug("Could not create dataProvider.")
|
||||
return nil
|
||||
}
|
||||
guard let pixelData = dataProvider.data else {
|
||||
owsFailDebug("dataProvider has no data.")
|
||||
return nil
|
||||
}
|
||||
let bytesPerPixel: Int = cgImage.bitsPerPixel / 8
|
||||
guard bytesPerPixel == 4 else {
|
||||
owsFailDebug("Invalid bytesPerPixel: \(bytesPerPixel).")
|
||||
return nil
|
||||
}
|
||||
let imageWidth: Int = cgImage.width
|
||||
let imageHeight: Int = cgImage.height
|
||||
guard imageWidth > 0,
|
||||
imageHeight > 0 else {
|
||||
owsFailDebug("Invalid image size.")
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert the location from points to pixels and clamp to the image bounds.
|
||||
let xPixels: Int = Int(round(locationPoints.x * self.scale)).clamp(0, imageWidth - 1)
|
||||
let yPixels: Int = Int(round(locationPoints.y * self.scale)).clamp(0, imageHeight - 1)
|
||||
let dataLength = (pixelData as Data).count
|
||||
let data: UnsafePointer<UInt8> = CFDataGetBytePtr(pixelData)
|
||||
let index: Int = (imageWidth * yPixels + xPixels) * bytesPerPixel
|
||||
guard index >= 0, index < dataLength else {
|
||||
owsFailDebug("Invalid index.")
|
||||
return nil
|
||||
}
|
||||
|
||||
let red = CGFloat(data[index]) / CGFloat(255.0)
|
||||
let green = CGFloat(data[index+1]) / CGFloat(255.0)
|
||||
let blue = CGFloat(data[index+2]) / CGFloat(255.0)
|
||||
let alpha = CGFloat(data[index+3]) / CGFloat(255.0)
|
||||
|
||||
return UIColor(red: red, green: green, blue: blue, alpha: alpha)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
// The most permissive GR possible. Accepts any number of touches in any locations.
|
||||
private class PaletteGestureRecognizer: UIGestureRecognizer {
|
||||
|
||||
|
|
|
@ -187,9 +187,9 @@ public class ImageEditorTextViewController: OWSViewController, VAlignTextViewDel
|
|||
paletteView.delegate = self
|
||||
self.view.addSubview(paletteView)
|
||||
paletteView.autoAlignAxis(.horizontal, toSameAxisOf: textView)
|
||||
paletteView.autoPinEdge(toSuperviewEdge: .trailing, withInset: 20)
|
||||
paletteView.autoPinEdge(toSuperviewEdge: .trailing, withInset: 0)
|
||||
// This will determine the text view's size.
|
||||
paletteView.autoPinEdge(.leading, to: .trailing, of: textView, withOffset: 8)
|
||||
paletteView.autoPinEdge(.leading, to: .trailing, of: textView, withOffset: 0)
|
||||
|
||||
updateNavigationBar()
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue