본문 바로가기

GDI & GDI+

텍스트 Outline 효과

728x90
반응형
텍스트에 Outline 효과를 부여하는 다양한 방법

1. 텍스트 출력 시작 좌표를 이동하는 방법
    for (int x = -m_nOutlineWidth; x <= m_nOutlineWidth; ++x)
        for (int y = -m_nOutlineWidth; y <= m_nOutlineWidth; ++y)
            pDC->TextOut(m_ptTextOut.x + x, m_ptTextOut.y + y, m_strText, m_strText.GetLength());
    pDC->SetTextColor(RGB(255,255,255));
    pDC->TextOut(m_ptTextOut.x, m_ptTextOut.y, m_strText, m_strText.GetLength());

원래의 텍스트를 출력하고자 하는 좌표값은 m_ptTextOut 이다.
m_nOutlineWidth 가 외곽선의 두께를 의미하는데,
두께만큼 텍스트 출력좌표를 이동해가면서 텍스트를 그린 후에
흰색으로 텍스트 색상을 변경한 후 원래 텍스트 출력 좌표에 텍스트를 출력하면 다음과 같은 결과가 나타난다.
사용자 삽입 이미지

2. GDI의 Path 오브젝트를 활용
    pDC->BeginPath();
    pDC->TextOut(m_ptTextOut.x, m_ptTextOut.y, m_strText, m_strText.GetLength());
    pDC->EndPath();

    CPen penOutline;
    penOutline.CreatePen(PS_SOLID, m_nOutlineWidth, m_colorText);

    CPen* pOldPen = pDC->SelectObject(&penOutline);
    pDC->StrokePath();
    pDC->SelectObject(pOldPen);

    pDC->SetTextColor(RGB(255,255,255));

    pDC->TextOut(m_ptTextOut.x, m_ptTextOut.y, m_strText, m_strText.GetLength());
CDC 클래스의 BeginPath 함수를 호출하여 Path 저장을 시작하여 텍스트를 출력한다.
EndPath 호출로 Path 저장을 완료하면 폰트의 외곽선이 Path에 저장된다.
적절한 두께의 Pen 오브젝트를 선택하여 StrokePath 함수로 Path를 그린 후
흰색으로 텍스트 색상을 변경하여 원래의 텍스트를 출력한다.
그 결과는 다음과 같다.
사용자 삽입 이미지

3. GDI+ 의 GraphicsPath 를 활용
참조 : http://support.microsoft.com/kb/307208
    Graphics g(pDC->GetSafeHdc());

    g.SetTextRenderingHint(TextRenderingHintAntiAlias);
    g.SetSmoothingMode(SmoothingModeAntiAlias);

    FontFamily fontFamily(m_strFamilyName);
    double fontSize = /*20.0 **/ m_dFontPoint;
    const StringFormat* pStringFormat = StringFormat::GenericTypographic();    // !!!
    Color colorText;
    colorText.SetFromCOLORREF(m_colorText);

    GraphicsPath path;
    path.AddString(m_strText, m_strText.GetLength(),
        &fontFamily,
        FontStyleRegular,
        fontSize,
        PointF(m_ptTextOut.x, m_ptTextOut.y),
        pStringFormat);

    Pen pen(colorText, m_nOutlineWidth);
    g.DrawPath(&pen, &path);

    pDC->SetTextColor(RGB(255,255,255));

    pDC->TextOut(m_ptTextOut.x, m_ptTextOut.y, m_strText, m_strText.GetLength());
GDI의 Path 를 사용하는 원리와 동일하다.
다만 GDI+ 의 객체를 사용하는 방법을 알아야 한다.
중요한 점은 StringFormat::GenericTypographic() 을 사용하는 부분이다.
우선 결과부터 확인해 보도록 하자.
사용자 삽입 이미지
GDI+에서는 Path를 따라서 그리는 경우에 AntiAliasing 효과를 줄 수 있으므로 훨씬 깔끔한 외곽선 효과가 나타날 수 있다.
앞에서 언급한 StringFormat::GenericTypographic() 을 사용하면 GDI에서 출력하는 글자의 폭과 GDI+에서 출력하는 글자의 폭을 동일시할 수 있게 된다.

예제 프로젝트

4. GDI에서 Path를 구하여 GDI+의 Path에 적용하는 방법
이 방법의 필요성은 다음과 같은 이유때문이다.
(1) GDI와 GDI+를 함께 사용해야 하는 경우
(2) AntiAliasing 효과를 적용시키기 위해서
우선 코드를 살펴보도록 하자(조금 길다).
    pDC->BeginPath();
    pDC->TextOut(m_ptTextOut.x, m_ptTextOut.y, m_strText, m_strText.GetLength());
    pDC->EndPath();

    int nNumPts = pDC->GetPath(NULL, NULL, 0);
    LPPOINT lpPoints = NULL;
    LPBYTE lpTypes = NULL;

    lpPoints = new POINT[nNumPts];
    lpTypes = new BYTE[nNumPts];

    nNumPts = pDC->GetPath(lpPoints, lpTypes, nNumPts);

    Graphics g(pDC->GetSafeHdc());

    g.SetTextRenderingHint(TextRenderingHintAntiAlias);
    g.SetSmoothingMode(SmoothingModeAntiAlias);

    GraphicsPath path;

    Point lastPoint;
    std::vector<Point> vecPoint;
    for (int i = 0; i < nNumPts; ++i)
    {
        Point point(lpPoints[i].x, lpPoints[i].y);

        switch (lpTypes[i])
        {
        case PT_MOVETO:
            lastPoint = point;
            if (i > 0)
                path.CloseFigure();
            path.StartFigure();
            break;
        case PT_LINETO:
            path.AddLine(lastPoint, point);
            lastPoint = point;
            break;
        case PT_BEZIERTO:
            if (vecPoint.empty())
            {
                vecPoint.push_back(lastPoint);
            }
            if (vecPoint.size() < 4)
            {
                vecPoint.push_back(point);
            }
            if (4 == vecPoint.size())
            {
                path.AddBezier(vecPoint[0], vecPoint[1], vecPoint[2], vecPoint[3]);

                vecPoint.clear();
                lastPoint = point;
            }
            break;
        case PT_CLOSEFIGURE:
            path.CloseFigure();
            break;
        }
    }
    path.CloseFigure();

    delete[] lpPoints;
    delete[] lpTypes;

    Color colorText;
    colorText.SetFromCOLORREF(m_colorText);
    Pen pen(colorText, m_nOutlineWidth);
    g.DrawPath(&pen, &path);

핵심 원리는 이렇다.
GDI에서 Path를 구한다.
Path를 이루는 각 포인트를 GDI+의 Path에 적용시킨다.

결과 화면은 다음과 같다.
사용자 삽입 이미지

예제 프로젝트

포스트에서 확인할 수 있는 이미지들은 외곽선의 두께가 모두 1이다.
AntiAliasing 효과를 제대로 확인해보기 위해서 두께값을 10 정도로 적용한 결과는 다음과 같다.
1.
사용자 삽입 이미지
2.
사용자 삽입 이미지
3.
사용자 삽입 이미지
4.
사용자 삽입 이미지
반응형

'GDI & GDI+' 카테고리의 다른 글

[C#] 이미지 출력 (확대/축소)  (0) 2008.12.03
VC++에서 GDI+ 사용 방법  (0) 2008.09.04