Resizing or Clipping after a rotation - android

I have a 10x10 tablelayout with each cell containing an ImageView. I simulate the moving across this gamegrid by updating the pictures in each grid on a move event. I also rotate the image to align the image with the direction of travel. My problem is that as the image rotates it causes my cell rows and columns to expand to fit the rotated image.
What I would like is to have the image cropped to remain within the dimensions of the table row and columns. If that isnt possible then I would like to scale the image to remain within the dimensions of the original table cell.
Another potential issue is that the images are getting scaled initially to fit into the cells (this is done automatically as the table expands to fill the screen). I don't want to change that behavior. I just want to trim off the corners of the image as they pass the original boundaries of the table cell when they are rotated.
My rotation code is below:
Matrix matrix = new Matrix();
matrix.postRotate((float) (360 - sector.getShip().getHeading()));
Bitmap bMap = BitmapFactory.decodeResource(image.getContext().getResources(), getShip().getGridSymbol());
image.setImageBitmap(Bitmap.createBitmap(bMap, 0, 0, bMap.getWidth(), bMap.getHeight(), matrix, true));
image.setImageResource(sector.getShip().gridParams.default);
My table configuration is below:
<TableLayout android:id="#+id/quadrantGrid" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_alignWithParentIfMissing="true" android:stretchColumns="*" android:background="#000000">
...
<TableRow>
<TextView android:text="1" android:background="#404040" android:gravity="center"></TextView>
<ImageView android:id="#+id/grid00"></ImageView>
<ImageView android:id="#+id/grid01"></ImageView>
<ImageView android:id="#+id/grid02"></ImageView>
<ImageView android:id="#+id/grid03"></ImageView>
<ImageView android:id="#+id/grid04"></ImageView>
<ImageView android:id="#+id/grid05"></ImageView>
<ImageView android:id="#+id/grid06"></ImageView>
<ImageView android:id="#+id/grid07"></ImageView>
<ImageView android:id="#+id/grid08"></ImageView>
<ImageView android:id="#+id/grid09"></ImageView>
</TableRow>
...
UPDATE:
I found a solution, though I'm not sure its the most efficient solution.
All my images are 25x25. And so the OS figures how to scale those images to fit into the cell. The solution just performs two operations. First it rotates, and then it creates a smaller image from the rotated image. (note: When I created my image I made sure the important parts of the image would be contained within a circle with a diameter of the width of the image to allow for this type of cropping).
My code is below. I would prefer to perform both operations within one method, but Im not sure how so I just break it down into two steps.
Matrix matrix = new Matrix();
matrix.postRotate((float) (360 - sector.getShip().getHeading()));
Bitmap bMap = BitmapFactory.decodeResource(image.getContext().getResources(), getShip().getGridSymbol());
bMap = Bitmap.createBitmap(bMap, 0, 0, 25, 25, matrix, true);
int x = (bMap.getWidth() - 25) / 2;
int y = (bMap.getHeight() - 25) / 2;
image.setImageBitmap(Bitmap.createBitmap(bMap, x, y, 25, 25));

Try adding
android:scaleType="centerCrop"
to each ImageView item in your xml layout.

The final solution has to leverage the "getMeasuredHeight()" of the image. I didnt have to use the getMeasuredWidth() because for my particular layout the height is the limiting factor.
Here is the final bitmap code that works:
private Bitmap getRotatedShipBitmap(SectorGridImageView image,
Bitmap bitmap, Matrix matrix) {
bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(),
bitmap.getHeight(), matrix, true);
int yHeight = (image.getMeasuredHeight() == 0) ? bitmap.getHeight()
: image.getMeasuredHeight();
int xWidth = yHeight;
int x = (bitmap.getWidth() - xWidth) / 2;
int y = (bitmap.getHeight() - yHeight) / 2;
return Bitmap.createBitmap(bitmap, x, y, xWidth, yHeight);
}

Related

cropping a Bitmap based on overlay in Android - Camera API 2

