NekoX/TMessagesProj/src/main/java/org/telegram/ui/Components/Crop/CropView.java

1002 lines
34 KiB
Java

package org.telegram.ui.Components.Crop;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PointF;
import android.graphics.RectF;
import android.os.Build;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewTreeObserver;
import android.widget.FrameLayout;
import android.widget.ImageView;
import org.telegram.messenger.AndroidUtilities;
import org.telegram.messenger.FileLoader;
import org.telegram.messenger.FileLog;
import org.telegram.messenger.ImageLoader;
import org.telegram.messenger.LocaleController;
import org.telegram.messenger.MediaController;
import org.telegram.messenger.R;
import org.telegram.messenger.SharedConfig;
import org.telegram.messenger.VideoEditedInfo;
import org.telegram.ui.ActionBar.AlertDialog;
import org.telegram.ui.Components.PaintingOverlay;
import java.io.File;
import java.io.FileOutputStream;
import java.util.ArrayList;
import static android.graphics.Paint.FILTER_BITMAP_FLAG;
public class CropView extends FrameLayout implements CropAreaView.AreaViewListener, CropGestureDetector.CropGestureListener {
private static final float EPSILON = 0.00001f;
private static final int RESULT_SIDE = 1280;
private static final float MAX_SCALE = 30.0f;
private View backView;
private CropAreaView areaView;
private ImageView imageView;
private Matrix presentationMatrix;
private Matrix overlayMatrix;
private PaintingOverlay paintingOverlay;
private RectF previousAreaRect;
private RectF initialAreaRect;
private float rotationStartScale;
private CropRectangle tempRect;
private Matrix tempMatrix;
private Bitmap bitmap;
private boolean freeform;
private float bottomPadding;
private boolean animating;
private CropGestureDetector detector;
private boolean hasAspectRatioDialog;
private class CropState {
private float width;
private float height;
private float x;
private float y;
private float scale;
private float minimumScale;
private float baseRotation;
private float orientation;
private float rotation;
private Matrix matrix;
private CropState(Bitmap bitmap, int bRotation) {
width = bitmap.getWidth();
height = bitmap.getHeight();
x = 0.0f;
y = 0.0f;
scale = 1.0f;
baseRotation = bRotation;
rotation = 0.0f;
matrix = new Matrix();
}
private void updateBitmap(Bitmap bitmap, int rotation) {
float ps = width / bitmap.getWidth();
scale *= ps;
width = bitmap.getWidth();
height = bitmap.getHeight();
updateMinimumScale();
float[] values = new float[9];
matrix.getValues(values);
matrix.reset();
matrix.postScale(scale, scale);
matrix.postTranslate(values[2], values[5]);
updateMatrix();
}
private boolean hasChanges() {
return Math.abs(x) > EPSILON || Math.abs(y) > EPSILON || Math.abs(scale - minimumScale) > EPSILON
|| Math.abs(rotation) > EPSILON || Math.abs(orientation) > EPSILON;
}
private float getWidth() {
return width;
}
private float getHeight() {
return height;
}
private float getOrientedWidth() {
return (orientation + baseRotation) % 180 != 0 ? height : width;
}
private float getOrientedHeight() {
return (orientation + baseRotation) % 180 != 0 ? width : height;
}
private void translate(float x, float y) {
this.x += x;
this.y += y;
matrix.postTranslate(x, y);
}
private float getX() {
return x;
}
private float getY() {
return y;
}
private void scale(float s, float pivotX, float pivotY) {
scale *= s;
matrix.postScale(s, s, pivotX, pivotY);
}
private float getScale() {
return scale;
}
private float getMinimumScale() {
return minimumScale;
}
private void rotate(float angle, float pivotX, float pivotY) {
rotation += angle;
matrix.postRotate(angle, pivotX, pivotY);
}
private float getRotation() {
return rotation;
}
private float getOrientation() {
return orientation + baseRotation;
}
private float getOrientationOnly() {
return orientation;
}
private float getBaseRotation() {
return baseRotation;
}
private void reset(CropAreaView areaView, float orient, boolean freeform) {
matrix.reset();
x = 0.0f;
y = 0.0f;
rotation = 0.0f;
orientation = orient;
updateMinimumScale();
scale = minimumScale;
matrix.postScale(scale, scale);
}
private void updateMinimumScale() {
float w = (orientation + baseRotation) % 180 != 0 ? height : width;
float h = (orientation + baseRotation) % 180 != 0 ? width : height;
if (freeform) {
minimumScale = areaView.getCropWidth() / w;
} else {
float wScale = areaView.getCropWidth() / w;
float hScale = areaView.getCropHeight() / h;
minimumScale = Math.max(wScale, hScale);
}
}
private void getConcatMatrix(Matrix toMatrix) {
toMatrix.postConcat(matrix);
}
private Matrix getMatrix() {
Matrix m = new Matrix();
m.set(matrix);
return m;
}
}
private CropState state;
public interface CropViewListener {
void onChange(boolean reset);
void onAspectLock(boolean enabled);
}
private CropViewListener listener;
public CropView(Context context) {
super(context);
previousAreaRect = new RectF();
initialAreaRect = new RectF();
presentationMatrix = new Matrix();
overlayMatrix = new Matrix();
tempRect = new CropRectangle();
tempMatrix = new Matrix();
animating = false;
backView = new View(context);
backView.setBackgroundColor(0xff000000);
backView.setVisibility(INVISIBLE);
addView(backView);
imageView = new ImageView(context);
imageView.setDrawingCacheEnabled(true);
imageView.setScaleType(ImageView.ScaleType.MATRIX);
addView(imageView);
detector = new CropGestureDetector(context);
detector.setOnGestureListener(this);
areaView = new CropAreaView(context);
areaView.setListener(this);
addView(areaView);
}
@Override
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
boolean result = super.drawChild(canvas, child, drawingTime);
if (child == imageView && paintingOverlay != null) {
canvas.save();
canvas.setMatrix(overlayMatrix);
paintingOverlay.draw(canvas);
canvas.restore();
}
return result;
}
public boolean isReady() {
return !detector.isScaling() && !detector.isDragging() && !areaView.isDragging();
}
public void setListener(CropViewListener l) {
listener = l;
}
public void setBottomPadding(float value) {
bottomPadding = value;
areaView.setBottomPadding(value);
}
public void setAspectRatio(float ratio) {
areaView.setActualRect(ratio);
}
public void setBitmap(Bitmap b, int rotation, boolean fform, boolean same, PaintingOverlay overlay) {
freeform = fform;
paintingOverlay = overlay;
if (b == null) {
bitmap = null;
state = null;
imageView.setImageDrawable(null);
} else {
bitmap = b;
if (state == null || !same) {
state = new CropState(bitmap, rotation);
imageView.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
reset();
imageView.getViewTreeObserver().removeOnPreDrawListener(this);
return false;
}
});
} else {
state.updateBitmap(bitmap, rotation);
}
imageView.setImageBitmap(bitmap);
}
}
public void willShow() {
areaView.setFrameVisibility(true);
areaView.setDimVisibility(true);
areaView.invalidate();
}
public void hideBackView() {
backView.setVisibility(INVISIBLE);
}
public void showBackView() {
backView.setVisibility(VISIBLE);
}
public void setFreeform(boolean fform) {
areaView.setFreeform(fform);
freeform = fform;
}
public void show() {
backView.setVisibility(VISIBLE);
imageView.setVisibility(VISIBLE);
areaView.setDimVisibility(true);
areaView.setFrameVisibility(true);
areaView.invalidate();
}
public void hide() {
backView.setVisibility(INVISIBLE);
imageView.setVisibility(INVISIBLE);
areaView.setDimVisibility(false);
areaView.setFrameVisibility(false);
areaView.invalidate();
}
public void reset() {
areaView.resetAnimator();
areaView.setBitmap(bitmap, state.getBaseRotation() % 180 != 0, freeform);
areaView.setLockedAspectRatio(freeform ? 0.0f : 1.0f);
state.reset(areaView, 0, freeform);
areaView.getCropRect(initialAreaRect);
updateMatrix();
resetRotationStartScale();
if (listener != null) {
listener.onChange(true);
listener.onAspectLock(false);
}
}
public void updateMatrix() {
presentationMatrix.reset();
presentationMatrix.postTranslate(-state.getWidth() / 2, -state.getHeight() / 2);
presentationMatrix.postRotate(state.getOrientation());
state.getConcatMatrix(presentationMatrix);
presentationMatrix.postTranslate(areaView.getCropCenterX(), areaView.getCropCenterY());
imageView.setImageMatrix(presentationMatrix);
overlayMatrix.reset();
if (state.getBaseRotation() == 90 || state.getBaseRotation() == 270) {
overlayMatrix.postTranslate(-state.getHeight() / 2, -state.getWidth() / 2);
} else {
overlayMatrix.postTranslate(-state.getWidth() / 2, -state.getHeight() / 2);
}
overlayMatrix.postRotate(state.getOrientationOnly());
state.getConcatMatrix(overlayMatrix);
overlayMatrix.postTranslate(areaView.getCropCenterX(), areaView.getCropCenterY());
invalidate();
}
private void fillAreaView(RectF targetRect, boolean allowZoomOut) {
final float[] currentScale = new float[]{1.0f};
float scale = Math.max(targetRect.width() / areaView.getCropWidth(),
targetRect.height() / areaView.getCropHeight());
float newScale = state.getScale() * scale;
boolean ensureFit = false;
if (newScale > MAX_SCALE) {
scale = MAX_SCALE / state.getScale();
ensureFit = true;
}
float statusBarHeight = (Build.VERSION.SDK_INT >= 21 ? AndroidUtilities.statusBarHeight : 0);
final float x = (targetRect.centerX() - imageView.getWidth() / 2) / areaView.getCropWidth() * state.getOrientedWidth();
final float y = (targetRect.centerY() - (imageView.getHeight() - bottomPadding + statusBarHeight) / 2) / areaView.getCropHeight() * state.getOrientedHeight();
final float targetScale = scale;
final boolean animEnsureFit = ensureFit;
ValueAnimator animator = ValueAnimator.ofFloat(0.0f, 1.0f);
animator.addUpdateListener(animation -> {
float value = (Float) animation.getAnimatedValue();
float deltaScale = (1.0f + ((targetScale - 1.0f) * value)) / currentScale[0];
currentScale[0] *= deltaScale;
state.scale(deltaScale, x, y);
updateMatrix();
});
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
if (animEnsureFit)
fitContentInBounds(false, false, true);
}
});
areaView.fill(targetRect, animator, true);
initialAreaRect.set(targetRect);
}
private float fitScale(RectF contentRect, float scale, float ratio) {
float scaledW = contentRect.width() * ratio;
float scaledH = contentRect.height() * ratio;
float scaledX = (contentRect.width() - scaledW) / 2.0f;
float scaledY = (contentRect.height() - scaledH) / 2.0f;
contentRect.set(contentRect.left + scaledX, contentRect.top + scaledY,
contentRect.left + scaledX + scaledW, contentRect.top + scaledY + scaledH);
return scale * ratio;
}
private void fitTranslation(RectF contentRect, RectF boundsRect, PointF translation, float radians) {
float frameLeft = boundsRect.left;
float frameTop = boundsRect.top;
float frameRight = boundsRect.right;
float frameBottom = boundsRect.bottom;
if (contentRect.left > frameLeft) {
frameRight += contentRect.left - frameLeft;
frameLeft = contentRect.left;
}
if (contentRect.top > frameTop) {
frameBottom += contentRect.top - frameTop;
frameTop = contentRect.top;
}
if (contentRect.right < frameRight) {
frameLeft += contentRect.right - frameRight;
}
if (contentRect.bottom < frameBottom) {
frameTop += contentRect.bottom - frameBottom;
}
float deltaX = boundsRect.centerX() - (frameLeft + boundsRect.width() / 2.0f);
float deltaY = boundsRect.centerY() - (frameTop + boundsRect.height() / 2.0f);
float xCompX = (float) (Math.sin(Math.PI / 2 - radians) * deltaX);
float xCompY = (float) (Math.cos(Math.PI / 2 - radians) * deltaX);
float yCompX = (float) (Math.cos(Math.PI / 2 + radians) * deltaY);
float yCompY = (float) (Math.sin(Math.PI / 2 + radians) * deltaY);
translation.set(translation.x + xCompX + yCompX, translation.y + xCompY + yCompY);
}
public RectF calculateBoundingBox(float w, float h, float rotation) {
RectF result = new RectF(0, 0, w, h);
Matrix m = new Matrix();
m.postRotate(rotation, w / 2.0f, h / 2.0f);
m.mapRect(result);
return result;
}
public float scaleWidthToMaxSize(RectF sizeRect, RectF maxSizeRect) {
float w = maxSizeRect.width();
float h = (float) Math.floor(w * sizeRect.height() / sizeRect.width());
if (h > maxSizeRect.height()) {
h = maxSizeRect.height();
w = (float) Math.floor(h * sizeRect.width() / sizeRect.height());
}
return w;
}
private static class CropRectangle {
float[] coords = new float[8];
CropRectangle() {
}
void setRect(RectF rect) {
coords[0] = rect.left;
coords[1] = rect.top;
coords[2] = rect.right;
coords[3] = rect.top;
coords[4] = rect.right;
coords[5] = rect.bottom;
coords[6] = rect.left;
coords[7] = rect.bottom;
}
void applyMatrix(Matrix m) {
m.mapPoints(coords);
}
void getRect(RectF rect) {
rect.set(coords[0], coords[1], coords[2], coords[7]);
}
}
private void fitContentInBounds(boolean allowScale, boolean maximize, boolean animated) {
fitContentInBounds(allowScale, maximize, animated, false);
}
private void fitContentInBounds(final boolean allowScale, final boolean maximize, final boolean animated, final boolean fast) {
if (state == null) {
return;
}
float boundsW = areaView.getCropWidth();
float boundsH = areaView.getCropHeight();
float contentW = state.getOrientedWidth();
float contentH = state.getOrientedHeight();
float rotation = state.getRotation();
float radians = (float) Math.toRadians(rotation);
RectF boundsRect = calculateBoundingBox(boundsW, boundsH, rotation);
RectF contentRect = new RectF(0.0f, 0.0f, contentW, contentH);
float initialX = (boundsW - contentW) / 2.0f;
float initialY = (boundsH - contentH) / 2.0f;
float scale = state.getScale();
tempRect.setRect(contentRect);
Matrix matrix = state.getMatrix();
matrix.preTranslate(initialX / scale, initialY / scale);
tempMatrix.reset();
tempMatrix.setTranslate(contentRect.centerX(), contentRect.centerY());
tempMatrix.setConcat(tempMatrix, matrix);
tempMatrix.preTranslate(-contentRect.centerX(), -contentRect.centerY());
tempRect.applyMatrix(tempMatrix);
tempMatrix.reset();
tempMatrix.preRotate(-rotation, contentW / 2.0f, contentH / 2.0f);
tempRect.applyMatrix(tempMatrix);
tempRect.getRect(contentRect);
PointF targetTranslation = new PointF(state.getX(), state.getY());
float targetScale = scale;
if (!contentRect.contains(boundsRect)) {
if (allowScale && (boundsRect.width() > contentRect.width() || boundsRect.height() > contentRect.height())) {
float ratio = boundsRect.width() / scaleWidthToMaxSize(boundsRect, contentRect);
targetScale = fitScale(contentRect, scale, ratio);
}
fitTranslation(contentRect, boundsRect, targetTranslation, radians);
} else if (maximize && rotationStartScale > 0) {
float ratio = boundsRect.width() / scaleWidthToMaxSize(boundsRect, contentRect);
float newScale = state.getScale() * ratio;
if (newScale < rotationStartScale)
ratio = 1.0f;
targetScale = fitScale(contentRect, scale, ratio);
fitTranslation(contentRect, boundsRect, targetTranslation, radians);
}
float dx = targetTranslation.x - state.getX();
float dy = targetTranslation.y - state.getY();
if (animated) {
final float animScale = targetScale / scale;
final float animDX = dx;
final float animDY = dy;
if (Math.abs(animScale - 1.0f) < EPSILON
&& Math.abs(animDX) < EPSILON && Math.abs(animDY) < EPSILON) {
return;
}
animating = true;
final float[] currentValues = new float[]{1.0f, 0.0f, 0.0f};
ValueAnimator animator = ValueAnimator.ofFloat(0.0f, 1.0f);
animator.addUpdateListener(animation -> {
float value = (Float) animation.getAnimatedValue();
float deltaX = animDX * value - currentValues[1];
currentValues[1] += deltaX;
float deltaY = animDY * value - currentValues[2];
currentValues[2] += deltaY;
state.translate(deltaX * currentValues[0], deltaY * currentValues[0]);
float deltaScale = (1.0f + ((animScale - 1.0f) * value)) / currentValues[0];
currentValues[0] *= deltaScale;
state.scale(deltaScale, 0, 0);
updateMatrix();
});
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
animating = false;
if (!fast)
fitContentInBounds(allowScale, maximize, animated, true);
}
});
animator.setInterpolator(areaView.getInterpolator());
animator.setDuration(fast ? 100 : 200);
animator.start();
} else {
state.translate(dx, dy);
state.scale(targetScale / scale, 0, 0);
updateMatrix();
}
}
public void rotate90Degrees() {
if (state == null) {
return;
}
areaView.resetAnimator();
resetRotationStartScale();
float orientation = (state.getOrientation() - state.getBaseRotation() - 90.0f) % 360;
boolean fform = freeform;
if (freeform && areaView.getLockAspectRatio() > 0) {
areaView.setLockedAspectRatio(1.0f / areaView.getLockAspectRatio());
areaView.setActualRect(areaView.getLockAspectRatio());
fform = false;
} else {
areaView.setBitmap(bitmap, (orientation + state.getBaseRotation()) % 180 != 0, freeform);
}
state.reset(areaView, orientation, fform);
updateMatrix();
if (listener != null)
listener.onChange(orientation == 0 && areaView.getLockAspectRatio() == 0);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (animating) {
return true;
}
boolean result = false;
if (areaView.onTouchEvent(event))
return true;
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
onScrollChangeBegan();
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
onScrollChangeEnded();
break;
}
try {
result = detector.onTouchEvent(event);
} catch (Exception ignore) {
}
return result;
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return true;
}
@Override
public void onAreaChangeBegan() {
areaView.getCropRect(previousAreaRect);
resetRotationStartScale();
if (listener != null) {
listener.onChange(false);
}
}
@Override
public void onAreaChange() {
areaView.setGridType(CropAreaView.GridType.MAJOR, false);
float x = previousAreaRect.centerX() - areaView.getCropCenterX();
float y = previousAreaRect.centerY() - areaView.getCropCenterY();
state.translate(x, y);
updateMatrix();
areaView.getCropRect(previousAreaRect);
fitContentInBounds(true, false, false);
}
@Override
public void onAreaChangeEnded() {
areaView.setGridType(CropAreaView.GridType.NONE, true);
fillAreaView(areaView.getTargetRectToFill(), false);
}
public void onDrag(float dx, float dy) {
if (animating) {
return;
}
state.translate(dx, dy);
updateMatrix();
}
public void onFling(float startX, float startY, float velocityX, float velocityY) {
}
public void onScrollChangeBegan() {
if (animating) {
return;
}
areaView.setGridType(CropAreaView.GridType.MAJOR, true);
resetRotationStartScale();
if (listener != null) {
listener.onChange(false);
}
}
public void onScrollChangeEnded() {
areaView.setGridType(CropAreaView.GridType.NONE, true);
fitContentInBounds(true, false, true);
}
public void onScale(float scale, float x, float y) {
if (animating) {
return;
}
float newScale = state.getScale() * scale;
if (newScale > MAX_SCALE)
scale = MAX_SCALE / state.getScale();
float statusBarHeight = (Build.VERSION.SDK_INT >= 21 ? AndroidUtilities.statusBarHeight : 0);
float pivotX = (x - imageView.getWidth() / 2) / areaView.getCropWidth() * state.getOrientedWidth();
float pivotY = (y - (imageView.getHeight() - bottomPadding - statusBarHeight) / 2) / areaView.getCropHeight() * state.getOrientedHeight();
state.scale(scale, pivotX, pivotY);
updateMatrix();
}
public void onRotationBegan() {
areaView.setGridType(CropAreaView.GridType.MINOR, false);
if (rotationStartScale < 0.00001f) {
rotationStartScale = state.getScale();
}
}
public void onRotationEnded() {
areaView.setGridType(CropAreaView.GridType.NONE, true);
}
private void resetRotationStartScale() {
rotationStartScale = 0.0f;
}
public void setRotation(float angle) {
float deltaAngle = angle - state.getRotation();
state.rotate(deltaAngle, 0, 0);
fitContentInBounds(true, true, false);
}
@SuppressLint("WrongThread")
private void editBitmap(String path, Bitmap b, Canvas canvas, Bitmap canvasBitmap, Bitmap.CompressFormat format, float scale, ArrayList<VideoEditedInfo.MediaEntity> entities, boolean clear) {
try {
if (clear) {
canvasBitmap.eraseColor(0);
}
if (b == null) {
b = BitmapFactory.decodeFile(path);
}
float sc = Math.max(b.getWidth(), b.getHeight()) / (float) Math.max(bitmap.getWidth(), bitmap.getHeight());
Matrix matrix = new Matrix();
matrix.postTranslate(-b.getWidth() / 2, -b.getHeight() / 2);
matrix.postScale(1.0f / sc, 1.0f / sc);
matrix.postRotate(state.getOrientationOnly());
state.getConcatMatrix(matrix);
matrix.postScale(scale, scale);
matrix.postTranslate(canvasBitmap.getWidth() / 2, canvasBitmap.getHeight() / 2);
canvas.drawBitmap(b, matrix, new Paint(FILTER_BITMAP_FLAG));
FileOutputStream stream = new FileOutputStream(new File(path));
canvasBitmap.compress(format, 87, stream);
stream.close();
if (entities != null && !entities.isEmpty()) {
float[] point = new float[4];
float newScale = 1.0f / sc * scale * state.scale;
for (int a = 0, N = entities.size(); a < N; a++) {
VideoEditedInfo.MediaEntity entity = entities.get(a);
point[0] = entity.x * b.getWidth() + entity.viewWidth * entity.scale / 2;
point[1] = entity.y * b.getHeight() + entity.viewHeight * entity.scale / 2;
point[2] = entity.textViewX * b.getWidth();
point[3] = entity.textViewY * b.getHeight();
matrix.mapPoints(point);
float widthScale = b.getWidth() / (float) canvasBitmap.getWidth();
newScale *= widthScale;
if (entity.type == 0) {
entity.viewWidth = entity.viewHeight = canvasBitmap.getWidth() / 2;
} else if (entity.type == 1) {
entity.fontSize = canvasBitmap.getWidth() / 9;
}
entity.scale *= newScale;
entity.x = (point[0] - entity.viewWidth * entity.scale / 2) / canvasBitmap.getWidth();
entity.y = (point[1] - entity.viewHeight * entity.scale / 2) / canvasBitmap.getHeight();
entity.textViewX = point[2] / canvasBitmap.getWidth();
entity.textViewY = point[3] / canvasBitmap.getHeight();
entity.width = entity.viewWidth * entity.scale / canvasBitmap.getWidth();
entity.height = entity.viewHeight * entity.scale / canvasBitmap.getHeight();
entity.textViewWidth = entity.viewWidth / (float) canvasBitmap.getWidth();
entity.textViewHeight = entity.viewHeight / (float) canvasBitmap.getHeight();
entity.rotation -= (state.getRotation() + state.getOrientationOnly()) * (Math.PI / 180);
}
}
b.recycle();
} catch (Throwable e) {
FileLog.e(e);
}
}
public Bitmap getResult(MediaController.MediaEditState editState) {
if (state == null || !state.hasChanges() && state.getBaseRotation() < EPSILON && freeform) {
return bitmap;
}
RectF cropRect = new RectF();
areaView.getCropRect(cropRect);
RectF sizeRect = new RectF(0, 0, RESULT_SIDE, RESULT_SIDE);
float w = scaleWidthToMaxSize(cropRect, sizeRect);
int width = (int) Math.ceil(w);
int height = (int) (Math.ceil(width / areaView.getAspectRatio()));
float scale = width / areaView.getCropWidth();
Bitmap resultBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Matrix matrix = new Matrix();
matrix.postTranslate(-state.getWidth() / 2, -state.getHeight() / 2);
matrix.postRotate(state.getOrientation());
state.getConcatMatrix(matrix);
matrix.postScale(scale, scale);
matrix.postTranslate(width / 2, height / 2);
Canvas canvas = new Canvas(resultBitmap);
if (editState.paintPath != null) {
editBitmap(editState.paintPath, null, canvas, resultBitmap, Bitmap.CompressFormat.PNG, scale, null, false);
if (!editState.paintPath.equals(editState.fullPaintPath)) {
editBitmap(editState.fullPaintPath, null, canvas, resultBitmap, Bitmap.CompressFormat.PNG, scale, editState.mediaEntities, true);
}
}
if (editState.filterPath != null) {
if (editState.croppedPath == null) {
File f = new File(FileLoader.getDirectory(FileLoader.MEDIA_DIR_CACHE), SharedConfig.getLastLocalId() + "_temp.jpg");
editState.croppedPath = f.getAbsolutePath();
}
Bitmap b = ImageLoader.loadBitmap(editState.getPath(), null, bitmap.getWidth(), bitmap.getHeight(), true);
editBitmap(editState.croppedPath, b, canvas, resultBitmap, Bitmap.CompressFormat.JPEG, scale, null, false);
}
canvas.drawBitmap(bitmap, matrix, new Paint(FILTER_BITMAP_FLAG));
return resultBitmap;
}
private void setLockedAspectRatio(float aspectRatio) {
areaView.setLockedAspectRatio(aspectRatio);
RectF targetRect = new RectF();
areaView.calculateRect(targetRect, aspectRatio);
fillAreaView(targetRect, true);
if (listener != null) {
listener.onChange(false);
listener.onAspectLock(true);
}
}
public void showAspectRatioDialog() {
if (state == null) {
return;
}
if (areaView.getLockAspectRatio() > 0) {
areaView.setLockedAspectRatio(0);
if (listener != null) {
listener.onAspectLock(false);
}
return;
}
if (hasAspectRatioDialog) {
return;
}
hasAspectRatioDialog = true;
String[] actions = new String[8];
final Integer[][] ratios = new Integer[][]{
new Integer[]{3, 2},
new Integer[]{5, 3},
new Integer[]{4, 3},
new Integer[]{5, 4},
new Integer[]{7, 5},
new Integer[]{16, 9}
};
actions[0] = LocaleController.getString("CropOriginal", R.string.CropOriginal);
actions[1] = LocaleController.getString("CropSquare", R.string.CropSquare);
int i = 2;
for (Integer[] ratioPair : ratios) {
if (areaView.getAspectRatio() > 1.0f) {
actions[i] = String.format("%d:%d", ratioPair[0], ratioPair[1]);
} else {
actions[i] = String.format("%d:%d", ratioPair[1], ratioPair[0]);
}
i++;
}
AlertDialog dialog = new AlertDialog.Builder(getContext())
.setItems(actions, (dialog12, which) -> {
hasAspectRatioDialog = false;
switch (which) {
case 0: {
float w = state.getBaseRotation() % 180 != 0 ? state.getHeight() : state.getWidth();
float h = state.getBaseRotation() % 180 != 0 ? state.getWidth() : state.getHeight();
setLockedAspectRatio(w / h);
}
break;
case 1: {
setLockedAspectRatio(1.0f);
}
break;
default: {
Integer[] ratioPair = ratios[which - 2];
if (areaView.getAspectRatio() > 1.0f) {
setLockedAspectRatio(ratioPair[0] / (float) ratioPair[1]);
} else {
setLockedAspectRatio(ratioPair[1] / (float) ratioPair[0]);
}
}
break;
}
})
.create();
dialog.setCanceledOnTouchOutside(true);
dialog.setOnCancelListener(dialog1 -> hasAspectRatioDialog = false);
dialog.show();
}
public void updateLayout() {
float w = areaView.getCropWidth();
if (w == 0) {
return;
}
if (state != null) {
areaView.calculateRect(initialAreaRect, state.getWidth() / state.getHeight());
areaView.setActualRect(areaView.getAspectRatio());
areaView.getCropRect(previousAreaRect);
float ratio = areaView.getCropWidth() / w;
state.scale(ratio, 0, 0);
updateMatrix();
}
}
public float getCropLeft() {
return areaView.getCropLeft();
}
public float getCropTop() {
return areaView.getCropTop();
}
public float getCropWidth() {
return areaView.getCropWidth();
}
public float getCropHeight() {
return areaView.getCropHeight();
}
}