Android:Save a Bitmap to Bmp File Format

Android : save a Bitmap to bmp file format

(I'm answering my own question)

Here is my current solution. It is derived from this source : https://github.com/ultrakain/AndroidBitmapUtil (thanks to ultrakain and @Francescoverheye )

I just fix a little bug in computation of the dummy bytes that must be added to each row (so that the length of each row in bytes is a multiple of 4 (as required by the bmp format specifications).

I also made some changes to improve the performances.

import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;

import android.graphics.Bitmap;
import android.util.Log;

public class AndroidBmpUtil {

private static final int BMP_WIDTH_OF_TIMES = 4;
private static final int BYTE_PER_PIXEL = 3;

/**
* Android Bitmap Object to Window's v3 24bit Bmp Format File
* @param orgBitmap
* @param filePath
* @return file saved result
*/
public static boolean save(Bitmap orgBitmap, String filePath) throws IOException {
long start = System.currentTimeMillis();
if(orgBitmap == null){
return false;
}

if(filePath == null){
return false;
}

boolean isSaveSuccess = true;

//image size
int width = orgBitmap.getWidth();
int height = orgBitmap.getHeight();

//image dummy data size
//reason : the amount of bytes per image row must be a multiple of 4 (requirements of bmp format)
byte[] dummyBytesPerRow = null;
boolean hasDummy = false;
int rowWidthInBytes = BYTE_PER_PIXEL * width; //source image width * number of bytes to encode one pixel.
if(rowWidthInBytes%BMP_WIDTH_OF_TIMES>0){
hasDummy=true;
//the number of dummy bytes we need to add on each row
dummyBytesPerRow = new byte[(BMP_WIDTH_OF_TIMES-(rowWidthInBytes%BMP_WIDTH_OF_TIMES))];
//just fill an array with the dummy bytes we need to append at the end of each row
for(int i = 0; i < dummyBytesPerRow.length; i++){
dummyBytesPerRow[i] = (byte)0xFF;
}
}

//an array to receive the pixels from the source image
int[] pixels = new int[width * height];

//the number of bytes used in the file to store raw image data (excluding file headers)
int imageSize = (rowWidthInBytes+(hasDummy?dummyBytesPerRow.length:0)) * height;
//file headers size
int imageDataOffset = 0x36;

//final size of the file
int fileSize = imageSize + imageDataOffset;

//Android Bitmap Image Data
orgBitmap.getPixels(pixels, 0, width, 0, 0, width, height);

//ByteArrayOutputStream baos = new ByteArrayOutputStream(fileSize);
ByteBuffer buffer = ByteBuffer.allocate(fileSize);

/**
* BITMAP FILE HEADER Write Start
**/
buffer.put((byte)0x42);
buffer.put((byte)0x4D);

//size
buffer.put(writeInt(fileSize));

//reserved
buffer.put(writeShort((short)0));
buffer.put(writeShort((short)0));

//image data start offset
buffer.put(writeInt(imageDataOffset));

/** BITMAP FILE HEADER Write End */

//*******************************************

/** BITMAP INFO HEADER Write Start */
//size
buffer.put(writeInt(0x28));

//width, height
//if we add 3 dummy bytes per row : it means we add a pixel (and the image width is modified.
buffer.put(writeInt(width+(hasDummy?(dummyBytesPerRow.length==3?1:0):0)));
buffer.put(writeInt(height));

//planes
buffer.put(writeShort((short)1));

//bit count
buffer.put(writeShort((short)24));

//bit compression
buffer.put(writeInt(0));

//image data size
buffer.put(writeInt(imageSize));

//horizontal resolution in pixels per meter
buffer.put(writeInt(0));

//vertical resolution in pixels per meter (unreliable)
buffer.put(writeInt(0));

buffer.put(writeInt(0));

buffer.put(writeInt(0));

/** BITMAP INFO HEADER Write End */

int row = height;
int col = width;
int startPosition = (row - 1) * col;
int endPosition = row * col;
while( row > 0 ){
for(int i = startPosition; i < endPosition; i++ ){
buffer.put((byte)(pixels[i] & 0x000000FF));
buffer.put((byte)((pixels[i] & 0x0000FF00) >> 8));
buffer.put((byte)((pixels[i] & 0x00FF0000) >> 16));
}
if(hasDummy){
buffer.put(dummyBytesPerRow);
}
row--;
endPosition = startPosition;
startPosition = startPosition - col;
}

FileOutputStream fos = new FileOutputStream(filePath);
fos.write(buffer.array());
fos.close();
Log.v("AndroidBmpUtil" ,System.currentTimeMillis()-start+" ms");

return isSaveSuccess;
}

/**
* Write integer to little-endian
* @param value
* @return
* @throws IOException
*/
private static byte[] writeInt(int value) throws IOException {
byte[] b = new byte[4];

b[0] = (byte)(value & 0x000000FF);
b[1] = (byte)((value & 0x0000FF00) >> 8);
b[2] = (byte)((value & 0x00FF0000) >> 16);
b[3] = (byte)((value & 0xFF000000) >> 24);

return b;
}

/**
* Write short to little-endian byte array
* @param value
* @return
* @throws IOException
*/
private static byte[] writeShort(short value) throws IOException {
byte[] b = new byte[2];

b[0] = (byte)(value & 0x00FF);
b[1] = (byte)((value & 0xFF00) >> 8);

return b;
}
}

Android: Save Bitmap object as a bmp (1bpp) file format

Answering my own question...

After some tough search all over the web I realized I had to create 2 things: a bitmap black and white - and did that using the approach of making all 0's for colors below 128 and 255's for the rest, like this (this is C# code, as I'm using Xamarin to code my app):

private void ConvertArgbToOneColor (Bitmap bmpOriginal, int width, int height){
int pixel;
int k = 0;
int B=0,G=0,R=0;
try{
for(int x = 0; x < height; x++) {
for(int y = 0; y < width; y++, k++) {
pixel = bmpOriginal.GetPixel(y, x);

R = Color.GetRedComponent(pixel);
G = Color.GetGreenComponent(pixel);
B = Color.GetBlueComponent(pixel);

R = G = B = (int)(0.299 * R + 0.587 * G + 0.114 * B);
if (R < 128) {
m_imageArray[k] = 0;
} else {
m_imageArray[k] = 1;
}
}
if(m_dataWidth>width){
for(int p=width;p<m_dataWidth;p++,k++){
m_imageArray[k]=1;
}
}
}
}catch (Exception e) {
System.Console.WriteLine ("Converting to grayscale ex: " + e.Message);
}
}

Then get the byteArray of Monochrome image:

int length = 0;
for (int i = 0; i < m_imageArray.Length; i = i + 8) {
byte first = m_imageArray[i];
for (int j = 0; j < 7; j++) {
byte second = (byte) ((first << 1) | m_imageArray[i + j]);
first = second;
}
m_rawImage[length] = first;
length++;
}

And finally create the bitmap "by hand" using the following variables and placing them into a FileOutputStream to save the file:

    private static int FILE_HEADER_SIZE = 14;
private static int INFO_HEADER_SIZE = 40;

// Bitmap file header
private byte[] bfType = { (byte) 'B', (byte) 'M' };
private int bfSize = 0;
private int bfReserved1 = 0;
private int bfReserved2 = 0;
private int bfOffBits = FILE_HEADER_SIZE + INFO_HEADER_SIZE + 8;

// Bitmap info header
private int biSize = INFO_HEADER_SIZE;
private int biWidth = 0;
private int biHeight = 0;
private int biPlanes = 1;
private int biBitCount = 1;
private int biCompression = 0;
private int biSizeImage = 0;
private int biXPelsPerMeter = 0x0;
private int biYPelsPerMeter = 0x0;
private int biClrUsed = 2;
private int biClrImportant = 2;

// Bitmap raw data
private byte[] bitmap;

// Scanlinsize;
int scanLineSize = 0;

// Color Pallette to be used for pixels.
private byte[] colorPalette = { 0, 0, 0, (byte) 255, (byte) 255,
(byte) 255, (byte) 255, (byte) 255 };

Save bitmap to location

try (FileOutputStream out = new FileOutputStream(filename)) {
bmp.compress(Bitmap.CompressFormat.PNG, 100, out); // bmp is your Bitmap instance
// PNG is a lossless format, the compression factor (100) is ignored
} catch (IOException e) {
e.printStackTrace();
}

creating .bmp image file from Bitmap class

Try following code to save image as PNG format

try {
FileOutputStream out = new FileOutputStream(filename);
bitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
} catch (Exception e) {
e.printStackTrace();
}
out.flush();
out.close();

Here, 100 is quality to save in Compression. You can pass anything between 0 to 100. Lower the digit, poor quality with decreased size.

Note

You need to take permission in Android Manifest file.

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

Edit

To save your image to .BMP format, Android Bitmap Util will help you. It has very simple implementation.

String sdcardBmpPath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/sample_text.bmp";
Bitmap testBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.sample_text);
AndroidBmpUtil bmpUtil = new AndroidBmpUtil();
boolean isSaveResult = bmpUtil.save(testBitmap, sdcardBmpPath);

Android save bitmap to image file

1.Check it if you don't request the runtime permission yet: https://developer.android.com/training/permissions/requesting

2.Or if your android is higher than 10:
https://developer.android.com/about/versions/11/privacy/storage#scoped-storage

  • After you update your app to target Android 11, the system ignores the requestLegacyExternalStorage flag.

Then you have to use SAF or MediaStore API to store the bitmap in the "public directory".

SAF:
https://developer.android.com/guide/topics/providers/document-provider

MediaStore API:
https://developer.android.com/reference/android/provider/MediaStore

public void onBtnSavePng(View view) {
try {
String fileName = getCurrentTimeString() + ".jpg";

ContentValues values = new ContentValues();
values.put(MediaStore.Images.Media.DISPLAY_NAME, fileName);
values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpg");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
values.put(MediaStore.MediaColumns.RELATIVE_PATH, "DCIM/");
values.put(MediaStore.MediaColumns.IS_PENDING, 1);
} else {
File directory = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM);
File file = new File(directory, fileName);
values.put(MediaStore.MediaColumns.DATA, file.getAbsolutePath());
}

Uri uri = getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);

try (OutputStream output = getContentResolver().openOutputStream(uri)) {
Bitmap bm = textureView.getBitmap();
bm.compress(Bitmap.CompressFormat.JPEG, 100, output);
}
} catch (Exception e) {
Log.d("onBtnSavePng", e.toString()); // java.io.IOException: Operation not permitted
}
}

