2017-02-14 20 views
3

Моя цель состоит в том, чтобы динамически поместить некоторый произвольный текст в HICON изображение Я использую следующий код (во время выполнения.):Как нарисовать текст с прозрачностью с помощью GDI?

//Error checks are omitted for brevity 

//First create font 
LOGFONT lf = {0}; 
lf.lfHeight = -58; 
lf.lfWeight = FW_NORMAL; 
lf.lfOutPrecision = OUT_TT_PRECIS; //Use TrueType fonts for anti-alliasing 
lf.lfQuality = CLEARTYPE_QUALITY; 
lstrcpy(lf.lfFaceName, L"Segoe UI"); 

HFONT hFont = ::CreateFontIndirect(&lf); 


//HICON hIcon = original icon to use as a source 
//I'm using a large 256x256 pixel icon 
hIcon = (HICON)::LoadImage(theApp.m_hInstance, MAKEINTRESOURCE(IDI_ICON_GREEN_DIAMOND), IMAGE_ICON, 256, 256, LR_DEFAULTCOLOR); 

ICONINFO ii = {0}; 
::GetIconInfo(hIcon, &ii); 

BITMAP bm = {0}; 
::GetObject(ii.hbmColor, sizeof(bm), &bm); 
SIZE szBmp = {bm.bmWidth, bm.bmHeight}; 

HDC hDc = ::GetDC(hWnd); 
HDC hMemDC = ::CreateCompatibleDC(hDc); 

HGDIOBJ hOldBmp = ::SelectObject(hMemDC, ii.hbmColor); 
HGDIOBJ hOldFont = ::SelectObject(hMemDC, hFont); 

::SetBkMode(hMemDC, TRANSPARENT); 
::SetTextColor(hMemDC, RGB(255, 0, 0));  //Red text 

//Draw text 
//NOTE that DrawText API behaves in a similar way 
::TextOut(hMemDC, 0, 0, L"Hello", 5); 

::SelectObject(hMemDC, hOldFont); 
::SelectObject(hMemDC, hOldBmp); 


//We need a simple mask bitmap for the icon 
HBITMAP hBmpMsk = ::CreateBitmap(szBmp.cx, szBmp.cy, 1, 1, NULL); 

ICONINFO ii2 = {0}; 
ii2.fIcon = TRUE; 
ii2.hbmColor = ii.hbmColor; 
ii2.hbmMask = hBmpMsk; 

//Create updated icon 
HICON hIcon2 = ::CreateIconIndirect(&ii2); 


//Cleanup 
::DeleteObject(hBmpMsk); 
::DeleteDC(hMemDC); 
::ReleaseDC(hWnd, hDc); 
::DeleteObject(ii.hbmColor); 
::DeleteObject(ii.hbmMask); 

::DeleteObject(hFont); 

, а затем я могу отобразить значок в моем окне из OnPaint() обработчика (так что я могу видеть, как это получается), как например:

::DrawIconEx(dc.GetSafeHdc(), 0, 0, 
    hIcon2, 
    256, 256, NULL, 
    ::GetSysColorBrush(COLOR_BTNFACE), 
    DI_NORMAL); 

так вот что я получаю:

enter image description here

Чтобы посмотреть, что происходит в пикселях в моем hIcon2 Я позвонил GetDIBits на свой ii.hbmColor по вышеуказанному коду. Полученный массив пикселей, где предполагается мое слово «Hello» должен был быть показан выглядел следующим образом:

enter image description here

Пиксели кодируются как BGRA в этом дампа памяти, поэтому четвёртую байт в каждом DWORD выступает за прозрачность: 0 = прозрачный, FF = непрозрачный. Но в этом случае TextOut не заполняет прозрачность или оставляет его как 0, что интерпретируется как «полностью прозрачное». Вместо этого он, кажется, предварительно размножает его в цветах RGB.

Обратите внимание, что если я продолжаю смотреть дальше по той же битовой карты, где начинается зеленый алмаз, изображение пикселей, кажется, прозрачность байты установлены правильно:

enter image description here

Любая идея, как сделать текст таким образом, чтобы API может установить эти байты прозрачности?

EDIT: Как было предложено ниже я попытался следующий GDI + метод:

HGDIOBJ hOldBmp = ::SelectObject(hMemDC, ii.hbmColor); 

