阅读提示:
《Delphi图像处理》系列以效率为侧重点,一般代码为PASCAL,核心代码采用BASM。
《C++图像处理》系列以代码清晰,可读性为主,全部使用C++代码。
尽可能保持二者内容一致,可相互对照。
本文代码必须包括文章《Delphi图像处理 -- 数据类型及公用过程》中的ImageData.pas单元。
设置图像关键颜色,使图像的某种或某个范围的颜色成为透明色,是图片合成、动画显示中经常用的图像处理手段。下面是实现代码:
procedure ColorKeyEQ;
asm
punpckldq mm2, mm2 // mm2 = ColorKey ColorKey
mov eax, ecx
shr ecx, 1 // 每次比较2个像素
@@yLoop:
push ecx
@@xLoop:
dec ecx
js @@1
movq mm0, [edi] // mm0 = A1 R1 G1 B1 A0 R0 G0 B0
pcmpeqd mm0, mm2 // 按32位数对2个像素进行比较
pandn mm0, [edi] // 比较结果取非后同原象素值作与运算
movq [edi], mm0 // 存入结果
add edi, 8
jmp @@xLoop
@@1:
test eax, 1 // 比较可能存在的最后一个像素
jz @@2
movd mm0, [edi]
pcmpeqd mm0, mm2
movd ecx, mm0
not ecx
and [edi], ecx
add edi, 4
@@2:
pop ecx
add edi, ebx
dec edx
jnz @@yLoop
end;
procedure ColorKeyArea;
asm
pxor mm7, mm7 // 比较命令是按有符号数进行的,因此
punpcklbw mm2, mm7 // ColorLow按字节扩展为字
punpcklbw mm3, mm7 // ColorHigh按字节扩展为字
@@yLoop:
push ecx
@@xLoop:
movd mm0, [edi] // mm0 = 00 00 00 00 A R G B
punpcklbw mm0, mm7 // mm0 = 00 A 00 R 00 G 00 B
movq mm1, mm2
pcmpgtw mm1, mm0 // 比较 ColorLow 是否大于 mm0
packsswb mm1, mm1 // 比较结果按字压缩为字节
movd eax, mm1
test eax, eax
jnz @@Next // 如果 ColorLow 小于等于 mm0
pcmpgtw mm0, mm3 // 比较 mm0 是否大于 ColorHigh
packsswb mm0, mm0 // 比较结果按字压缩为字节
movd eax, mm0
test eax, eax
jnz @@Next // if (argb < ColorLow || argb > ColorLor)
and [edi], eax // *(int* )edi = 0
@@Next:
add edi, 4
loop @@xLoop
pop ecx
add edi, ebx
dec edx
jnz @@yLoop
end;
// 设置色键(透明范围)。colorLow 低色键值; colorHigh 高色键值
// 当像素A、R、G、B值同时大于等于colorLow和小于等于colorHigh时为透明色
procedure ImageSetColorKey(var Data: TImageData; ColorLow, ColorHigh: LongWord);
asm
push edi
push ebx
movd mm2, edx
movd mm3, ecx
xor edx, ecx
push edx
call _SetDataRegs
pop eax
test eax, eax
jz @@1
call ColorKeyArea
jmp @@Exit
@@1:
call ColorKeyEQ
@@Exit:
emms
pop ebx
pop edi
end;
// 按坐标颜色设置色键。x,y 图像坐标点, Precision 色键容差
procedure ImageSetColorKeyPoint(var Data: TImageData; x, y: Integer; Precision: LongWord);
asm
push edi
push ebx
test edx, edx
js @@Exit
cmp edx, [eax].TImageData.Width
jae @@Exit
test ecx, ecx
js @@Exit
cmp ecx, [eax].TImageData.Height
jae @@Exit
imul ecx, [eax].TImageData.Stride
shl edx, 2 // edx = data->Scan0 +
add edx, ecx // y * data->Stride + x * 4
add edx, [eax].TImageData.Scan0
pxor mm7, mm7
movd mm0, precision // mm0 = precision (4 word)
punpcklwd mm0, mm0
punpcklwd mm0, mm0
movd mm2, [edx] // mm2 = mm3 = argb (byte --> word)
punpcklbw mm2, mm7
movq mm3, mm2
paddsw mm3, mm0 // mm3 += mm0 (colorHigh)
psubsw mm2, mm0 // mm2 -= mm0 (colorLow)
packuswb mm3, mm3
packuswb mm2, mm2
call _SetDataRegs
movq mm0, mm2
pxor mm0, mm3
movd eax, mm0
test eax, eax
jz @@2
call ColorKeyArea
jmp @@end
@@2:
call ColorKeyEQ
@@end:
emms
@@Exit:
pop ebx
pop edi
end;
上面的代码中,提供了2种设置图像关键颜色过程:
ImageSetColorKey过程,设置图像的关键颜色范围,ColorLow和ColorHigh分别为图像关键颜色的低色键值和高色键值,只要是处于高低色键范围内的颜色都会成为透明色,如果ColorLow与ColorHigh相等,只有一种颜色成为透明色。
ImageSetColorKeyPoint过程,是通过图像的位置和颜色容差来设置关键颜色的。因为很多图像的背景色都不是单纯的某种颜色,靠ImageSetColorKey过程也不好确定关键颜色的高低色键,这时可以给定一个图像坐标,以这个坐标位置的像素颜色为基准,凡是在基准颜色上下容差范围内的颜色都会成为透明色。
下面是个简单的调用例子:
procedure TForm1.Button3Click(Sender: TObject);
var
bmp: TGpBitmap;
g: TGpGraphics;
data: TImageData;
begin
bmp := TGpBitmap.Create('..\media\56-3.jpg');
g := TGpGraphics.Create(Canvas.Handle);
g.DrawImage(bmp, 0, 0);
data := LockGpBitmap(bmp);
ImageSetColorKeyPoint(data, 175, 5, 39);
UnlockGpBitmap(bmp, data);
g.DrawImage(bmp, data.Width, 0);
g.Free;
bmp.Free;
end;
下面是运行效果截图:
《Delphi图像处理》系列使用GDI+单元下载地址和说明见文章《GDI+ for VCL基础 -- GDI+ 与 VCL》。
因水平有限,错误在所难免,欢迎指正和指导。邮箱地址:maozefa@hotmail.com
这里可访问《Delphi图像处理 -- 文章索引》。