我已将byte arrays
存储在数据库中,之前已从BufferedImage
转换为:
ImageIO.write(bufferedImage, extension, bao);
return bao.toByteArray();
使用这些图像我想创建纹理图集(即精灵图纸)。最天真的解决方案是将每个byte[]
转换回BufferedImage
(约500k次),将它们写入纹理图集,完成后转换纹理图集“BufferedImage
到byte[]
回来。
我想最优雅的方法就是连接所有byte arrays
,但我怀疑它会起作用(有标题等),还是它?
答案 0 :(得分:2)
“天真”的方式也是唯一的方式,也是一种好方法。我不知道你为什么不想这样做。
我想最优雅的方法是连接所有字节数组,但我怀疑它会起作用(有标题等) - 或者它会不会? -
它不会更优雅,只有当图像最初以一些非常原始的原始或无标题位图格式保存时才会起作用。
答案 1 :(得分:2)
仅当您只有未压缩的图像数据(如BMP,TIFF,TGA或PBM文件格式)时才可以这样做。
假设您有一大堆BMP文件(在3D编程中很常见),您必须更改标题中的一些字节。
您需要的更多信息:
此外,您必须正确设置内容的字节顺序。如果你想将你的图像连接在另一个之下,那么你只需要将第二个图像的字节连接到第一个(除了标题的字节;内容通常以字节 54 开始,请查看位置的字节 11到14 。
出于可视化目的:
#########
#########
######### Image 1
#########
#########
=========
=========
========= Image 2
=========
=========
如果要将它们连接到右侧,则必须读取每个图像的每个第一行并将它们连接起来。然后第二行等等......:
Image 1 Image 2
#########=========
#########=========
#########=========
#########=========
#########=========
为了您的目的,我建议将它们放在彼此之下。
然后你必须要知道,图像的图像字节顺序是相反的(从底部的第一个像素开始,然后是从底部开始的第二个像素,依此类推......)。 还有一个可能的填充字节列,请参阅http://en.wikipedia.org/wiki/BMP_file_format#Pixel_storage。
可能还有一些字节,您必须在生成的BMP格式文件头中进行设置。要进一步读取字节头结构,请参阅:http://en.wikipedia.org/wiki/BMP_file_format#File_structure
第一张图片的字节可能是这样的(只有红色像素):
-- Header ----------------------------------------------------------------------
42 4D *86 00 00 00* 00 00 00 00 *36 00 00 00* 28 00 BM........6...(.
00 00 05 00 00 00 *05 00 00 00* 01 00 18 00 00 00 ................
00 00 50 00 00 00 00 00 00 00 00 00 00 00 00 00 ..P.............
00 00 00 00 00 00 ......
-- Body / Content --------------------------------------------------------------
v---v---v---< red pixel RGB FF,00,00 in reverse >
< Padding >---v 00 00 FF 00 00 FF 00 00 FF 00 ..........
00 FF 00 00 FF 00 00 00 FF 00 00 FF 00 00 FF 00 ................
00 FF 00 00 FF 00 00 00 FF 00 00 FF 00 00 FF 00 ................
00 FF 00 00 FF 00 00 00 FF 00 00 FF 00 00 FF 00 ................
00 FF 00 00 FF 00 00 00 FF 00 00 FF 00 00 FF 00 ................
00 FF 00 00 FF 00 ......
这可能是两者的串联字节顺序(假设第二个图像只包含蓝色像素):
-- Header ----------------------------------------------------------------------
42 4D *D6 00 00 00* 00 00 00 00 *36 00 00 00* 28 00 BM........6...(.
00 00 05 00 00 00 *0A 00 00 00* 01 00 18 00 00 00 ................
00 00 A0 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00 00 00 00 00 00 ......
-- Body / Content --------------------------------------------------------------
v---v---v---< blue pixel RGB 00,00,FF in reverse >
< Padding >---v FF 00 00 FF 00 00 FF 00 00 FF ..........
00 00 FF 00 00 00 FF 00 00 FF 00 00 FF 00 00 FF ................
00 00 FF 00 00 00 FF 00 00 FF 00 00 FF 00 00 FF ................
00 00 FF 00 00 00 FF 00 00 FF 00 00 FF 00 00 FF ................
00 00 FF 00 00 00 FF 00 00 FF 00 00 FF 00 00 FF ................
00 00 FF 00 00 00 00 00 FF 00 00 FF 00 00 FF 00 ................
00 FF 00 00 FF 00 00 00 FF 00 00 FF 00 00 FF 00 ................
00 FF 00 00 FF 00 00 00 FF 00 00 FF 00 00 FF 00 ................
00 FF 00 00 FF 00 00 00 FF 00 00 FF 00 00 FF 00 ................
00 FF 00 00 FF 00 00 00 FF 00 00 FF 00 00 FF 00 ................
00 FF 00 00 FF 00 ......
(明星很重要* ... *)
如您所见,在第一张图片的标题中,您可以找到05 00 00 00
的大小,这意味着5个像素的高度。在第二个标题中,它设置为0A 00 00 00
,它是十六进制的,表示10个像素。宽度由它们前面的4个字节表示(在这种情况下不会被修改,因为宽度将是相同的)。
如果将这两个字节顺序与BMP文件头和内容的描述进行比较,您可以想象,如何正确设置字节。
由于我对自己非常感兴趣,如何做到这一点,我已经编写了一个示例程序来完成任务:
import java.awt.image.BufferedImage;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import javax.imageio.ImageIO;
public class ConcatImages {
private static final int POS_FILE_SIZE = 2;
private static final int POS_START_CONTENT = 10;
private static final int POS_WIDTH = 18;
private static final int POS_HEIGHT = 22;
public static void main(final String[] args) throws IOException, IllegalAccessException {
final String[] files = {
"image1.bmp", "image2.bmp", "image3.bmp"
};
concatBMPFiles(files, "result_image.bmp");
}
private static void concatBMPFiles(final String[] filenames, final String resultFilename) throws IOException, IllegalAccessException {
final byte[][] fileContents = new byte[filenames.length][];
int i = 0;
for (final String file : filenames) {
fileContents[i++] = readImageBytes(file);
}
final byte[] result = concatBMPImageData(fileContents);
final OutputStream out = new BufferedOutputStream(new FileOutputStream(resultFilename));
out.write(result);
out.close();
}
private static byte[] concatBMPImageData(final byte[] ... imageDatas) throws IllegalAccessException {
int newFileSize = 0;
int newHeight = 0;
int compWidth = -1;
for (final byte[] imageData : imageDatas) {
if (compWidth > -1) {
// remove header length for all images, except the first
newFileSize -= getInt(imageData, POS_START_CONTENT);
if (compWidth != getInt(imageDatas[0], POS_WIDTH)) {
throw new IllegalAccessException("All images must have the same width!");
}
} else {
compWidth = getInt(imageDatas[0], POS_WIDTH);
}
newHeight += getInt(imageData, POS_HEIGHT);
newFileSize += imageData.length;
}
newFileSize += getInt(imageDatas[0], POS_START_CONTENT);
final byte[] result = new byte[newFileSize];
int idx = 0;
// read header from first image
for (int i = 0; i < getInt(imageDatas[0], POS_START_CONTENT); i++) {
result[idx++] = imageDatas[0][i];
}
// read content from all images
for (int fIdx = imageDatas.length - 1; fIdx >= 0; fIdx--) {
final int startContentDest = getInt(imageDatas[fIdx], POS_START_CONTENT);
for (int i = startContentDest; i < imageDatas[fIdx].length; i++) {
result[idx++] = imageDatas[fIdx][i];
}
}
// set new file size to header
setInt(result, POS_FILE_SIZE, newFileSize);
// set new height to header
setInt(result, POS_HEIGHT, newHeight);
return result;
}
private static byte[] readImageBytes(final String filename) throws IOException {
final BufferedImage image = ImageIO.read(new File(filename));
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write(image, "bmp", baos);
return baos.toByteArray();
}
private static int getInt(byte[] src, int start) {
return ((0xFF & src[start + 3]) << 24) |
((0xFF & src[start + 2]) << 16) |
((0xFF & src[start + 1]) << 8) |
(0xFF & src[start]);
}
private static void setInt(byte[] src, int start, int newValue) {
byte[] value = intToByteArr(newValue);
src[start] = value[3];
src[start + 1] = value[2];
src[start + 2] = value[1];
src[start + 3] = value[0];
}
private static byte[] intToByteArr(int value) {
byte[] result = new byte[4];
for (int i = 0; i < 4; i++) {
int shift = i << 3;
result[3 - i] = (byte) ((value & (0xff << shift)) >>> shift);
}
return result;
}
}
这只是第一个版本,适用于简单的BMP文件。出于您的目的,您可能必须直接使用concatBMPImageData
方法而不是concatBMPFiles
。让我知道,如果这对你有用!