Android - Rotate a part of an Image - android

I basically need to rotate 90 degres a small part of an ImageView (e.g):
In the Image above, I'd like to rotate the 4 so it displays correctly. Only the 4, the rest should remain vertical as it is.
Is there a way I can achieve it?
By Implementing the method suggested by MikeM. I'm getting the following result.
As you can see there are two major things I need to fix:
The rotated square is working, although in the wring position. How do I found out the exact coordinates of the 4
The background of the image, has been changed to black. It used to be transparent

If you know, or can figure, the coordinates and dimensions of the region you'd like to rotate, then the process is relatively straightforward.
Load the image as a mutable Bitmap.
Create a second, rotated Bitmap of the desired region from the original.
Create a Canvas on the original Bitmap.
Clear the clipped area, if necessary.
Draw the rotated region back onto the original.
In the following example, it's assumed that the region's coordinates (x, y) and dimensions (width, height) are already known.
// Options necessary to create a mutable Bitmap from the decode
BitmapFactory.Options options = new BitmapFactory.Options();
options.inMutable = true;
// Load the Bitmap, here from a resource drawable
Bitmap bmp = BitmapFactory.decodeResource(getResources(), resId, options);
// Create a Matrix for 90° counterclockwise rotation
Matrix matrix = new Matrix();
matrix.postRotate(-90);
// Create a rotated Bitmap from the desired region of the original
Bitmap region = Bitmap.createBitmap(bmp, x, y, width, height, matrix, false);
// Create our Canvas on the original Bitmap
Canvas canvas = new Canvas(bmp);
// Create a Paint to clear the clipped region to transparent
Paint paint = new Paint();
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
// Clear the region
canvas.drawRect(x, y, x + width, y + height, paint);
// Draw the rotated Bitmap back to the original,
// concentric with the region's original coordinates
canvas.drawBitmap(region, x + width / 2f - height / 2f, y + height / 2f - width / 2f, null);
// Cleanup the secondary Bitmap
region.recycle();
// The resulting image is in bmp
imageView.setImageBitmap(bmp);
To address the concerns in the edit:
The rotated region's figures in the original example were based on the image with the long axis vertical. The image in the edit had been rotated to vertical after that region had been modified.
The black background was due to having inserted the resulting image into MediaStore, which saves images in the JPEG format, which does not support transparency.

Related

Crop bottom portion of a bitmap programmatically

I have a bitmap taken from a camera. I want to crop the image so it only leaves the bottom portion of it. The cropped image should be 80% less the height of the original bitmap, so I want only the 20% of the bottom part starting from the left edge.
I'm doing this explicitly in the code without any Android cropping intent whatsoever.
An image to visualize what I want to achieve:
I've managed to crop the top part of the bitmap by using this code:
final Bitmap toBeCropped = BitmapFactory.decodeFile(mFile.getPath());
final BitmapFactory.Options bitmapOptions = new BitmapFactory.Options();
bitmapOptions.inTargetDensity = 1;
toBeCropped.setDensity(Bitmap.DENSITY_NONE);
int fromHere = (int) (toBeCropped.getHeight() * 0.2);
Bitmap croppedBitmap = Bitmap.createBitmap(toBeCropped, 0, 0, toBeCropped.getWidth(), fromHere);
mPreviewHalf.setImageBitmap(croppedBitmap);
But I couldn't find a way to start the cropping 80% from the top. I'm thinking of getting the y-coordinate of the Bitmap, so that I could crop any image sizes and always get the bottom portion only. But can anyone point to me how do I get this coordinate from a bitmap? Or do I have to take it from the layout itself?
I am not familiar with operations on Bitmaps but from inspecting your code and looking at the API my guess would be that you need to specify the y coordinates on the following line to match the starting point:
Bitmap croppedBitmap = Bitmap.createBitmap(toBeCropped, 0, "here", toBeCropped.getWidth(), fromHere);
So my guess would be something like the following:
Bitmap croppedBitmap = Bitmap.createBitmap(toBeCropped, 0, (toBeCropped.getHeight() * 0.8), toBeCropped.getWidth(), fromHere);
in this case fromHere will define the number of rows you want to crop not the starting point (which is 20% of the total as you have pointed out)
This is how I do it:
topcutoff is what you want to cut of on top of the image and buttomcutoff on the buttom (if needed)
height = height - topcutoff;
height = height - bottomcutoff;
croppedBitmap = Bitmap.createBitmap(croppedBitmap, 0, topcutoff, width, height);
Basically you just set a startpoint (topcutoff) from where to begin displaying the bitmap. In your case this would be the position after 80% of your bitmap.
This might also explain some things: Google Bitmap Documentation
"int: The y coordinate of the first pixel in source", so where you want to begin displaying your image.

