NekoX/TMessagesProj/src/main/java/org/telegram/ui/Components/PhotoFilterBlurControl.java

513 lines
23 KiB
Java

/*
* This is the source code of Telegram for Android v. 5.x.x
* It is licensed under GNU GPL v. 2 or later.
* You should have received a copy of the license in this archive (see LICENSE).
*
* Copyright Nikolai Kudashov, 2013-2018.
*/
package org.telegram.ui.Components;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.RectF;
import android.os.Build;
import android.view.MotionEvent;
import android.widget.FrameLayout;
import org.telegram.messenger.AndroidUtilities;
import org.telegram.ui.BubbleActivity;
public class PhotoFilterBlurControl extends FrameLayout {
public interface PhotoFilterLinearBlurControlDelegate {
void valueChanged(Point centerPoint, float falloff, float size, float angle);
}
private final static float BlurInsetProximity = AndroidUtilities.dp(20);
private final static float BlurMinimumFalloff = 0.1f;
private final static float BlurMinimumDifference = 0.02f;
private final static float BlurViewCenterInset = AndroidUtilities.dp(30.0f);
private final static float BlurViewRadiusInset = AndroidUtilities.dp(30.0f);
private enum BlurViewActiveControl {
BlurViewActiveControlNone,
BlurViewActiveControlCenter,
BlurViewActiveControlInnerRadius,
BlurViewActiveControlOuterRadius,
BlurViewActiveControlWholeArea,
BlurViewActiveControlRotation
}
private final int GestureStateBegan = 1;
private final int GestureStateChanged = 2;
private final int GestureStateEnded = 3;
private final int GestureStateCancelled = 4;
private final int GestureStateFailed = 5;
private BlurViewActiveControl activeControl;
private Point startCenterPoint = new Point();
private float startDistance;
private float startRadius;
private Size actualAreaSize = new Size();
private Point centerPoint = new Point(0.5f, 0.5f);
private float falloff = 0.15f;
private float size = 0.35f;
private float angle;
private RectF arcRect = new RectF();
private float pointerStartX;
private float pointerStartY;
private float startPointerDistance;
private float pointerScale = 1;
private boolean isMoving;
private boolean isZooming;
private boolean checkForMoving = true;
private boolean checkForZooming;
private int type;
private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
private Paint arcPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private boolean inBubbleMode;
private PhotoFilterLinearBlurControlDelegate delegate;
public PhotoFilterBlurControl(Context context) {
super(context);
setWillNotDraw(false);
paint.setColor(0xffffffff);
arcPaint.setColor(0xffffffff);
arcPaint.setStrokeWidth(AndroidUtilities.dp(2));
arcPaint.setStyle(Paint.Style.STROKE);
inBubbleMode = context instanceof BubbleActivity;
}
public void setType(int blurType) {
type = blurType;
invalidate();
}
public void setDelegate(PhotoFilterLinearBlurControlDelegate delegate) {
this.delegate = delegate;
}
private float getDistance(MotionEvent event) {
if (event.getPointerCount() != 2) {
return 0;
}
float x1 = event.getX(0);
float y1 = event.getY(0);
float x2 = event.getX(1);
float y2 = event.getY(1);
return (float) Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
}
private float degreesToRadians(float degrees) {
return degrees * (float) Math.PI / 180.0f;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
int action = event.getActionMasked();
switch (action) {
case MotionEvent.ACTION_POINTER_DOWN:
case MotionEvent.ACTION_DOWN: {
if (event.getPointerCount() == 1) {
if (checkForMoving && !isMoving) {
float locationX = event.getX();
float locationY = event.getY();
Point centerPoint = getActualCenterPoint();
Point delta = new Point(locationX - centerPoint.x, locationY - centerPoint.y);
float radialDistance = (float) Math.sqrt(delta.x * delta.x + delta.y * delta.y);
float innerRadius = getActualInnerRadius();
float outerRadius = getActualOuterRadius();
boolean close = Math.abs(outerRadius - innerRadius) < BlurInsetProximity;
float innerRadiusOuterInset = close ? 0 : BlurViewRadiusInset;
float outerRadiusInnerInset = close ? 0 : BlurViewRadiusInset;
if (type == 0) {
float distance = (float) Math.abs(delta.x * Math.cos(degreesToRadians(angle) + Math.PI / 2) + delta.y * Math.sin(degreesToRadians(angle) + Math.PI / 2));
if (radialDistance < BlurViewCenterInset) {
isMoving = true;
} else if (distance > innerRadius - BlurViewRadiusInset && distance < innerRadius + innerRadiusOuterInset) {
isMoving = true;
} else if (distance > outerRadius - outerRadiusInnerInset && distance < outerRadius + BlurViewRadiusInset) {
isMoving = true;
} else if ((distance <= innerRadius - BlurViewRadiusInset) || distance >= outerRadius + BlurViewRadiusInset) {
isMoving = true;
}
} else if (type == 1) {
if (radialDistance < BlurViewCenterInset) {
isMoving = true;
} else if (radialDistance > innerRadius - BlurViewRadiusInset && radialDistance < innerRadius + innerRadiusOuterInset) {
isMoving = true;
} else if (radialDistance > outerRadius - outerRadiusInnerInset && radialDistance < outerRadius + BlurViewRadiusInset) {
isMoving = true;
}
}
checkForMoving = false;
if (isMoving) {
handlePan(GestureStateBegan, event);
}
}
} else {
if (isMoving) {
handlePan(GestureStateEnded, event);
checkForMoving = true;
isMoving = false;
}
if (event.getPointerCount() == 2) {
if (checkForZooming && !isZooming) {
handlePinch(GestureStateBegan, event);
isZooming = true;
}
} else {
handlePinch(GestureStateEnded, event);
checkForZooming = true;
isZooming = false;
}
}
break;
}
case MotionEvent.ACTION_POINTER_UP:
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP: {
if (isMoving) {
handlePan(GestureStateEnded, event);
isMoving = false;
} else if (isZooming) {
handlePinch(GestureStateEnded, event);
isZooming = false;
}
checkForMoving = true;
checkForZooming = true;
break;
}
case MotionEvent.ACTION_MOVE: {
if (isMoving) {
handlePan(GestureStateChanged, event);
} else if (isZooming) {
handlePinch(GestureStateChanged, event);
}
}
}
return true;
}
private void handlePan(int state, MotionEvent event) {
float locationX = event.getX();
float locationY = event.getY();
Point actualCenterPoint = getActualCenterPoint();
float dx = locationX - actualCenterPoint.x;
float dy = locationY - actualCenterPoint.y;
float radialDistance = (float) Math.sqrt(dx * dx + dy * dy);
float shorterSide = Math.min(actualAreaSize.width, actualAreaSize.height);
float innerRadius = shorterSide * falloff;
float outerRadius = shorterSide * size;
float distance = (float) Math.abs(dx * Math.cos(degreesToRadians(angle) + Math.PI / 2.0f) + dy * Math.sin(degreesToRadians(angle) + Math.PI / 2.0f));
switch (state) {
case GestureStateBegan: {
pointerStartX = event.getX();
pointerStartY = event.getY();
boolean close = Math.abs(outerRadius - innerRadius) < BlurInsetProximity;
float innerRadiusOuterInset = close ? 0 : BlurViewRadiusInset;
float outerRadiusInnerInset = close ? 0 : BlurViewRadiusInset;
if (type == 0) {
if (radialDistance < BlurViewCenterInset) {
activeControl = BlurViewActiveControl.BlurViewActiveControlCenter;
startCenterPoint = actualCenterPoint;
} else if (distance > innerRadius - BlurViewRadiusInset && distance < innerRadius + innerRadiusOuterInset) {
activeControl = BlurViewActiveControl.BlurViewActiveControlInnerRadius;
startDistance = distance;
startRadius = innerRadius;
} else if (distance > outerRadius - outerRadiusInnerInset && distance < outerRadius + BlurViewRadiusInset) {
activeControl = BlurViewActiveControl.BlurViewActiveControlOuterRadius;
startDistance = distance;
startRadius = outerRadius;
} else if (distance <= innerRadius - BlurViewRadiusInset || distance >= outerRadius + BlurViewRadiusInset) {
activeControl = BlurViewActiveControl.BlurViewActiveControlRotation;
}
} else if (type == 1) {
if (radialDistance < BlurViewCenterInset) {
activeControl = BlurViewActiveControl.BlurViewActiveControlCenter;
startCenterPoint = actualCenterPoint;
} else if (radialDistance > innerRadius - BlurViewRadiusInset && radialDistance < innerRadius + innerRadiusOuterInset) {
activeControl = BlurViewActiveControl.BlurViewActiveControlInnerRadius;
startDistance = radialDistance;
startRadius = innerRadius;
} else if (radialDistance > outerRadius - outerRadiusInnerInset && radialDistance < outerRadius + BlurViewRadiusInset) {
activeControl = BlurViewActiveControl.BlurViewActiveControlOuterRadius;
startDistance = radialDistance;
startRadius = outerRadius;
}
}
setSelected(true, true);
}
break;
case GestureStateChanged: {
if (type == 0) {
switch (activeControl) {
case BlurViewActiveControlCenter: {
float translationX = locationX - pointerStartX;
float translationY = locationY - pointerStartY;
Rect actualArea = new Rect((getWidth() - actualAreaSize.width) / 2, (Build.VERSION.SDK_INT >= 21 && !inBubbleMode ? AndroidUtilities.statusBarHeight : 0) + (getHeight() - actualAreaSize.height) / 2, actualAreaSize.width, actualAreaSize.height);
Point newPoint = new Point(Math.max(actualArea.x, Math.min(actualArea.x + actualArea.width, startCenterPoint.x + translationX)), Math.max(actualArea.y, Math.min(actualArea.y + actualArea.height, startCenterPoint.y + translationY)));
centerPoint = new Point((newPoint.x - actualArea.x) / actualAreaSize.width, ((newPoint.y - actualArea.y) + (actualAreaSize.width - actualAreaSize.height) / 2) / actualAreaSize.width);
}
break;
case BlurViewActiveControlInnerRadius: {
float d = distance - startDistance;
falloff = Math.min(Math.max(BlurMinimumFalloff, (startRadius + d) / shorterSide), size - BlurMinimumDifference);
}
break;
case BlurViewActiveControlOuterRadius: {
float d = distance - startDistance;
size = Math.max(falloff + BlurMinimumDifference, (startRadius + d) / shorterSide);
}
break;
case BlurViewActiveControlRotation: {
float translationX = locationX - pointerStartX;
float translationY = locationY - pointerStartY;
boolean clockwise = false;
boolean right = locationX > actualCenterPoint.x;
boolean bottom = locationY > actualCenterPoint.y;
final boolean b = Math.abs(translationY) > Math.abs(translationX);
if (!right && !bottom) {
if (b) {
if (translationY < 0) {
clockwise = true;
}
} else {
if (translationX > 0) {
clockwise = true;
}
}
} else if (right && !bottom) {
if (b) {
if (translationY > 0) {
clockwise = true;
}
} else {
if (translationX > 0) {
clockwise = true;
}
}
} else if (right && bottom) {
if (b) {
if (translationY > 0) {
clockwise = true;
}
} else {
if (translationX < 0) {
clockwise = true;
}
}
} else {
if (b) {
if (translationY < 0) {
clockwise = true;
}
} else {
if (translationX < 0) {
clockwise = true;
}
}
}
float d = (float) Math.sqrt(translationX * translationX + translationY * translationY);
angle += d * ((clockwise ? 1 : 0) * 2 - 1) / (float) Math.PI / 1.15f;
pointerStartX = locationX;
pointerStartY = locationY;
}
break;
default:
break;
}
} else if (type == 1) {
switch (activeControl) {
case BlurViewActiveControlCenter: {
float translationX = locationX - pointerStartX;
float translationY = locationY - pointerStartY;
Rect actualArea = new Rect((getWidth() - actualAreaSize.width) / 2, (Build.VERSION.SDK_INT >= 21 && !inBubbleMode ? AndroidUtilities.statusBarHeight : 0) + (getHeight() - actualAreaSize.height) / 2, actualAreaSize.width, actualAreaSize.height);
Point newPoint = new Point(Math.max(actualArea.x, Math.min(actualArea.x + actualArea.width, startCenterPoint.x + translationX)), Math.max(actualArea.y, Math.min(actualArea.y + actualArea.height, startCenterPoint.y + translationY)));
centerPoint = new Point((newPoint.x - actualArea.x) / actualAreaSize.width, ((newPoint.y - actualArea.y) + (actualAreaSize.width - actualAreaSize.height) / 2) / actualAreaSize.width);
}
break;
case BlurViewActiveControlInnerRadius: {
float d = radialDistance - startDistance;
falloff = Math.min(Math.max(BlurMinimumFalloff, (startRadius + d) / shorterSide), size - BlurMinimumDifference);
}
break;
case BlurViewActiveControlOuterRadius: {
float d = radialDistance - startDistance;
size = Math.max(falloff + BlurMinimumDifference, (startRadius + d) / shorterSide);
}
break;
default:
break;
}
}
invalidate();
if (delegate != null) {
delegate.valueChanged(centerPoint, falloff, size, degreesToRadians(angle) + (float) Math.PI / 2.0f);
}
}
break;
case GestureStateEnded:
case GestureStateCancelled:
case GestureStateFailed: {
activeControl = BlurViewActiveControl.BlurViewActiveControlNone;
setSelected(false, true);
}
break;
default:
break;
}
}
private void handlePinch(int state, MotionEvent event) {
switch (state) {
case GestureStateBegan: {
startPointerDistance = getDistance(event);
pointerScale = 1;
activeControl = BlurViewActiveControl.BlurViewActiveControlWholeArea;
setSelected(true, true);
}
case GestureStateChanged: {
float newDistance = getDistance(event);
pointerScale += (newDistance - startPointerDistance) / AndroidUtilities.density * 0.01f;
falloff = Math.max(BlurMinimumFalloff, falloff * pointerScale);
size = Math.max(falloff + BlurMinimumDifference, size * pointerScale);
pointerScale = 1;
startPointerDistance = newDistance;
invalidate();
if (delegate != null) {
delegate.valueChanged(centerPoint, falloff, size, degreesToRadians(angle) + (float) Math.PI / 2.0f);
}
}
break;
case GestureStateEnded:
case GestureStateCancelled:
case GestureStateFailed: {
activeControl = BlurViewActiveControl.BlurViewActiveControlNone;
setSelected(false, true);
}
break;
default:
break;
}
}
private void setSelected(boolean selected, boolean animated) {
/*if (animated) {
[UIView animateWithDuration:0.16f delay:0.0f options:UIViewAnimationOptionBeginFromCurrentState animations:^
{
self.alpha = selected ? 0.6f : 1.0f;
} completion:nil];
} else {
self.alpha = selected ? 0.6f : 1.0f;
}*/
}
public void setActualAreaSize(float width, float height) {
actualAreaSize.width = width;
actualAreaSize.height = height;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Point centerPoint = getActualCenterPoint();
float innerRadius = getActualInnerRadius();
float outerRadius = getActualOuterRadius();
canvas.translate(centerPoint.x, centerPoint.y);
if (type == 0) {
canvas.rotate(angle);
float space = AndroidUtilities.dp(6.0f);
float length = AndroidUtilities.dp(12.0f);
float thickness = AndroidUtilities.dp(1.5f);
for (int i = 0; i < 30; i++) {
canvas.drawRect(i * (length + space), -innerRadius, i * (length + space) + length, thickness - innerRadius, paint);
float left = -i * (length + space) - space - length;
float right = -i * (length + space) - space;
canvas.drawRect(left, -innerRadius, right, thickness - innerRadius, paint);
canvas.drawRect(i * (length + space), innerRadius, length + i * (length + space), thickness + innerRadius, paint);
canvas.drawRect(left, innerRadius, right, thickness + innerRadius, paint);
}
length = AndroidUtilities.dp(6.0f);
for (int i = 0; i < 64; i++) {
canvas.drawRect(i * (length + space), -outerRadius, length + i * (length + space), thickness - outerRadius, paint);
float left = -i * (length + space) - space - length;
float right = -i * (length + space) - space;
canvas.drawRect(left, -outerRadius, right, thickness - outerRadius, paint);
canvas.drawRect(i * (length + space), outerRadius, length + i * (length + space), thickness + outerRadius, paint);
canvas.drawRect(left, outerRadius, right, thickness + outerRadius, paint);
}
} else if (type == 1) {
float radSpace = 6.15f;
float radLen = 10.2f;
arcRect.set(-innerRadius, -innerRadius, innerRadius, innerRadius);
for (int i = 0; i < 22; i++) {
canvas.drawArc(arcRect, i * (radSpace + radLen), radLen, false, arcPaint);
}
radSpace = 2.02f;
radLen = 3.6f;
arcRect.set(-outerRadius, -outerRadius, outerRadius, outerRadius);
for (int i = 0; i < 64; i++) {
canvas.drawArc(arcRect, i * (radSpace + radLen), radLen, false, arcPaint);
}
}
canvas.drawCircle(0, 0, AndroidUtilities.dp(8), paint);
}
private Point getActualCenterPoint() {
return new Point((getWidth() - actualAreaSize.width) / 2 + centerPoint.x * actualAreaSize.width, (Build.VERSION.SDK_INT >= 21 && !inBubbleMode ? AndroidUtilities.statusBarHeight : 0) + (getHeight() - actualAreaSize.height) / 2 - (actualAreaSize.width - actualAreaSize.height) / 2 + centerPoint.y * actualAreaSize.width);
}
private float getActualInnerRadius() {
return (Math.min(actualAreaSize.width, actualAreaSize.height)) * falloff;
}
private float getActualOuterRadius() {
return (Math.min(actualAreaSize.width, actualAreaSize.height)) * size;
}
}