[英]Merge images into one file without creating the full image in memory
我需要在 Android 中“分塊處理”一些任意大小的位圖。 我可以輕松地使用 BitmapRegionDecoder 創建較小的塊進行處理,但是一旦完成,我需要將它們重新組合成一個文件。 由於最終圖像可以是任意大小,因此不能在內存中創建相應的位圖並使用畫布在其上書寫。
我看過這個線程,但我想在沒有外部庫的情況下做到這一點(我在使用 Android 時受到這些限制)。 理想情況下,我追求某種 BitmapRegionEncoder。 只要是圖像(PNG、JPG 甚至 BMP),我就不太關心輸出格式。 如果在 Java 中不可能,我也很高興使用 JNI 在 C 中完成。
一個簡單的方法是將塊存儲在一個表結構中,然后讀回它們並將數據寫回文件作為圖像文件。
這是一個示例表結構。
public class Chunk
{
private int chunkId;
private byte[] chunkdata;
private int nextchunkId;
}
從表中讀取塊的方法
private Chunk getChunk(int index){
Chunk chunk = null;
if(index == 1){ // this assumes that the chunk id starts from 1
//get and return Chunk where chunkId == 1 from the table
}
else{
// get and return Chunk where nextchunkId == index from the table
}
return chunk
}
現在將塊直接寫入二進制文件
private void mergeChunksToFile(){
int index = 1; // this assumes that the chunk id starts from 1
// Create a binary file in append mode to store the data, which is the image
Chunk chunk = getChunk(index);
while(chunk != null){
// Here, write chunk.chunkdata to the binary file
index = chunk.nextchunkId;
// get the next chunk
chunk = getChunk(index);
}
}
這可能不是最好的解決方案,但它應該可以幫助您了解如何在不使用任何外部庫的情況下進行操作
我最終做了什么:
public class BitmapChunkWriter {
private static final int BMP_WIDTH_OF_TIMES = 4;
private static final int BYTE_PER_PIXEL = 3;
private FileOutputStream fos;
private byte[] dummyBytesPerRow;
private boolean hasDummy;
private int imageSize;
private int fileSize;
private int rowWidthInBytes;
/**
* Android Bitmap Object to Window's v3 24bit Bmp Format File
* @param imageWidth
* @param imageHeight
* @param filePath
* @return file saved result
*/
public void writeHeader(int imageWidth, int imageHeight, String filePath) throws IOException {
//image dummy data size
//reason : the amount of bytes per image row must be a multiple of 4 (requirements of bmp format)
dummyBytesPerRow = null;
hasDummy = false;
rowWidthInBytes = BYTE_PER_PIXEL * imageWidth; //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;
}
}
//the number of bytes used in the file to store raw image data (excluding file headers)
imageSize = (rowWidthInBytes + (hasDummy ? dummyBytesPerRow.length : 0)) * imageHeight;
//file headers size
int imageDataOffset = 0x36;
//final size of the file
fileSize = imageSize + imageDataOffset;
//ByteArrayOutputStream baos = new ByteArrayOutputStream(fileSize);
ByteBuffer buffer = ByteBuffer.allocate(imageDataOffset);
/**
* 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(imageWidth + (hasDummy ? (dummyBytesPerRow.length == 3 ? 1 : 0) : 0)));
buffer.put(writeInt(imageHeight));
//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));
fos = new FileOutputStream(filePath);
fos.write(buffer.array());
}
public void writeChunk(Bitmap bitmap) throws IOException {
int chunkWidth = bitmap.getWidth();
int chunkHeight = bitmap.getHeight();
//an array to receive the pixels from the source image
int[] pixels = new int[chunkWidth * chunkHeight];
//Android Bitmap Image Data
bitmap.getPixels(pixels, 0, chunkWidth, 0, 0, chunkWidth, chunkHeight);
//the number of bytes used in the file to store raw image data (excluding file headers)
//int imageSize = (rowWidthInBytes + (hasDummy ? dummyBytesPerRow.length : 0)) * height;
int chunkSize = (rowWidthInBytes + (hasDummy ? dummyBytesPerRow.length : 0)) * chunkHeight;
ByteBuffer buffer = ByteBuffer.allocate(chunkSize);
int row = chunkHeight;
int col = chunkWidth;
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;
}
fos.write(buffer.array());
}
public void finish() throws IOException {
fos.close();
}
/**
* 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;
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.