I am using Camera api to capture ID cards pictures, I have an over lay as shown in the image bellow. I want to crop the image in the box. could you suggest on how exactly it should be done. I have written down what I have tried and the results it gives me.
This is the screenshot of the id i want to capture..
Output.
white rectangular box is a photo frame which is right in the centre in a Relative layout
<View
android:id="#+id/photo_frame"
android:layout_width="match_parent"
android:layout_height="212dp"
android:background="#drawable/bg_photo_frame"
android:layout_centerInParent="true"
android:layout_margin="#dimen/double_padding"
android:visibility="visible"/>
How do I calculate this frame to cut the image
this is what I have to cut the image which needs modification but not sure what is the way forward
public Bitmap cutImage(final Bitmap srcBmp, final int pixepWidth, final int pixelsHeight, float widthRatio) {
// Bitmap croppedBitmap = Bitmap.createBitmap(bitmap, 20, 20, pixepWidth, pixelsHeight);
// return croppedBitmap;
Bitmap dstBmp;
if (srcBmp.getWidth() >= srcBmp.getHeight()){
dstBmp = Bitmap.createBitmap(
srcBmp,
srcBmp.getWidth()/2 - srcBmp.getHeight()/2,
0,
srcBmp.getHeight(),
srcBmp.getHeight()
);
}else{
dstBmp = Bitmap.createBitmap(
srcBmp,
0,
srcBmp.getHeight()/2 - srcBmp.getWidth()/2,
srcBmp.getWidth(),
srcBmp.getWidth()
);
}
return dstBmp;
}
Resizing view using onTouchListener
You apply some kind of touchListener to the overlay view for resizing and moving the overlay. ImageButton seems to work, so I use ImageButton with a frame.xml to draw a border.
Then you apply the global coordinates of the overlay (x, y, width, height) against the coordinates of the bitmap (x, y, width, height).
The overlay x and the overlay y can’t be less than 0, and the overlay x + the overlay width cannot be greater than the bitmap width.
To perform the crop, use Bitmap.createBitmap():
Bitmap.createBitmap(Bitmap source, int x, int y, int width, int height)
How to crop Bitmap?

drawable fit <bitmap> width to the view while keeping aspect ratio

Let's say I have a horizontally long image, "grass". Now, I want to use it as a background image for views, but I want to dock it to the bottom. After searching the web, I discovered that I need to wrap the image as a drawable like:
<?xml version="1.0" encoding="utf-8"?>
<bitmap
xmlns:android="http://schemas.android.com/apk/res/android"
android:src="#drawable/grass"
android:gravity="bottom|left" />
The problem is that the image is clipped to the right. So I have tried bottom|left|right but then the aspect ratio was not kept. Can I make the image fit horizontally, but keep the aspect ratio by automatically scaling it vertically? Or is this not possible with drawables?
what your searching for is centerInside ScaleType . please remove the ScaleType from the ImageView and try this :
public static synchronized Bitmap centerInside(Bitmap bitmap,int width,int height){
if(bitmap.getWidth() == bitmap.getHeight()){
Log.i("crop","already matched");
return Bitmap.createScaledBitmap(bitmap, width, height, true);
}
//int size = width > height ? width : height;
float scale = ImageUtils.calculateImageSampleSize(bitmap.getWidth(), bitmap.getHeight(),width,height);
width = (int) ((float)bitmap.getWidth() / scale);
height = (int) ((float)bitmap.getHeight() / scale);
bitmap = Bitmap.createScaledBitmap(bitmap, width, height, true);
if (bitmap.getWidth() >= bitmap.getHeight()){
bitmap = Bitmap.createBitmap(
bitmap,
bitmap.getWidth()/2 - bitmap.getHeight()/2,
0,
bitmap.getHeight(),
bitmap.getHeight()
);
}else{
bitmap = Bitmap.createBitmap(
bitmap,
0,
bitmap.getHeight()/2 - bitmap.getWidth()/2,
bitmap.getWidth(),
bitmap.getWidth()
);
}
return bitmap;
}
You cannot achieve this with XML only. If you don't want to have to draw the bitmap programmatically, you wrap an ImageView in a RelativeLayout and set the ImageView's scaleType property:
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<ImageView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:src="#drawable/grass"
android:scaleType="fitXY" />
</RelativeLayout>
If I understand Your requirement right Your image need to stretch vertically and be in bounds horizontally.
A good solution I could suggest is creating a 9 patch image out of Your drawable and define the area where the image can stretch.
You can use this tutorial for reference.

