Windows Bitmap 파일 내보내기 라이브러리

17146 단어 WindowsC++

입문


Windows Bitmap File을 토해내는 C++ 단일 제목 라이브러리를 만들었습니다.
https://github.com/kaityo256/winbitmap
이렇게 사용합니다.
#include "winbitmap.hpp"

int main() {
  // Prepare a canvas (width, height)
  winbitmap::canvas canvas(256, 256);

  // Draw a filled rectangle
  canvas.set_color(255, 255, 255);
  canvas.fill_rect(0, 0, 256, 256);

  // Draw a filled circle
  canvas.set_color(255, 0, 0);
  canvas.fill_circle(128, 128, 32);

  // Draw a line
  canvas.set_color(0, 0, 0);
  canvas.moveto(0, 0);
  canvas.lineto(256, 256);

  // Draw a rectangle
  canvas.set_color(0, 0, 255);
  canvas.draw_rect(32, 32, 64, 32);

  canvas.save_to_file("test.bmp");
  std::cout << "Saved to test.bmp" << std::endl;
}
위의 작업을 컴파일하고 실행하면 다음 비트맵 파일이 토출됩니다.

색상, 선, 원, 직사각형만 지정할 수 있지만 STL을 제외하고는 필요한 라이브러리가 없기 때문에 기본적으로 어디서든 이동할 수 있습니다.하지만 실마리를 고려하지 않았기 때문에 큰 실마리를 가진 기계에서 운행하면 이상해질 수 있다.
지금은 이런 창고가 필요 없을 것 같지만, 모처럼 예전에 만든 물건은 노란색 모자의 타이어가 교환되는 동안 한가해서 왜 이런 물건을 만들었는지 써 보려고 한다.

왜 그랬어?


나의 전공은 슈퍼컴퓨터로 분자동력학 계산을 하는 것이다.분자동력학법이란 한 마디로 하면 많은 알갱이가 있는데 그것들을 이동하는 시뮬레이션이다.그럼 시뮬레이션을 하면 결과를 가시화하고 싶어요.특히 분자동력학법은 시간에 따라 발전하기 때문에 그 결과를 영화로 정리하고 싶다.
지금은 입자계의 시각화 응용 프로그램이 많지만 예전에는 충실하지 않았다.몇 안 되는 응용 프로그램을 슈퍼컴퓨터에서 구축하는 데 실패하는 것도 번거롭다.로컬에서 데이터를 가져와도 대규모 계산을 하려면 상당한 파일을 다운로드해야 해 고통스럽다.또 무력한 현지 기계로 대량의 데이터를 가시화하는 것도 힘든 일이다.
따라서 슈퍼컴퓨터로 시뮬레이션하는 동시에 가시화하고 싶은 수요도 있다.그러나 지금은 예전의 슈퍼컴퓨터 컴파일러의 규격이 낡아서 필요한 라이브러리를 정리하는 것이 번거롭다.그래서 나는 다른 창고에 의존하지 않고 그림을 그릴 필요가 있다.
이럴 때 이전의 수치 계산사는 PostScript를 자주 사용했다.PostScript는 텍스트 파일이므로 내보내기가 쉽습니다.그리고 회화 언어로서 기능이 상당히 높기 때문에 기본적으로 무엇이든지 할 수 있다.저도 PostScript로 출력하고 래스터로 변환하는 경우가 많습니다.
그러나 입자 수가 증가하면 물론 PostScript의 파일 크기도 증가합니다.또한 3차원 공간에 대량의 입자가 떠다니는 상태를 가시화하면 표면의 입자만 보이지만 PostScript는 모든 입자를 보존해 낭비한다.또 GS에게 너무 큰 파일을 먹게 하면 묘사가 죽을 정도로 느려진다.래스터 이미지를 직접 저장하는 방법이 필요합니다.
당시에도 GIF 등 출력된 라이브러리가 있었다.하지만 당시 GIF 알고리즘의 라이선스가 잘 알려지지 않은 상태와 외부 라이브러리를 힘들게 컴파일하는 것을 싫어하는 나는 직접 Windows Bitmap을 토해냈다.

Windows Bitmap File 구조


