I am trying to achieve frames functionality , such that if i provide an image After Capturing/retrieving from Gallery ,i have done this part , now where i am stuck is How can i merge two images with respect to frame image accordingly!!
Now solution for combining two images is clearly given Here and Here
But they are not explaining the behaviour of adjusting one image with another such that in my case , Here are some examples:
I am already using Libraries like picasso and EasyImage so if they can help?
Edit:
Test Frame example
I made example. Please refer this repository.
https://github.com/nshmura/TestFrame/
Frame class merges picture's bitmap and frame's bitmap.
public class Frame {
//filename of frame
private String mFrameName;
//Rect of picture area in frame
private final Rect mPictureRect;
//degree of rotation to fit picture and frame.
private final float mRorate;
public Frame(String frameName,int left, int top, int right, int bottom, float rorate) {
mFrameName = frameName;
mPictureRect = new Rect(left, top, right, bottom);
mRorate = rorate;
}
public Bitmap mergeWith(Context context, Bitmap pictureBitmap) {
Bitmap frameBitmap = AssetsUtil.getBitmapFromAsset(context, mFrameName);
Bitmap.Config conf = Bitmap.Config.ARGB_8888;
Bitmap bitmap = Bitmap.createBitmap(frameBitmap.getWidth(), frameBitmap.getHeight(), conf);
Canvas canvas = new Canvas(bitmap);
Matrix matrix = getMatrix(pictureBitmap);
canvas.drawBitmap(pictureBitmap, matrix, null);
canvas.drawBitmap(frameBitmap, 0, 0, null);
return bitmap;
}
Matrix getMatrix(Bitmap pictureBitmap) {
float widthRatio = mPictureRect.width() / (float) pictureBitmap.getWidth();
float heightRatio = mPictureRect.height() / (float) pictureBitmap.getHeight();
float ratio;
if (widthRatio > heightRatio) {
ratio = widthRatio;
} else {
ratio = heightRatio;
}
float width = pictureBitmap.getWidth() * ratio;
float height = pictureBitmap.getHeight() * ratio;
float left = mPictureRect.left - (width - mPictureRect.width()) / 2f;
float top = mPictureRect.top - (height - mPictureRect.height()) / 2f;
Matrix matrix = new Matrix();
matrix.postRotate(mRorate);
matrix.postScale(ratio, ratio);
matrix.postTranslate(left, top);
return matrix;
}
}
Use like this:
//This is sample picture.
//Please take picture form gallery or camera.
Bitmap pictureBitmap = AssetsUtil.getBitmapFromAsset(this, "picture.jpg");
//This is sample frame.
// the number of left, top, right, bottom is the area to show picture.
// last argument is degree of rotation to fit picture and frame.
Frame frameA = new Frame("frame_a.png", 113, 93, 430, 409, 4);
Bitmap mergedBitmap = frameA. mergeWith(this, pictureBitmap);
//showing result bitmap
ImageView imageView = (ImageView) findViewById(R.id.image);
imageView.setImageBitmap(mergedBitmap);
Result is below:
I've been searching for a solution to this problem for quite some time and haven't found anything to match my needs yet.
I want to scale a bitmap to a specific size while maintaining aspect ratio.
Think of it as scaling a bitmap using fitCenter in an ImageView, only in a new bitmap.
The source bitmap has to fit inside the destination bitmap which has a specific size, and the rest of the pixels have to be transparent.
I have tried using Glide like so:
Glide.with(context).load(url)
.asBitmap()
.override(1280, 720)
.fitCenter()
.into(1280, 720)
.get();
But this method returns a bitmap that fits only width (or hight) and wraps the size.
I've heard that using Canvas is a possible solution but haven't found any way of achieving my goal using it.
Any help or insight would be greatly appreciated. I will post any needed clarifications if requested.
I managed to solve it using this function:
Bitmap resizeBitmap(Bitmap image, int destWidth, int destHeight) {
Bitmap background = Bitmap.createBitmap(destWidth, destHeight, Bitmap.Config.ARGB_8888);
float originalWidth = image.getWidth();
float originalHeight = image.getHeight();
Canvas canvas = new Canvas(background);
float scaleX = (float) 1280 / originalWidth;
float scaleY = (float) 720 / originalHeight;
float xTranslation = 0.0f;
float yTranslation = 0.0f;
float scale = 1;
if (scaleX < scaleY) { // Scale on X, translate on Y
scale = scaleX;
yTranslation = (destHeight - originalHeight * scale) / 2.0f;
} else { // Scale on Y, translate on X
scale = scaleY;
xTranslation = (destWidth - originalWidth * scale) / 2.0f;
}
Matrix transformation = new Matrix();
transformation.postTranslate(xTranslation, yTranslation);
transformation.preScale(scale, scale);
Paint paint = new Paint();
paint.setFilterBitmap(true);
canvas.drawBitmap(image, transformation, paint);
return background;
}
I am having a problem. I have a post draw listener where I draw a scaled version of a bitmap. The problem stems from the fact that I tend to do some scaling every time a zoom happens (zoom in scale up, zoom out scale down). The problem is I am not able to recycle the bitmap because when I try to do so after drawing
canvas.draw(scaledbitmap,0,0,null);
scaledBitmap.recycle()
i get the Canvas canot draw recycled bitmap exception
Does anyone know how I would go about recycling a bitmap after I am done with it so that another can be scaled afterwards and I don't get the OutOfMemoryException crash.
Some Code to show you exactly how I am using it:
private SpenDrawListener mPosteDrawListener = new SpenDrawListener() {
#Override
public void onDraw(Canvas canvas, float x, float y, float ratio,
float frameStartX, float frameStartY, RectF updateRect) {
if(mLineDrawingBitmap == null)
mLineDrawingBitmap = loadLineDrawingBitmap(mLineDrawingFileName);
Bitmap bm = Bitmap.createScaledBitmap(mLineDrawingBitmap, (int)(mLineDrawingBitmap.getWidth() * ratio), (int)(mLineDrawingBitmap.getHeight() * ratio), true);
/*
float pointX = (mScreenRect.width() - bm.getWidth()) / 2;
float pointY = mScreenRect.height() / 2 - bm.getHeight();
*/
float pointX = frameStartX - (x * ratio);
float pointY = frameStartY - (y * ratio);
//canvas.drawBitmap(bm, 0, 0,null);
canvas.drawBitmap(bm, pointX, pointY, null);
//bm.recycle();
}
};
After several hours of trial and error I have figured out how to scale nicely AND not crash the app with an OutOfMemoryException: Below is the code for drawing and scaling at runtime with no crash (as long as the image is not too large when decoded). I will use my own PostDrawListener but I believe it can be used anywhere with minor modification
private SpenDrawListener mPosteDrawListener = new SpenDrawListener() {
#Override
public void onDraw(Canvas canvas, float x, float y, float ratio,
float frameStartX, float frameStartY, RectF updateRect) {
if(mLineDrawingBitmap == null)
mLineDrawingBitmap = loadLineDrawingBitmap(mLineDrawingFileName);
float pointX = frameStartX - (x * ratio);
float pointY = frameStartY - (y * ratio);
//Create a new Matrix
Matrix m = new Matrix();
//Use any scaling ratio you want
m.postScale(ratio, ratio);
//Use any translation you want
m.postTranslate(pointX, pointY);
//when using below call you will not be creating a new bitmap, just
//using the original with runtime modifications
canvas.drawBitmap(mLineDrawingBitmap, m, null);
}
};
I have an already decoded bitmap that I would like to temporarily scale before drawing it on a canvas. So decoding a file and setting the size before is out of the question. I would like to keep the size of the existing bitmap and just scale it to be smaller before drawing it on the canvas. Is this posible?
using Matrix postScale(sx, sy, px, py) scales it correctly but doesn't position it right. And canvas.drawBitmap doesn't have an option with matrix and x & y position from what I can see.
Any suggestions?
Here is the code:
public static Bitmap scaleBitmap(Bitmap bitmap, int width, int height) {
final int bitmapWidth = bitmap.getWidth();
final int bitmapHeight = bitmap.getHeight();
final float scale = Math.min((float) width / (float) bitmapWidth,
(float) height / (float) bitmapHeight);
final int scaledWidth = (int) (bitmapWidth * scale);
final int scaledHeight = (int) (bitmapHeight * scale);
final Bitmap decoded = Bitmap.createScaledBitmap(bitmap, scaledWidth, scaledHeight, true);
final Canvas canvas = new Canvas(decoded);
return decoded;
}
Please note: Pass the bitmap to scale and it's new height and width.
I'm trying to scale and rotate in single operation before creting the final bitmap but the preRotate, postConcat doesn't seem to work.
Bitmap bmp = ... original image ...
Matrix m = new Matrix()
m.setScale(x, y);
m.preRotate(degrees, (float) width / 2, (float) height / 2);
Bitmap.createBitmap(bmp, 0, 0, bmp.getWidth(), bmp.getHeight(), m, true);
It only applies the scale and not rotation.
The answer was given, but to make things more clear to anyone reading this:
1) if you wish to perform ONE transformation in your bitmap, you CAN use SET (setRotate, setScale etc).
But note that any call to a "set" method OVERRIDES other transformations. It's like a new matrix. That's why OP's rotation was not working. These calls are not performed line by line. It's like they are scheduled to be done at runtime by the GPU when the new bitmap is being drawn. It's like when resolving your matrix, GPU rotated it, but then, created a scaled new one, ignoring previous matrix.
2) if you wish to perform more then one transformation, then you MUST use "pre" or "post" methods.
And what is the difference between a postRotate and a preRotate, for example? Well, this matrix math stuff is not my strength, but what I know is that the graphic cards make these transformations using matrix multiplication. It seems to be way more efficient. And as far as I remember from school, when multiplicating matrices the order IS important. A X B != B X A. So, scale a matrix and then rotate it is different from rotate and then scale it.
BUUUUT, as far as the final result in the screen is the same, we high level programmers usually do not need to know these differences. The GPU does.
Well, in that rare cases when you are performing really complicated matrix operations, and results are not what you expected or the performance is terrible and you need to deeply understand these methods to fix your code, well, then android documentation can not be of much help anyway. Instead, a good Linear Algebra book would be your best friend. ;)
This is the code
public class Bitmaptest 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.android);
int width = bitmapOrg.getWidth();
int height = bitmapOrg.getHeight();
int newWidth = 200;
int newHeight = 200;
// 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(45);
// recreate the new Bitmap
Bitmap resizedBitmap = Bitmap.createBitmap(bitmapOrg, 0, 0,
newWidth, newHeight, 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);
}
}
If you face the issue of OutOfMemory with above answers, than use below:
Bitmap MyFinalBitmap = Bitmap.createBitmap(CurrentBitmap, 0, 0,CurrentBitmap.getWidth()/2, CurrentBitmap.getHeight()/2,matrix, true);
Canvas holds a matrix stack und you can use it with the methods:
Canvas.save()
Doc:
/**
* Saves the current matrix and clip onto a private stack.
*
* Subsequent calls to translate,scale,rotate,skew,concat or clipRect,
* clipPath will all operate as usual, but when the balancing call to
* restore() is made, those calls will be forgotten, and the settings that
* existed before the save() will be reinstated.
*
* #return The value to pass to restoreToCount() to balance this save()
*/
Canvas.restore()
Doc:
/**
* This call balances a previous call to save(), and is used to remove all
* modifications to the matrix/clip state since the last save call. It is
* an error to call restore() more times than save() was called.
*/
example:
A custom View(Android) which looks like a rotary knob(e.g. potentiometer)
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
viewX = getWidth(); //views width
viewY = getHeight(); //views height
setMeasuredDimension(widthMeasureSpec, heightMeasureSpec); //a must call for every custom view
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
double tempAngel = 3.6 * barValue;
int deltaX = bitmap.getWidth() / 2;
int deltaY = bitmap.getHeight() / 2;
...
canvas.save();
canvas.translate(viewX / 2, viewY / 2); //translate drawing point to center
canvas.rotate((float) tempAngel); //rotate matrix
canvas.save(); //save matrix. your drawing point is still at (viewX / 2, viewY / 2)
canvas.translate(deltaX * -1, deltaY * -1); //translate drawing point a bit up and left to draw the bitmap in the middle
canvas.drawBitmap(bitmap, 0,0, bitmapPaint); // draw bitmap to the tranlated point at 0,0
canvas.restore(); //must calls...
canvas.restore();
}
All of the previous answer assume that this change to the bitmap is being made in a view. However in my case I was making the change to be saved out. Figured I would answer it for those in a similar boat.
There are two ways to do translation. Below dx is the translation in the X axis, and dy is the translation in the Y axis. The other variables should be self explanatory.
1 - Translation within the image (without rotation)
val newBitmap = Bitmap.createBitmap(originalBitmap, dx, dy, newWidth, newHeight, matrix, false)
2 - Complex matrix
matrix.postTranslate(dx, dy)
val newBitmap = Bitmap.createBitmap(newWidth, newHeight, Bitmap.Config.ARGB_8888)
val canvas = Canvas(newBitmap)
canvas.drawBitmap(originalBitmap, matrix, null)
Matrix rotateMatrix = new Matrix();
rotateMatrix.postRotate(rotation);
rotatedBitmap = Bitmap.createBitmap(loadedImage, 0, 0,loadedImage.getWidth(), loadedImage.getHeight(),rotateMatrix, false);
Refer to the following code, seems to work. In your code you are defining Matrix as m but referring to it as matrix
public class FourthActivity extends Activity {
private static final int WIDTH = 50;
private static final int HEIGHT = 50;
private static final int STRIDE = 64;
private static int[] createColors() {
int[] colors = new int[STRIDE * HEIGHT];
for (int y = 0; y < HEIGHT; y++) {
for (int x = 0; x < WIDTH; x++) {
int r = x * 255 / (WIDTH - 1);
int g = y * 255 / (HEIGHT - 1);
int b = 255 - Math.min(r, g);
int a = Math.max(r, g);
colors[y * STRIDE + x] = (a << 24) | (r << 16) | (g << 8) | b;
}
}
return colors;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main2);
final ImageView view1 = (ImageView) findViewById(R.id.imageView1);
int[] colors = createColors();
final Bitmap bmp1 = Bitmap.createBitmap(colors, 0, STRIDE, WIDTH, HEIGHT,
Bitmap.Config.ARGB_8888);
view1.setImageBitmap(bmp1);
Button button = (Button) findViewById(R.id.button1);
button.setOnClickListener(new OnClickListener(){
#Override
public void onClick(View v) {
Matrix matrix = new Matrix();
matrix.setScale(2, 2);
matrix.preRotate(45, (float) WIDTH / 2, (float) HEIGHT / 2);
Bitmap bmp2 = Bitmap.createBitmap(bmp1, 0, 0,
bmp1.getWidth(), bmp1.getHeight(), matrix, true);
ImageView view2 = (ImageView) findViewById(R.id.imageView2);
view2.setImageBitmap(bmp2);
}
});
}
}
Use matrix to scale area of original bitmap to 50% and compress bitmap until it's size < 200k
Compress bitmap to a specific byte size in Android