We are building an Android application that involves image editing. A few of the features include rotating an image and erasing part of the image.
We are using the following library: https://github.com/nimengbo/StickerView
We have successfully created a function to rotate and erase the image. However, when we tried to perform the following actions:
We found the following bug:
From the above image, the yellow line is the actual movement of the finger (straight down vertical across the sticker). But, the resulting erased path was found to be diagonal.
This problem only exists when the image is rotated. It does not exist when the image is not rotated.
After further debugging, we have a few assumptions from the above problems:
How can we ensure that the path is still referencing the right path on what the finger is touching even after being rotated?
Here is the code we have in our StickerView.java
class that extends ImageView
class.
onTouchEvent
@Override
public boolean onTouchEvent(MotionEvent event) {
int action = MotionEventCompat.getActionMasked(event);
float[] pointXY = new float[2];
pointXY = getAbsolutePosition(event.getX(0),event.getY(0));
float xPoint = pointXY[0];
float yPoint = pointXY[1];
switch (action) {
case MotionEvent.ACTION_DOWN:
// first touch
// if it is inside the image
if (isInBitmap(event)) {
// set isInSide to true
isInSide = true;
// if it is a scratch
if(doScratch){
// start creating the scratch path
mScratchPath = new Path();
mScratchPath.moveTo(xPoint, yPoint);
mScratchPath.lineTo(xPoint, yPoint);
paths.add(new Pair<Path, Paint>(mScratchPath, mScratchCurrentPaint));
}
}
break;
case MotionEvent.ACTION_MOVE:
// if two fingers touch and is not a scratch,
// then it means we can rotate / resize / pan
if (isPointerDown && !doScratch) {
// reset matrix
matrix.reset();
// get the center point
scaledImageCenterX = (mImageWidth * mScaleFactor) / 2 ;
scaledImageCenterY = (mImageHeight * mScaleFactor) / 2;
// ROTATE THE IMAGE !!!
matrix.postRotate(lastRotateDegree, scaledImageCenterX, scaledImageCenterY);
// done to call onDraw
invalidate();
}
break;
}
if (operationListener != null) {
operationListener.onEdit(this);
}
// if it is a scratch
if(doScratch){
// then for every point, create a scratch path
mScratchPath.lineTo(xPoint, yPoint);
invalidate();
}else{
mScaleDetector.onTouchEvent(event);
mRotateDetector.onTouchEvent(event);
mMoveDetector.onTouchEvent(event);
mShoveDetector.onTouchEvent(event);
}
return handled;
}
onDraw
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// if the image exists
if (mBitmap != null) {
// save canvas
canvas.save();
// if it is a scratch
if(doScratch){
// scratch the image
mFillCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
// Draw our surface, nice an pristine
final Drawable surface = mScratchSurface;
if(surface != null) {
surface.draw(mFillCanvas);
}
//Scratch the surface
if(paths != null) {
for (Pair<Path, Paint> p : paths) {
mFillCanvas.drawPath(p.first,p.second);
}
}
mBitmap = mFillCache;
}
canvas.drawBitmap(mBitmap, matrix, bitmapPaint);
canvas.restore();
}
}
getAbsolutePosition function
public float[] getAbsolutePosition(float Ax, float Ay) {
float[] mMatrixValues = new float[9];
matrix.getValues(mMatrixValues);
float x = mImageWidth - ((mMatrixValues[Matrix.MTRANS_X] - Ax) / mMatrixValues[Matrix.MSCALE_X]) - (mImageWidth - getTranslationX());
float y = mImageHeight - ((mMatrixValues[Matrix.MTRANS_Y] - Ay) / mMatrixValues[Matrix.MSCALE_X]) - (mImageHeight - getTranslationY());
return new float[] { x, y};
}
Answer: Most phone cameras are landscape, meaning if you take the photo in portrait, the resulting photos will be rotated 90 degrees. In this case, the camera software should populate the Exif data with the orientation that the photo should be viewed in.
Step 1 − Open Xcode→SingleViewApplication→name it RotateImage. Step 2 − Open Main. storyboard, add UIImageView and add 2 buttons as shown below name them ROTATE BY 90 DEGREES AND ROTATE BY 45 DEGREES.
You are rotating the entire view in the onDraw method. Not only this affects the performance (you are repeating the same transformation every time), it affects the outcome as you noticed since it rotates everything in the view, including the paths.
If for some reason you must do it this way (not recommended), then you need to rotate the paths in the opposite direction by the same amount. I strongly suggest you don't do that. Try to find a way around rotating the background inside the onDraw, instead. For example, whenever the background is rotated, you can create a new background (outside the onDraw method) and use that background in the onDraw method.
You need to apply the rotation matrics on your coordinates. Here theta is the angle by which the image is rotated.
Image source wikipedia
In code it will be something like this
int getAbsoluteX(int x, int y, double theta)
{
int x_new = (int)(x*Math.cos(theta) - y*Math.sin(theta));
return x_new;
}
int getAbsoluteY(int x, int y, double theta)
{
int y_new = (int)(x*Math.sin(theta) + y*Math.cos(theta));
return y_new;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With