java - 从 RGB 整数数组生成位图。我的尝试有什么问题?
问题描述
我想在我的游戏中添加截图功能。
int width = 320;
int height = width / 4*3;
pixel 是一个 int[],包含 76800 个 RGB int 值,对应于屏幕上随时出现的每个像素
public static void buildBitmap() {
File f = new File("C:/scr/game_name" + LocalDate.now().toString() +".bmp");
try(FileOutputStream fos = new FileOutputStream(f)){
fos.write(66);
fos.write(77);
fos.write(230428); //width * height * 3
fos.write(0);
fos.write(0);
fos.write(0);
fos.write(0);
fos.write(0);
fos.write(0);
fos.write(0);
fos.write(26);
fos.write(0);
fos.write(0);
fos.write(0);
fos.write(12);
fos.write(0);
fos.write(0);
fos.write(0);
fos.write(320);
fos.write(0);
fos.write(240);
fos.write(0);
fos.write(1);
fos.write(0);
fos.write(24);
fos.write(0);
for(int y = height-1; y > 0; y--) {
for(int x = 0; x < width-1; x++){
fos.write(pixels[x + y * width] & 0xFF); //blue
fos.write((pixels[x + y * width] >> 8) & 0xFF); //green
fos.write((pixels[x + y * width] >> 16) & 0xFF); //red
}
}
fos.write(0);
fos.write(0);
}catch(IOException e) {
e.printStackTrace();
}
System.out.println("Screenshot saved to " + f);
}
应该填充写入文件的嵌套循环使实际图像数据从下到上遍历数组:从左到右,将 RGB int 值转换为单独的蓝色、绿色、红色并将它们写入文件(在那个命令)。
它在数学上是合理的,生成的图像虽然变形和毁容,但至少可以识别出来自游戏。我的代码有什么问题?
输出图像的宽度也为 64,这是为什么呢?
解决方案
你需要确保你的标题是正确的。例如,标头中的文件大小字段需要为 4 个字节(参见:BMP 文件格式)。要准确写入正确的字节数,您可以使用DataOutputStream。
您需要反转值的字节序。Java 是大端,BMP 文件是小端。(查看代码中的所有调用reverseBytes
)。
您的循环忽略每行和最后一行中的最后一个字节。
您也忽略了位图步幅。这些是每行末尾的额外填充字节。由于您的示例是 320 像素宽,因此恰好没有额外的字节,但是要处理奇数大小的宽度,您应该处理这些。
File f = new File("C:/scr/game_name" + LocalDate.now().toString() +".bmp");
try (FileOutputStream fos = new FileOutputStream(f)){
DataOutputStream dos = new DataOutputStream(fos);
int header_size = 14 + 40; // Size of both headers
int width = 320;
int height = 240;
short bpp = 24;
// Calculate the stride
int stride = 4 * ((width * bpp + 31) / 32);
// BITMAPFILEHEADER
dos.writeByte(66); // B
dos.writeByte(77); // M
int fileSize = (stride * height) + header_size;
dos.writeInt(Integer.reverseBytes(fileSize)); // Actual size of entire file
dos.writeShort(0); // Reserved
dos.writeShort(0); // Reserved
dos.writeInt(Integer.reverseBytes(header_size)); // starting address of bitmap image data
// BITMAPINFOHEADER
dos.writeInt(Integer.reverseBytes(40)); // Size of header
dos.writeInt(Integer.reverseBytes(width)); // Width
dos.writeInt(Integer.reverseBytes(height)); // Height
dos.writeShort(Short.reverseBytes((short)1)); // Color planes
dos.writeShort(Short.reverseBytes(bpp)); // BPP
dos.writeInt(0); // Compression method
dos.writeInt(0); // Image size
dos.writeInt(0); // Horizontal res
dos.writeInt(0); // Vertical res
dos.writeInt(0); // Number of colors
dos.writeInt(0); // Important colors
for (int y = height - 1; y >= 0; y--) {
for(int x = 0; x < width; x++) {
dos.writeByte(pixels[x + y * width] & 0xFF); //blue
dos.writeByte((pixels[x + y * width] >> 8) & 0xFF); //green
dos.writeByte((pixels[x + y * width] >> 16) & 0xFF); //red
}
// Add padding bytes
for (int s = width * 3; s < stride; s++) {
dos.writeByte(0);
}
}
fos.close();
}
catch(IOException e) {
e.printStackTrace();
}
从长远来看,您最好找到一个可以为您完成所有这些工作的第三方库。
推荐阅读
- angular - NGRX/RXJS 观察者在页面刷新时未发出
- python - 列表索引超出范围,使用 sort(key=lambda) 进行索引
- go - 如何将特定字符串转换为键和值的拆分“=”并在golang中将其转换为json
- jsf - “目标无法到达,标识符‘mainBean’解析为空”JSF
- python - Python 3 Schedule 随时运行
- javascript - 是否可以编译 javascript 来保护浏览器 webapp 的源代码?
- c# - 如果班级发生变化,如何强制其他地方发生变化?
- javascript - 延迟后的承诺
- php - 使用 ajax 从 mysql DB 检索和打印数据到文本框(如何在同一个 php 页面中使用它两次)
- r - 处理发生在同一日期的值