安卓:保存位图BMP格式的文件 [英] Android : save a Bitmap to bmp file format

查看:174
本文介绍了安卓:保存位图BMP格式的文件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在内存中的位图,我需要将其保存在一个bmp文件(使用 BMP格式的文件)。

有没有办法做到这一点在Android?

(我看了很多帖子建议使用PNG格式 - 这是无损的 - 但是,这不是我所需要的:我真的需要在 bmp格式)。

我已经有一些code。使用将其保存在JPEG或PNG的<一个href=\"http://developer.android.com/reference/android/graphics/Bitmap.html#com$p$pss%28android.graphics.Bitmap.Com$p$pssFormat,%20int,%20java.io.OutputStream%29\"相对=nofollow> Bitmap.com preSS 方式:

  / **
 *保存数据使用的格式到文件。
 *当格式为空:位图将被保存为BMP格式
 ** /公共无效writeBitmapToFile(位图数据,文件档案,Bitmap.Com pressFormat格式){
    FileOutputStream中OS = NULL;
    尝试{
        OS =新的FileOutputStream(文件);
        如果(格式== NULL){            // TODO:将数据写入到文件中使用bmp格式        }其他{
            data.com preSS(格式,100,OS); //确定为JPEG和PNG
        }
        os.flush();
    }赶上(例外五){
        //无关code
    } {最后
        //无关code
    }
}


解决方案

(我回答我的问题)

这是我目前的解决方案。正是从这个源得到: https://github.com/ultrakain/AndroidBitmapUtil (感谢ultrakain和@Francescoverheye)