Graphics grpx(hMemDC); 

RectF rcfTxt(0.0f, 0.0f, (REAL)szBmp.cx, (REAL)szBmp.cy); 
Font gdiFont(L"Segoe UI", 58.0f, FontStyleRegular, UnitPixel); 

SolidBrush gdiBrush(Color(255, 0, 0)); 

StringFormat gdiSF; 
gdiSF.SetAlignment(StringAlignmentNear); 
gdiSF.SetFormatFlags(StringFormatFlagsNoWrap); 
gdiSF.SetHotkeyPrefix(HotkeyPrefixNone); 

//The reason I was using GDI was because I was setting 
//spacing between letters using SetTextCharacterExtra() 
//Unfortunately with GDI+ this does not work! 
HDC hTmpDC = grpx.GetHDC(); 
::SetTextCharacterExtra(hTmpDC, -4); //This doesn't do anything! 
grpx.ReleaseHDC(hTmpDC); 

grpx.DrawString(L"Hello", 5, &gdiFont, rcfTxt, &gdiSF, &gdiBrush); 

::SelectObject(hMemDC, hOldBmp); 

и к тому же не в состоянии установить характер интервала между (который я мог бы с GDI, используя SetTextCharacterExtra) вот что я получил (немного увеличенный для наглядности):

enter image description here

Так ясно все еще проблема с прозрачностью.

+0

GDI не понимает альфа; Вместо этого рассмотрите gdiplus. –

+0