(Android)How can I show a part of image?

I have an image like this->
And I want to show like this->
Then I can choose how much I can see(0~1,0=can not see,1=Whole picture,0.5=half picture,....etc,read from user input)
How to do that?
I tried to use android:scaleType but it is not working.
My best suggestion is to wrap your BitmapDrawable with a ClipDrawable.
ClipDrawable lets you define clipping for any other drawable, so instead of drawing the entire drawable, only a part of it will be drawn.
How would that work? Your ImageView can display a drawable - assignable through setImageDrawable(). Naturally, you would place a BitmapDrawable of your image bitmap there. If you wrap your BitmapDrawable first with ClipDrawable, and only then assign to the ImageView, you should be fine.
<ImageView
android:id="#+id/imageView1"
android:layout_width="100dp"
android:layout_height="100dp"
android:src="#drawable/clip_source" />
and this is clip_source drawable:
<?xml version="1.0" encoding="utf-8"?>
<clip xmlns:android="http://schemas.android.com/apk/res/android"
android:clipOrientation="vertical"
android:drawable="#drawable/your_own_drawable"
android:gravity="top" />
You can define the amount of clipping by calling the function setLevel() on your ClipDrawable (clip_source). A level of 0 means the image is completely hidden and a level of 10000 means the image is completely revealed. You can use any int value in the middle.
You'll have to set the level in code, so your code should first get a reference to the ClipDrawable. You can do this by running getDrawable() on your ImageView instance. When you have a reference to your ClipDrawable, simply run setLevel(5000) on it (or any other number 0-10000).
ImageView img = (ImageView) findViewById(R.id.imageView1);
mImageDrawable = (ClipDrawable) img.getDrawable();
mImageDrawable.setLevel(5000);
here is another way to achieve this
public static Bitmap getScaleBitmap(Bitmap bitmap) {
Bitmap output = Bitmap.createBitmap(bitmap.getWidth(),
bitmap.getHeight(), Config.ARGB_8888);
Canvas canvas = new Canvas(output);
final int color = 0xff424242;
final Paint paint = new Paint();
final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight()/2);
final RectF rectF = new RectF(rect);
final float roundPx = 0;
paint.setAntiAlias(true);
canvas.drawARGB(0, 0, 0, 0);
paint.setColor(color);
canvas.drawRoundRect(rectF, roundPx, roundPx, paint);
paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
canvas.drawBitmap(bitmap, rect, rect, paint);
return output;
}
Here in below line
final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight()/2);
set hight as you require..
Enjoy :)
I've achieved this by setting the scrollY on my ImageView in the markup. From android's documentation, clearly this is not an option if you're dealing with devices running less than API 14. Also, in my case, I'm using fixed size images that I'm loading based on application state, so they're always the same size. As such, I'm able to just implement it in markup. I realize this approach won't work for everyone.
I wrap the image in a layout that's half as tall as the icon and intentionally have the icon's size at it's actual size. Without the scrollY set, it displays only the top half of the image. Setting the scrollY moves it to where I want it - displaying only the lower half of the image.
<LinearLayout
android:layout_width="match_parent"
android:layout_height="50dp"
android:orientation="horizontal"
android:gravity="center_horizontal">
<ImageView
android:id="#+id/icon"
android:layout_width="100dp"
android:layout_height="100dp"
android:src="#drawable/default_icon"
android:scrollY="50dp" />
</LinearLayout>
So, from my example, it's only displaying the bottom half of the icon.
Here's an easy way.. Create a separate layout in your main layout just for your images. Make sure it is realitive. Now put your image view for your pictures in side the layout and make it fill parent. OK now make a blank image and add that image view to the bottom of the layout and the top to the center of the layout. Just mess with the margins and size of the imageviews till it looks the way you want it. Hope this helps.
Convert your drawable into a bitmap and crop to get top of the image to form another bitmap, then display it into your image view.
// to convert drawable to bitmap
Bitmap resource = BitmapFactory.decodeResource(context.getResources(),
R.drawable.your_resource);
// to set width of bitmap to imageview's width if necessary,
int width = imageView.getMeasuredWidth();
// to crop the bitmap to specific width and height,
resource = Bitmap.createBitmap(resource, 0, 0, width, height);
here is the code to crop image into two part
public static Bitmap getScaleBitmap(Bitmap bitmap, int check) {
final Bitmap toBeCropped = bitmap;
final BitmapFactory.Options bitmapOptions = new BitmapFactory.Options();
bitmapOptions.inTargetDensity = 1;
toBeCropped.setDensity(Bitmap.DENSITY_NONE);
int top = 0;
int bottom = 0;
int targetheight = 0;
if (check == 0) {// return 1st half of image
top = 0;
bottom = bitmap.getHeight() / 2;
} else {// return 2nd half of image
top = (bitmap.getHeight() / 2) - 10;
bottom = (bitmap.getHeight() / 2) - 10;
}
int fromHere = (int) (toBeCropped.getHeight() * 0.5);
Bitmap croppedBitmap = Bitmap.createBitmap(toBeCropped, 0, top, toBeCropped.getWidth(), bottom);
return croppedBitmap;
}

