For an android 2D-game, I want to create a image scale animation on surfaceview canvas. Following is my working code, I am not sure about the approach is correct or not.
Please suggest me, if I am wrong.
public void drawScaledImage(Canvas canvas, Bitmap bitmap, float scale) {
width = bitmap.getWidth();
height = bitmap.getHeight();
Bitmap tempBitmap = Bitmap.createScaledBitmap(bitmap, (int) (width * scale), (int) (height * scale), false);
canvas.drawBitmap(tempBitmap, getX(), getActualY(), paint);
}
Following code will be executing on every 16ms (FPS 60) with a new scale value.
Please suggest the correct approach to create a image scale animation in android canvas.
I want to create the animation memory and process optimised way.
Thanks.
Related
So, I'm using this library https://github.com/thuytrinh/android-collage-views to add "MultiTouchListener" feature to my ImageView. Basically I let user to modify a photo to his needs using rotation, scale and translation. Now the only problem is how to save it. I did it like this:
Bitmap bitmap = Bitmap.createBitmap(imageContainer.getWidth(), imageContainer.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
canvas.drawColor(Color.WHITE);
imageContainer.draw(canvas);
It works, but image is not big enough - it's as big as view on phone so it depends on screen resolution. And I want to "apply" these transformations on given bitmap with full size. And I want transformed image to look like on screen (so it'll need to crop everything out of screen)
I tried the following:
Bitmap newBitmap = Bitmap.createBitmap(image.getWidth(), image.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(newBitmap);
canvas.drawColor(Color.WHITE);
Paint paint = new Paint();
paint.setAntiAlias(true);
canvas.drawBitmap(image, imageView.getMatrix(), paint);
But it doesn't look as expected.
User screen:
And output image (without cropping, because I don't want which side I should crop):
How can I fix this? Is there any solution?
Here is one way to do it, definitely not perfect but should give you a good start :
In this, container refers to the view that contains the transformed ImageView, the phone case on your screenshot and src the raw source bitmap.
First, you need to compute the desired width and height of the output bitmap, i.e the size it would be to make the image fit in it while keeping the ratio of the container :
float containerWidth = containerView.getWidth();
float containerHeight = containerView.getHeight();
float srcWidth = src.getWidth();
float srcHeight = src.getHeight();
float containerRatio = containerWidth / containerHeight;
float srcRatio = srcWidth / srcHeight;
float outputWidth, outputHeight;
if(srcRatio > containerRatio) { //fits in width
outputWidth = srcWidth;
outputHeight = srcWidth / containerRatio;
}
else if(srcRatio < containerRatio) { //fits in height
outputHeight = srcHeight;
outputWidth = srcHeight * containerRatio;
}
else {
outputWidth = srcWidth;
outputHeight = srcHeight;
}
Apply the ratio between container width/height and output width/height to the translation part of the matrix that hold the transformation that the user did
float containerToOutputRatioWidth = outputWidth / containerWidth;
float containerToOutputRatioHeight = outputHeight / containerHeight;
float[] values = new float[9];
transformedImageView.getMatrix().getValues(values);
values[2] = values[2] * containerToOutputRatioWidth;
values[5] = values[5] * containerToOutputRatioHeight;
Matrix outputMatrix = new Matrix();
outputMatrix.setValues(values);
Draw the output bitmap as you were doing with the correct size (outputWidth, outputHeight) and matrix (outputMatrix).
Bitmap newBitmap = Bitmap.createBitmap(Math.round(outputWidth), Math.round(outputHeight), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(newBitmap);
canvas.drawColor(Color.WHITE);
Paint paint = new Paint();
paint.setAntiAlias(true);
canvas.drawBitmap(src, outputMatrix, paint);
.
Warnings
You should be careful about memory allocation, this code will lead to allocate some massive bitmaps, you should implement some kind of limit that get along with your needs. (Also do allocation and drawing in background)
You might need to do some adjustment depending on where you place the image in the first place.
I am developing an application in which I need to fit an Bitmap into Imageview with specific dimensions(let's suppose 350dpx50dp - height*width).
I wanted to do something similar like this: http://gyazo.com/d739d03684e46411feb58d66acea1002
I have looked here for solutions. I found this code for scale the bitmap and fit it into imageview, but the problem is that imageview becomes greater when I add the bitmap into him:
private void scaleImage(Bitmap bitmap, ImageView view)
{
// Get current dimensions AND the desired bounding box
int width = bitmap.getWidth();
int height = bitmap.getHeight();
int bounding = dpToPx(350);
// Determine how much to scale: the dimension requiring less scaling is
// closer to the its side. This way the image always stays inside your
// bounding box AND either x/y axis touches it.
float xScale = ((float) bounding) / width;
float yScale = ((float) bounding) / height;
float scale = (xScale <= yScale) ? xScale : yScale;
// Create a matrix for the scaling and add the scaling data
Matrix matrix = new Matrix();
matrix.postScale(scale, scale);
// Create a new bitmap and convert it to a format understood by the ImageView
Bitmap scaledBitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, true);
// Apply the scaled bitmap
view.setImageBitmap(scaledBitmap);
}
Using this code I can get this : http://gyazo.com/e9871db2130ac33668156fc0cf773594
But that's not what I wanted, I want to keep the dimensions of imageview and add the bitmap into imageview without modifying the dimensions of imageview and occupying all the imageview's surface. Like the first image.
Why don't you just add android:scaleType="fitXY" to your ImageView in xml?
I am trying to scale up a bitmap from its center point in Android to achieve a zoom effect, but without success. The code I have is:
float scaleWidth = ((float) width + (i * 5)) / width;
float scaleHeight = ((float) height + (i * 5)) / height;
Matrix matrix = new Matrix();
matrix.setScale(scaleWidth, scaleHeight, scaleWidth / 2, scaleHeight / 2);
Bitmap rescaledBitmap = Bitmap.createBitmap(src, 0, 0, width, height, matrix, true);
result.add(rescaledBitmap);
I am setting the pivot point by dividing the dimensions by 2, but the effect is just that the image is scaled from 0, 0 as coordinates instead of from the center. What I want is for the image to be a fixed size, but scaled up from its center point (thus cropping the image).
I'm going to offer an alternative solution using a property animator since that is a cleaner solution I think.
SomeLayout.xml (The key here is that the ViewGroup is the same size as the View, so it will clip as you requested (like google maps zoom in))
<FrameLayout
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_gravity="center">
<View
android:id="#+id/zoom"
android:layout_width="100dp"
android:layout_height="100dp"
android:background="#drawable/myCoolImage"
/>
</FrameLayout>
Code: (the 1,2,1 will start at a scale of 1x then 2x then back to 1x, it takes a list of values)
final View view = findViewById(R.id.zoom);
view.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
final ObjectAnimator animator = ObjectAnimator.ofFloat(view, View.SCALE_X, 1, 2, 1)
.ofFloat(view, View.SCALE_Y, 1, 2, 1)
.setDuration(5000);
animator.setInterpolator(new AccelerateDecelerateInterpolator());
animator.start();
}
});
So with this version image if you had a zoom +, and zoom - views with onClickListeners, you could basically simulate the controlled zooming as long as you know what values you want to zoom with.
Also as previously noted the ViewGroup being the same size as the internal view will force the animation to clip to it's parent bounds instead of being completely visible.
References:
Google Android ObjectAnimator
Sure this will come late, but for all looking after me:
double scaleFactor = 0.75; // Set this to the zoom factor
int widthOffset = (int) ((1 - scaleFactor)/2 * bmp.getWidth());
int heightOffset = (int) ((1 - scaleFactor)/2 * bmp.getHeight());
int numWidthPixels = bmp.getWidth() - 2 * widthOffset;
int numHeightPixels = bmp.getHeight() - 2 * heightOffset;
Bitmap rescaledBitmap = Bitmap.createBitmap(bmp, widthOffset, heightOffset, numWidthPixels, numHeightPixels, null, true);
This example will zoom in on the center of a bitmap and with a factor of 25%.
The second and third arguments in createBitmap take the x/y coordinates of the top left corner. You're sending 0,0, so if I understand correctly...
The image is correctly scaled, but the image is not centered, right?
To center it, you need to find the correct (x,y) point for the top left corner. This should be 1/4th the original width/height.
So...
Bitmap rescaledBitmap = Bitmap.createBitmap(src, (width/2), (height/2), width, height, matrix, true);
should work.
I need to resize my image with custom size. The image is taken from device camera or gallery, i tired the below code, but the image is stretched, i need the image in square shape with out any stretch.
public Bitmap decodeSampledBitmapFromFile(Bitmap bm, int boundBoxInDp) {
boundBoxInDp=300;
int height = bm.getHeight();
int width = bm.getWidth();
float scaleWidth = ((float) boundBoxInDp) / width;
float scaleHeight = ((float) boundBoxInDp) / height;
Matrix matrix = new Matrix();
matrix.postScale(scaleWidth, scaleHeight);
Bitmap resizedBitmap = Bitmap.createBitmap(bm, 0, 0, width, height,
matrix, false);
return resizedBitmap;
}
If you already have a bitmap, you could use the following code to resize:
Bitmap originalBitmap = <original initialization>;
Bitmap resizedBitmap = Bitmap.createScaledBitmap(originalBitmap, newWidth, newHeight, false);
Or you can use following library for resize image
https://github.com/biokys/cropimage
This won't fit your image in a bounding box (the failure of which is presumably what you're calling "stretch"). It will not handle rectangular bitmaps in your square bounding box, nor will it handle images smaller than the bounding box particularly well. You probably want something like:
public Bitmap decodeSampledBitmapFromFile(Bitmap bm, int boundBoxInDp) {
boundSquareInPx=convertToPixels(300);
int maxDimen = Math.max(bm.getHeight(), bm.getWidth())
float scale = (maxDimen <= boundSquareInPx) ? 1 : boundSquareInPx / (float) maxDimen;
float scaleWidth = scale * bm.getWidth();
float scaleHeight = scale * bm.getHeight();
Matrix matrix = new Matrix();
matrix.postScale(scaleWidth, scaleHeight);
Bitmap resizedBitmap = Bitmap.createBitmap(bm, 0, 0, width, height,
matrix, true);
return resizedBitmap;
}
Few notes: if you image is smaller than your bound it won't fit it- obvious modifications do that.
Secondly, dp != px; the Bitmap object returns px, so you're going to have to convert to px from dp (which is well documented elsewhere).
Use postTranslate(...) if you need to center the correspondingly cropped bitmap.
The documentation is here; this is allready the best library I know for resizing in Android- I've never needed anything else, and I've been in the game a while and work with this frequently.
If you need, in my opinion, the best introduction to working with the API efficiently: read the source code to ImageView and Drawable instances; a really worthwhile personal development exercise would be to use the SDK to implement a fading transition drawable that is center cropped, as that's rather annoyingly one of the only things missing from the Android library, and would involve a heck of a lot of the kind of coding you're trying to do above.
NB:
As you'll note, another answerer has pointed out the existence of createScaledBitmap, which is probably much clearer code; I just wanted to point out how what you were doing was basically right and how it could be improved.
Best.
Take a look at https://github.com/coomar2841/image-chooser-library/blob/d27b542d2487132b0150be382f39e9ef95aafe68/src/com/kbeanie/imagechooser/threads/MediaProcessorThread.java.
The method called compressAndSaveImage.
I am creating a tile board game.
I want to rotate a bitmap tile piece by a few pre-determined degrees.
When I rotate my bitmap, the size changes.
For example, if I want a 75x75 triangle tile piece, on rotation I get a 68x68 back from this code. How can I keep things the same size so everything remains the size for the board?
Here's what I'm using to rotate:
public class RotatebitmapActivity extends Activity {
#Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
LinearLayout linLayout = new LinearLayout(this);
// load the origial BitMap (500 x 500 px)
Bitmap bitmapOrg = BitmapFactory.decodeResource(getResources(),
R.drawable.t123);
int width = bitmapOrg.getWidth();
int height = bitmapOrg.getHeight();
int newWidth = 75;
int newHeight = 75;
// calculate the scale - in this case = 0.4f
float scaleWidth = ((float) newWidth) / width;
float scaleHeight = ((float) newHeight) / height;
// createa matrix for the manipulation
Matrix matrix = new Matrix();
// resize the bit map
matrix.postScale(scaleWidth, scaleHeight);
// rotate the Bitmap
matrix.postRotate(120);
// recreate the new Bitmap
Bitmap resizedBitmap = Bitmap.createBitmap(bitmapOrg, 0, 0,
width, height, matrix, true);
// make a Drawable from Bitmap to allow to set the BitMap
// to the ImageView, ImageButton or what ever
BitmapDrawable bmd = new BitmapDrawable(resizedBitmap);
ImageView imageView = new ImageView(this);
// set the Drawable on the ImageView
imageView.setImageDrawable(bmd);
// center the Image
imageView.setScaleType(ScaleType.CENTER);
// add ImageView to the Layout
linLayout.addView(imageView,
new LinearLayout.LayoutParams(
LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT
)
);
// set LinearLayout as ContentView
setContentView(linLayout);
}
What is happening in your code, is that the bitmap that is created is the correct size. However, all Bitmaps are automatically scaled to fit the density of the screen that is currently in use.
There are two ways (that I know of) to get the result you desire.
You can set the density of a bitmap after creating it and before wrapping it in a drawable. To do this, add code similar to:
resizedBitmap.setDensity(DisplayMetric.DENSITY_HIGH);
This code sets the density that the bitmap is designed for, and can prevent Android from auto-scaling the image.
The second solution is more of a methodology than a specific solution to this particular problem. I won't go into detail, for that see Supporting Multiple Screens | Android Developers
Hopefully someone will happen along that can answer your question better than I can.