@JonathanPotter: Спасибо. Да, похоже, что GDI + - единственный способ сделать это с поддержкой альфа-канала. Хотя причина, по которой я пошел с простым GDI, заключается в том, что он предоставил ['SetTextCharacterExtra'] (https://msdn.microsoft.com/en-us/library/windows/desktop/dd145092 (v = vs.85) .aspx) функция для изменения интервала между символами. Вы знаете, могу ли я использовать его с GDI +? – c00000fd

ответ

1

Взято из старого сообщения Microsoft MVP Майк Д Саттон here.

При создании постоянного тока он изначально имеет «запас» объекты по умолчанию выбранных в него, в том числе акции 1 * 1 * 1 Bitmap. Так как битмап уже выбран в DC, когда вы вызываете DrawText(), он все равно будет попробует и сделает его, хотя почти все (кроме один пиксель) будут обрезаны.

Что вам нужно сделать, так это создать растровое изображение, либо DDB, либо DIBSection, и выбрать его в своем DC перед рисунком .

Во-первых, хотя вы должны найти размер вашего Bitmap, так как вы хотите это достаточно большой, чтобы отобразить текст в, так для этого вы используете DrawText() еще раз позвонить на начальном DC, но включают флаг DT_CALCRECT , То, что это делает, а не рисовать что угодно, просто измеряет, насколько большой текст и сбрасывает это в RECT, который вы передаете . Отсюда вы можете пойти и создать свой DIBSection с помощью этих размеров и выбрать его в своем DC. Наконец, выполните ваш существующий вызов DrawText() (вы также можете использовать SetBkMode/Color()) , который отобразит текст в DIBSection, из которого вы можете получить данные .

Это, кажется, работает очень хорошо здесь:

HBITMAP CreateAlphaTextBitmap(LPCSTR inText, HFONT inFont, COLORREF inColour) { 
    int TextLength = (int)strlen(inText); 
    if (TextLength <= 0) return NULL; 

    // Create DC and select font into it 
    HDC hTextDC = CreateCompatibleDC(NULL); 
    HFONT hOldFont = (HFONT)SelectObject(hTextDC, inFont); 
    HBITMAP hMyDIB = NULL; 

    // Get text area 
    RECT TextArea = {0, 0, 0, 0}; 
    DrawText(hTextDC, inText, TextLength, &TextArea, DT_CALCRECT); 

    if ((TextArea.right > TextArea.left) && (TextArea.bottom > TextArea.top)) { 
     BITMAPINFOHEADER BMIH; 
     memset(&BMIH, 0x0, sizeof(BITMAPINFOHEADER)); 

     void *pvBits = NULL; 

     // Specify DIB setup 
     BMIH.biSize = sizeof(BMIH); 
     BMIH.biWidth = TextArea.right - TextArea.left; 
     BMIH.biHeight = TextArea.bottom - TextArea.top; 
     BMIH.biPlanes = 1; 
     BMIH.biBitCount = 32; 
     BMIH.biCompression = BI_RGB; 

     // Create and select DIB into DC 
     hMyDIB = CreateDIBSection(hTextDC, (LPBITMAPINFO)&BMIH, 0, (LPVOID*)&pvBits, NULL, 0); 
     HBITMAP hOldBMP = (HBITMAP)SelectObject(hTextDC, hMyDIB); 

     if (hOldBMP != NULL) { 
      // Set up DC properties 
      SetTextColor(hTextDC, 0x00FFFFFF); 
      SetBkColor(hTextDC, 0x00000000); 
      SetBkMode(hTextDC, OPAQUE); 

      // Draw text to buffer 
      DrawText(hTextDC, inText, TextLength, &TextArea, DT_NOCLIP); 

      BYTE* DataPtr = (BYTE*)pvBits; 
      BYTE FillR = GetRValue(inColour); 
      BYTE FillG = GetGValue(inColour); 
      BYTE FillB = GetBValue(inColour); 
      BYTE ThisA; 

      for (int LoopY = 0; LoopY < BMIH.biHeight; LoopY++) { 
       for (int LoopX = 0; LoopX < BMIH.biWidth; LoopX++) { 
        ThisA = *DataPtr; // Move alpha and pre-multiply with RGB 
        *DataPtr++ = (FillB * ThisA) >> 8; 
        *DataPtr++ = (FillG * ThisA) >> 8; 
        *DataPtr++ = (FillR * ThisA) >> 8; 
        *DataPtr++ = ThisA; // Set Alpha 
       } 
      } 

      // De-select bitmap 
      SelectObject(hTextDC, hOldBMP); 
     } 
    } 

    // De-select font and destroy temp DC 
    SelectObject(hTextDC, hOldFont); 
    DeleteDC(hTextDC); 

    // Return DIBSection 
    return hMyDIB; 
} 

Если вам нужен пример того, как назвать то попробовать что-то вроде этого (INDC является DC воздавать):

void TestAlphaText(HDC inDC, int inX, int inY) { 
    const char *DemoText = "Hello World!\0"; 

    RECT TextArea = {0, 0, 0, 0}; 
    HFONT TempFont = CreateFont(50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "Arial\0"); 
    HBITMAP MyBMP = CreateAlphaTextBitmap(DemoText, TempFont, 0xFF); 
    DeleteObject(TempFont); 

    if (MyBMP) { // Create temporary DC and select new Bitmap into it 
     HDC hTempDC = CreateCompatibleDC(inDC); 
     HBITMAP hOldBMP = (HBITMAP)SelectObject(hTempDC, MyBMP); 

     if (hOldBMP) { 
      BITMAP BMInf; // Get Bitmap image size 
      GetObject(MyBMP, sizeof(BITMAP), &BMInf); 

      // Fill blend function and blend new text to window 
      BLENDFUNCTION bf; 
      bf.BlendOp = AC_SRC_OVER; 
      bf.BlendFlags = 0; 
      bf.SourceConstantAlpha = 0x80; 
      bf.AlphaFormat = AC_SRC_ALPHA; 
      AlphaBlend(inDC, inX, inY, BMInf.bmWidth, BMInf.bmHeight, 
       hTempDC, 0, 0, BMInf.bmWidth, BMInf.bmHeight, bf); 

      // Clean up 
      SelectObject(hTempDC, hOldBMP); 
      DeleteObject(MyBMP); 
      DeleteDC(hTempDC); 
     } 
    } 
} 

Все кредиты для ответа и кода переходят к оригинальным плакатам на этом форуме, я просто перепродал его, чтобы этот ответ был действительным, если ссылки умирают.

+0

На самом деле он не отвечает на вопрос. Кажется, что то, что он делает (в проводке Google), является предварительным умножением текста на сплошном цветовом фоне. Мой фон прозрачен. – c00000fd

+0

@ c00000fd, я думаю, что так и есть. Вам нужно сделать свой текст в другой HBitmap с предварительно умноженной альфой. Затем AlphaBlend (https://msdn.microsoft.com/en-us/library/windows/desktop/dd183351(v=vs.85).aspx) это на ваш окончательный растровый рисунок. – Ani