优化无止境——特定场景的位图叠加算法优化


最近需要写一段位图操作的代码,功能是将一幅特定的Logo位图贴到视频中的每
一帧上,实现类似电视台台标的效果,限制就是要过滤小位图中的特定颜色,避
免出现难看的矩形块。

如果用 GDI来做,用 TransparentBlt可以做到,可实际环境不允许,只能直接操作
位图数据。

最先在网上找了一些位图透明叠加的实现,多数是采用mask的方法,也就是针对
Logo位图,设计一张同样大小和颜色深度的mask位图,将需要过滤的颜色置为黑
色,然后用bitblt两次,第一次将Logo位图和mask进行and,第二次将and过的结
果copy到视频帧上。

这个方法比较通用,但是也比较烦琐,如果不考虑bitblt,完全可以自己写位图
操作函数,凡是遇到特定颜色就跳过,假设需要过滤的颜色为黑色(RGB值为0),
也假设位图是256位的,也就是每个像素的颜色用一个字节就可以表示。

用以下代码可以实现位图叠加的基本功能:

void BitmapOverlay(...)
{
    BYTE *pSource = ...    /*    来源位图数据,也即Logo位图的数据    */
    DWORD dwSourceHeight = ... /*    Logo位图的高度    */
    DWORD dwSourceWidth  = ... /*    Logo位图的宽度    */
   
    BYTE *pTarget = ...    /*    目标位图数据,也即视频帧的位图数据    */
    DWORD dwTargetHeight = ... /*    目标位图的高度    */
    DWORD dwTargetWidth  = ... /*    目标位图的宽度    */
   
    DWORD dwOffsetX = ... /* Logo位图在目标位图上的X偏移位置    */
    DWORD dwOffsetY = ... /* Logo位图在目标位图上的Y偏移位置    */
   
    for (DWORD dwSourceLine = 0; dwSourceLine < dwSourceHeight; dwLine++) {
        for (DWORD dwSourcePixel = 0; dwSourcePixel < dwSourceWidth; dwSourcePixel++) {
            DWORD dwSourcePos = dwSourceLine*dwSourceWidth + dwSourcePixel;
           
            if (pSource[dwSourcePos]) {
                DWORD dwTargetLine = dwSourceLine + dwOffsetY;
                DWORD dwTargetPixel = dwSourcePixel + dwOffsetX;
                DWORD dwTargetPos = dwTargetLine*dwTargetWidth + dwTargetPixel;

                pTarget[dwTargetPos] = pSource[dwSourcePos];
            }
        }
    }
}
上面的代码实现了位图叠加的功能,但是性能...实在太低了。

很明显需要优化的地方就是循环中的乘法,将其修改为加法实现,
void BitmapOverlay(...)
{
    ...
   
    DWORD dwSourcePos = 0;    /*    Logo位图当前待处理像素的位置    */
    DWORD dwTargetPos = 0;    /*    目标位图当前待处理像素的位置    */
   
    dwTargetPos = dwOffsetY * dwTargetWidth + dwOffsetX;
   
    for (DWORD dwSourceLine = 0; dwSourceLine < dwSourceHeight; dwLine++) {
        for (DWORD dwSourcePixel = 0; dwSourcePixel < dwSourceWidth; dwSourcePixel++) {
            if (pSource[dwSourcePos]) {
                pTarget[dwTargetPos] = pSource[dwSourcePos];
            }
   
            dwSourcePos++;
            dwTargetPos++;
        }
       
        /*    跳到目标位图下一行的对应位置    */
        dwTargetPos += (dwTargetWidth - dwSourceWidth + dwOffsetX);
    }
}

上面代码的性能,比优化前提高了很多,但这仍然是在Bitblt的思路上,
Bitblt本身是一个通用的GDI函数,而我们要处理的是一个特定的场景,
其中Logo位图的数据是恒定的,只是目标位图的数据在不断变化。

既然存在恒定的数据,那就可以采用预处理的方法,减少后期的计算量。

t ypedef tagLogoData {
    BYTE byColor;
    DWORD dwPos;
} LogoData;

void PreProcessSource(...)
{
    BYTE *pSource = ...    /*    来源位图数据,也即Logo位图的数据    */
    DWORD dwSourceHeight = ... /*    Logo位图的高度    */
    DWORD dwSourceWidth  = ... /*    Logo位图的宽度    */
   
    DWORD dwTargetHeight = ... /*    目标位图的高度    */
    DWORD dwTargetWidth  = ... /*    目标位图的宽度    */
   
    DWORD dwOffsetX = ... /* Logo位图在目标位图上的X偏移位置    */
    DWORD dwOffsetY = ... /* Logo位图在目标位图上的Y偏移位置    */

    DWORD dwSourcePos = 0;    /*    Logo位图当前待处理像素的位置    */
    DWORD dwTargetPos = 0;    /*    目标位图当前待处理像素的位置    */
   
    dwTargetPos = dwOffsetY * dwTargetWidth + dwOffsetX;

    LogoData *pLogoData = new .../*    分配足够的内存给Logo数据的数组    */;
    DWORD dwLogoDataSize = 0;
   
    for (DWORD dwSourceLine = 0; dwSourceLine < dwSourceHeight; dwLine++) {
        for (DWORD dwSourcePixel = 0; dwSourcePixel < dwSourceWidth; dwSourcePixel++) {
            if (pSource[dwSourcePos]) {
                pLogoData->byColor = pSource[dwSourcePos];
                pLogoData->dwPos = dwTargetPos;
                pLogoData++;
                dwLogoDataSize++;
            }
   
            dwSourcePos++;
            dwTargetPos++;
        }
       
        /*    跳到目标位图下一行的对应位置    */
        dwTargetPos += (dwTargetWidth - dwSourceWidth + dwOffsetX);
    }
}

此时的位图叠加函数就变得非常简单
void BitmapOverlay(...)
{
    LogoData *pLogoData = ...    /*    Logo数据的数组            */;
    DWORD dwLogoDataSize = ...    /*    Logo数据的数组的长度    */;

    for (DWORD dwIndex = 0; dwIndex < dwLogoDataSize; dwIndex++) {
        pTarget[pLogoData->dwPos] = pLogoData->byColor;
    }
}

如果Logo位图中的有效数据较少(比如是文字),那采用预处理的做法就能
有效提高性能。

如果是较大块的图片,则还有进一步优化的余地——上面所有的实现中,都采
取了按字节赋值的方法,实际上对于较大块的图片来说,连续的有效的位图数
据占绝大多数,只需要在预处理时,将有效的联系位图数据组成一个个内存块,
然后在在叠加时就可以CopyMemory来进一步提高性能。

 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值