0

I have some code in my application to select fonts. It shows a sample of the current font like this visually:

Sample Font

The code for that is here:

void CExportSettingsDlg::OnPaint()
{
    CPaintDC    dc( this ); // device context for painting

    // This will draw the sample text for us using the current font
    DrawSampleText( &dc );

    // Do not call CDialog::OnPaint() for painting messages
}

void CExportSettingsDlg::DrawSampleText(CDC *pDC)
{
    CDC         dcMem;
    CPen        *pOldPen;
    CBrush      *pOldBrush;
    CBitmap     *pBmpOld;
    CFont       *pOldFont, fntSample;
    LOGFONT     lfSampleFont;
    int         nFontHeightPixels, iBackModeOld;
    COLORREF    crTextColourOld;
    CString     strSample;

    ASSERT( pDC != NULL );
    ASSERT( m_plfCurrentFont != NULL );
    if( pDC != NULL && m_plfCurrentFont != NULL )
    {
        strSample.LoadString( IDS_STR_SAMPLE );

        dcMem.CreateCompatibleDC( NULL );

        // Select our gdi stuff into dc
        pBmpOld = (CBitmap*)dcMem.SelectObject( &m_bmpSample );
        pOldPen = (CPen*)dcMem.SelectStockObject( BLACK_PEN );
        pOldBrush = (CBrush*)dcMem.SelectStockObject( WHITE_BRUSH );

        // Create the font
        fntSample.CreateFontIndirect( m_plfCurrentFont );

        // Get it back out so that we can adjust the font size correctly
        fntSample.GetLogFont( &lfSampleFont );

        // Font height in pixels
        nFontHeightPixels = theApp.GetFontHeightPixels(lfSampleFont.lfHeight, &dcMem);

        // Update font
        lfSampleFont.lfHeight = nFontHeightPixels;
        fntSample.DeleteObject();
        fntSample.CreateFontIndirect( &lfSampleFont );

        // Erase display  (will draw white rectangle with black border)
        dcMem.Rectangle( m_rctSampleMem );

        // Select our font and colour values and modes
        pOldFont = (CFont*)dcMem.SelectObject( &fntSample );
        crTextColourOld = dcMem.SetTextColor( m_crCurrentFontColour );
        iBackModeOld = dcMem.SetBkMode( TRANSPARENT );

        // Display the text
        dcMem.DrawText( strSample, m_rctSampleText, DT_SINGLELINE|DT_VCENTER|DT_CENTER );

        // Blast main memory dc to display
        pDC->BitBlt( m_rctSample.left, m_rctSample.top,
            m_rctSample.Width(), m_rctSample.Height(),
            &dcMem, 0, 0, SRCCOPY );

        // Restore old font and colour values and modes
        dcMem.SelectObject( pOldFont );
        dcMem.SetTextColor( crTextColourOld );
        dcMem.SetBkMode( iBackModeOld );
        dcMem.SelectObject( pBmpOld );
        dcMem.SelectObject( pOldPen );
        dcMem.SelectObject( pOldBrush );

        // Release memory
        dcMem.DeleteDC();
        fntSample.DeleteObject();
        // brushes and pens don't need deleting as they are stock objects
    }
}

Now, I have other code that displays a standard CFontDialog and on there the user can also set the font colour:

Font Dialog

As you can see, on this dialog it uses a transparent background for the font sample so the white text shows up. But to be fair, it is showing up more because of the skinning I am using.

On my window I am using a white background. So you see my problem. That is just it. You can't see it. :)

Without getting complicated, and considering the fact I am colour blind (red / greens) is there a simple technique we can use to always ensure that the sample background in my sample will always be suitable given the colour of the font being rendered?

Thank you.

Community
  • 1
  • 1