Windows Bitmap File, 이른바'BMP'파일은 Windows 사용자에게 익숙한 형식이죠.이것 또한 DIB(Device Independent Bitmap) 등으로 불리며 OS/2부터 사용된 역사 파일 형식입니다.실제로 BMP라고 하지만 해상도와 압축 형식에 따라 그 조합에 따라 형식의 종류도 상당히 많다.따라서 어떤 BMP 파일은 이쪽 응용 프로그램에서 읽을 수 있지만 이쪽 응용 프로그램에서 읽을 수 없는 경우가 자주 발생한다.압축 형식을 지정하는 것은 매우 번거롭다.BMP 파일은 무압축에 자주 사용되기 때문에 BMP를 지원하는 응용 프로그램에서도 무압축 BMP만 읽을 수 있습니다.옛날에 저는 Borland C++ Builder, 그 Tcanvas를 썼어요.의 기본 저장 형식은 압축형 BMP입니다. 한동안 눈치채지 못했지만 다른 응용 프로그램에서 읽지 못해 힘들었던 기억이 납니다.
나는 지금 모든 BMP에 대응하는 것도 상당히 어렵다고 생각한다.하지만 읽기는 힘들지만 뱉는 것은 한 가지 격식에 대응하면 되기 때문에 간단하다.여기서 24자리의 미압축 비트맵을 토해내세요.24비트가 압축되지 않으면 팔레트를 저장할 필요가 없기 때문에 매우 쉽다.해야 할 일은 구조체와 BITMAPFILEHEADER 를 저장한 다음 BGR 순서대로 비트맵 데이터를 배열하는 것입니다 (RGB 순서가 아님을 주의하십시오).
유일하게 주의해야 할 점은 줄당 4의 배수를 반올림할 수 있다는 것이다.예를 들어, 너비 102, 높이 128을 고려한 24비트 압축되지 않은 비트맵 파일입니다.24비트 압축 해제는 픽셀을 24비트 (3바이트) 로 표시하는 형식입니다.따라서 한 줄은 102x3=306바이트만 있으면 되지만 최근 4의 배수로 올라가 308바이트로 설정한다.따라서 비트맵 부분은 308x128=39424 바이트입니다.여기에 제목 부분의 54바이트를 더하면 비트맵 파일은 전체적으로 394478바이트이다.
제목에 관해서 나는 아래의 원본을 보는 것이 비교적 빠르다고 생각한다.
  void save_to_file(const char *filename) {
    DWORD bfSize = image_buffer.size() + 54;
    WORD bfReserved1 = 0;
    WORD bfReserved2 = 0;
    DWORD bfOffBits = 54;

    DWORD biSize = 40;
    DWORD biWidth = width;
    DWORD biHeight = height;
    WORD biPlanes = 1;
    WORD biBitCount = 24;
    DWORD biCompression = 0;
    DWORD biSizeImage = 0;
    DWORD biXPelsPerMeter = 0;
    DWORD biYPelsPerMeter = 0;
    DWORD biClrUsed = 0;
    DWORD biClrImportant = 0;

    std::ofstream fs(filename);
    // BITMAPFILEHEADER
    fs.write("BM", sizeof(WORD));
    fs.write((char *)&bfSize, sizeof(DWORD));
    fs.write((char *)&bfReserved1, sizeof(WORD));
    fs.write((char *)&bfReserved2, sizeof(WORD));
    fs.write((char *)&bfOffBits, sizeof(DWORD));
    // BITMAPINFOHEADER
    fs.write((char *)&biSize, sizeof(DWORD));
    fs.write((char *)&biWidth, sizeof(DWORD));
    fs.write((char *)&biHeight, sizeof(DWORD));
    fs.write((char *)&biPlanes, sizeof(WORD));
    fs.write((char *)&biBitCount, sizeof(WORD));
    fs.write((char *)&biCompression, sizeof(DWORD));
    fs.write((char *)&biSizeImage, sizeof(DWORD));
    fs.write((char *)&biXPelsPerMeter, sizeof(DWORD));
    fs.write((char *)&biYPelsPerMeter, sizeof(DWORD));
    fs.write((char *)&biClrUsed, sizeof(DWORD));
    fs.write((char *)&biClrImportant, sizeof(DWORD));
    // DATA
    fs.write((char *)image_buffer.data(), image_buffer.size());
    fs.close();
  }
이 중 BITMAPINFOHEADER 은 BYTE, 즉 image_bufferunsigned char이다.24비트 무압축으로 결정되면 구조체에 설정된 부분은 전체 파일 크기, 너비, 높이만 있기 때문에 쉽다.

입자 시스템 가시화


이 라이브러리는 선, 원, 사각만 그릴 수 있지만 가시화 입자계로 충분하다.그래서 입자 좌표를 가시화하는 샘플도 만들었다.저장소의 sample 폴더에 배치std::vector.3차원 좌표를 저장한 draw3d.hpp 을 건네주면 이런 느낌의 파일을 토해낸다.

입자 좌표를 한 번만 변환하고 z 좌표에 따라 정렬하고 순서대로 그리면 된다.작업과 재료를 훔쳐서 틀에 입자를 그렸으니 신경 쓰지 마세요.가능하다면, 나는 분자동력학 방법 코드를 저장하는 결과가 매우 쉽다고 생각한다.

총결산


나는 BMP 파일을 토하는 라이브러리를 만들어 보았다.STL 이외의 라이브러리에 의존하지 않으므로 어디서든 이동할 수 있습니다.그럼에도 현재VMDcdview 등 시각화 응용도 풍부하고 이미지를 출력하는 라이브러리도 많기 때문에 이런 라이브러리는 필요 없겠죠.이런 시대에는 수치 계산사가 자신의 시뮬레이션 결과를 가시화하기 위해 만든 것이다.며칠 전 저장소를 정리하던 중 BMP 출력 코드가 나왔기 때문에 조금 정리해서 GitHub와 Qiita에 묻었습니다.R.I.P.
나는 노란 모자가 타이어를 바꾸는 것이 느리다고 불평하는 것이 아니다.차라리 눈이 올 것 같아서 급하게 타이어를 바꿔달라고 부탁했는데 고마워요. 

좋은 웹페이지 즐겨찾기