Обратите внимание, что код выше очень медленный. Я обнаружил, что для оптимизации вышеописанного кода необходимо сделать массив байтов для хранения всех данных до его размещения. Конечно, я работал в Xamarin C #, поэтому, возможно, именно поэтому. Во всяком случае, вот мой код Xamarin, если у кого-то такая же проблема.
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<byte>(ref final, finalPriorLength + b.Length);
Array.Copy(b, 0, final, finalPriorLength, b.Length);
if (hasDummy)
{
finalPriorLength = final.Length;
Array.Resize<byte>(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<byte>(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;
}
}
}
Чтобы использовать его, это код вызова:
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);
}