diff --git a/app_pojavlauncher/build.gradle b/app_pojavlauncher/build.gradle
index d53d80841a..49d842fd65 100644
--- a/app_pojavlauncher/build.gradle
+++ b/app_pojavlauncher/build.gradle
@@ -201,7 +201,6 @@ dependencies {
implementation 'com.github.Mathias-Boulay:android_gamepad_remapper:a0fe7e72f2'
implementation 'com.github.Mathias-Boulay:virtual-joystick-android:2e7aa25e50'
-
// implementation 'com.intuit.sdp:sdp-android:1.0.5'
// implementation 'com.intuit.ssp:ssp-android:1.0.5'
diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/imgcropper/ImageCropperView.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/imgcropper/ImageCropperView.java
index 43c4f07dc5..d2068ab0d4 100644
--- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/imgcropper/ImageCropperView.java
+++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/imgcropper/ImageCropperView.java
@@ -7,6 +7,7 @@
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Rect;
+import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.MotionEvent;
@@ -18,17 +19,23 @@
import top.defaults.checkerboarddrawable.CheckerboardDrawable;
public class ImageCropperView extends AppCompatImageView {
- private final Matrix mZoomMatrix = new Matrix();
- private final Matrix mTranslateMatrix = new Matrix();
+
private final Matrix mTranslateInverse = new Matrix();
+ private final Matrix mTranslateMatrix = new Matrix();
+ private final Matrix mPrescaleMatrix = new Matrix();
private final Matrix mImageMatrix = new Matrix();
+ private final Matrix mZoomMatrix = new Matrix();
+ private final RectF mSelectionHighlight = new RectF();
+ private final Rect mSelectionRect = new Rect();
+ public boolean horizontalLock, verticalLock;
private float mLastTouchX, mLastTouchY;
+ private float mHighlightThickness;
private float mLastDistance = -1f;
private int mLastTrackedPointer;
- private final Rect mSelectionFrameRect = new Rect();
- private Paint mSelectionPaint;
private float mSelectionPadding;
private Bitmap mOriginalBitmap;
+ private Paint mSelectionPaint;
+
public ImageCropperView(Context context) {
super(context);
init();
@@ -48,9 +55,13 @@ private void init() {
setBackground(new CheckerboardDrawable.Builder().build());
setScaleType(ScaleType.MATRIX);
mSelectionPadding = Tools.dpToPx(24);
+ mHighlightThickness = Tools.dpToPx(1);
mSelectionPaint = new Paint();
mSelectionPaint.setColor(Color.RED);
- mSelectionPaint.setStrokeWidth(Tools.dpToPx(1));
+ mSelectionPaint.setStrokeWidth(mHighlightThickness);
+ // Divide the thickness by 2 since we will be needing only half of it for
+ // rect highlight correction.
+ mHighlightThickness /= 2;
mSelectionPaint.setStyle(Paint.Style.STROKE);
}
@@ -123,7 +134,7 @@ public boolean onTouchEvent(MotionEvent event) {
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
- canvas.drawRect(mSelectionFrameRect, mSelectionPaint);
+ canvas.drawRect(mSelectionHighlight, mSelectionPaint);
}
private int findPointerIndex(MotionEvent event, int id) {
@@ -134,6 +145,8 @@ private int findPointerIndex(MotionEvent event, int id) {
}
private void pan(int panX, int panY) {
+ if(horizontalLock) panX = 0;
+ if(verticalLock) panY = 0;
mTranslateMatrix.postTranslate(panX, panY);
computeImageMatrix();
}
@@ -151,9 +164,9 @@ private void zoom(float zoomLevel, float midpointX, float midpointY) {
}
private void computeImageMatrix() {
- mImageMatrix.reset();
- mImageMatrix.preConcat(mTranslateMatrix);
- mImageMatrix.preConcat(mZoomMatrix);
+ mImageMatrix.set(mPrescaleMatrix);
+ mImageMatrix.postConcat(mZoomMatrix);
+ mImageMatrix.postConcat(mTranslateMatrix);
setImageMatrix(mImageMatrix);
}
@@ -169,10 +182,10 @@ public Bitmap crop(int targetMaxSide) {
// By inverting the matrix we will effectively "divide" our rectangle by it, thus getting
// its two points on the bitmap's surface. Math be cool indeed.
float[] src = new float[] {
- mSelectionFrameRect.left,
- mSelectionFrameRect.top,
- mSelectionFrameRect.right,
- mSelectionFrameRect.bottom
+ mSelectionRect.left,
+ mSelectionRect.top,
+ mSelectionRect.right,
+ mSelectionRect.bottom
};
float[] dst = new float[4];
imageInverse.mapPoints(dst, 0, src, 0, 2);
@@ -215,18 +228,54 @@ protected void onSizeChanged(int w, int h, int oldw, int oldh) {
// Calculate the corners of the new selection frame. It should always appear at the center of the view.
int centerShiftX = (w - lesserDimension) / 2;
int centerShiftY = (h - lesserDimension) / 2;
- mSelectionFrameRect.left = centerShiftX;
- mSelectionFrameRect.top = centerShiftY;
- mSelectionFrameRect.right = centerShiftX + lesserDimension;
- mSelectionFrameRect.bottom = centerShiftY + lesserDimension;
+ mSelectionRect.left = centerShiftX;
+ mSelectionRect.top = centerShiftY;
+ mSelectionRect.right = centerShiftX + lesserDimension;
+ mSelectionRect.bottom = centerShiftY + lesserDimension;
+ // Adjust the selection highlight rectnagle to be bigger than the selection area
+ // by the highlight thickness, to make sure that the entire inside of the selection highlight
+ // will fit into the image
+ mSelectionHighlight.left = mSelectionRect.left - mHighlightThickness;
+ mSelectionHighlight.top = mSelectionRect.top + mHighlightThickness;
+ mSelectionHighlight.right = mSelectionRect.right + mHighlightThickness;
+ mSelectionHighlight.bottom = mSelectionRect.bottom - mHighlightThickness;
+ computePrescaleMatrix();
}
- private void reset() {
+ /**
+ * Computes a prescale matrix.
+ * This matrix basically centers the source image in the selection rect.
+ * Mainly intended for convenience of implementing a "Reset" button sometime in the future
+ */
+ private void computePrescaleMatrix() {
+ if(mOriginalBitmap == null) return;
+ int selectionRectWidth = mSelectionRect.width();
+ int selectionRectHeight = mSelectionRect.height();
+ int imageWidth = mOriginalBitmap.getWidth();
+ int imageHeight = mOriginalBitmap.getHeight();
+ float hRatio = (float)selectionRectWidth / imageWidth ;
+ float vRatio = (float)selectionRectHeight / imageHeight;
+ float ratio = Math.min (hRatio, vRatio);
+ float centerShift_x = (selectionRectWidth - imageWidth*ratio) / 2;
+ float centerShift_y = (selectionRectWidth - imageHeight*ratio) / 2;
+ centerShift_x += mSelectionRect.left;
+ centerShift_y += mSelectionRect.top;
+ mPrescaleMatrix.reset();
+ mPrescaleMatrix.postScale(ratio, ratio);
+ mPrescaleMatrix.postTranslate(centerShift_x, centerShift_y);
+ computeImageMatrix();
+ }
+
+ public void resetTransforms() {
mTranslateMatrix.reset();
mZoomMatrix.reset();
mTranslateInverse.reset();
- mImageMatrix.reset();
- setImageMatrix(mImageMatrix);
+ computeImageMatrix();
+ }
+
+ private void reset() {
+ computePrescaleMatrix();
+ resetTransforms();
mLastDistance = -1f;
}
diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/utils/CropperUtils.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/utils/CropperUtils.java
index 35c0361dac..8e66744a95 100644
--- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/utils/CropperUtils.java
+++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/utils/CropperUtils.java
@@ -4,7 +4,8 @@
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
-import android.util.TypedValue;
+import android.view.View;
+import android.widget.ToggleButton;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
@@ -31,14 +32,14 @@ private static void openCropperDialog(Context context, Uri selectedUri,
AlertDialog.Builder builder = new AlertDialog.Builder(context)
.setTitle(R.string.cropper_title);
builder.setView(R.layout.dialog_cropper);
- // The default cropper options
- //cropImageView.setPadding(padding, padding, padding, 0);
builder.setPositiveButton(android.R.string.ok, null);
builder.setNegativeButton(android.R.string.cancel, null);
AlertDialog dialog = builder.show();
ImageCropperView cropImageView = dialog.findViewById(R.id.crop_dialog_view);
+ assert cropImageView != null;
try (InputStream inputStream = context.getContentResolver().openInputStream(selectedUri)){
cropImageView.loadBitmap(BitmapFactory.decodeStream(inputStream));
+ bindViews(dialog, cropImageView);
dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener(v->{
dialog.dismiss();
cropperListener.onCropped(cropImageView.crop(256));
@@ -49,17 +50,22 @@ private static void openCropperDialog(Context context, Uri selectedUri,
}
}
-
- private static int getDialogPadding(Context context) {
- TypedValue typedPadding = new TypedValue();
- if(context.getTheme().resolveAttribute(R.attr.dialogPreferredPadding, typedPadding, true)) {
- return TypedValue.complexToDimensionPixelSize(
- typedPadding.data,
- context.getResources().getDisplayMetrics()
- );
- }else {
- return 0;
- }
+ private static void bindViews(AlertDialog alertDialog, ImageCropperView imageCropperView) {
+ ToggleButton horizontalLock = alertDialog.findViewById(R.id.crop_dialog_hlock);
+ ToggleButton verticalLock = alertDialog.findViewById(R.id.crop_dialog_vlock);
+ View reset = alertDialog.findViewById(R.id.crop_dialog_reset);
+ assert horizontalLock != null;
+ assert verticalLock != null;
+ assert reset != null;
+ horizontalLock.setOnClickListener(v->
+ imageCropperView.horizontalLock = horizontalLock.isChecked()
+ );
+ verticalLock.setOnClickListener(v->
+ imageCropperView.verticalLock = verticalLock.isChecked()
+ );
+ reset.setOnClickListener(v->
+ imageCropperView.resetTransforms()
+ );
}
@SuppressWarnings("unchecked")
diff --git a/app_pojavlauncher/src/main/res/layout/dialog_cropper.xml b/app_pojavlauncher/src/main/res/layout/dialog_cropper.xml
index 3fce4e5d87..abfa4db5e7 100644
--- a/app_pojavlauncher/src/main/res/layout/dialog_cropper.xml
+++ b/app_pojavlauncher/src/main/res/layout/dialog_cropper.xml
@@ -1,10 +1,48 @@
-
+ android:layout_height="wrap_content">
+
-
\ No newline at end of file
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="8dp"
+ android:layout_marginEnd="8dp"
+ android:layout_marginBottom="8dp"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent" />
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app_pojavlauncher/src/main/res/values/strings.xml b/app_pojavlauncher/src/main/res/values/strings.xml
index 717401de8f..1689c72010 100644
--- a/app_pojavlauncher/src/main/res/values/strings.xml
+++ b/app_pojavlauncher/src/main/res/values/strings.xml
@@ -372,4 +372,7 @@
Downloading game metadata (%s)
Downloading game files… (%d/%d, %.2f MB)
Select image region
+ V. lock
+ H. lock
+ Reset