C++メモ 外部ライブラリを使わずにVisual Studioに用意されているものだけでWindows用のビットマップを作ってみる

C++

野暮の用事で。
ポイントはこんなとこ。

  • Windows.hに定義されとるBITMAPFILEHEADER構造体とBITMAPINFOHEADER構造体を使う
  • 構造体を含めてバイナリ形式で順番にファイルを作成する
  • BITMAPFILEHEADER構造体とBITMAPINFOHEADER構造体はファイルの先頭で出力する
  • 1ピクセルはRGBの3つとアルファチャンネル1つの計4バイト(8×3+8の計32)で表現する

んで、ビットマップを作るメソッドはこんな感じ。

//
// ヘッダーのインクルードをする
// ※mainメソッドでも共通で使う
//
#include <iostream>
#include <string>
#include <fstream>
#include <vector>
#include <Windows.h>
using namespace std;

/////////////////////////////////////////////////////////////////////
//
//	ビットマップファイルを作成する
//
// @parameter filepath ファイルパス
// @parameter width 画像の幅(ピクセル)
// @parameter height 画像の高さ(ピクセル)
// @parameter pixel 画像情報(32ビット)
//
/////////////////////////////////////////////////////////////////////
void CreateBitmap(const string &filepath, const int width, const int height, const vector<unsigned char>& pixels)
{
	//
	// 変数を定義する
	//
	ofstream writer;
	int bitcount = 8 * 3 + 8;		// RGB×8bit+アルファ値で32

	//
	// ビットマップを作成する
	//
	try
	{
		// ローカル変数を定義する
		int count = (width * height * (bitcount / 8));	// ピクセル数(4バイト×画素数)
		int filesize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + count;

		// ビットマップヘッダーを定義する
		BITMAPINFOHEADER infoheader;
		::memset(&infoheader, 0, sizeof(BITMAPINFOHEADER));
		infoheader.biSize = sizeof(BITMAPINFOHEADER);
		infoheader.biWidth = width;
		infoheader.biHeight = height;
		infoheader.biPlanes = 1;
		infoheader.biBitCount = bitcount;
		infoheader.biCompression = BI_RGB;
		infoheader.biSizeImage = (width * height * (bitcount / 8));
		infoheader.biXPelsPerMeter = 0;
		infoheader.biYPelsPerMeter = 0;
		infoheader.biClrUsed = 0;
		infoheader.biClrImportant = 0;

		// ファイルヘッダーを定義する
		BITMAPFILEHEADER fileheader;
		::memset(&fileheader, 0, sizeof(BITMAPFILEHEADER));
		fileheader.bfType = ('M' << 8) | 'B';
		fileheader.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + infoheader.biSizeImage;
		fileheader.bfReserved1 = 0;
		fileheader.bfReserved2 = 0;
		fileheader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);

		// ファイルを開く
		writer = ofstream(filepath, ios::out | ios::trunc | ios::binary);
		if (!writer.is_open())
		{
			throw new exception("ファイルの作成に失敗しました...");
		}

		// 画像を書き込む
		writer.write((char*)&fileheader, sizeof(BITMAPFILEHEADER));
		writer.write((char*)&infoheader, sizeof(BITMAPINFOHEADER));
		writer.write((const char*)&pixels[0], pixels.size());
	}
	catch (const exception & ex)
	{
		// エラー情報を出力する
		cout << ex.what() << endl;
		// ファイルを閉じる
		if (writer.is_open())
		{
			writer.close();
		}
		throw ex;
	}

	// ファイルを閉じる
	if (writer.is_open())
	{
		writer.close();
	}
}

テスト用としてmain関数はこんな感じ。

/////////////////////////////////////////////////////////////////////
//
//	エントリーポイント
//
/////////////////////////////////////////////////////////////////////
int main()
{
	// 変数を定義する
	string filepath = "C:\\Users\\tetsuyanbo\\Documents\\Work\\sample.bmp";
	int width = 200;	// 画像の幅
	int height = 100;	// 画像の高さ
	int bitcount = 32;	// チャンネル数
	int size = (width * height * (bitcount / 8));	// 画素数
	vector<unsigned char> pixels(size);	// 画像情報のリスト

	// ビットマップを作成する
	CreateBitmap(filepath, width, height, pixels);
}

実行してみると…、ファイルは出来とる。

ファイルを開いてみたら、こんな感じ。

んまま、明日への自分へのメモってことで。