Win32 Text Drawing Puzzle

巧了我就是萌 提交于 2019-12-22 13:49:10

问题


I've got a little text drawing puzzle under Win32. I'm trying to draw some instructions for users of my application at the top of the window.

Please refer to the following window (I've changed the background color on the text so you can see the boundaries)


(source: billy-oneal.com)

I'm currently using DrawTextEx to draw the text to my window, but the problem is that it does not fill the entire RECTangle that I give it. Not drawing that area is just fine, until the window resizes:


(source: billy-oneal.com)

When the text is re wrapped due to the window sizing, because DrawTextEx doesn't clear it's background, these artifacts are leftover.

I tried using FillRect to fill in the area behind the text drawing call, which does eliminate the visual artifacts, but then causes the text to flicker constantly, as it is completely erased and then completely redrawn to the display.

Any ideas on how one might get the area not containing text to be drawn with the background color?

EDIT: I'd like to avoid having to double buffer the form if at app possible.

EDIT2: I solved the problem by only redrawing the text when I detect that the wrapping changes during a resize.


回答1:


Use double buffering?

Draw everything to a bitmap and draw the bitmap to the window. Flickering is commonly a double buffering issue.




回答2:


There are many possible solutions and without seeing your code, it's hard to tell which method would be best so I'd suggest taking a look at this article on flicker free drawing




回答3:


SetBkMode + SetBkColor ?




回答4:


Well since nobody seems to know what to do about it, I implemented it this way:

std::vector<std::wstring> wrapString(HDC hDC, const std::wstring& text, const RECT& targetRect, HFONT font)
{
    std::vector<std::wstring> result;
    RECT targetRectangle;
    CopyRect(&targetRectangle, &targetRect);

    //Calculate the width of the bounding rectangle.
    int maxWidth = targetRectangle.right - targetRectangle.left;

    //Draw the lines one at a time
    std::wstring currentLine;
    for(std::wstring::const_iterator it = text.begin(); it != text.end(); currentLine.push_back(*it), it++)
    {
        if(*it == L'\r' || *it == L'\n')
        { //Hard return
            while(it != text.end() && (*it == L'\r' || *it == L'\n')) it++;
            result.push_back(currentLine);
            currentLine.clear();
        }
        else
        { //Check for soft return
            SIZE sizeStruct;
            GetTextExtentPoint32(hDC, currentLine.c_str(), static_cast<int>(currentLine.length()), &sizeStruct);
            if (sizeStruct.cx > maxWidth)
            {
                std::wstring::size_type lineLength = currentLine.find_last_of(L' ');
                if (lineLength == currentLine.npos)
                { //Word is longer than a line.
                    for(;it != text.end() && !iswspace(*it);it++) currentLine.push_back(*it);
                }
                else
                { //Clip word to line.
                    //Backtrack our scan of the source text.
                    it -= currentLine.length() - lineLength - 1;
                    //Remove the clipped word
                    currentLine.erase(lineLength);
                }
                result.push_back(currentLine);
                currentLine.clear();
            }
        }
    }
    //Last remaining text.
    result.push_back(currentLine);
    return result;
}

void DrawInstructionsWithFilledBackground(HDC hDC, const std::wstring& text, RECT& targetRectangle, HFONT font, COLORREF backgroundColor)
{
    //Set up our background color.
    int dcIdx = SaveDC(hDC);
    HBRUSH backgroundBrush = CreateSolidBrush(backgroundColor);
    SelectObject(hDC, backgroundBrush);
    SelectObject(hDC, font);
    SetBkColor(hDC, backgroundColor);

    std::vector<std::wstring> lines(wrapString(hDC, text, targetRectangle, font));
    for(std::vector<std::wstring>::const_iterator it = lines.begin(); it!=lines.end(); it++)
    {
        RECT backgroundRect = targetRectangle;
        DrawText(hDC, const_cast<LPWSTR>(it->c_str()), static_cast<int>(it->length()), &backgroundRect, DT_CALCRECT | DT_NOCLIP | DT_SINGLELINE);
        backgroundRect.left = backgroundRect.right;
        backgroundRect.right = targetRectangle.right;
        if (backgroundRect.right >= backgroundRect.left)
        FillRect(hDC, &backgroundRect, backgroundBrush);
        ExtTextOut(hDC, targetRectangle.left, targetRectangle.top, ETO_OPAQUE, NULL, it->c_str(), static_cast<UINT>(it->length()), NULL);
        targetRectangle.top += backgroundRect.bottom - backgroundRect.top;
    }
    instructionsWrap = lines;
    //Restore the DC to it's former glory.
    RestoreDC(hDC, dcIdx);
    DeleteObject(backgroundBrush);
}



回答5:


Get/Calculate the rect used by the DrawText call and clip it with something like ExcludeClipRect before calling FillRect



来源:https://stackoverflow.com/questions/1835749/win32-text-drawing-puzzle

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!