How to save image file in BMP format?

There's no built-in encoder for BMP according to this reference. BMP not being an overly complex format, it probably wouldn't be rocket science to write/find a Java implementation.

How to Save bitmap to app folder in android

You can just do this:

try {
FileOutputStream fileOutputStream = context.openFileOutput("Your File Name", Context.MODE_PRIVATE);
bitmap.compress(Bitmap.CompressFormat.PNG, 100, fileOutputStream);
fileOutputStream.close();
} catch (Exception e) {
e.printStackTrace();
}

It saves your bitmap in the "files" directory in app folder.

Save Bitmap into File and return File having bitmap image

I try to make some corrections on your code
I assume that you want to use filename instead of bitmap as parameter

 private File savebitmap(String filename) {
String extStorageDirectory = Environment.getExternalStorageDirectory().toString();
OutputStream outStream = null;

File file = new File(filename + ".png");
if (file.exists()) {
file.delete();
file = new File(extStorageDirectory, filename + ".png");
Log.e("file exist", "" + file + ",Bitmap= " + filename);
}
try {
// make a new bitmap from your file
Bitmap bitmap = BitmapFactory.decodeFile(file.getName());

outStream = new FileOutputStream(file);
bitmap.compress(Bitmap.CompressFormat.PNG, 100, outStream);
outStream.flush();
outStream.close();
} catch (Exception e) {
e.printStackTrace();
}
Log.e("file", "" + file);
return file;

}


Related Topics



Leave a reply



Submit