阅读提示:
《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》。
因水平有限,错误在所难免,欢迎指正和指导。邮箱地址:[email protected]
这里可访问《Delphi图像处理 -- 文章索引》。