Scale and crop image in XML

I'm trying to scale and crop image at the same time and show it from left to right screen edge. I receive image that is just little bit wider than users screen and I'm able to scale it like this (XML):
<ImageView
android:id="#+id/category_image_top"
android:layout_width="match_parent"
android:layout_height="170dp"
android:maxHeight="170dp"
android:scaleType="centerCrop"
android:adjustViewBounds="true"
android:focusable="false"
/>
But this is what I get:
I would like to align image to top right like so:
Is this possible? I've tried all scaleTypes but noting works, image is either scaled to fit by X and Y (fitXY, fitStart) or image cropped but centered (centerCrop). I need something like android:scaleType="cropStart"
<ImageView
android:id="#+id/category_image_top"
android:layout_width="match_parent"
android:layout_height="170dp"
android:maxHeight="170dp"
android:scaleType="centerCrop"
android:paddingLeft="half of your screen width"
android:paddingBottom="half of your screen height"
android:adjustViewBounds="true"
android:focusable="false"
/>
You can set padding to move image left or right and also top and bottom padding to move up and down
As I didn't find a way to deal with this situation through xml (views) I turned (as #serenskye suggested) to code. Here's my code, I hope it helps (ps: I've changed my logic a little bit, I wanted to fit image by width so I've scaled it to predefined imageWidght and then cropped it to imageHeight)
//bm is received image (type = Bitmap)
Bitmap scaledImage = null;
float scaleFactor = (float) bm.getWidth() / (float) imageWidth;
//if scale factor is 1 then there is no need to scale (it will stay the same)
if (scaleFactor != 1) {
//calculate new height (with ration preserved) and scale image
int scaleHeight = (int) (bm.getHeight() / scaleFactor);
scaledImage = Bitmap.createScaledBitmap(bm, imageWidth, scaleHeight, false);
}
else {
scaledImage = bm;
}
Bitmap cropedImage = null;
//if cropped height is bigger then image height then there is no need to crop
if (scaledImage.getHeight() > imageHeight)
cropedImage = Bitmap.createBitmap(scaledImage, 0, 0, imageWidth, imageHeight);
else
cropedImage = scaledImage;
iv.setImageBitmap(cropedImage);
add
android:layout_gravity="center"

Android: high quality image resizing / scaling

