I have a picture (bitmap) and I want to draw some shapes and rotated
text on it.
This works fine as long as the picture doesn't get too large. However,
when using a picture (2560 x 1920 pixels)taken with the build-in
camera of my android 2.1 phone, the result is distorted.
It looks like the rotation back, after drawing the rotated text, has
not been completed. Also, the distortion point is not always the same,
like it depends on the cpu usage.
You can see some resulting pictures here:
http://dl.dropbox.com/u/4751612/Result1.png
http://dl.dropbox.com/u/4751612/Result2.png
The code is executed inside a AsyncTask. The strange this is that this code works fine in one Activity, but not in another. In both activities the AsyncTask is executed when a button is clicked.
These are some excerpts of the code I'm using.
// Load the image from the MediaStore
c = MediaStore.Images.Media.query(context.getContentResolver(),
Uri.parse(drawing.fullImage), new String[] {MediaColumns.DATA});
if (c != null && c.moveToFirst()) {
imageFilePath = c.getString(0);
bitmap = ImageUtil.getBitmap(new File(imageFilePath), 10000);
}
c.close();
// Create a canvas to draw on
drawingBitmap = Bitmap.createBitmap(bitmap.getWidth(),
bitmap.getHeight(), Bitmap.Config.ARGB_8888);
canvas = new Canvas(drawingBitmap);
// Draw image
canvas.drawBitmap(bitmap, 0, 0,
MeasureFactory.getMeasurePaint(context));
// calculate text width
rect = new Rect();
paint.getTextBounds(text, 0, text.length(), rect);
// Draw rotated text
canvas.save();
canvas.rotate(-angle, centerPoint.x, centerPoint.y);
canvas.drawText(text, centerPoint.x-Math.abs(rect.exactCenterX()),
Math.abs(centerPoint.y-rect.exactCenterY()), paint);
canvas.restore();
// Upload the bitmap to the Media Library
Uri uri =
getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
values);
OutputStream outStream = getContentResolver().openOutputStream(uri);
drawingBitmap.compress(Bitmap.CompressFormat.JPEG, 90, outStream);
outStream.flush();
outStream.close();
Thanks in advance for any help.
Since it works as long as the resolution isn't too high, I would just rescale all images to something that works.
You can accomplish this using
Bitmap scaledBitmap = Bitmap.createScaledBitmap(bitmap, 800 /* width */, 600 /* height */, true);
This turned out to be a memory problem although no OutOfMemoryException was visible in the log.
So, I "solved" it by scaling the image if the resolution is too high, as suggested by ingo. The problem is that I don't know how to determine the limits of a device. I suppose they are different for every device and depends on the current memory usage.
Related
I am working on application that allows users to shade a portion of bitmap when the swipe on it. Then when he finishes swiping I call a method to crop that portion of bitmap and perform some function(but cropped bitmap is not saved anywhere so doubt there is any problem here). On click on imageView I reset the bitmap with original bitmap. There is also a functionality to rotate the bitmap.I have two bitmap objects bill(user actually swipes on it) and billOrg(original bitmap as it is). Below are the methods.
private void drawShade(float left,float top,float right,float bottom){
//this method draws shade on bitmap. Coordinates are sent from onTouchEvent
TAG = "drawShade";
//parseTouchPointsString();
Bitmap tempBitmap = Bitmap.createBitmap(bill.getWidth(), bill.getHeight(), Bitmap.Config.RGB_565);
Log.d(TAG,"bill:"+bill.isMutable()+"tempBit:"+tempBitmap.isMutable()+"");
Canvas tempCanvas = new Canvas(tempBitmap);
tempCanvas.drawBitmap(bill,0,0,null);
tempCanvas.drawRoundRect(new RectF(left,top,right,bottom), 10, 10, shadePaint);
imgView.setImageDrawable(new BitmapDrawable(getResources(), tempBitmap));
if(fingerUp) {
Log.e("fingerUp",fingerUp+"");
bill = tempBitmap;
}
Log.d(TAG,"shade drawn at:"+left+","+top+","+right+","+bottom);
}
Here is method to rotate bitmap :
public void rotateImage(View v){
TAG = "rotateImage";
Matrix matrix = new Matrix();
matrix.postRotate(90);
Bitmap rotatedBitmap = Bitmap.createBitmap(bill , 0, 0, bill.getWidth(), bill.getHeight(), matrix, true);
bill = rotatedBitmap.copy(rotatedBitmap.getConfig(),true);
createScaledBitmap();
billOrg = bill.copy(rotatedBitmap.getConfig(),true);//bill.copy(bill.getConfig(),false);
setImage(bill);
rl.invalidate();
}
Method to reset bitmap on click :
private void resetImageView(boolean saveShade){
//the boolean var here tell whether to keep the shaded portion after reset or not
if(!saveShade) { //app crashes in this if block although I have try-catch.
try {
Canvas canvas = new Canvas(bill);
Log.e("resetImageView", "billOrg:" + billOrg.isMutable() + ",bill:" + bill.isMutable()); //returns true for both bitmap objects
canvas.drawBitmap(billOrg, 0, 0, null);
touchBounds = "";
tv_res.setText("");
rl.invalidate();
setImage(bill);
}catch(Exception ex){
Log.e("resetImage",ex.getMessage());
}
}else{
Canvas canvas = new Canvas(bill);
canvas.drawBitmap(bill, 0, 0, null);
touchBounds = "";
tv_res.setText("");
rl.invalidate();
setImage(bill);
}
}
All is fine except when user first swipes the image --> then rotates the image --> then clicks on bitmap.
All I get is this error: jni_helper.cc:110 Bitmap is of the wrong format: 4. No other exceptions.
As per my knowledge bitmaps usually throw errors if we try to modify a bitmap object which is immutable but I have made it mutable in all places. It prints mutable in logs too. I guess I am doing something wrong while modifying the bitmaps. I know might be confusing for you. I don't know how well I have explained. If you need any clarity kindly ask. I need some help.
Ok so I found out what was causing the problem.
Bitmap tempBitmap = Bitmap.createBitmap(bill.getWidth(), bill.getHeight(), Bitmap.Config.RGB_565);
In the drawShade() method was culprit. I changed the "Bitmap.Config.RGB_565" to bill.getConfig() and it was fixed.
I have a list of items where I am displaying a bitmap next to the item's name. This bitmap is to be created from 2 images, I have a background image with a smaller foreground image to add on top of the background.
I am seeing that the background image appears to not be present on some of my rows in my list. It is not consistent when and which row has the combined bitmap without the background. It is not always the same row where the combined bitmap does not have the background and it is not always the first or not always the last row where the bitmap does not have the background. And sometimes the whole list has every row with the correct image.
The image below is a mockup showing my issue.
My code for creating the combined bitmap is as follows.
Bitmap combinedBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas combinedCanvas = new Canvas(combinedBitmap);
// Add the first bitmap to the canvas (this is my background and this is what appears to be
// missing on some rows in my list on some occasions)
combinedCanvas.drawBitmap(backgroundBitmap, 0, 0, null);
// my second smaller image, on top of the first image but 1 pixel in
// from the left and 20 pixels down from the top
combinedCanvas.drawBitmap(foregroundBitmap, 1, 20, null);
return combinedBitmap;
Note: My backgroundBitmap is generated from a Drawable using the following code
Bitmap backgroundBitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(),
drawable.getMinimumHeight(),
Bitmap.Config.ARGB_8888);
backgroundBitmap.setDensity(resources.getDisplayMetrics().densityDpi);
Canvas canvas = new Canvas(backgroundBitmap);
drawable.draw(canvas);
Any suggestions of what I have wrong or even where to look to try and resolve this would be greatly appreciated.
EDIT: I have tested adding a colour to the background of my combinedCanvas to try and see where the image generation is going wrong by adding the following code
// TEMP: fill the canvas in red for now so I can see which combinedBitmaps are missing
// the background image
combinedCanvas.drawColor(Color.RED);
Now the rows which do not have the background are coloured in red. This indicates that the code above to create the combined canvas is somehow not adding the backgroundBitmap. I have checked and my background image is not null for every row in my list.
This method works fine for me. It's in C# (Xamarin), you'll have to translate it to Java I'm afraid.
public static Bitmap CombineImages(Bitmap background, Bitmap foreground)
{
int width = background.Width, height = background.Height;
Bitmap cs = Bitmap.CreateBitmap(width, height, Bitmap.Config.Argb8888);
Canvas comboImage = new Canvas(cs);
background = Bitmap.CreateScaledBitmap(background, width, height, true);
comboImage.DrawBitmap(background, 0, 0, null);
int top = (int)(0.05 * height);
int left = (int)(width - (foreground.Width + (width * 0.05)));
comboImage.DrawBitmap(foreground, left, top, null);
return cs;
}
The left and top are hardcoded for my requirements, it would be better to pass them in as arguments.
i'm trying to draw a Bitmap but only with a selected area from the original Bitmap or Imageview. The requirement is that the final picture must show only 1/3 from the top of the original Bitmap. I attach a draft. I suppose that i should use canvas, but i do not know exactly how it works.
Thanks in advance!!
Bitmap getTheReducedBitmap(Bitmap fullLengthBitnap)
{
Bitmap backDrop=Bitmap.createBitmap(fullLengthBitnap.getWidth(), fullLengthBitnap.getHeight()/3, Bitmap.Config.RGB_565);
Canvas can = new Canvas(backDrop);
can.drawBitmap(fullLengthBitnap, 0, 0, null);
return backDrop;
}
This is the documentation for the method you should use.
private void draw(Canvas c, Bitmap bmp){
Rect r=new Rect(0,0,bmp.width,bmp.height/3);
Rect drawR=new Rect(0,0,c.width,c.height/3);
c.drawBitmap(bmp,r,drawR,null);
}
or as a one liner:
c.drawBitmap(bmp,new Rect(0,0,bmp.width,bmp.height/3),new Rect(0,0,c.width,c.height/3),null);
It allows you to specify where on the canvas you want to draw it too, and where you want the segment to be from.
#Eu.Dr. 's answer would not work if you wanted to draw anything else on the canvas below it.
val newBitmap=Bimtap
.createBitmap(your_view.width,your_view.height,Bitmap.Config.ALPHA_8)
//note: for tablet mode your_view.width,height will increase drastically so
//you might want
//to fix the size of drawing area for optimization
//ALPHA_8 each pixel requires 1 byte of memory.
//RGB_565 Each pixel is stored on 2 bytes
//ARGB_8888 Each pixel is stored on 4 bytes
//after this you can further apply the compress like
[Refer more from google][1]
val stream = ByteArrayOutputStream();
newBitmap.compress(Bitmap.CompressFormat.PNG, 80, stream);
val byteArray = stream.toByteArray(); // convert drawing photo to byte array
// save it in your internal storage.
val storageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES
+"attendance_sheet.png");
try{
val fo = FileOutputStream(storageDir);
fo.write(byteArray);
fo.flush();
fo.close();
}catch(Exception ex){
}
I read many posts there? But i don't find correctly answer.
I try do something this:
#Override
public void onPictureTaken(byte[] paramArrayOfByte, Camera paramCamera) {
try {
Bitmap bitmap = BitmapFactory.decodeByteArray(paramArrayOfByte, 0,
paramArrayOfByte.length);
int width = bitmap.getWidth();
int height = bitmap.getHeight();
FileOutputStream os = new ileOutputStream(Singleton.mPushFilePath);
Matrix matrix = new Matrix();
matrix.postRotate(90);
Bitmap resizedBitmap = Bitmap.createBitmap(bitmap, 0, 0, width,
height, matrix, false);
resizedBitmap.compress(Bitmap.CompressFormat.JPEG, 95, os);
os.close();
...
Is there a way to rotate picture, without using BitmapFactory? I want rotate picture without loss of quality!
Perhaps you can take the picture already rotated as you desire using Camera.setDisplayOrientation? Check Android camera rotate. Further, investigate Camera.Parameters.setRotation(). One of these techniques should do the trick for you.
Otherwise your code looks fine except for using parameter 95 on Bitmap.compress, you need to use 100 for lossless compression.
To avoid out-of-memory exception, use Camera.Parameters.setPictureSize() to take a lower resolution picture (e.g. 3Mpx). i.e. do you really need an 8Mpx photo? Make sure to use Camera.Parameters.getSupportedPictureSizes() to determine the supported sizes on your device.
i created a new image by combining two images. but the size of the final is image is reduced to the size(resolution) of the screen. the code used is
Bitmap pic = BitmapFactory.decodeResource(getResources(), R.drawable.me);
Bitmap stat= BitmapFactory.decodeResource(getResources(), R.drawable.static);
Bitmap out1 = Bitmap.createBitmap(stat) ;
Canvas comboImage = new Canvas(out1);
comboImage.drawBitmap(map, 0, 0, null);
comboImage.drawBitmap(pic, 150f, 30f, null);
after this i am storing the image as
OutputStream os = null;
os = new FileOutputStream("/sdcard/DCIM/Camera/" + "myNewFileName5.png");
out1.compress(CompressFormat.PNG, 100, os);
os.flush();
os.close();
dimension of image is staic 640x480. but my final image is 320x240, which is my phones screen resolution. is it because i use Canvas? is there any way to do this without changing the image sizes?
Check your API version? There's an explanation at In the onDraw() method, why does the supplied Canvas already have scale? that for an API level lower than 4 the canvas has a default scaled compatibility mode as if it were targeting a G1 screen.