Overview
OpenGLでWindowに表示している内容を直接画像として出力したいと思ったが、面倒そうだったので画像出力専用の構造体と関数を作った。
OpenCVを使えばそりゃ簡単にできるが、使うライブラリはなるべく減らしたいと思った。
参考までにコードを載せる。直接bitmapにバイナリを書き込んでいるので、注意してはいるものの環境によっては動かないかもしれない。
Visual Studio 2019環境では正常に動作。
参考にしたサイトはこちら→
https://sonson.jp/blog/2006/04/03/opengl-bitmap-1/
・・・だが、ところどころ抜けていて色々とリンクも切れているので、自分で補完した。
注意点として、ダブルバッファリングしている場合はglReadPixelはデフォルトでバックサイドの情報を取得する模様。
glReadBuffer(GL_FRONT)とすれば、glReadPixelがWindowに表示されているフォアグラウンドのバッファを取得してくれる。
また、bitmapのヘッダ構造体は情報量としては54byteだが、構造体のサイズとしては4の倍数である56byteになった。
ここは環境依存かどうかわからないが、ofstreamで書き込むバイト数を直接指定してぶち込んでいる。
この関数の注意点として、Windowのwidthとheightの取得にのみGLUTの機能を使っている。
GLUTじゃない場合は要調整。
WriteBitmapを走らせるたびに連番のファイル名でbitmapイメージを一枚、image/フォルダに出力するようにしている。
当然処理がだいぶ重くなるので、デバッグや発表資料作りとかにしか使えないかなぁ。
常用するにはきつい。
typedef struct _BitmapAllHeader {
//Bitmap header
char distinct1 = 'B'; // 'B'
char distinct2 = 'M'; // 'M'
int filesize = 0; // total file size = imagedata + 54
short reserve1 = 0; // 0
short reserve2 = 0; // 0
int offset = 54; // 14+40=54
//Bitmap info header
int header = 40; // infoheader size = 40
int width = 0; // bitmap width
int height = 0; // bitmap height
short plane = 1; // 1
short bits = 24; // 24bit color per pixel
int compression = 0; // 0 -> no compression
int comp_image_size = 0; // imagedata
int x_resolution = 3780; // 3780 = 96dpi
int y_resolution = 3780; // 3780
int palette_num = 0; // 0
int important_palette_num = 0; //0
_BitmapAllHeader(int w, int h) :width(w), height(h) {
int writeWidth = (int)((width * 3 + 3) / 4) * 4;
comp_image_size = writeWidth * height;
filesize = comp_image_size + offset;
};
}BitmapAllHeader;
int WriteBitmap(string filename, bool printinfo = false) {
int width = glutGet(GLUT_WINDOW_WIDTH);
int height = glutGet(GLUT_WINDOW_HEIGHT);
BitmapAllHeader header(width, height);
static int count = 1; // 画像連番作成のため
ofstream ofs("image/" + filename + to_string(count) + ".bmp", ios::binary);
if (!ofs) {
cout << "Failed to open file" << endl;
return -1;
}
int offset = offsetof(BitmapAllHeader, filesize);
ofs.write((char*)&header, 2);
ofs.write((char*)&header + offset, 52);
GLubyte* data = (GLubyte*)malloc(width * height * 3 * (sizeof(GLubyte)));
glPixelStorei(GL_PACK_ALIGNMENT, 1);
glReadPixels(0, 0, window_width, window_height, GL_RGB, GL_UNSIGNED_BYTE, data);
const char zero = 0;
for (int y = 0; y < height; y++) {
// RGB -> BGR
for (int x = 0; x < width; x++) {
ofs.write((char*)(data + x * 3 + 3 * y * width + 2), sizeof(GLubyte));
ofs.write((char*)(data + x * 3 + 3 * y * width + 1), sizeof(GLubyte));
ofs.write((char*)(data + x * 3 + 3 * y * width), sizeof(GLubyte));
}
// 0 fill
if (width * 3 % 4 != 0)
for (int j = 0; j < 4 - (width * 3) % 4; j++)
ofs.write(&zero, sizeof(GLubyte));
}
ofs.close();
free(data);
count++;
if (printinfo) {
cout << "image/" << filename << to_string(count) << ".bmp saved successfully" << endl;
}
return 0;
}