我刚固定一个小错误中必须添加到每一行(该伪字节的计算,使得在字节每行的长度是4的倍数(所要求的bmp格式规范)。

我也做了一些修改,以提高性能。

 进口java.io.FileOutputStream中;
进口java.io.IOException异常;
进口java.nio.ByteBuffer中;进口android.graphics.Bitmap;
进口android.util.Log;公共类AndroidBmpUtil {    私有静态最终诠释BMP_WIDTH_OF_TIMES = 4;
    私有静态最终诠释BYTE_PER_PIXEL = 3;    / **
     * Android的位图对象窗口的V3 24位BMP格式的文件
     * @参数orgBitmap
     * @参数文件路径
     * @返回文件保存的结果
     * /
    公共静态布尔保存(位图orgBitmap,字符串文件路径)抛出IOException
        长启动= System.currentTimeMillis的();
        如果(orgBitmap == NULL){
            返回false;
        }        如果(文件路径== NULL){
            返回false;
        }        布尔isSaveSuccess =真;        //图片大小
        INT宽度= orgBitmap.getWidth();
        INT高度= orgBitmap.getHeight();        //图像伪数据的大小
        //原因:每幅影像行的字节数必须是4(bmp格式的要求)的倍数
        字节[] dummyBytesPerRow = NULL;
        布尔hasDummy = FALSE;
        INT rowWidthInBytes = BYTE_PER_PIXEL *宽度; //源图像宽度*字节数为en code一个像素。
        如果(rowWidthInBytes%BMP_WIDTH_OF_TIMES大于0){
            hasDummy =真;
            //虚拟的字节数,我们需要在每一行添加
            dummyBytesPerRow =新的字节[(BMP_WIDTH_OF_TIMES-(rowWidthInBytes%BMP_WIDTH_OF_TIMES))];
            //只需填写一个阵列,我们需要在每行的最后追加虚拟字节
            的for(int i = 0; I&LT; dummyBytesPerRow.length;我++){
                dummyBytesPerRow [I] =(字节)0xFF的;
            }
        }        //阵列从源图像接收像素
        INT [] =像素新INT [宽*高]。        //在文件中用于存储原始图像数据的字节数(不包括文件头)
        INT IMAGESIZE =(rowWidthInBytes +(hasDummy dummyBytesPerRow.length:0))*高度;
        //文件头的大小
        INT imageDataOffset = 0x36数据;        //文件的最终尺寸
        INT档案大小= IMAGESIZE + imageDataOffset;        // Android的位图图像数据
        orgBitmap.getPixels(像素,0,宽度,0,0,宽度,高度);        // ByteArrayOutputStream BAOS =新ByteArrayOutputStream(档案大小);
        ByteBuffer的缓冲= ByteBuffer.allocate(档案大小);        / **
         *位图文件标题写启动
         ** /
        buffer.put((字节)的0x42);
        buffer.put((字节)送出0x4d);        //尺寸
        buffer.put(writeInt(档案大小));        //保留
        buffer.put(与writeShort((短)0));
        buffer.put(与writeShort((短)0));        //图像数据开始偏移
        buffer.put(writeInt(imageDataOffset));        / **位图文件标题写结束* /        // *********        / **位图信息标题写开始* /
        //尺寸
        buffer.put(writeInt(0x28));        //宽,高
        //如果我们增加每行3个虚拟字节数:这意味着我们增加一个像素(和图像的宽度被修改。
        buffer.put(writeInt(宽+(hasDummy(dummyBytesPerRow.length == 3 1:0):?0)));
        buffer.put(writeInt(高度));        //飞机
        buffer.put(与writeShort((短)1));        //位计数
        buffer.put(与writeShort((短)24));        //位COM pression
        buffer.put(writeInt(0));        //图像数据大小
        buffer.put(writeInt(IMAGESIZE));        在每米像素//水平分辨率
        buffer.put(writeInt(0));        //垂直分辨率,每米像素(不可靠)
        buffer.put(writeInt(0));        buffer.put(writeInt(0));        buffer.put(writeInt(0));        / **位图信息标题写结束* /        INT行=高度;
        INT COL =宽度;
        指定startPosition INT =(行 - 1)* COL;
        INT endPosition =行*关口;
        而(行大于0){
            的for(int i =指定startPosition; I&LT; endPosition;我++){
                buffer.put((字节)(像素由[i]&放大器; 0x000000FF));
                buffer.put((字节)((象素由[i]&放大器; 0x0000FF00)GT;→8));
                buffer.put((字节)((象素由[i]&放大器; 0x00FF0000)GT;&GT; 16));
            }
            如果(hasDummy){
                buffer.put(dummyBytesPerRow);
            }
            行 - ;
            endPosition指定startPosition =;
            =指定startPosition指定startPosition - 关口;
        }        FOS的FileOutputStream =新的FileOutputStream(文件路径);
        fos.write(buffer.array());
        fos.close();
        Log.v(AndroidBmpUtil,System.currentTimeMillis的() - 启动+毫秒);        返回isSaveSuccess;
    }    / **
     *写整数小端
     * @参数值
     * @返回
     *引发IOException
     * /
    私人静态的byte [] writeInt(int值)抛出IOException
        字节[] B =新的字节[4];        B [0] =(字节)(值安培; 0x000000FF);
        B〔1〕=(字节)((值安培; 0x0000FF00)GT;→8);
        B〔2] =(字节)((值安培; 0x00FF0000)GT;&GT; 16);
        B〔3〕=(字节)((值安培; 0xFF000000)GT;&GT; 24);        返回b;
    }    / **
     *短写little-endian字节数组
     * @参数值
     * @返回
     *引发IOException
     * /
    私人静态的byte []与writeShort(short值)抛出IOException
        字节[] B =新的字节[2];        B [0] =(字节)(值安培;设为0x00FF);
        B〔1〕=(字节)((值安培;为0xFF00)GT;→8);        返回b;
    }
}

I have a Bitmap in memory and I need to save it in a bmp file (using the bmp file format).

Is there any way to do it on Android ?

(I read a lot of post suggesting to use the png format - which is loss-less - but, that's not what I need: I really need the bmp format).

I already have some code to save it in jpeg or png using the Bitmap.compress method :

/**
 * Save data to file using format.
 * When format is null : the bitmap will be saved in bmp format
 **/

public void writeBitmapToFile(Bitmap data, File file, Bitmap.CompressFormat format) {
    FileOutputStream os = null;
    try {
        os = new FileOutputStream(file);
        if(format==null){

            //TODO : write data to file using the bmp format

        }else{
            data.compress(format, 100, os); //ok for JPEG and PNG
        }
        os.flush();
    } catch (Exception e) {
        //irrelevant code
    } finally {
        //irrelevant code
    }
}

解决方案

(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;
    }
}

这篇关于安卓:保存位图BMP格式的文件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