secondsen 发表于 2012-11-12 11:24:41

alphablend的问题

图片太大。。。截一部分贴上来

桌面widget,自己写alphablend(MMX的),好吧,其实是粘贴别人的,关于mmx的alphablend网上一抓一大把。。我自己呢,MMX还没有完全搞懂。

来吧,代码。。

void Widget_HTC_Weather::AlphaBlend32(UINT* pDstBmp, UINT* pSrcBmp, int bit_w, int bit_h, int rc_x, int rc_y, int rc_w, int rc_h, int x, int y, int sw, int sh, int blendalpha)
{
    // alpha异常
    if (blendalpha < 0 || blendalpha > 255) return;
    // rect尺寸为0或负
    if (rc_w <= 0) return;
    if (rc_h <= 0) return;
    // 越界
    if (x >= sw) return;
    if (y >= sh) return;
    if (rc_w + rc_x - 1 < 0) return;
    if (rc_h + rc_y - 1 < 0) return;
    if (x < 0)
    {
        rc_x -= x;
        rc_w += x;
        x = 0;
    }
    if (y < 0)
    {
        rc_y -= y;
        rc_h += y;
        y = 0;
    }
    if (rc_x >= bit_w) return;
    if (rc_y >= bit_h) return;
    // 修正边界
    if (rc_x < 0)
    {
        rc_w += rc_x;
        rc_x = 0;
    }
    if (rc_y < 0)
    {
        rc_h += rc_y;
        rc_y = 0;
    }
    if (rc_x + rc_w > bit_w) rc_w = bit_w - rc_x;
    if (rc_y + rc_h > bit_h) rc_h = bit_h - rc_y;
    if (x + rc_w > sw) rc_w = sw - x;
    if (y + rc_h > sh) rc_h = sh - y;
    const int nextLineOffset_src = (bit_w - rc_w) * 4;    // 混合完一行像素后,通过加上该值,便可直接定位到下行起始像素
    const int nextLineOffset_dst = (sw - rc_w) * 4;

    pDstBmp += y * sw + x;
    pSrcBmp += rc_y * bit_w + rc_x;
    const int ff = 0xff;

    __asm
    {
        mov            edi, pDstBmp        ; 目的像素
            mov            esi, pSrcBmp        ; 源像素
            xor            ebx, ebx            ; 已混合的高度
            mov            ecx, rc_w            ; 要混合的宽度

BLEND_BEGIN:
        cmp            dword ptr, 0x00FFFFFF; 如果alpha为0,则跳过混合部分
            jna            BLEND_END

            mov            eax, blendalpha        ; 获取blend alpha
            mov            edx, dword ptr
            shr            edx, 24                ; edx 获取源像素 alpha
            imul        eax, edx
            ;cwd
            mov            edx, 0
            idiv        ff
            shl            eax, 24
            mov            edx, eax
            mov            eax, dword ptr
            and            eax, 0x00ffffff
            or            eax, edx

            movd        mm0,             ; 把目的像素值移入mm0寄存器的低32位
            ;movd        mm1,             ; 把源像素值移入mm1寄存器的低32位
            movd        mm1, eax

            ; Core Begin
            pxor        mm2, mm2            ; 把MM2清0
            punpcklbw    mm0, mm2            ; src:8 bit到16 bit以容纳结果,32bit expand to 64 bit
            punpcklbw    mm1, mm2            ; dst:8 bit到16 bit以容纳结果.32bit expand to 64 bit
            movq        mm3, mm1            ; 因为要用dst的Alpha值
            punpckhwd    mm3, mm3            ; 高字移动到双字
            punpckhdq    mm3, mm3            ; 双字移动到四字,现在有八个像素的Alpha了!
            movq        mm4, mm0            ; mm4 = dst
            movq        mm5, mm1            ; mm5 = src
            psubusw        mm4, mm1            ; dst-src,饱和减,小于0为0
            psubusw        mm5, mm0            ; src-dst,饱和减,小于0为0
            pmullw        mm4, mm3            ; Alpha * (src-dst)
            pmullw        mm5, mm3            ; Alpha * (dst-src)
            psrlw        mm4, 8                ; 除以256,now mm4 get the result,(src-dst)<0 部分
            psrlw        mm5, 8                ; 除以256,now mm5 get the result,(dst-src)>0 部分
            paddusw        mm0, mm5            ; 饱和加到原图象:D=Alpha*(O-S)+S,(src-dst)<0 部分
            psubusw        mm0, mm4            ; 饱和加到原图象D=S-Alpha*(S-O),(dst-src)>0 部分
            packuswb    mm0, mm0            ; 紧缩到低32bit
            ; Core End

            ;movd        eax, mm0
            ;and            eax, 0x00ffffff
            ;mov            edx, dword ptr
            ;and            edx, 0xff000000
            ;or            eax, edx
            movd        , mm0            ; 混合结果写进目的像素
            ;mov            dword ptr, eax

BLEND_END:
        add            edi, 4
            add            esi, 4
            ;loop        BLEND_BEGIN                ; 循环
            dec            ecx
            cmp            ecx, 0                ; rc_w <= 0 跳出循环
            jg            BLEND_BEGIN

            add            esi, nextLineOffset_src    ; 加上偏移量,使定位到下行起始处
            add            edi, nextLineOffset_dst

            inc            ebx
            mov            ecx, rc_w

            cmp            ebx, rc_h                ; 若ebx小于rc_h,则转移到上面继续混合
            jb            BLEND_BEGIN

            EMMS                                ; 因为从mm0到mm7,这些寄存器是“借用”浮点寄存器的低64位,所以每次在用完MMX指令后一定要用EMMS指令将寄存器清空
    }
}

问题。。。在图片上。。。原本不贴 太阳那幅图,正常。但是贴上太阳,图像整体变成半透明了,咋办哇?

我看到 颜色 dst,src的混合计算公式

结果色 = dst * (1-src.alpha) + src * src.alpha
那么,其实我有个疑问。。 结果色.alpha怎么算。。

上面的公式 通常默认 dst.alpha = 1.0 (255)的,但是我想混两个不透明色,而不是把透明色混到不透明色上,怎么办?

问题叙述完毕,上图中。。。。

tamashii 发表于 2013-2-4 17:03:08

会用__asm的大触手你好~~~

tamashii 发表于 2013-2-4 17:06:30

D为底色,S为覆盖到上面的颜色
Dr = Sr + ((Dr - Sr) * Sa / 255)
Dg = Sg + ((Dg - Sg) * Sa / 255)
Db = Sb + ((Db - Sb) * Sa / 255)
Da = 255

secondsen 发表于 2013-2-4 17:36:52

两个半透明色混合的结果会被强制转换成不透明的

tamashii 发表于 2013-2-4 19:09:48

桌面应用我不太懂。
不过由于游戏背景本身是黑色的,所以第一个半透明与黑色做AlphaBlend,得到图像A
第二个半透明图像再与图像A做Alphablend,得到图像B

桌面应用的话大概就是:以第一个半透明图像的大小的桌面截图作为背景,然后用第一个半透明图像和这个桌面截图进行AlphaBlend,得到图像A。
然后再用图像A与第二个半透明图像进行AlphaBlend,得到图像B。

这么说你能听懂吧?

secondsen 发表于 2013-2-4 22:23:23

那刷新就需要不停的截图。就是麻烦点。
页: [1]
查看完整版本: alphablend的问题