16位图像Alpha混合的实现
发布时间:2011-3-29 20:21
发布者:1770309616
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; pDst := BmpDst.ScanLine; 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; pDst := BmpDst.ScanLine; 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; pDst := BmpDst.ScanLine; 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 |
网友评论