From 871dceac3a6b84c39999bfcc7aac5675e30f656a Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Fri, 1 Mar 2019 11:21:19 -0500 Subject: [PATCH] Improve palette interactions. --- .../ImageEditorBrushViewController.swift | 2 +- .../ImageEditor/ImageEditorPaletteView.swift | 98 ++++++++----------- .../ImageEditorTextViewController.swift | 4 +- 3 files changed, 44 insertions(+), 60 deletions(-) diff --git a/SignalMessaging/Views/ImageEditor/ImageEditorBrushViewController.swift b/SignalMessaging/Views/ImageEditor/ImageEditorBrushViewController.swift index afa220829..63a7590a7 100644 --- a/SignalMessaging/Views/ImageEditor/ImageEditorBrushViewController.swift +++ b/SignalMessaging/Views/ImageEditor/ImageEditorBrushViewController.swift @@ -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 diff --git a/SignalMessaging/Views/ImageEditor/ImageEditorPaletteView.swift b/SignalMessaging/Views/ImageEditor/ImageEditorPaletteView.swift index a4040e774..2e7689604 100644 --- a/SignalMessaging/Views/ImageEditor/ImageEditorPaletteView.swift +++ b/SignalMessaging/Views/ImageEditor/ImageEditorPaletteView.swift @@ -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 = 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 { diff --git a/SignalMessaging/Views/ImageEditor/ImageEditorTextViewController.swift b/SignalMessaging/Views/ImageEditor/ImageEditorTextViewController.swift index a865aa5e3..f243a6f90 100644 --- a/SignalMessaging/Views/ImageEditor/ImageEditorTextViewController.swift +++ b/SignalMessaging/Views/ImageEditor/ImageEditorTextViewController.swift @@ -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() }