I have set a prescaled Bitmap as ImageView's source. Then I've read Matrix of an ImageView and shift Bitmap of an ImageView via matrix.postTranslate(shiftX, shiftY).
Now I want to zoom in / out and image while maintaining center of ImageView at the same point of Bitmap that was before scale.
If I try to zoom in an image with matrix.postScale(zoom, zoom), point that I want to maintain (blue dot) shifts to other place (purple dot).
I have tried several different ways to shift Bitmap back, but I cant get it to work correctly. I know initial Bitmap size, ImageView size, distances marked by doted line. Tried to calculate needed shift and use matrix.postTranslate(-zoomshiftX, -zoomshiftY) afterwards, but it doesn't shift correctly.
Even found out, that underlying Bitmap's pixel count doesnt change after matrix.postScale() function and tried matrix.postTranslate(-zoomshiftX/zoom, -zoomshiftY/zoom) - but still no luck.
How do I achieve such zoom?
Take a look at my question here regarding creating a zoomable ViewGroup. I've described code snippets from my end solution, and some of it might be helpful.
Extending RelativeLayout, and overriding dispatchDraw() to create a zoomable ViewGroup
Maybe this can be helpful:
If you got two fingers on screen, you can get the event of the two fingers and get the mid point:
PointF mid;
MotionEvent event;
float x = event.getX(0) - event.getX(1);
float y = event.getY(0) - event.getY(1);
mid.set(x / 2, y / 2);
Then you can set the scalation having the mid point in the center of the screen:
matrix.postScale(scale, scale, mid.x, mid.y);
Related
I am currently using this gitup touchimageview https://github.com/MikeOrtiz/TouchImageView library.... After zoom,based on the zoom percentage I want to map the longpress coordinates to original image coordinates. Any help will be appreciated
Try to get matrix of ImageView:
float[] values = new float[9];
getImageMatrix().getValues(values);
With this array, you have position of top-left corner in terms of image on indexes 2 and 5. For example, when values[2], values[5] is -10,-10 it means, that left top corner of screen is 10,10 pixel of image. So, you can get coordinate of long press:
float imageX = (pressX - values[2])/scale;
float imageY = (pressY - values[5])/scale;
Recently I work with zoomed images, and use this library: https://github.com/chrisbanes/PhotoView
I think its a bit better, it have some predifined touches with image coordinates, and is still improved (last commit ~2 months ago)
When drawing on view without zoom it works fine. See the screenshot
But when zooming and then drawing on view It is slightly up or down. See the screenshot
Here is my code for Custom View http://www.paste.org/78026 and for zoom http://www.paste.org/78027 and my xml http://www.paste.org/78028
Please can you tell me where I am wrong
Finally after lot of searching I found how to get relative X,Y when View is zoomed. It may be helpful to someone
// Get the values of the matrix
float[] values = new float[9];
matrix.getValues(values);
// values[2] and values[5] are the x,y coordinates of the top left corner of the drawable image, regardless of the zoom factor.
// values[0] and values[4] are the zoom factors for the image's width and height respectively. If you zoom at the same factor, these should both be the same value.
// event is the touch event for MotionEvent.ACTION_UP
float relativeX = (event.getX() - values[2]) / values[0];
float relativeY = (event.getY() - values[5]) / values[4];
I'm hoping someone can help me out. I'm making an image manipulation app, and I found I needed a better way to load in large images.
My plan, is to iterate through "hypothetical" pixels of an image (a "for loop" that covers width/height of the base image, so each iteration represents a pixel), scale/translate/rotate that pixels position relative to the view, then use this information to determine which pixels are being displayed in the view itself, then use a combination of BitmapRegionDecoder and BitmapFactory.Options to load in only the section of image that the output actually needs rather than a full (even if scaled) image.
So far I seem to have covered scale of the image and translation properly, but I can't seem to figure out how to calculate rotation. Since it's not a real Bitmap pixel I can't use Matrix.rotate =( Here is the image translations in the onDraw of the view, imgPosX and imgPosY hold the center point of the image:
m.setTranslate(-userImage.getWidth() / 2.0f, -userImage.getHeight() / 2.0f);
m.postScale(curScale, curScale);
m.postRotate(angle);
m.postTranslate(imgPosX, imgPosY);
mCanvas.drawBitmap(userImage.get(), m, paint);
and here is the math so far of how I'm trying to determine if an images pixel is on the screen:
for(int j = 0;j < imageHeight;j++) {
for(int i = 0;i < imageWidth;i++) {
//image starts completely center in view, assume image is original size for simplicity
//this is the original starting position for each pixel
int x = Math.round(((float) viewSizeWidth / 2.0f) - ((float) newImageWidth / 2.0f) + i);
int y = Math.round(((float) viewSizeHeight / 2.0f) - ((float) newImageHeight / 2.0f) + j);
//first we scale the pixel here, easy operation
x = Math.round(x * imageScale);
y = Math.round(y * imageScale);
//now we translate, we do this by determining how many pixels
//our images x/y coordinates have differed from it's original
//starting point, imgPosX and imgPosY in the view start in center
//of view
x = x + Math.round((imgPosX - ((float) viewSizeWidth / 2.0f)));
y = y + Math.round((imgPosY - ((float) viewSizeHeight / 2.0f)));
//TODO need rotation here
}
}
so, assuming my math up until rotation is correct (probably not but it appears to be working so far), how would I then calculate the rotation from that pixels position? I've tried other similar questions like:
Link 1
Link 2
Link 3
without using rotation the pixels I expect to actually be on the screen are represented (I made text file that outputs the results in 1's and 0's so I can have a visual representation of whats on the screen), but with the formula found in those questions the information isn't what is expected. (Scenario: I've rotated an image so only the top left corner is visible in the view. Using the info from Here to rotate the pixel, I should expect to see a triangular set of 1's in the upper left corner of the output file, but that's not the case)
So, how would I calculate a a pixels position after rotation without using the Android matrix? But still get the same results.
And if I've just messed it up entirely my apologies =( Any help would be appreciated, this project has gone on for so long and I want to finally be done lol
If you need any more information I will provide as much as I possibly can =) Thank you for your time
I realize this question is particularly difficult so I will be posting a bounty as soon as SO allows.
You do not need to create your own Matrix, use the existing one.
http://developer.android.com/reference/android/graphics/Matrix.html
You can map bitmap coordinates to screen coordinates by using
float[] coords = {x, y};
m.mapPoints(coords);
float sx = coords[0];
float sy = coords[1];
If you want to map screen to bitmap coordinates, you can create the inverse matrix
Matrix inverse = new Matrix(m);
inverse.inverse();
inverse.mapPoints(...)
I think your overall approach is going to be slow, as doing the pixel manipulation on the CU from Java has a lot of overhead. When drawing bitmaps normally, the pixel manipulation is done on the GPU.
I have an image (ImageView). I have certain areas on the image. When a use taps the screen, I want to detect which area was selected.
I have identified the area boundaries on the original image, but the x and y of MotionEvent are off.
I tried dip-to-pixel conversion (TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dipValue, metrics)), but they are still off. It certainly has to do with the screen size / density / etc., but how exactly to get the pixel value of the touch event for the original image? (or, vice-versa - how to convert the original image coordinates to something that is comparable with the x and y of the motion event)
I managed to calculate it using the right and bottom coordinates of the ImageView (pic) compared to the real image size (568x1207).
float xCoef = 568f / pic.getRight();
float yCoef = 1207f / pic.getBottom();
float x = event.getX() * xCoef;
float y = event.getY() * yCoef;
I have a canvas that I am using a ScaleGestureDetector to zoom in my android app. Here is the code I have so far:
//SCALING --------------------------------------------------
//get center of the viewport
int centerX = xLoc+((int)(screenWidth/2*scaleFactor));
int centerY = yLoc+((int)(screenHeight/2*scaleFactor));
scaleFactor /= detector.getScaleFactor();
// Don't let the object get too small or too large.
scaleFactor = Math.max(1.0f, Math.min(scaleFactor, maxScaleFactor));
//Make sure the viewport is repositioned
xLoc = centerX-((int)(screenWidth/2*scaleFactor));
yLoc = centerY-((int)(screenHeight/2*scaleFactor));
//-----------------------------------------------------------
This works great to zoom in and out of my canvas. The xLoc and yLoc represent the top left corner of my viewport in relation to the entire image I am drawing a portion of. The problem I have with this code though, is that It zooms in to the center of the viewport. I would like to be able to use detector.getFocuxX() and detector.getFocusY() to zoom into the
focus point, just like the pinch to zoom works in the android browser.
Basically I need to adjust the position of the viewport (xLoc and yLoc) so that it gives the appearance of zooming into the focus point of the scale gesture.
I cannot figure this part out. If anyone has an idea of how I can do this without using the canvas matrix transformations (which I have seen used in many of the topics I found before posting this) I would really appreciate it! Even if you could point me to someone else who has done something similar.
A single onScale function call does not provide enough information to implement scaling around apparent focus point. You will need to preserve values of getFocusX() and getFocusY() from a previous onScale call and use them to influence your scaling in the next call.