如何创建文字图片

一次,同事向我请教如何创建文字图片的问题,我想因该是不难的,但是效果总是出不来,我俩为此用了一下午的时间来解决这个问题,后来在一篇外文上找到了解决方案,用它的一试,效果是出来了。问题是有几处关键的代码,我们弄不明白,资料又无从查起,这让我们很老火;都知道做技术的,对所在技术不明其理,那是不行的。 当然我们运气好,最终弄明白了那些代码的含义。归根结底,就是GDI API函数忽略了Alpha值的问题。借此总结下心得,以备自用。

首先要搞明白,什么场景下会用到文字图片?在回答这个问题前,我们需要有个共识,所有的GDI函数都不能处理Alpha channel,除了AlphaBlend函数(但是这个函数只是从一个DC拷贝目的DC,并不能直接控制Alpha,需要通过CreateDIBSection函数创建一个带Alpha channel的HBITMAP,然后选到内存DC里去进行操作。),如果在内存DC里调用DrawText或TextOut绘制文本,那么他的alpha值就全是零。然后调用AlphaBlend函数,把内存DC复制到目的DC,就会导致文字区域显示透明,看不见。所以为了解决这个问题,我们就要用到文字图片,或者用GDI+绘制,GDI+没有Alpha的问题。

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, 0x00FF0000); 
            SetBkMode(hTextDC, TRANSPARENT); 

            // 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; 
}
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)
        {
            // Get Bitmap image size
            BITMAP BMInf;
            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); 
        } 
    } 
} 
代码取至http://stackoverflow.com/questions/5309914/updatelayeredwindow-and-drawtext

我们先来分析下CreateAlphaTextBitmap函数,这个函数功能是创建带alpha channel的文本,返回HBTIMAP。主流程是:

1、首先创建内存DC、字体选到内存DC里、获取文本的区域;

2、然后调用CreateDIBSection创建带有alpha通道的HBITMAP、把HBITMAP选进内存DC;然后调用SetTextColor设置文本的颜色,这里需要注意的是,这个颜色并不是最终

      绘制出来的颜色,最终的颜色是由inColour值决定。这里0x00FF0000值的顺序是ARGB,但是在内存的布局顺序是BGRA,这个认识很重要。接着因为不需要文字背景,所       以调用SetBkMode(hTextDC, TRANSPARENT);然后就是绘制文本

3、最后改变每个像素的Alpha值、颜色值。先取出第一个字节的值保存下来,前面说过,在内存里的布局顺序BGRA,第一个字节是Blue,在像素扫描过程中,大部分时候Blue值都是0,只有经过文字区域时,Blue值是由之前设置的文本颜色0x00FF0000决定的,所以文本区域Blue是FF; 知道了这个理解起来就相当的容易了,接下来就是分别取出颜色值填充。


然后就是TestAlphaText函数,这个函数很简单,就是调用CreateAlphaTextBitmap函数获取HBITMAP,然后把HBITMAP选进内存DC,最后调用AlphaBlend函数把文字图片画到指定的DC上去。


效果图:


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值