Android ImageView get bitmap after zoom using setImageMatrix

In Android I have an ImageView which user can manipulate the position and zoom level in the UI.
Is it possible to get the zoomed image (changed image) from the ImageView instead of original one.
Here are the methods I tried but no use.
imageView.buildDrawingCache(true);
Bitmap bmp = imageView.getDrawingCache(true);
I can get the Matrix but don't know how to use it.
Matrix m = imageView.getImageMatrix();
The zoom effect is applied at the Canvas level. So, what you can do is get the current Canvas augmentations and then draw your Bitmap into a new one with the changes.
Matrix transformMatrix = imageView.getImageMatrix()
Bitmap original = bmp;
Bitmap adjusted = Bitmap.createBitmap(original.getWidth(),
original.getHeight(),
original.getConfig());
Canvas canvas = new Canvas(adjusted);
canvas.setMatrix(transformMatrix);
canvas.drawBitmap(original, 0, 0, null);
//at this point adjusted bitmap contains the zoomed image
When you get a Matrix from an ImageView, make changes to t, then use imageView.setImageMatrix(m) apply the changes back to the ImageView. You will want to look into using one of the postScale() matrix methods to make a change in the size of image rendered.

Android Canvas not drawing more than screen dimensions

My App have a feature that let's the user capture photo and add drawing on that photo.
All the photo's are re-sized to exactly 900 x 900.
To allow the user add drawing to the image. I keep a transparent image over the original image and do the drawing on the transparent image. Drawing is done using canvas.
But when drawing in a device that has 720 x 480 (height x width). If i create a 900 x 900 transparent image and draw a line from 0,0 to 900,900, canvas only draws line from 0,0 to 480,480.
Below is the respective portion of the code:
Preparing Canvas:
holder = getHolder();
if (holder.getSurface().isValid()) {
Canvas canvas = holder.lockCanvas();
canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
/* original image is 900 x 900 */
overlayBitmap = Bitmap.createBitmap(originalImage.getWidth(), originalImage.getHeight(), originalImage.getConfig());
canvas.setBitmap(overlayBitmap);
}
Drawing Line:
canvas.drawLine(0, 0, 900, 900, paint);
I have no idea why i am having this issue. It is because of using canvas?? Is there any work around? Any help is highly appreciated :-)
After some more reading about canvas and also help from this post i was able to fix the issue.
The issue was in the canvas clip rectangle. It was (0,0,480,480) by default as device display was 720 x 480 i guess? So whatever was on the bitmap was always clipped down to 480 x 480.
Later i modified my code like this:
holder = getHolder();
if (holder.getSurface().isValid()) {
Canvas canvas = holder.lockCanvas();
canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
/* original image is 900 x 900 */
overlayBitmap = Bitmap.createBitmap(originalImage.getWidth(), originalImage.getHeight(), originalImage.getConfig());
canvas.setBitmap(overlayBitmap);
/* set extended clip rectangle for the larger target bitmap */
Rect clipRect = canvas.getClipBounds();
clipRect.set(0, 0, image.getWidth(), image.getHeight());
canvas.clipRect(clipRect, Region.Op.REPLACE);
}
After replacing clip rectangle size with image size everything worked just fine.
Just because a photo is 900x900, that doesn't mean that it's exactly those many pixels when displayed on the device screen. Device screens can have very different sizes, and when an image (or any view for that matter) is told to expand its with to fit the screen, or measured in dp (device independent pixels), the actual number of device pixels will vary depending on the device screen.
Your code needs to be sensitive to these differences. When you do your drawing, don't assume the size of the Canvas of a view. Instead, ask the view how big it is, and perform all the drawing based on the actual measured size.

Set android bitmap size keeping aspect ratio based on minimum height and width

