Using 'brush' to render text with DrawText into DIBsection 1bpp bitmap

Greg Wilson-Lindberg 126 Reputation points
2022-10-10T21:13:36.957+00:00

I'm wondering if there is a way to set a 'brush' or pixel map for the DrawText() call to use so that the elements of the text are not solid lines. I would like the rendered text to use alternating pixels.

I can't find any examples of anything like this.

Regards,
Greg

Windows API - Win32
Windows API - Win32
A core set of Windows application programming interfaces (APIs) for desktop and server applications. Previously known as Win32 API.
2,452 questions
.NET CLI
.NET CLI
A cross-platform toolchain for developing, building, running, and publishing .NET applications.
324 questions
C++
C++
A high-level, general-purpose programming language, created as an extension of the C programming language, that has object-oriented, generic, and functional features in addition to facilities for low-level memory manipulation.
3,569 questions
{count} votes

2 answers

Sort by: Most helpful
  1. Viorel 113.2K Reputation points
    2022-10-11T12:59:04.353+00:00

    If you decide to use C++/CLI to simplify some tasks, then consider an example:

    #include <msclr/auto_handle.h>  
      
    . . .  
      
    using namespace System::Drawing;  
    using namespace System::Drawing::Imaging;  
    using namespace System::Drawing::Drawing2D;  
    using namespace msclr;  
      
    auto_handle<Bitmap> bmp( gcnew Bitmap( 200, 100, PixelFormat::Format24bppRgb ));  
    auto_handle<Graphics> g( Graphics::FromImage( bmp.get() ));  
      
    g->Clear( Color::White );  
      
    HatchBrush brush( HatchStyle::DottedDiamond, Color::White, Color::Black );  
    System::Drawing::Font font( "Arial", 50 );  
      
    g->DrawString( "Hello", %font, %brush, 10, 10, StringFormat::GenericDefault );  
      
    bmp->Save( "test1.bmp", ImageFormat::Bmp );  
      
    auto_handle<Image> bmp2( bmp->Clone( Rectangle( 0, 0, bmp->Width, bmp->Height ), PixelFormat::Format1bppIndexed ));  
    bmp2->Save( "test2.bmp", ImageFormat::Bmp );  
    

    The generated "test2.bmp" file will be a 1-bit-per-pixel one.

    You can use different styles of HatchBrush, or build a TextureBrush, or use other kinds of brushes.


  2. Castorix31 82,126 Reputation points
    2022-10-16T06:38:02+00:00

    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);  
    

    250720-drawtext-brush.jpg

    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;  
    }      
    
    0 comments No comments