文字色と背景色

文字色の変更

前ページまでで、画面に文字列を表示することが出来るようになりました。
今度は文字の色を変更してみましょう。

SetTextColor関数

文字色の設定はSetTextColor関数で行います。

COLORREF SetTextColor(
 HDC hdc,
 COLORREF color
);
デバイスコンテキストhdcのテキスト色をcolorに設定する。
戻り値は変更前の色。
失敗した場合はCLR_INVALIDが返される。

デバイスコンテキストはテキスト色やフォントなどの描画に必要な情報を保持しています。
DrawText等のGDI関数はこの情報に従って文字や図形を描画します。
SetTextColor関数はデバイスコンテキストに対してテキスト描画色を設定します。

第一引数hdcはデバイスコンテキストハンドルです。
第二引数colorはテキスト色の指定です。
戻り値は関数実行の前に設定されていたテキスト色です。

RGBマクロ

色情報はCOLORREF型を指定するのですが、このデータ型はRGBマクロで作成できます。

void RGB(
 r,
 g,
 b
);
RGB情報を設定するマクロ。
赤色の強度をr、緑色の強度をg、青色の強度をbでそれぞれ0~255の範囲で設定する。
値はCOLORREF型(DWORD型の別名)に変換される。

コンピューター上の色の設定は光の三原色の原理で行われ、つまり赤/緑/青(Red/Green/Blue)の強さで調節します。
それぞれの値は0~255の範囲で設定し、全てが最大値の場合は白色、全てが最低値の場合は黒色となります。
赤を最大値に、その他を最低値に設定すると赤色になります。


//白
COLORREF color1 = RGB(255, 255, 255);
//黒
COLORREF color2 = RGB(0, 0, 0);
//赤
COLORREF color3 = RGB(255, 0, 0);

COLORREF型は4バイトのデータで、下位ビットから赤/緑/青が1バイトずつ格納されています。
COLORREF型からそれぞれの単体の値が欲しい場合はシフト演算で取り出す以外に、 GetRValueマクロ(赤)、GetGValueマクロ(緑)、GetBValueマクロ(青)が用意されているのでこれを利用するのが簡単です。
(シフト演算式に置き換えられ、0~255の値が得られます)

サンプルコード


#include <windows.h>

//ウィンドウの生成等は省略

//ウィンドウプロシージャ
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	HDC hdc;
	PAINTSTRUCT ps;
	RECT rect;

	static const WCHAR* str1 = L"ABCDEFG";
	static const WCHAR* str2 = L"HIJKLMN";
	static const WCHAR* str3 = L"OPQRSTU";

	switch (message)
	{
	case WM_PAINT: //ウィンドウの描画
		GetClientRect(hWnd, &rect);
		hdc = BeginPaint(hWnd, &ps);

		rect.top += DrawText(hdc, str1, -1, &rect, DT_WORDBREAK);

		//文字色の変更
		COLORREF color = SetTextColor(hdc, RGB(255, 0, 0));
		rect.top += DrawText(hdc, str2, -1, &rect, DT_WORDBREAK);

		//元の色に戻す
		SetTextColor(hdc, color);
		rect.top += DrawText(hdc, str3, -1, &rect, DT_WORDBREAK);

		EndPaint(hWnd, &ps);
		break;

	case WM_DESTROY: //ウィンドウの破棄
		PostQuitMessage(0);
		break;

	default:
		return DefWindowProc(hWnd, message, wParam, lParam);
	}
	return 0;
}

このコードは二行目のDrawText関数の描画が赤色で行われます。
文字色の変更

GetTextColor関数

現在の文字色はGetTextColor関数で取得できます。

COLORREF GetTextColor(
 HDC hdc
);
デバイスコンテキストhdcに設定されているテキスト色を取得する。
失敗した場合はCLR_INVALIDを返す。

使い方は説明しなくてもわかると思うのでサンプルコードは省略します。

背景色の変更

SetBkColor関数

