I have a project that needs to rotate a bitmap, the bitmap is in GRAYSCALE/Black and White mode. When I rotate that bitmap, it works but the background (the white part of the bitmap) is rotated too. Is there anyway to rotate just the foreground (the non white part of the bitmap) only? or if I must create a new function to do that, can you explain the algorithm?
Thanks for your help..
I edit for the relevant code :
int i;
InputStream inStream = context.getResources().openRawResource(R.raw.register2);
BufferedInputStream bis = new BufferedInputStream(inStream, 8000);
DataInputStream dis=new DataInputStream(bis);
byte[] music=null;
try {
music = new byte[inStream.available()];
i=0;
while(dis.available()>0)
{
music[i]=dis.readByte();
i++;
}
dis.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Bitmap bm = Bitmap.createBitmap(260, 300, Bitmap.Config.ARGB_8888);
ByteBuffer byteBuff=ByteBuffer.allocate(260*300);
bm.setHasAlpha(false);
if(false)
byteBuff.put(music);
int[] intbuffer = new int[260*300];
for (int x=0; x<intbuffer.length; ++x)
intbuffer[x] = (int) music[x];
bm.setPixels(intbuffer, 0, 260, 0, 0, 260, 300);
//img1.setImageBitmap(bm);
//Canvas canvas=new Canvas(bm);
Matrix matrix=new Matrix();
matrix.postRotate(-90);
Bitmap rotated=Bitmap.createBitmap(bm, 0, 0, 260, 300, matrix, true);
//matrix.setRotate(4,bm.getWidth()/2,bm.getHeight()/2);
//canvas.drawBitmap(bm, matrix, new Paint());
img1.setImageBitmap(rotated);
Given that your foreground and background pixels are diff in color-
You need to extract/separate out your foreground pixels and background pixels in two different bitmaps. The two new bitmaps that you create should only have the pixels extracted from your original bitmap rest everything should be transparent.
Once you have your foreground and background bitmap separated , just rotate your foreground and then merge back.
Related
I want to get the picture of the view(not ImageView) inside,but the picture I got was not clear,Of course the original picture is clear ,here is my codes
public static Bitmap getDrawBitmap(View designView){
designView.destroyDrawingCache();
designView.setDrawingCacheEnabled(true);
designView.setDrawingCacheQuality(View.DRAWING_CACHE_QUALITY_HIGH);
return designView.getDrawingCache();
}
I have tried to scale the View,and then getDrawingCache(),but it didn't work,the view's size didn't change
Bitmap bitmap=ImgUtils.getDrawBitmap(testBtn);
float w=1260f;
float h=1660f;
float x= w/(bitmap.getWidth()/DrawAttribute.density);
float y= h/(bitmap.getHeight()/DrawAttribute.density);
Log.e("x-y",x+"-"+y);
Bitmap result=Bitmap.createBitmap(1260,1660,Bitmap.Config.ARGB_8888);
Canvas offScreen = new Canvas(result);
Matrix matrix=new Matrix();
matrix.setScale(x,y);
offScreen.drawBitmap(bitmap, matrix, null);
//存储路径
OutputStream stream = null;
try {
if (LiApplication.creatDir()) {
stream = new FileOutputStream(Environment.getExternalStorageDirectory()+ "/yc/img/"
+ "save.png");
}
} catch (FileNotFoundException e) {
}
Bitmap.CompressFormat format = Bitmap.CompressFormat.PNG;
result.compress(format, 100, stream);
This is a Demo code for taking screen shot of whole View.
View v = (View) findViewById(R.id.myView);
Bitmap mybitmap = Bitmap.createBitmap(
v.getChildAt(0).getWidth(),
v.getChildAt(0).getHeight(),
Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(mybitmap);
iv.getChildAt(0).draw(c);
//remember here View may be any view including ScrollView or what ever it may be.
saveBitmap(bitmap);
Hope this will help. Thanks
I have a little problem with rendering an object offscreen to create a bitmap of it and displaying it in an imageview. It does not shot the alpha channel correctly.
It works fine, when I save the bitmap as png and loading it then. But when I directly load it into an imageview, I will see a white background, which is the actual background color without the alpha channel.
Here the code for exporting the bitmap from my EGL Surface:
public Bitmap exportBitmap() {
ByteBuffer buffer = ByteBuffer.allocateDirect(w*h*4);
GLES20.glReadPixels(0, 0, w, h, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, buffer);
Bitmap bitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
bitmap.copyPixelsFromBuffer(buffer);
Log.i("Render","Terminating");
EGL14.eglMakeCurrent(oldDisplay, oldDrawSurface, oldReadSurface, oldCtx);
EGL14.eglDestroySurface(eglDisplay, eglSurface);
EGL14.eglDestroyContext(eglDisplay, eglCtx);
EGL14.eglTerminate(eglDisplay);
return bitmap;
}
And here the code for setting the imageview (r is the class containing the previous function):
Bitmap bm;
bm = r.exportBitmap();
ImageView infoView = (ImageView)findViewById(R.id.part_icon);
infoView.setImageBitmap(bm);
Do I have to set some flags on the ImageView or set something in the config of the bitmap?
I'll add some code examples plus images to clarify the problem:
First the way I want it to work:
bm = renderer.exportBitmap();
Second the way it works, with the save to png workaround:
bm = renderer.exportBitmap();
//PNG To Bitmap
String path = Environment.getExternalStorageDirectory()+"/"+getName()+".png";
bm.compress(CompressFormat.PNG, 100, new FileOutputStream(new File(path)));
bm = BitmapFactory.decodeFile(path);
Third to clarify my premultiplied. Alpha is taken into account the wrong way:
bm = renderer.exportBitmap();
for(int x=bm.getWidth()-50; x<bm.getWidth(); x++) {
for(int y=bm.getHeight()-50; y<bm.getHeight(); y++) {
int px = bm.getPixel(x,y);
bm.setPixel(x, y,
Color.argb(255,
Color.red(px),
Color.green(px),
Color.blue(px)));
}
}
Sorry for the long post.
I have changed the exportBitmap() function. Basically I read the pixels and write them again. This is solving the problem. I really don't know why. It would be nice, if somebody could explain it to me. It is not that clean to create a new integer array for these bitmapdata.
public Bitmap exportBitmap() {
ByteBuffer buffer = ByteBuffer.allocateDirect(w*h*4);
GLES20.glReadPixels(0, 0, w, h, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, buffer);
Bitmap bitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
bitmap.copyPixelsFromBuffer(buffer);
//This is the modification
int[] pixels = new int[w*h];
bitmap.getPixels(pixels, 0, w,0, 0,w, h);
bitmap.setPixels(pixels, 0, w,0, 0,w, h);
EGL14.eglMakeCurrent(oldDisplay, oldDrawSurface, oldReadSurface, oldCtx);
EGL14.eglDestroySurface(eglDisplay, eglSurface);
EGL14.eglDestroyContext(eglDisplay, eglCtx);
EGL14.eglTerminate(eglDisplay);
return bitmap;
}
I have the following code for creating a bitmap:
public PixelMapper(Path inputPath){
RectF src = new RectF();
inputPath.computeBounds(src, true);
int width = (int)src.width()+1;
int height = (int)src.height()+1;
largeBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ALPHA_8);
Paint canvasPaint = new Paint();
canvasPaint.setAntiAlias(false);
canvasPaint.setColor(Color.BLACK);
canvasPaint.setStyle(Style.STROKE);
canvasPaint.setStrokeWidth(5);
Canvas canvas = new Canvas(largeBitmap);
canvas.drawPath(inputPath, canvasPaint);
bitmap = Bitmap.createScaledBitmap(largeBitmap, SIDE_OF_BITMAP, SIDE_OF_BITMAP, true);
bitmap = Bitmap.createBitmap(bitmap);//so that a immutable bitmap is created
drawPixelMap();
}
public void drawPixelMap(){
for(int x=0; x<bitmap.getWidth(); x++){
String msg="";
for(int y=0; y<bitmap.getHeight(); y++){
msg = Integer.toHexString( bitmap.getPixel(x,y) );
Log.v("bitmap", msg);
}
}
}
int[] pixels = new int[64];
bitmap.getPixels(pixels, 0, bitmap.getWidth(), 0, 0, SIDE_OF_BITMAP, SIDE_OF_BITMAP);
bitmap.setPixels(pixels, 0, 8, 0, 0, 8, 8);
for ( int pixel : pixels)
Log.v("bitmap", Integer.toHexString(pixel) );
The problem is that all the log messages are "0": both getPixel and getPixels return "0". What is worse is that if I remove the line bitmap.getPixels(...); and leave the bitmap.setPixels(...) line,the image is still drawn as before. It seems that the bitmap variable is just a reference and a bitmap doesn't exist, and for some reason, I am unable to get those pixels.
I know the bitmap is created as required as I am able to view it on a ImageView. It shows black and white pixels with a few grey ones too. Code:
Bitmap newBitmap = Bitmap.createScaledBitmap(bitmap, 128, 128, false);
imageView1.setImageBitmap(newBitmap);
The SIDE_OF_BITMAP = 8, all classes (Path, Bitmap, Canvas) are of android.
I tried saving the bitmap to file with the following code:
public String saveToStorage(String fileName){
if( !storageDir.exists() )
storageDir.mkdirs();
File file = new File(storageDir, fileName + ".png");
try{
OutputStream out = new FileOutputStream(file);
boolean result = bitmap.compress(CompressFormat.PNG, 100, out);
out.close();
return Boolean.toString(result);
}
catch (FileNotFoundException e){
Log.e("save", "cannot save file", e);
return "File not found exception";
}
catch (IOException e){
Log.e("save", "IO error", e);
return "IO Exception";
}
}
but it returns "false" i.e. the bitmap.compress method returns false. Please give me any help at all, not necessarily sample code.
I found my mistake. It lies in the line
largeBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ALPHA_8);
The Bitmap.Config.ALPHA_8 only creates a mask. It does not create actual pixels. That is why I was getting the erroneous results. However, when I was setting it to a ImageView I was getting the required result because its background (by default) is white.
I solved my problem by changing the Bitmap.Config.ALPHA_8 to Bitmap.Config.ARGB_8888
The line
bitmap = Bitmap.createBitmap(bitmap);
creates a immutable bitmap of the same dimension/color depth, etc as the source bitmap, but not converting the source bitmap from mutable to immutable. So your new bitmap is blank.
You can first create a immutable bitmap using newBitmap = Bitmap.createBitmap(int[], int, int, Bitmap.Config), then create a canvas for it by canvas = new Canvas(newBitmap). Your bitmap can now be copied to newBitmap by canvas.drawBitmap(bitmap, 0, 0, null).
I'm trying to build a ring from twenty copies of a single image, which is a 1/20th slice of the full ring. I generate bitmaps that are this original image rotated to their correct degree amounts. The original image is a 130x130 square
The code that generates the rotated slices looks like this:
Bitmap bmp = BitmapFactory.decodeResource(context.getResources(), R.drawable.battery_green);
FileOutputStream fos;
for(int i = 0; i < 20; i++) {
String idName = "batt_s_"+i;
Matrix m = new Matrix();
m.setRotate((i * 18)-8, bmp.getWidth()/2, bmp.getHeight()/2);
Bitmap newBmp = Bitmap.createBitmap(bmp, 0, 0, bmp.getWidth(), bmp.getHeight(), m, true);
try {
fos = context.openFileOutput(idName+"_"+color+".png", Context.MODE_WORLD_READABLE);
newBmp.compress(CompressFormat.PNG, 100, fos);
fos.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
m.reset();
BattConfigureSmall.saveInitPref(context, true);
}
The ImageViews that these generated bitmaps are eventually being slotted into all have scaleType="center" in their XML. However, the generated output looks like this:
Not exactly a perfect ring. The slices themselves, if rotated correctly, do make a perfect ring, because in API level 11 and up I'm using the android:rotate XML attribute on these ImageViews, but I need to support API levels 7-10 as well, so can anybody give me some advice? Thank you.
Don't use the matrix with createBitmap for this scenario, it'll do some odd things with image sizing, I think. Instead, create a new Bitmap and Canvas then draw to it with the matrix:
Bitmap bmp = BitmapFactory.decodeResource(context.getResources(), R.drawable.battery_green);
FileOutputStream fos;
Paint paint = new Paint();
paint.setAntiAlias(true);
Matrix m = new Matrix();
for(int i = 0; i < 20; i++) {
String idName = "batt_s_"+i;
m.setRotate((i * 18)-8, bmp.getWidth()/2, bmp.getHeight()/2);
Bitmap newBmp = Bitmap.createBitmap(bmp.getWidth(), bmp.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(newBmp);
canvas.drawBitmap(bmp, m, paint);
try {
fos = context.openFileOutput(idName+"_"+color+".png", Context.MODE_WORLD_READABLE);
newBmp.compress(CompressFormat.PNG, 100, fos);
fos.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
m.reset();
BattConfigureSmall.saveInitPref(context, true);
}
I have an app that displays quite a few images for the user, and we've been seeing a lot of error reports with OutOfMemoryError exception.
What we currently do is this:
// Check if image is a landscape image
if (bmp.getWidth() > bmp.getHeight()) {
// Rotate it to show as a landscape
Matrix m = image.getImageMatrix();
m.postRotate(90);
bmp = Bitmap.createBitmap(bmp, 0, 0, bmp.getWidth(), bmp.getHeight(), m, true);
}
image.setImageBitmap(bmp);
The obvious problem with this is that we have to recreate the bitmap from the image on memory and rotate the matrix, this is quite expensive for the memory.
My question is simple:
Is there a better way to rotate images without causing OutOfMemoryError?
2 methods of rotating a large image:
using JNI , like on this post.
using a file : it's a very slow way (depending on the input and the device , but still very slow) , which puts the decoded rotated image into the disk first , instead of putting it into the memory .
code of using a file is below:
private void rotateCw90Degrees()
{
Bitmap bitmap=BitmapFactory.decodeResource(getResources(),INPUT_IMAGE_RES_ID);
// 12 => 7531
// 34 => 8642
// 56 =>
// 78 =>
final int height=bitmap.getHeight();
final int width=bitmap.getWidth();
try
{
final DataOutputStream outputStream=new DataOutputStream(new BufferedOutputStream(openFileOutput(ROTATED_IMAGE_FILENAME,Context.MODE_PRIVATE)));
for(int x=0;x<width;++x)
for(int y=height-1;y>=0;--y)
{
final int pixel=bitmap.getPixel(x,y);
outputStream.writeInt(pixel);
}
outputStream.flush();
outputStream.close();
bitmap.recycle();
final int newWidth=height;
final int newHeight=width;
bitmap=Bitmap.createBitmap(newWidth,newHeight,bitmap.getConfig());
final DataInputStream inputStream=new DataInputStream(new BufferedInputStream(openFileInput(ROTATED_IMAGE_FILENAME)));
for(int y=0;y<newHeight;++y)
for(int x=0;x<newWidth;++x)
{
final int pixel=inputStream.readInt();
bitmap.setPixel(x,y,pixel);
}
inputStream.close();
new File(getFilesDir(),ROTATED_IMAGE_FILENAME).delete();
saveBitmapToFile(bitmap); //for checking the output
}
catch(final IOException e)
{
e.printStackTrace();
}
}
you can try:
image.setImageBitmap(null);
// Check if image is a landscape image
if (bmp.getWidth() > bmp.getHeight()) {
// Rotate it to show as a landscape
Matrix m = image.getImageMatrix();
m.postRotate(90);
bmp = Bitmap.createBitmap(bmp, 0, 0, bmp.getWidth(), bmp.getHeight(), m, true);
}
BitmapDrawable bd = new BitmapDrawable(mContext.getResources(), bmp);
bmp.recycle();
bmp = null;
setImageDrawable(bd);
bd = null;
When working with lots of Bitmaps be sure to call recycle() on them as soon as they are not needed. This call will instantly free memory associated with a particular bitmap.
In your case if you do not need the original bitmap after rotation, then recycle it. Something along the lines of:
Bitmap result = bmp;
// Check if image is a landscape image
if (bmp.getWidth() > bmp.getHeight()) {
// Rotate it to show as a landscape
Matrix m = image.getImageMatrix();
m.postRotate(90);
result = Bitmap.createBitmap(bmp, 0, 0, bmp.getWidth(), bmp.getHeight(), m, true);
// rotating done, original not needed => recycle()
bmp.recycle();
}
image.setImageBitmap(result);