Andrew Truckle
  • 17,769
  • 16
  • 66
  • 164
  • 1
    Probably related: [Formula to determine brightness of RGB color](http://stackoverflow.com/q/596216/1889329). Use a white background, as long as the font brightness is in the lower half, switch to black background starting at 0.5 and up. I believe Windows uses a similar scheme to decide on the color of desktop shortcut item texts. – IInspectable Mar 06 '17 at 19:28
  • @IInspectable there are lots of answers to that question, most of which go over my head. I understand what you have described, but which of the answers do that? – Andrew Truckle Mar 06 '17 at 19:32
  • 1
    All of the formulas of the accepted answer will work. I'd probably pick the first or second, because they are easier to calculate. They take an RGB value (your font color) and calculate a single luminance value in the range [0 .. 1] (0 being dark, 1 bright). Use that value to decide on the background: Bright background for values from [0 .. 0.5), and dark backgrounds for values [0.5 .. 1]. – IInspectable Mar 06 '17 at 19:42
  • Assuming you're not dealing with transparency, have you considered using an RGB color for the background that is the bitwise opposite of your foreground color? It can make for some lively backgrounds. – Joseph Willcoxson Mar 06 '17 at 20:16
  • @JoeWillcoxson No, I haven't. I don't need anything too elaborate. How do you do that? – Andrew Truckle Mar 06 '17 at 20:19
  • 1
    Assume rgbfg is foreground color. DWORD rgbbg = (~rgbfg) & 0x00ffffff; More or less... – Joseph Willcoxson Mar 06 '17 at 20:48
  • The other option is to add a color picker and allow them to choose the background color. Then if there is a problem with them seeing it, it is their fault. – Joseph Willcoxson Mar 06 '17 at 20:51
  • @JoeWillcoxson Thanks for that. It does have interesting results. Interestingly if I pick a grey font then that becomes invisible. I will stick with the original approach because it covers all basis. I also appreciate your other suggestion but I don't want to do that really. The user already has support for background colours through the CSS styling file. But it is not worth trying to read that file. So ... this will do. But as you say, in the final analysis if they pick silly colour combinations it is their fault. – Andrew Truckle Mar 06 '17 at 20:56

1 Answers1

0

Based on the comments that directed me to another question I was able to resolve this issue:

void CExportSettingsDlg::DrawSampleText(CDC *pDC)
{
    CDC         dcMem;
    CPen        *pOldPen;
    CBrush      *pOldBrush;
    CBitmap     *pBmpOld;
    CFont       *pOldFont, fntSample;
    LOGFONT     lfSampleFont;
    int         nFontHeightPixels, iBackModeOld;
    COLORREF    crTextColourOld;
    CString     strSample;

    ASSERT( pDC != NULL );
    ASSERT( m_plfCurrentFont != NULL );
    if( pDC != NULL && m_plfCurrentFont != NULL )
    {
        strSample.LoadString( IDS_STR_SAMPLE );

        dcMem.CreateCompatibleDC( NULL );

        // Select our gdi stuff into dc
        pBmpOld = (CBitmap*)dcMem.SelectObject( &m_bmpSample );
        pOldPen = (CPen*)dcMem.SelectStockObject( BLACK_PEN );
        //pOldBrush = (CBrush*)dcMem.SelectStockObject( WHITE_BRUSH );
        CBrush brBackground;
        double dLumi = (0.2126 * GetRValue(m_crCurrentFontColour))
            + (0.7152*GetGValue(m_crCurrentFontColour)) + (0.0722*GetBValue(m_crCurrentFontColour));
        if (dLumi <= 0.5)
            brBackground.CreateSolidBrush(PALETTERGB(255, 255, 255));
        else
            brBackground.CreateSolidBrush(PALETTERGB(0, 0, 0));
        pOldBrush = (CBrush*)dcMem.SelectObject(&brBackground);

        // Create the font
        fntSample.CreateFontIndirect( m_plfCurrentFont );

        // Get it back out so that we can adjust the font size correctly
        fntSample.GetLogFont( &lfSampleFont );

        // Font height in pixels
        nFontHeightPixels = theApp.GetFontHeightPixels(lfSampleFont.lfHeight, &dcMem);

        // Update font
        lfSampleFont.lfHeight = nFontHeightPixels;
        fntSample.DeleteObject();
        fntSample.CreateFontIndirect( &lfSampleFont );

        // Erase display  (will draw  a white rectangle with black border)
        dcMem.Rectangle( m_rctSampleMem );

        // Select our font and colour values and modes
        pOldFont = (CFont*)dcMem.SelectObject( &fntSample );
        crTextColourOld = dcMem.SetTextColor( m_crCurrentFontColour );
        iBackModeOld = dcMem.SetBkMode( TRANSPARENT );

        // Display the text
        dcMem.DrawText( strSample, m_rctSampleText, DT_SINGLELINE|DT_VCENTER|DT_CENTER );

        // Blast main memory dc to display
        pDC->BitBlt( m_rctSample.left, m_rctSample.top,
            m_rctSample.Width(), m_rctSample.Height(),
            &dcMem, 0, 0, SRCCOPY );

        // Restore old font and colour values and modes
        dcMem.SelectObject( pOldFont );
        dcMem.SetTextColor( crTextColourOld );
        dcMem.SetBkMode( iBackModeOld );
        dcMem.SelectObject( pBmpOld );
        dcMem.SelectObject( pOldPen );
        dcMem.SelectObject( pOldBrush );

        // Release memory
        dcMem.DeleteDC();
        fntSample.DeleteObject();
        // brushes and pens don't need deleting as they are stock objects
        brBackground.DeleteObject();
    }
}
Andrew Truckle
  • 17,769
  • 16
  • 66
  • 164