I'm currently loading an image that can be either landscape or portrait.
I'm then wanting to resize the bitmap to draw directly onto a canvas for a full screen image.
I need keep the aspect ratio but have the image not fit to the screen but crop off any image that's bigger than the screen.
I can resize it and fit it to the screen with the following:
Matrix m = new Matrix();
m.setRectToRect(new RectF(0, 0, b.getWidth(), b.getHeight()), new RectF(0, 0, MyWallpaperService.this.width, MyWallpaperService.this.height), Matrix.ScaleToFit.CENTER);
b = Bitmap.createBitmap(b, 0, 0, b.getWidth(), b.getHeight(), m, true);
but I don't want to have the black bars at the top/bottom or sides, but I can't think of the routine to resize correctly for this.
As you are drawing the image onto the canvas yourself the first step is to calculate the dimensions required for the final image based on the screen size and the original image aspect ratio.
Then use the static function createScaledBitmap from the Bitmap class to resize your bitmap to match the calculated dimensions. Make sure you only call this function once and store the bitmap for use in the drawing routine.
Finally draw the bitmap so only the area of the bitmap you want to see is on the screen.
A more memory friendly approach is to add another step to crop the image using Bitmap.createBitmap before calling Bitmap.createScaledBitmap. This reduces the chance of encountering memory issues caused by the user selecting a source image that is thin and long.

Rotating a bitmap using matrix

While rotating a bitmap using matrix, vertex is not stable..
Matrix matrix = new Matrix();
matrix.postRotate(mDegree,100,100);
mCompasstemp = Bitmap.createBitmap(mCompPic, 0, 0, mCompPic.getWidth(), mCompPic.getHeight(), matrix, true);
mCompassHud.setImageBitmap(mCompasstemp);
Output of my code is like
-bitmap will rotate.
-vertex of my bitmap is not stable.
-Bitmap is resizing
I need disable image resizing and make the rotation stable.Can you please suggest a solution for this?
Rather than creating your new Bitmap directly from the original, another (more straight-forward, imho) option is to create the resultant Bitmap, create a Canvas with that Bitmap, then do your rotation/translation/scaling on the Canvas and draw the original Bitmap onto the new Bitmap via the Canvas.
Basically, you're looking, then, at:
scaledImage = Bitmap.createBitmap (croppedWidth, croppedHeight, Bitmap.Config.ARGB_8888);
Canvas offscreenCanvas = new Canvas (scaledImage);
Matrix matrix = new Matrix();
matrix.setRotate (rotations, centreX, centreY);
matrix.postScale(scaleX, scaleY);
offscreenCanvas.setMatrix (matrix);
offscreenCanvas.drawBitmap (pickedImage, 0, 0, new Paint(Paint.DITHER_FLAG));
Not sure if this is what your looking for but it might help.
Android uses its built in compatibility features to scale and render a bitmap appropriately for screens with different pixel densities. There are two methods of scaling, pre-scaling and auto-scaling.
It will pre-scale bitmaps' from resources and auto-scales when the bitmap is being drawn internally (which is what your doing be using createBitmap).
Go to http://developer.android.com/guide/practices/screens_support.html and check under:
4.Use density and/or size-specific resources:
Pre-scaling and auto-scaling of bitmaps and nine-patches
I have tried this code, and the rotate is stable at the center of the bitmap
matrix.reset();
matrix.setRotate(degree, Xpos+bitmap.getWidth()/2, Ypos+bitmap.getHeight()/2);
and then in canvas doDraw()
canvas.setMatrix(matrix);
canvas.drawBitmap(bitmap, Xpos, Ypos, null);
canvas.setMatrix(null);
The Xpos and Ypos is the X and Y position of the bitmap
The setMatrix(null), set the matrix to null, so that the rotate didn't affect the after bitmap
And it didn't always create new bitmap, so it's great for performance
I hope that help
I know its an old question but, all answers with code imply a canvas, so heres a solution without a canvas that worked for me :
Matrix matrix = new Matrix();
matrix.postRotate(mDegree,100,100);
mCompasstemp = Bitmap.createBitmap(mCompPic, 0, 0, mCompPic.getWidth(),
mCompPic.getHeight(), matrix, true);
mCompasstemp = Bitmap.createScaledBitmap(mCompassTemp, mCompPic.getWidth(),
mCompic.getHeight(), false);
mCompassHud.setImageBitmap(mCompasstemp);
So basically after rotating your bitmap you rescale it to the desired size. Might not be best practice, but at least you dont have to create an extra canvas if you dont want/need to, since the involving Paint() Object is not an inexpensive operation either.

Categories

Resources