You can use the method from Feng Yuan book "Windows Graphics Programming Win32 GDI and DirectDraw" :
int nWidth = 256, nHeight = 256;
COLORREF clrBackground = RGB(0, 0, 255);
COLORREF clrForeground = RGB(255, 255, 0);
HBITMAP hBitmap = CreateMonochromeBitmap(nWidth, nHeight, clrBackground, clrForeground);
HDC hDC = GetDC(NULL);
HDC hDCMem = CreateCompatibleDC(hDC);
HBITMAP hBitmapOld = (HBITMAP)SelectObject(hDCMem, hBitmap);
HBRUSH hBrush = CreateHatchBrush(HS_DIAGCROSS, clrForeground);
SetBkColor(hDCMem, clrBackground);
WCHAR wsText[] = L"This is a test";
int nFontSize = -MulDiv(31, GetDeviceCaps(hDC, LOGPIXELSY), 72);
HFONT hFont = CreateFont(nFontSize, 0, 0, 0, FW_DONTCARE, FALSE, FALSE, FALSE, ANSI_CHARSET, OUT_RASTER_PRECIS, CLIP_DEFAULT_PRECIS,
NONANTIALIASED_QUALITY, VARIABLE_PITCH | FF_DONTCARE, L"Arial");
HFONT hFontOld = (HFONT)SelectObject(hDCMem, hFont);
ColorText(hDCMem, 10, 10, wsText, lstrlen(wsText), hBrush);
SelectObject(hDCMem, hFontOld);
DeleteObject(hFont);
DeleteObject(hBrush);
// Test copy bitmap on screen
BitBlt(hDC, 0, 0, nWidth, nHeight, hDCMem, 0, 0, SRCCOPY);
SelectObject(hDCMem, hBitmapOld);
DeleteDC(hDCMem);
DeleteObject(hBitmap);
ReleaseDC(NULL, hDC);
HBITMAP CreateMonochromeBitmap(int cx, int cy, COLORREF clrBackground, COLORREF clrForeground)
{
struct
{
BITMAPINFOHEADER bi;
RGBQUAD bmiColors[2];
} dib = { 0 };
dib.bi.biSize = sizeof(dib.bi);
dib.bi.biWidth = cx;
dib.bi.biHeight = cy;
dib.bi.biPlanes = 1;
dib.bi.biBitCount = 1;
dib.bi.biCompression = BI_RGB;
dib.bmiColors[0].rgbBlue = GetBValue(clrBackground);
dib.bmiColors[0].rgbGreen = GetGValue(clrBackground);
dib.bmiColors[0].rgbRed = GetRValue(clrBackground);
dib.bmiColors[1].rgbBlue = GetBValue(clrForeground);
dib.bmiColors[1].rgbGreen = GetGValue(clrForeground);
dib.bmiColors[1].rgbRed = GetRValue(clrForeground);
HBITMAP hBitmap = NULL;
HDC hDC = CreateCompatibleDC(NULL);
if (hDC)
{
hBitmap = CreateDIBSection(hDC, (BITMAPINFO*)&dib, DIB_RGB_COLORS, NULL, NULL, 0);
DeleteDC(hDC);
}
return hBitmap;
}
// From book "Windows Graphics Programming Win32 GDI and DirectDraw""
// ABC extent of a text string
// ( A0, B0, C0 ) + ( A1, B1, C1 ) = ( A0, B0+C0+A1+B1, C1 }
BOOL GetTextABCExtent(HDC hDC, LPCTSTR lpString, int cbString, long* pHeight, ABC* pABC)
{
SIZE size;
if (!GetTextExtentPoint32(hDC, lpString, cbString, &size))
return FALSE;
*pHeight = size.cy;
pABC->abcB = size.cx;
ABC abc;
GetCharABCWidths(hDC, lpString[0], lpString[0], &abc); // first
pABC->abcB -= abc.abcA;
pABC->abcA = abc.abcA;
GetCharABCWidths(hDC, lpString[cbString - 1], lpString[cbString - 1], &abc); // last
pABC->abcB -= abc.abcC;
pABC->abcC = abc.abcC;
return TRUE;
}
BOOL GetOpaqueBox(HDC hDC, LPCTSTR lpString, int cbString, RECT* pRect, int x, int y)
{
long height;
ABC abc;
if (!GetTextABCExtent(hDC, lpString, cbString, &height, &abc))
return FALSE;
switch (GetTextAlign(hDC) & (TA_LEFT | TA_RIGHT | TA_CENTER))
{
case TA_LEFT: break;
case TA_RIGHT: x -= abc.abcB; break;
case TA_CENTER: x -= abc.abcB / 2; break;
default: assert(false);
}
switch (GetTextAlign(hDC) & (TA_TOP | TA_BASELINE | TA_BOTTOM))
{
case TA_TOP: break;
case TA_BOTTOM: y = -height; break;
case TA_BASELINE:
{
TEXTMETRIC tm;
GetTextMetrics(hDC, &tm);
y = -tm.tmAscent;
}
break;
default: assert(false);
}
pRect->left = x + min(abc.abcA, 0);
pRect->right = x + abc.abcA + abc.abcB + max(abc.abcC, 0);
pRect->top = y;
pRect->bottom = y + height;
return TRUE;
}
BOOL ColorText(HDC hDC, int x, int y, LPCTSTR pString, int nCount, HBRUSH hFore)
{
HGDIOBJ hOld = SelectObject(hDC, hFore);
RECT rect;
GetOpaqueBox(hDC, pString, nCount, &rect, x, y);
PatBlt(hDC, rect.left, rect.top,
rect.right - rect.left, rect.bottom - rect.top, PATINVERT);
int oldBk = SetBkMode(hDC, TRANSPARENT);
COLORREF oldColor = SetTextColor(hDC, RGB(0, 0, 0));
TextOut(hDC, x, y, pString, nCount);
SetBkMode(hDC, oldBk);
SetTextColor(hDC, oldColor);
BOOL rslt = PatBlt(hDC, rect.left, rect.top,
rect.right - rect.left, rect.bottom - rect.top, PATINVERT);
SelectObject(hDC, hOld);
return rslt;
}