TextOut関数やDrawText関数などは文字の描画を行いますが、正確には背景を塗りつぶしてから文字を描画しています。
ここで言う「背景」とはクライアント領域全体ではなく実際に文字が描画される領域の背景です。

初期値では白色で塗りつぶされますが、この色を変更する場合はSetBkColor関数を使用します。

COLORREF SetBkColor(
 HDC hdc,
 COLORREF color
);
デバイスコンテキストhdcの背景色をcolorに設定する。
戻り値は変更前の色。
失敗した場合はCLR_INVALIDが返される。

使い方はSetTextColor関数と同じです。


#include <windows.h>

//ウィンドウの生成等は省略

//ウィンドウプロシージャ
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	HDC hdc;
	PAINTSTRUCT ps;

	RECT rect;

	static const WCHAR* str1 = L"ABCDEFG";
	static const WCHAR* str2 = L"HIJKLMN";
	static const WCHAR* str3 = L"OPQRSTU";

	switch (message)
	{
	case WM_PAINT: //ウィンドウの描画
		GetClientRect(hWnd, &rect);
		hdc = BeginPaint(hWnd, &ps);

		rect.top += DrawText(hdc, str1, -1, &rect, DT_WORDBREAK);

		//色の変更
		COLORREF clrTxt =  SetTextColor(hdc, RGB(255, 0, 0));
		SetBkColor(hdc, RGB(0, 255, 255));
		rect.top += DrawText(hdc, str2, -1, &rect, DT_WORDBREAK);

		//テキスト色のみ元に戻す
		SetTextColor(hdc, clrTxt);
		rect.top += DrawText(hdc, str3, -1, &rect, DT_WORDBREAK);

		EndPaint(hWnd, &ps);
		break;

	case WM_DESTROY: //ウィンドウの破棄
		PostQuitMessage(0);
		break;

	default:
		return DefWindowProc(hWnd, message, wParam, lParam);
	}
	return 0;
}

このコードは「何も設定しない」「文字色と背景色を変更」「文字色だけを元に戻す」の順に文字を描画しています。
実行結果は以下のようになります。
文字色と背景色の変更

SetBkMode関数

既に背景色を設定している領域にテキストを描画する場合に、背景が塗りつぶされては困る事があります。
現在の背景色を残しながらテキストを描画するにはSetBkMode関数を使用します。

int SetBkMode(
 HDC hdc,
 int mode
);
デバイスコンテキストhdcの背景の描画モードをmodeに設定する。
戻り値は設定前の背景の描画モード。
失敗した場合は0が返される。

第二引数modeの描画モードは以下の定数を使用します。

定数 説明
TRANSPARENT 背景を塗りつぶさない
OPAQUE 背景を塗りつぶす

背景描画モードをTRANSPARENTに設定することで、背景色を塗りつぶさずに文字を出力することができます。
「Transparent」とは「透明」という意味です。
「透明色」で塗りつぶしているとも言えます。


//ウィンドウクラスの登録
ATOM MyRegisterClass(HINSTANCE hInstance, WNDPROC wndProc)
{
	WNDCLASSEX wcex;
	wcex.cbSize = sizeof(WNDCLASSEX);
	wcex.style = CS_HREDRAW | CS_VREDRAW;
	wcex.lpfnWndProc = wndProc;
	wcex.cbClsExtra = 0;
	wcex.cbWndExtra = 0;
	wcex.hInstance = hInstance;
	wcex.hIcon = LoadIcon(NULL, IDI_APPLICATION);
	wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
	wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 6); //背景色の変更
	wcex.lpszMenuName = NULL;
	wcex.lpszClassName = windowClass;
	wcex.hIconSm = NULL;

	return RegisterClassEx(&wcex);
}

#define BUFFERSIZE 128

