如何使用 Win32 TextOut 绘制波浪形下划线

dig*_*doo 3 c++ winapi textout

我正在编写一段代码,通过多次调用 TextOut 在屏幕上绘制文本。这是一个 C++ Win32 应用程序,我使用的是 Visual Studio 2012。在标记拼写错误等时,如何以 Word 的方式绘制波浪下划线?

TEXTMETRIC 有一个名为 tmUnderlined 的成员,它适用于普通下划线文本,但不适用于波浪下划线。我还发现微软的 RichEdit 控件支持波浪下划线,但我也不能使用它。

当然,我可以使用简单的正弦波创建一条路径,但在我这样做之前,我想检查是否有更标准的方法来做到这一点。

dig*_*doo 5

我自己实现了。这是代码,以防有人感兴趣:

BOOL SomeClass::drawWavyUnderline (WPPoint ptTextPos, int nWidth, COLORREF col)
  // Draws a wavy underline below the position a run of text at the given 
  // position and of the given width would occupy. This method consults the
  // height of the currently selected font in order to find the baseline where
  // the underline is drawn. 
  //    NOTE: The method will fail to find the correct position of the underline
  // if the current text alignment is not set to TA_LEFT!
  // @param ptTextPos (in): TextOut reference point.
  // @return: TRUE on success, FALSE on failure.
  {
  BOOL bResult = FALSE;
  Gdiplus::Graphics *pGfx = NULL;
  Gdiplus::Pen *pPen = NULL;
  Gdiplus::PointF *arrPts = NULL;
  // Determine the number of points required.
     static const float fNumPixelsPerSample = 1.2f;
     int nNumPts = (int)(nWidth / fNumPixelsPerSample);
     if (nNumPts <= 1)
        {
        goto outpoint;  // width too small or even negative!
        }
  // Retrieve information about the current GDI font.
     TEXTMETRIC tm;
     if (!::GetTextMetrics (/* HDC... */, &tm))
        {
        goto outpoint;  // failed to retrieve TEXTMETRIC!
        }
  // Create points array.
     arrPts = new Gdiplus::PointF [nNumPts];
     if (arrPts == NULL)
        {
        goto outpoint;  // out of mem!
        }
  // Fill points array.
     static const float fYOffset = 1.0f;
     static const float fAmp = 1.5f;
     Gdiplus::PointF *pScan = arrPts;
     for (int i = 0; i < nNumPts; i++, pScan++)
        {
        pScan->X = (Gdiplus::REAL)(ptTextPos.x + (float) i * nWidth / (nNumPts - 1));
        // The amplitude is computed as a function of the absolute position x rather
        // than the sample index i in order to make sure the waveform will start at
        // the correct point when two runs are drawn very near each-other.
        float fValue = (float)(fAmp * sin ((pScan->X / fNumPixelsPerSample)*(M_PI / 3.0)));
        pScan->Y = (Gdiplus::REAL)(ptTextPos.y + tm.tmAscent + tm.tmDescent*0.5f + fYOffset + fValue);
        }
  // Create GDI+ graphics object.
     HDC hdc = /* code to retrieve the HDC... */ ;
     if (hdc == NULL)
        {
        goto outpoint;  // missing HDC
        }
     pGfx = new Gdiplus::Graphics (hdc);
     if (pGfx == NULL)
        {
        goto outpoint;  // out of mem!
        }
  // Draw the lines.
     pPen = new Gdiplus::Pen (Gdiplus::Color (GetRValue (col), GetGValue (col), GetBValue (col)));
     if (pPen == NULL)
        {
        goto outpoint;  // out of mem!
        }
     pGfx->SetSmoothingMode (Gdiplus::SmoothingModeHighQuality);
     if (pGfx->DrawLines (pPen, arrPts, nNumPts) != Gdiplus::Ok)
        {
        goto outpoint;  // failed to draw the lines!
        }
  bResult = TRUE;
  outpoint:
  // Clean up.
     if (pPen != NULL)   delete pPen;
     if (pGfx != NULL)   delete pGfx;
     if (arrPts != NULL) delete[] arrPts;
  return bResult;
  }
Run Code Online (Sandbox Code Playgroud)

PS:如果你不喜欢 goto,可以随意重写它并使用 try..catch 代替!