Android : save a Bitmap to bmp file format

后端 未结 4 891
盖世英雄少女心
盖世英雄少女心 2020-12-01 19:28

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 sugg

4条回答
  •  时光取名叫无心
    2020-12-01 20:20

    Note, the code above is very slow. I've found that to optimize the code above it's necessary to make a byte array to store all the data before you put it. Of course, I was working in Xamarin C# so maybe that's why. Anyway, here's my Xamarin code in case anyone has the same problem.

          public class AndroidBmpUtil
            {
    
                private static int BMP_WIDTH_OF_TIMES = 4;
                private static 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 byte[] ConvertAndroidBitmapByteArray(Bitmap orgBitmap, String filePath)
                {
                    if (orgBitmap == null)
                    {
                        return null;
                    }
    
                    if (filePath == null)
                    {
                        return null;
                    }
    
                    //image size
                    int width = orgBitmap.Width;
                    int height = orgBitmap.Height;
    
                    //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;
                    bool 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((sbyte)0x42);
                    buffer.Put((sbyte)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;
    
                    // This while loop is a lengthy process
                    // Puts take a while so only do one by creating a big array called final
                    byte[] final = new byte[0];
                    while (row > 0)
                    {
                        // This array is also used to cut down on time of puts
                        byte[] b = new byte[(endPosition - startPosition)*3];
                        int counter = 0;
                        for (int i = startPosition; i < endPosition; i++)
                        {
    
                            b[counter] = (byte)((pixels[i] & 0x000000FF));
                            b[counter + 1] = (byte)((pixels[i] & 0x0000FF00) >> 8);
                            b[counter + 2] = (byte)((pixels[i] & 0x00FF0000) >> 16);
                            counter += 3;
                        }
                        int finalPriorLength = final.Length;
                        Array.Resize(ref final, finalPriorLength + b.Length);
                        Array.Copy(b, 0, final, finalPriorLength, b.Length);
    
                        if (hasDummy)
                        {
                            finalPriorLength = final.Length;
                            Array.Resize(ref final, finalPriorLength + dummyBytesPerRow.Length);
                            Array.Copy(dummyBytesPerRow, 0, final, finalPriorLength, dummyBytesPerRow.Length);
                        }
                        row--;
                        endPosition = startPosition;
                        startPosition = startPosition - col;
                    }
                    buffer.Put(final);
                    buffer.Rewind();
    
                    IntPtr classHandle = JNIEnv.FindClass("java/nio/ByteBuffer");
                    IntPtr methodId = JNIEnv.GetMethodID(classHandle, "array", "()[B");
                    IntPtr resultHandle = JNIEnv.CallObjectMethod(buffer.Handle, methodId);
                    byte[] result = JNIEnv.GetArray(resultHandle);
                    JNIEnv.DeleteLocalRef(resultHandle);
    
                    return result;
                }
    
                /**
                 * Write integer to little-endian
                 * @param value
                 * @return
                 * @throws IOException
                 */
                private static byte[] writeInt(int value)
                {
                    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)
                {
                    byte[] b = new byte[2];
    
                    b[0] = (byte)(value & 0x00FF);
                    b[1] = (byte)((value & 0xFF00) >> 8);
    
                    return b;
                }
            }
        }
    

    To use it here's the calling code:

    using (FileStream outStream = new    FileStream(@yourFilePath, FileMode.Create))
    {
        Bitmap Signature = Bitmap.CreateBitmap(user defined values...); 
        byte[] buffer = AndroidBmpUtil.ConvertAndroidBitmapByteArray(Signature, @yourFilePath); 
    
        // Actually create the file                                 
        outStream.Write(buffer, 0, buffer.Length);
    }
    

提交回复
热议问题