I need to scale down images coming from a Network stream without losing quality.
I am aware of this solution Strange out of memory issue while loading an image to a Bitmap object but it is too coarse - inSampleSize is an integer and does not allow finer control over the resulting dimensions. That is, I need to scale images to specific h/w dimensions (and keeping aspect ratio).
I dont mind having a DIY bicubic/lancoz algorithm in my code but I cant find any examples that would work on Android as they all rely on Java2D (JavaSE).
EDIT:
Ive attached a quick source. The original is 720x402 HD screen capture. Please ignore the top 2 thumbnails. The top large image is resized automatically by android (as part of layout) to about 130x72. It is nice and crisp. The bottom image is resized with API and has severe artifacting
I've also tried using the BitmapFactory and, as I said earlier, it has two problems - no way to scale to exact size and the scaled image is blurry.
Any ideas on how to fix the artifcating?
Thanks, S.O.!
package qp.test;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.os.Bundle;
import android.widget.ImageView;
public class imgview extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Bitmap original = BitmapFactory.decodeResource(getResources(), R.drawable.a000001570402);
Bitmap resized = getResizedBitmap(original, 130);
//Bitmap resized = getResizedBitmap2(original, 0.3f);
System.err.println(resized.getWidth() + "x" + resized.getHeight());
ImageView image = (ImageView) findViewById(R.id.ImageViewFullManual);
image.setImageBitmap(resized);
}
private Bitmap getResizedBitmap(Bitmap bm, int newWidth) {
int width = bm.getWidth();
int height = bm.getHeight();
float aspect = (float)width / height;
float scaleWidth = newWidth;
float scaleHeight = scaleWidth / aspect; // yeah!
// create a matrix for the manipulation
Matrix matrix = new Matrix();
// resize the bit map
matrix.postScale(scaleWidth / width, scaleHeight / height);
// recreate the new Bitmap
Bitmap resizedBitmap = Bitmap.createBitmap(bm, 0, 0, width, height, matrix, true);
bm.recycle();
return resizedBitmap;
}
private Bitmap getResizedBitmap2(Bitmap bm, float scale) {
/* float aspect = bm.getWidth() / bm.getHeight();
int scaleWidth = (int) (bm.getWidth() * scale);
int scaleHeight = (int) (bm.getHeight() * scale);
*/
// original image is 720x402 and SampleSize=4 produces 180x102, which is
// still too large
BitmapFactory.Options bfo = new BitmapFactory.Options();
bfo.inSampleSize = 4;
return BitmapFactory.decodeResource(getResources(), R.drawable.a000001570402, bfo);
}
}
And the layout
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
>
<!-- <TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="hullo" android:background="#00ff00"
/>
-->
<ImageView android:id="#+id/ImageViewThumbAuto"
android:layout_width="130dip" android:layout_height="72dip"
android:src="#drawable/a000001570402" />
<ImageView android:id="#+id/ImageViewThumbManual"
android:layout_width="130dip" android:layout_height="72dip"
android:src="#drawable/a000001570402"
android:layout_toRightOf="#id/ImageViewThumbAuto"
/>
<ImageView android:id="#+id/ImageViewFullAuto" android:layout_width="300dip"
android:layout_height="169dip"
android:scaleType="fitXY"
android:src="#drawable/a000001570402"
android:layout_below="#id/ImageViewThumbAuto"
/>
<ImageView android:id="#+id/ImageViewFullManual" android:layout_width="300dip"
android:layout_height="169dip"
android:scaleType="fitXY"
android:src="#drawable/a000001570402"
android:layout_below="#id/ImageViewFullAuto"
/>
</RelativeLayout>
You can use BitmapFactory.Options with BitmapFactory.decode function(s),
using inDensity and inTargetDensity
Example: you have 1600x1200 size of image and want to resize to 640x480
then 'inDensity'=5 and 'inTargetDensity'=2 (1600x2 equal to 640x5).
Hoping this help.
The top large image is simply scaled down to 450px by the layout so no artifacts.
The artifacts of the bottom large image result from scaling it down to 130px wide and then up again to about 450px by the layout. So the artifacts are made by your scaling. Try
Bitmap resized = getResizedBitmap(original, 450);
in your code and it should be fine. However, you need to adapt that to the actuall screen width of the phone either.
Briefly, good downscaling algorithm (not nearest neighbor like) consists of 2 steps:
downscale using BitmapFactory.Options::inSampleSize->BitmapFactory.decodeResource() as close as possible to the resolution that you need but not less than it
get to the exact resolution by downscaling a little bit using Canvas::drawBitmap()
Here is detailed explanation how SonyMobile resolved this task: http://developer.sonymobile.com/2011/06/27/how-to-scale-images-for-your-android-application/
Here is the source code of SonyMobile scale utils: http://developer.sonymobile.com/downloads/code-example-module/image-scaling-code-example-for-android/

Categories

Resources