• 16位图像Alpha混合的实现(用汇编写的,比MMX还要快)


    Alpha 混合的算法很简单,基于下面的公式就可以实现:

    D := A * (S - D) / 255 + D

    D 是目标图像的像素,

    S 是源图像的像素

    A 是 Alpha 值, 0 为全透明, 255 为不透明。

    下面是 16 位 565 格式的混合算法的实现,首先用最简单的方式实现,即逐个像素的处理:

    // 一次处理一个像素,比较简单,但速度较慢

    procedure AlphaBlend656(BmpDst, BmpSrc: TBitmap; Alpha: Byte);

    var

      i, j, W, H: Integer;

      pSrc, pDst: PWord;

      wSR, wSG, wSB: Word;

      wDR, wDG, wDB: Word;

    begin

      // 确定高宽

      if BmpDst.Width > BmpSrc.Width then

        W := BmpSrc.Width

      else

        W := BmpDst.Width;

      if BmpDst.Height > BmpSrc.Height then

        H := BmpSrc.Height

      else

        H := BmpDst.Height;

      for i := 0 to H - 1do

      begin

        pSrc := BmpSrc.ScanLine[i];

        pDst := BmpDst.ScanLine[i];

        for j := 0 to W - 1 do

        begin

          // D := A * (S - D) / 255 + D

          wSR := (pSrc^ shr 11);

          wSG := (pSrc^ shr 5) and $3F;

          wSB := pSrc^ and $1F;

          wDR := (pDst^ shr 11);

          wDG := (pDst^ shr 5) and $3F;

          wDB := pDst^ and $1F;

          pDst^ := (((Alpha * (wSR - wDR) shr 8) + wDR) shl 11) or

                   (((Alpha * (wSG - wDG) shr 8) + wDG) shl 5) or

                   ((Alpha * (wSB - wDB) shr 8) + wDB);

          Inc(pSrc);

          Inc(pDst);

        end;

      end;

    end;

    实现起来很简单,但速度比较慢,其实存在着一次处理两个像素的算法,下面是代码:

    // 一次处理两个像素 , 所以速度是 AlphaBlend656 的 2 倍

    procedure AlphaBlend656Fast(BmpDst, BmpSrc: TBitmap; Alpha: Byte);

    var

      i, j, W, H: Integer;

      pSrc, pDst: PWord;

      dwSR, dwSG, dwSB: LongWord;

      dwDR, dwDG, dwDB: LongWord;

      dwAdd64 : LongWord;

      dwAlphaOver4 : LongWord;

      odd: Boolean;

    begin

        // 确定高宽

      if BmpDst.Width > BmpSrc.Width then

        W := BmpSrc.Width

      else

        W := BmpDst.Width;

      if BmpDst.Height > BmpSrc.Height then

        H := BmpSrc.Height

      else

        H := BmpDst.Height;

      dwAdd64 := 64 or ( 64 shl 16 );

      dwAlphaOver4 := ( Alpha shr 2 ) or ( ( Alpha shr 2 ) shl 16 );

      if (W and $01) = 1 then

      begin

        odd := True;

        W := (W - 1) shr 1;

      end

      else begin

        odd := False;

        W := W shr 1;

      end;

      for i := 0 to H - 1 do

      begin

        pSrc := BmpSrc.ScanLine[i];

        pDst := BmpDst.ScanLine[i];

        for j := 0 to W - 1 do

        begin

          // D := A * (S - D) / 255 + D

          dwSR := (PLongWord(pSrc)^ shr 11) and $001F001F;

          dwSG := (PLongWord(pSrc)^ shr 5) and $003F003F;

          dwSB := PLongWord(pSrc)^ and $001F001F;

          dwDR := (PLongWord(pDst)^ shr 11) and $001F001F;

          dwDG := (PLongWord(pDst)^ shr 5) and $003F003F;

          dwDB := PLongWord(pDst)^ and $001F001F;

          PLongWord(pDst)^ := ((((Alpha * (dwSR + dwAdd64 - dwDR)) shr 8) + dwDR - dwAlphaOver4) and $001F001F) shl 11 or

                   ((((Alpha * (dwSG + dwAdd64 - dwDG)) shr 8) + dwDG - dwAlphaOver4 ) and $003F003F) shl 5 or

                   (((Alpha * (dwSB + dwAdd64 - dwDB)) shr 8) + dwDB - dwAlphaOver4 ) and $001F001F;

          Inc(pSrc, 2);

          Inc(pDst, 2);

        end;

        if odd then

        begin

          dwSR := (pSrc^ shr 11);

          dwSG := (pSrc^ shr 5) and $3F;

          dwSB := pSrc^ and $1F;

          dwDR := (pDst^ shr 11);

          dwDG := (pDst^ shr 5) and $3F;

          dwDB := pDst^ and $1F;

          pDst^ := Word((((Alpha * (dwSR - dwDR) shr 8) + dwDR) shl 11) or

                   (((Alpha * (dwSG - dwDG) shr 8) + dwDG) shl 5) or

                   ((Alpha * (dwSB - dwDB) shr 8) + dwDB));

          Inc(pSrc);

          Inc(pDst);

        end; 

      end;

    end;

    不过这还不够快,基本 MMX 指令的实现可以一次处理 4 个像素,下面是代码:

    // 利用 MMX 优化指令,一次可以处理 4 个像素,因此速度应该是 AlphaBlend656 的 4 倍

    procedure AlphaBlend656MMX(BmpDst, BmpSrc: TBitmap; Alpha: Byte);

    var

      i, j, W, H, Leave: Integer;

      pSrc, pDst: PWord;

      MaskR, MaskG, MaskB, Alpha64: Int64;

      wSR, wSG, wSB: Word;

      wDR, wDG, wDB: Word;

    begin

      // 确定高宽

      if BmpDst.Width > BmpSrc.Width then

        W := BmpSrc.Width

        else

        W := BmpDst.Width;

      if BmpDst.Height > BmpSrc.Height then

        H := BmpSrc.Height

      else

        H := BmpDst.Height;

      Leave := W and 3;             // 剩余的像素

      W := W shr 2;                 // 一次处理 4 个像素,因此取 W 整除 4 的值

      // 提取 RGB 通道的掩码

      MaskR := $f800f800f800f800;

      MaskG := $07e007e007e007e0;

      MaskB := $001f001f001f001f;

      // Alpha 值扩展到 64 位

      Alpha64 := Alpha;

      Alpha64 := Alpha64 or (Alpha64 shl 16) or (Alpha64 shl 32) or (Alpha64 shl 48);

      for i := 0 to H - 1do

      begin

        pSrc := BmpSrc.ScanLine[i];

        pDst := BmpDst.ScanLine[i];

        asm

          push    ecx               // 保存寄存器

          mov     ecx, W            // 设宽度

          cmp     ecx, 0            // 宽度是否为 0

          jz      @@exit565         // 如果宽度为 0 ,结束

          push    esi

          push    edi

          mov      esi, pSrc         // 开始处理

          mov     edi, pDst

        @@blend565_4:

        { mmx 的作用:

          mm0: red target value

          mm1: red source value

          mm2: green target value

          mm3: green source value

          mm4: blue target value

          mm5: blue source value

          mm6: original target pixel

          mm7: original source pixel

          D := A * (S - D) / 255 + D

        }

          movq    mm6, [edi]

          movq    mm7, [esi]

          movq    mm0, mm6

          pand    mm0, MaskR        // 提取目标的 R 通道

          movq    mm1, mm7

          pand    mm1, MaskR        // 提取源的 R 通道

          psrlw   mm0, 11           // 右移到最低位,便于接下来的计算

          psrlw   mm1, 11

          psubw   mm1, mm0          // SrcRed := SrcRed - DestRed

          pmullw  mm1, Alpha64      // SrcRed := SrcRed * Alpha

          psraw   mm1, 8            // SrcRed := SrcRed div 8

          paddw   mm1, mm0          // SrcRed := SrcRed + DestRed

          psllw   mm1, 11           // 左移回原来的位置,此已经 R 通道混合已经完毕

          movq    mm2, mm6

          pand    mm2, MaskG        // 提取目标的 G 通道

          movq    mm3, mm7

          pand    mm3, MaskG        // 提取源的 G 通道

          psrlw   mm2, 5            // 右移到最低位,便于接下来的计算

          psrlw   mm3, 5

          psubw   mm3, mm2          // SrcGreen := SrcGreen - DestGreen

          pmullw  mm3, Alpha64      // SrcGreen := SrcGreen * Alpha

          psraw   mm3, 8            // SrcGreen := SrcGreen div 8

          paddw   mm3, mm2          // SrcGreen := SrcGreen + DestGreen

          psllw   mm3, 5            // 左移回原来的位置,此已经 G 通道混合已经完毕

          movq    mm4, mm6

          pand    mm4, MaskB        // 提取目标的 B 通道

          movq    mm5, mm7

          pand    mm5, MaskB        // 提取源的 B 通道

          psubw   mm5, mm4          // SrcBlue := SrcBlue - DestBlue

          pmullw  mm5, Alpha64      // SrcBlue := SrcBlue * Alpha

          psraw   mm5, 8            // SrcBlue := SrcBlue div 8

          paddw   mm5, mm4          // SrcBlue := SrcBlue + DestBlue ,此已经 B 通道混合已经完毕

          por     mm1, mm3          // 合成像素

          por     mm1, mm5

          movq    [edi], mm1        // 赋给目标

          add     esi, 8            // 下 4 个像素

          add     edi, 8

          dec     ecx

          jnz      @@blend565_4

          mov     pSrc, esi

          mov     pDst, edi

          pop     edi

          pop     esi

          emms

        @@exit565:

          pop     ecx

        end;

        // 处理剩下的像素

        for j := 0 to Leave - 1 do

        begin

          wSR := (pSrc^ shr 11);

          wSG := (pSrc^ shr 5) and $3F;

          wSB := pSrc^ and $1F;

          wDR := (pDst^ shr 11);

          wDG := (pDst^ shr 5) and $3F;

          wDB := pDst^ and $1F;

          pDst^ := (((Alpha * (wSR - wDR) shr 8) + wDR) shl 11) or

                   (((Alpha * (wSG - wDG) shr 8) + wDG) shl 5) or

                   ((Alpha * (wSB - wDB) shr 8) + wDB);

          Inc(pSrc);

          Inc(pDst);

        end;

      end;

    end;

    下面是这三个函数的速度比较,目标图像是 600*450 的 16 位位图,源图像是 399*532 的 16 位位图,分别进行了 1000 次混合,结果如下:

    AlphaBlend656 :           4516

    AlphaBlend656Fast :       2562

    AlphaBlend656MMX :        1234

    没有意外, MMX 版本比普通的快了近 4 倍

    对于图像处理的优化有两个比较重要的点:

    1、 尽量用位移代替乘除。

    2、 一次能够同时处理多个像素,利用 MMX 指令可以做到这一点。

    最后是代码:

    https://files.getdropbox.com/u/524963/AlphaBlend16_565.rar

    http://blog.csdn.net/linzhengqun/article/details/4269259

  • 相关阅读:
    Django Admin后台管理用户密码修改
    【LOJ】#2122. 「HEOI2015」小 Z 的房间
    【LOJ】#2116. 「HNOI2015」开店
    【LOJ】#2117. 「HNOI2015」实验比较
    【LOJ】#2118. 「HEOI2015」兔子与樱花
    【LOJ】#2114. 「HNOI2015」菜肴制作
    【LOJ】#2115. 「HNOI2015」落忆枫音
    【LOJ】#2111. 「JLOI2015」战争调度
    【LOJ】#2110. 「JLOI2015」管道连接
    【LOJ】#2109. 「JLOI2015」骗我呢
  • 原文地址:https://www.cnblogs.com/findumars/p/5294134.html
Copyright © 2020-2023  润新知