//ウィンドウプロシージャ
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	HDC hdc;
	PAINTSTRUCT ps;
	RECT rect;

	static const WCHAR* str = L"ABCDEFG";

	switch (message)
	{
	case WM_PAINT: //ウィンドウの描画
		GetClientRect(hWnd, &rect);
		hdc = BeginPaint(hWnd, &ps);

		//デフォルト背景色(白)
		rect.top += DrawText(hdc, str, -1, &rect, DT_WORDBREAK);

		//背景色を変更
		SetBkColor(hdc, RGB(0, 255, 255));
		rect.top += DrawText(hdc, str, -1, &rect, DT_WORDBREAK);

		//背景を塗りつぶさないで文字を描画
		SetBkMode(hdc, TRANSPARENT);
		rect.top += DrawText(hdc, str, -1, &rect, DT_WORDBREAK);

		EndPaint(hWnd, &ps);
		break;

	case WM_DESTROY: //ウィンドウの破棄
		PostQuitMessage(0);
		break;

	default:
		return DefWindowProc(hWnd, message, wParam, lParam);
	}
	return 0;
}

このコードはウィンドウクラスの登録時に、WNDCLASS(EX)構造体のhbrBackgroundメンバでクライアント領域の背景色をグレーに変更しています。

テキストの出力は

  • 何も設定しない場合(デフォルトの背景色)
  • 背景色をシアンに設定
  • 背景色を塗りつぶさない設定

の順に行っています。
背景を塗りつぶさないで文字を描画

今までは白色のクライアント領域に、白色の背景色でテキストを描画していたため何も問題はなかったわけです。
背景色が異なる場合、テキスト出力の背景色も揃えるか、背景を塗りつぶさない設定で描画する必要があります。

DrawText関数などは「領域を背景色で塗りつぶしてから」テキストを描画します。
この時、描画領域にあった別の情報は塗りつぶされて見えなくなります。
TRANSPARENTモードでは、DrawText関数などはテキストのみを指定の領域に描画します。
既にその場所に別のテキスト等が描画されている場合は文字が重なって表示されます。


#define BUFFERSIZE 128

//ウィンドウプロシージャ
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	HDC hdc;
	PAINTSTRUCT ps;
	RECT rect;

	WCHAR buf[BUFFERSIZE];
	static int count;

	switch (message)
	{
	case WM_PAINT: //ウィンドウの描画
		GetClientRect(hWnd, &rect);
		StringCchPrintf(buf, BUFFERSIZE, L"左クリックの回数: %d", count);
		hdc = BeginPaint(hWnd, &ps);

		//背景を塗りつぶさない
		SetBkMode(hdc, TRANSPARENT);
		DrawText(hdc, buf, -1, &rect, DT_WORDBREAK);

		EndPaint(hWnd, &ps);
		break;
	case WM_LBUTTONUP: //マウス左クリック
		count++;

		//無効領域を作る
		//背景の塗りつぶしを行わない
		InvalidateRect(hWnd, NULL, FALSE);
		break;

	case WM_DESTROY: //ウィンドウの破棄
		PostQuitMessage(0);
		break;

	default:
		return DefWindowProc(hWnd, message, wParam, lParam);
	}
	return 0;
}

マウス左クリック時(WM_LBUTTONUPメッセージ)にInvalidateRect関数を使用して、無効領域を発生させています。
ただし第三引数にFALSEを指定しているため、背景は再描画されません。
(画面が消去されない)

この状態でテキストの背景色をSetBkMode関数でTRANSPARENTモードに設定すると、既存のテキストはそのままに新しいテキストが同じ場所に描画されます。
すると以下のように文字が重なってしまいます。
背景を塗りつぶさないで文字を描画2

背景色を塗りつぶさない設定は表示がおかしくなることがあるので注意しましょう。

GetBkColor関数

現在の背景色はGetBkColor関数で取得できます。

COLORREF GetBkColor(
 HDC hdc
);
デバイスコンテキストhdcに設定されている背景色を取得する。
失敗した場合はCLR_INVALIDを返す。

使い方は説明しなくてもわかると思うのでサンプルコードは省略します。