Delphi图像处理 -- 颜色矩阵变换

转载自阿发伯:http://blog.csdn.net/maozefa/article/details/8316430

阅读提示:

    《Delphi图像处理》系列以效率为侧重点,一般代码为PASCAL,核心代码采用BASM。

    《C++图像处理》系列以代码清晰,可读性为主,全部使用C++代码。

    尽可能保持二者内容一致,可相互对照。

    本文代码必须包括文章《Delphi图像处理 -- 数据类型及公用过程》中的ImageData.pas单元。

 

    本文在《GDI+ ColorMatrix的完全揭秘》的ColorMatrix原理揭秘的基础上,用Delphi代码来完整实现GDI+的ColorMatrix功能。

    GDI+中设置ColorMatrix时有2个枚举选项,在实际运用中极少使用,所以代码中以GDI+设置ColorMatrix的缺省方式实现。

    先给一个简易的浮点版本,因为该过程没有考虑子图处理,所以称之为简易版本,主要方便阅读者理解ColorMatrix实现原理:

[delphi] view plain copy
 
 print?
  1. procedure SetColorMatrixF(Data: TImageData; Matrix: TColorMatrix);  
  2. var  
  3.   I, J, Count: Integer;  
  4.   P: PRGBQuad;  
  5.   MainValue: Boolean;  
  6.   v: Integer;  
  7.   
  8.   procedure SetPixel;  
  9.   var  
  10.     Pixel: array[0..3] of Byte;  
  11.     I, J: Integer;  
  12.     ps: PByteArray;  
  13.   begin  
  14.     ps := Pointer(P);  
  15.     // 注意:为使矩阵与ARGB排列顺序一致,以下运算中调整了行列的顺序   
  16.     for I := to do  
  17.     begin  
  18.       if I < then  
  19.         J := 2 - I  
  20.       else  
  21.         J := I;  
  22.       // 如果只存在主对角线数据,只处理颜色缩放   
  23.       if MainValue then  
  24.         Pixel[J] := Round(Matrix[I, I] * ps[J])  
  25.       // 否则,处理所有颜色变换   
  26.       else  
  27.         Pixel[J] := Max(0, Min(255, Round(Matrix[0, I] * ps[2] +  
  28.                                           Matrix[1, I] * ps[1] +  
  29.                                           Matrix[2, I] * ps[0] +  
  30.                                           Matrix[3, I] * ps[3] +  
  31.                                           Matrix[4, I] * 255)));  
  32.     end;  
  33.     for I := to do  
  34.       ps[I] := Pixel[I];  
  35.   end;  
  36.   
  37. begin  
  38.   // 处理矩阵中大与255的值(取模),并判断主对角线外是否存在数据   
  39.   MainValue := True;  
  40.   for I := to do  
  41.     for J := to do  
  42.     begin  
  43.       v := Round(Matrix[I, J]) div 256;  
  44.       if v > then  
  45.         Matrix[I, J] := Matrix[I, J] - 256.0 * v;  
  46.       if (I <> J) and (Matrix[I, J] <> 0) then  
  47.         MainValue := False;  
  48.     end;  
  49.   Count := Data.Width * Data.Height;  
  50.   P := Data.Scan0;  
  51.   for I := to Count do  
  52.   begin  
  53.     SetPixel;  
  54.     Inc(P);  
  55.   end;  
  56. end;  

    因代码已经有了注释,而实现原理、公式已经在《GDI+ ColorMatrix的完全揭秘》中进行了详尽的介绍,所以本文不再累述。 

    该过程代码的特点是简单易读,缺点是效率较低,在我的P4 2.8G计算机上,处理一张千万像素的照片,耗时为1000ms左右(不包括GDI+图像格式转换耗时。千万像素的24位格式图像转换为32位格式,耗时就达650ms)。

    下面是一个MMX BASM代码的整数ColorMatrix实现过程: 

[delphi] view plain copy
 
 print?
  1. 过程定义:  
  2.   
  3.   // 设置图像颜色矩阵。参数:  
  4.   //   Dest输出图,Source原图,Data自身操作图像  
  5.   //   Matrix颜色矩阵  
  6.   procedure ImageSetColorMatrix(var Data: TImageData; Matrix: TColorMatrix); overload;  
  7.     {$IF RTLVersion >= 17.00}inline;{$IFEND}  
  8.   procedure ImageSetColorMatrix(var Dest: TImageData;  
  9.     const Source: TImageData; Matrix: TColorMatrix); overload;  
  10.   
  11. 实现代码:  
  12.   
  13. type  
  14.   PARGBQuadW = ^TARGBQuadW;  
  15.   TARGBQuadW = packed record  
  16.     wBlue: Word;  
  17.     wGreen: Word;  
  18.     wRed: Word;  
  19.     wAlpha: Word;  
  20.   end;  
  21.   
  22. procedure ImageSetColorMatrix(var Dest: TImageData;  
  23.   const Source: TImageData; Matrix: TColorMatrix);  
  24. asm  
  25.     push        esi  
  26.     push        edi  
  27.     push        ebx  
  28.     mov         ebx, eax  
  29.     mov         edi, ecx        // edi = matrix  
  30.     mov         esi, 4          // for (i = 4; i >= 0; i --)  
  31.     fldz                        // {  
  32. @@iLoop:  
  33.     mov         ecx, 4          //   for (j = 4; j >= 0; j --)  
  34. @@jLoop:                        //   {  
  35.     cmp         ecx, esi  
  36.     je          @@1  
  37.     mov         eax, esi  
  38.     imul        eax, 5  
  39.     add         eax, ecx  
  40.     fcom        dword ptr [edi+eax*4]  
  41.     fstsw       ax  
  42.     sahf  
  43.     je          @@1  
  44.     fstp        st(0)           //     if (i != j && matrix[i, j] != 0)  
  45.     jmp         @@TransformAll  //       goto TransformAll  
  46. @@1:  
  47.     dec         ecx  
  48.     jns         @@jLoop         //   }  
  49.     dec         esi  
  50.     jns         @@iLoop         // }  
  51.     fstp        st(0)  
  52.     fwait  
  53.   
  54.     // 处理颜色缩放(主对角线的数据)  
  55.   
  56.     sub         esp, 8+2  
  57.     mov         dword ptr [esp], 256  
  58.     fild        dword ptr [esp]  
  59.     fld         st(0)  
  60.     fmul        dword ptr [edi+(2*5+2)*4]  
  61.     fistp       dword ptr [esp]     // matrixI[0, 0] = matrix[2, 2] * 256  
  62.     fld         st(0)  
  63.     fmul        dword ptr [edi+(1*5+1)*4]  
  64.     fistp       dword ptr [esp+2]   // matrixI[0, 1] = matrix[1, 1] * 256  
  65.     fld         st(0)                 
  66.     fmul        dword ptr [edi+(0*5+0)*4]  
  67.     fistp       dword ptr [esp+4]   // matrixI[0, 2] = matrix[0, 0] * 256  
  68.     fmul        dword ptr [edi+(3*5+3)*4]  
  69.     fistp       dword ptr [esp+6]   // matrixI[0, 3] = matrix[3, 3] * 256  
  70.     mov         eax, ebx  
  71.     call        _SetCopyRegs  
  72.     pxor        mm7, mm7  
  73.     movq        mm1, [esp]      // mm1 = m44  m11  m22  m33  
  74. @@yLoop:  
  75.     push        ecx  
  76. @@xLoop:  
  77.     movd        mm0, [esi]  
  78.     punpcklbw   mm0, mm7        // mm0 = 00  A 00  R 00  G 00  B  
  79.     pmullw      mm0, mm1        // mm0 = A*m44 R*m11 G*m22 B*m33  
  80.     psrlw       mm0, 8          // mm0 = A*m44/256 R*m11/256 G*m22/256 B*m33/256  
  81.     packuswb    mm0, mm0        // mm0 = 00 00 00 00 An Rn Gn Bn  
  82.     movd        [edi], mm0  
  83.     add         esi, 4  
  84.     add         edi, 4  
  85.     loop        @@xLoop  
  86.     add         esi, eax  
  87.     add         edi, ebx  
  88.     pop         ecx  
  89.     dec         edx  
  90.     jnz         @@yLoop  
  91.     add         esp, 8+2  
  92.     jmp         @@end  
  93.   
  94.     // 处理全部颜色变换  
  95.   
  96. @@TransformAll:  
  97.     sub         esp, 5*8+2      // 浮点颜色矩阵行列交换转换为128倍整数  
  98.     mov         dword ptr [esp], 128  
  99.     fild        dword ptr [esp]  
  100.     mov         esi, esp        // esi = matrixI  
  101.     mov         eax, edi  
  102.     mov         ecx, 4          // for (i = 0; i < 4; i ++)  
  103. @@cvtLoop:                      // {  
  104.     fld         st(0)  
  105.     fmul        dword ptr [edi]  
  106.     fistp       dword ptr [esi]     // matrixI[i, 0] = matrix[0, i] * 128  
  107.     fld         st(0)  
  108.     fmul        dword ptr [edi+1*5*4]  
  109.     fistp       dword ptr [esi+2]   // matrixI[i, 1] = matrix[1, i] * 128  
  110.     fld         st(0)  
  111.     fmul        dword ptr [edi+2*5*4]  
  112.     fistp       dword ptr [esi+4]   // matrixI[i, 2] = matrix[2, i] * 128  
  113.     fld         st(0)  
  114.     fmul        dword ptr [edi+3*5*4]  
  115.     fistp       dword ptr [esi+6]   // matrixI[i, 3] = matrix[3, i] * 128  
  116.     add         esi, 8  
  117.     add         edi, 4  
  118.     loop        @@cvtLoop       // }  
  119.     fstp        st(0)  
  120.     add         eax, 4*5*4      // 浮点数平移量转换为255倍整数  
  121.     mov         dword ptr [esi], 255  
  122.     fild        dword ptr [esi]  
  123.     mov         ecx, 4          // for (j = 0; j < 4; j ++)  
  124. @@tLoop:  
  125.     fld         st(0)  
  126.     fmul        dword ptr [eax]  
  127.     fistp       dword ptr [esi]     // matrixI[4, j] = matrix[4, j] * 255  
  128.     add         esi, 2  
  129.     add         eax, 4  
  130.     loop        @@tLoop  
  131.     fstp        st(0)  
  132.     mov         esi, esp        // 红蓝(0、2列)交换  
  133.     mov         ecx, 5          // for (i = 0; i < 5; i ++)  
  134. @@swapLoop:                     //   matrixI[i, 0] <--> matrixI[i, 2]  
  135.     mov         ax, [esi].TARGBQuadW.wBlue  
  136.     xchg        ax, [esi].TARGBQuadW.wRed  
  137.     mov         [esi].TARGBQuadW.wBlue, ax  
  138.     add         esi, 8  
  139.     loop        @@swapLoop  
  140.     mov         eax, ebx  
  141.     call        _SetCopyRegs  
  142.     pxor        mm7, mm7  
  143.     pcmpeqb     mm4, mm4        // mm4 = FF FF FF FF FF FF FF FF  
  144.     psrlw       mm4, 15         // mm4 = 00 01 00 01 00 01 00 01  
  145. @@yLoopA:  
  146.     push        ecx  
  147. @@xLoopA:  
  148.     movd        mm0, [esi]  
  149.     punpcklbw   mm0, mm7        // mm0 = 00  A 00  R 00  G 00  B  
  150.     movq        mm1, mm0  
  151.     movq        mm2, mm0  
  152.     movq        mm3, mm0  
  153.     // esp+4: ecx push stack  
  154.     pmaddwd     mm0, [esp+16+4] // mm0 = A*m43+R*m13  G*m23+B*m33  蓝色行  
  155.     pmaddwd     mm1, [esp+8+4]  // mm1 = A*m42+R*m12  G*m22+B*m32  绿色行  
  156.     pmaddwd     mm2, [esp+4]    // mm2 = A*m41+R*m11  G*m21+B*m31  红色行  
  157.     pmaddwd     mm3, [esp+24+4] // mm3 = A*m44+R*m14  G*m24+B*m34  Alpha行  
  158.     psrad       mm0, 7          // mm0 = A*m43+R*m13/128  G*m23+B*m33/128  
  159.     psrad       mm1, 7          // mm1 = A*m42+R*m12/128  G*m22+B*m32/128  
  160.     psrad       mm2, 7          // mm2 = A*m41+R*m11/128  G*m21+B*m31/128  
  161.     psrad       mm3, 7          // mm3 = A*m44+R*m14/128  G*m24+B*m34/128  
  162.     packssdw    mm0, mm1        // mm0 = Ag+Rg  Gg+Bg  Ab+Rb  Gb+Bb  
  163.     packssdw    mm2, mm3        // mm2 = Aa+Ra  Ga+Ba  Ar+Rr  Gr+Br  
  164.     pmaddwd     mm0, mm4        // mm0 = Ag+Rg+Gg+Bg=Gn  Ab+Rb+Gb+Bb=Bn  
  165.     pmaddwd     mm2, mm4        // mm2 = Aa+Ra+Ga+Ba=An  Ar+Rr+Gr+Br=Rn  
  166.     packssdw    mm0, mm2        // mm0 = 00 An 00 Rn 00 Gn 00 Bn     
  167.     paddw       mm0, [esp+32+4] // mm0 = An+At Rn+Rt Gn+Gt Bn+Bt   平移行  
  168.     packuswb    mm0, mm0        // mm0 = 00 00 00 00 An Rn Gn Bn  
  169.     movd        [edi], mm0  
  170.     add         esi, 4  
  171.     add         edi, 4  
  172.     loop        @@xLoopA  
  173.     add         esi, eax  
  174.     add         edi, ebx  
  175.     pop         ecx  
  176.     dec         edx  
  177.     jnz         @@yLoopA  
  178.     add         esp, 5*8+2  
  179. @@end:  
  180.     emms  
  181. @@Exit:  
  182.     pop         ebx  
  183.     pop         edi  
  184.     pop         esi  
  185. end;  
  186.   
  187. procedure ImageSetColorMatrix(var Data: TImageData; Matrix: TColorMatrix);  
  188. begin  
  189.   ImageSetColorMatrix(Data, Data, Matrix);  
  190. end;  

    该过程中作了更详细的注释,其特点是处理速度较快。在我的机器上,不包括图像格式转换耗时,处理千万像素图片主对角线数据耗时不到50ms,而处理全部变换耗时350-400ms。  

    下面是一个测试程序代码。该测试代码界面与《GDI+ for VCL基础 -- 颜色调整矩阵ColorMatrix详解》是一样的。有兴趣的朋友可以同里面的测试代码作一下比较。  

[delphi] view plain copy
 
 print?
  1. unit main2;  
  2.   
  3. interface  
  4.   
  5. uses  
  6.   Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,  
  7.   Dialogs, StdCtrls, Buttons, Grids, ExtCtrls, Gdiplus, ImageData;  
  8.   
  9. type  
  10.   TForm1 = class(TForm)  
  11.     Label1: TLabel;  
  12.     PaintBox1: TPaintBox;  
  13.     SpeedButton1: TSpeedButton;  
  14.     SpeedButton2: TSpeedButton;  
  15.     SpeedButton3: TSpeedButton;  
  16.     SpeedButton4: TSpeedButton;  
  17.     StringGrid1: TStringGrid;  
  18.     BitBtn1: TBitBtn;  
  19.     BitBtn3: TBitBtn;  
  20.     BitBtn2: TBitBtn;  
  21.     procedure FormCreate(Sender: TObject);  
  22.     procedure FormDestroy(Sender: TObject);  
  23.     procedure StringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer;  
  24.       Rect: TRect; State: TGridDrawState);  
  25.     procedure StringGrid1GetEditText(Sender: TObject; ACol, ARow: Integer;  
  26.       var Value: string);  
  27.     procedure PaintBox1Paint(Sender: TObject);  
  28.     procedure BitBtn1Click(Sender: TObject);  
  29.     procedure BitBtn2Click(Sender: TObject);  
  30.     procedure BitBtn3Click(Sender: TObject);  
  31.     procedure SpeedButton2Click(Sender: TObject);  
  32.     procedure SpeedButton3Click(Sender: TObject);  
  33.     procedure SpeedButton1Click(Sender: TObject);  
  34.     procedure SpeedButton4Click(Sender: TObject);  
  35.     procedure StringGrid1SetEditText(Sender: TObject; ACol, ARow: Integer;  
  36.       const Value: string);  
  37.   private  
  38.     { Private declarations }  
  39.     Source: TGpBitmap;  
  40.     Dest: TGpBitmap;  
  41.     SrcData: TImageData;  
  42.     DstData: TImageData;  
  43.     Matrix: TColorMatrix;  
  44.   
  45.     function CheckFloatStr(Str: string): Double;  
  46.     procedure InitColorMatrix;  
  47.   public  
  48.     { Public declarations }  
  49.   end;  
  50.   
  51. var  
  52.   Form1: TForm1;  
  53.   
  54. implementation  
  55.   
  56. {$R *.dfm}  
  57.   
  58. procedure TForm1.BitBtn1Click(Sender: TObject);  
  59. begin  
  60.   ImageSetColorMatrix(DstData, SrcData, Matrix);  
  61.   PaintBox1.Invalidate;  
  62.   with StringGrid1 do  
  63.   begin  
  64.     Cells[Col, Row] := FloatToStr(Matrix[Row, Col]);  
  65.     Invalidate;  
  66.     SetFocus;  
  67.   end;  
  68. end;  
  69.   
  70. procedure TForm1.BitBtn2Click(Sender: TObject);  
  71. begin  
  72.   InitColorMatrix;  
  73.   BitBtn1.Click;  
  74. end;  
  75.   
  76. procedure TForm1.BitBtn3Click(Sender: TObject);  
  77. begin  
  78.   Close;  
  79. end;  
  80.   
  81. function TForm1.CheckFloatStr(Str: string): Double;  
  82. var  
  83.   i, len: Integer;  
  84.   dec, neg: Boolean;  
  85.   s: string;  
  86. begin  
  87.   Result := 0;  
  88.   len := Length(Str);  
  89.   if len = then Exit;  
  90.   dec := False;  
  91.   neg := False;  
  92.   i := 1;  
  93.   s := '';  
  94.   if (Str[i] = '-') or (Str[i] = '+') then  
  95.   begin  
  96.     if Str[i] = '-' then neg := True;  
  97.     Inc(i);  
  98.   end;  
  99.   while (i <= len) do  
  100.   begin  
  101.     if Str[i] = '.' then  
  102.     begin  
  103.       if dec then Break;  
  104.       dec := True;  
  105.     end  
  106.     else if (Str[i] < '0') or (Str[i] > '9') then Break;  
  107.     s := s + Str[i];  
  108.     Inc(i);  
  109.   end;  
  110.   if Length(s) > then  
  111.   begin  
  112.     if neg then s := '-' + s;  
  113.     Result := StrToFloat(s);  
  114.   end;  
  115. end;  
  116.   
  117. procedure TForm1.FormCreate(Sender: TObject);  
  118. var  
  119.   Bmp: TGpBitmap;  
  120.   Data: TBitmapData;  
  121.   R: TGpRect;  
  122. begin  
  123.   // 从文件装入图像到Bmp  
  124.   Bmp := TGpBitmap.Create('..\..\media\100_0349.jpg');  
  125.   R := GpRect(0, 0, Bmp.Width, Bmp.Height);  
  126.   // 分别建立新的源和目标图像数据到SrcData和DstData  
  127.   SrcData := NewImageData(R.Width, R.Height);  
  128.   DstData := NewImageData(R.Width, R.Height);  
  129.   // 将Bmp图像数据分别锁定拷贝到SrcData和DstData  
  130.   Data := TBitmapData(SrcData);  
  131.   Data := Bmp.LockBits(R, [imRead, imWrite, imUserInputBuf], pf32bppARGB);  
  132.   Bmp.UnlockBits(Data);  
  133.   Data.Scan0 := DstData.Scan0;  
  134.   Data := Bmp.LockBits(R, [imRead, imWrite, imUserInputBuf], pf32bppARGB);  
  135.   Bmp.UnlockBits(Data);  
  136.   Bmp.Free;  
  137.     // 分别用图像数据SrcData和DstData建立位图Source和Dest  
  138.     // 注:图像数据结构用于数据处理,位图用于显示,这样即可绑定数据结构和位图,  
  139.     //     又能避免每次处理图像数据时的锁定和解锁操作  
  140.   Source := TGpBitmap.Create(SrcData.Width, SrcData.Height, SrcData.Stride,  
  141.         pf32bppARGB, SrcData.Scan0);  
  142.   Dest := TGpBitmap.Create(DstData.Width, DstData.Height, DstData.Stride,  
  143.         pf32bppARGB, DstData.Scan0);  
  144.   
  145.   InitColorMatrix;  
  146. end;  
  147.   
  148. procedure TForm1.FormDestroy(Sender: TObject);  
  149. begin  
  150.   Dest.Free;  
  151.   Source.Free;  
  152.   FreeImageData(DstData);  
  153.   FreeImageData(SrcData);  
  154. end;  
  155.   
  156. procedure TForm1.InitColorMatrix;  
  157. var  
  158.   i, j: Integer;  
  159. begin  
  160.   for i := to do  
  161.   begin  
  162.     for j := to do  
  163.       if i = j then Matrix[i, j] := else Matrix[i, j] := 0;  
  164.   end;  
  165. end;  
  166.   
  167. procedure TForm1.PaintBox1Paint(Sender: TObject);  
  168. var  
  169.   g: TGpGraphics;  
  170. begin  
  171.   g := TGpGraphics.Create(PaintBox1.Canvas.Handle);  
  172.   try  
  173.     g.DrawImage(Source, 10, 10);  
  174.     g.DrawImage(Dest, SrcData.Width + 20, 10);  
  175.   finally  
  176.     g.Free;  
  177.   end;  
  178. end;  
  179.   
  180. procedure TForm1.SpeedButton1Click(Sender: TObject);  
  181. var  
  182.   i: Integer;  
  183. begin  
  184.     InitColorMatrix;  
  185.     for i := to do  
  186.   begin  
  187.     Matrix[0, i] := 0.30;  
  188.     Matrix[1, i] := 0.59;  
  189.     Matrix[2, i] := 0.11;  
  190.   end;  
  191.     BitBtn1.Click;  
  192. end;  
  193.   
  194. procedure TForm1.SpeedButton2Click(Sender: TObject);  
  195. var  
  196.   i: Integer;  
  197. begin  
  198.     InitColorMatrix;  
  199.     for i := to do  
  200.         Matrix[4, i] := 0.10;  
  201.     BitBtn1.Click;  
  202. end;  
  203.   
  204. procedure TForm1.SpeedButton3Click(Sender: TObject);  
  205. begin  
  206.   InitColorMatrix;  
  207.   Matrix[0, 0] := -1;  
  208.   Matrix[1, 1] := -1;  
  209.   Matrix[2, 2] := -1;  
  210.   BitBtn1.Click;  
  211. end;  
  212.   
  213. procedure TForm1.SpeedButton4Click(Sender: TObject);  
  214. begin  
  215.   InitColorMatrix;  
  216.   Matrix[3, 3] := 0.5;  
  217.   BitBtn1.Click;  
  218. end;  
  219.   
  220. procedure TForm1.StringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer;  
  221.   Rect: TRect; State: TGridDrawState);  
  222. var  
  223.   Text: string;  
  224. begin  
  225.   Text := Format('%.2f', [Matrix[ARow, ACol]]);  
  226.   StringGrid1.Canvas.FillRect(Rect);  
  227.   StringGrid1.Canvas.Pen.Color := clBtnShadow;  
  228.   StringGrid1.Canvas.Rectangle(Rect);  
  229.   InflateRect(Rect, -2, -2);  
  230.   DrawText(StringGrid1.Canvas.Handle, PChar(text), Length(text), &Rect, DT_RIGHT);  
  231. end;  
  232.   
  233. procedure TForm1.StringGrid1GetEditText(Sender: TObject; ACol, ARow: Integer;  
  234.   var Value: string);  
  235. begin  
  236.   Value := Format('%.2f', [Matrix[ARow, ACol]]);  
  237. end;  
  238.   
  239. procedure TForm1.StringGrid1SetEditText(Sender: TObject; ACol, ARow: Integer;  
  240.   const Value: string);  
  241. begin  
  242.   Matrix[ARow, ACol] := CheckFloatStr(Value);  
  243. end;  
  244.   
  245. end.  

     下面是运行效果图:

效果图
    该效果图是个处理-1矩阵实现图像取反的画面,仔细观察右边的取反图,不难发现其中有不少黑色的点,尤其是画面右上角“圣亚”2个字下面,更是出现一些难看的色斑,这些问题在《GDI+ ColorMatrix的完全揭秘》中已经作了详细的解说。其实这个问题完全可以进行改进,但本文的目的是揭秘和完整实现,改进后,一些非主要效果肯定会与“正版”不一样。^_^
 
后记(2012/12/20):
    昨天整理BLOG,更新《C++图像处理 -- 颜色矩阵变换》文章时,却发现Win7环境下的GDI+ 颜色矩阵变换效果同XP环境下的效果有很大不同(比如前面例子运行的反色效果图在Win7中为黑色图),而本文前面的颜色矩阵变换代码是仿XP效果写的,因此,重新写了一段仿Win7 GDI+颜色矩阵变换效果的代码贴在下面:
[delphi] view plain copy
 
 print?
  1. procedure ImageSetColorMatrix(var Dest: TImageData;  
  2.   const Source: TImageData; Matrix: TColorMatrix); overload;  
  3. asm  
  4.     push      ebp  
  5.     push      esi  
  6.     push      edi  
  7.     push      ebx  
  8.     // ebp为16字节对齐的128位栈内存地址  
  9.     sub       esp, 32  
  10.     mov       ebp, esp  
  11.     add       ebp, 16  
  12.     and       ebp, -16  
  13.   
  14.     // 检查颜色矩阵除主对角线和虚拟列外的数据项,如不等于零,执行全部颜色变换  
  15.     mov       edi, ecx  
  16.     mov       esi, 4          // for (i = 4; i >= 0; i --)  
  17. @@iLoop:                      // {  
  18.     mov       ecx, 3          //   for (j = 3; j >= 0; j --)  
  19. @@jLoop:                      //   {  
  20.     cmp       ecx, esi        //     if (i == j) continue  
  21.     je        @@1  
  22.     lea       ebx, [esi+esi*4]  
  23.     add       ebx, ecx        //     index = i * 5 + j  
  24.     cmp       dword ptr[edi+ebx*4], 0  
  25.     jne       @@Transform     //     if (Matrix[Index]) goto @@Transform  
  26. @@1:  
  27.     dec       ecx  
  28.     jns       @@jLoop         //   }  
  29.     dec       esi  
  30.     jns       @@iLoop         // }  
  31.   
  32.     // 处理颜色缩放  
  33.     mov       ebx, [edi+(2*5+2)*4]  
  34.     mov       ecx, [edi+(1*5+1)*4]  
  35.     mov       [ebp], ebx  
  36.     mov       [ebp+4], ecx  
  37.     mov       ebx, [edi+(0*5+0)*4]  
  38.     mov       ecx, [edi+(3*5+3)*4]  
  39.     mov       [ebp+8], ebx  
  40.     mov       [ebp+12], ecx  
  41.     movaps    xmm1, [ebp]     // xmm1 = m44 m11 m22 m33  
  42.     pxor      xmm7, xmm7  
  43.     call      _SetCopyRegs  
  44. @@yLoop_Scale:  
  45.     push      ecx  
  46. @@xLoop_Scale:  
  47.     movd      xmm0, [esi]  
  48.     punpcklbw xmm0, xmm7  
  49.     punpcklwd xmm0, xmm7  
  50.     cvtdq2ps  xmm0, xmm0  
  51.     mulps     xmm0, xmm1      // xmm0 = [A R G B] * [m44 m11 m22 m33]  
  52.     cvtps2dq  xmm0, xmm0  
  53.     packssdw  xmm0, xmm7  
  54.     packuswb  xmm0, xmm7  
  55.     movd      [edi], xmm0  
  56.     add       esi, 4  
  57.     add       edi, 4  
  58.     loop      @@xLoop_Scale  
  59.     pop       ecx  
  60.     add       esi, eax  
  61.     add       edi, ebx  
  62.     dec       edx  
  63.     jnz       @@yLoop_Scale  
  64.     jmp       @@Exit  
  65.   
  66.     // 处理全部的颜色变换  
  67. @@Transform:  
  68.     // 颜色矩阵按行分别装入sse寄存器,不包括虚拟位列  
  69.     movups    xmm1, [edi+0*5*4]  
  70.     movups    xmm2, [edi+1*5*4]  
  71.     movups    xmm3, [edi+2*5*4]  
  72.     movups    xmm4, [edi+3*5*4]  
  73.     movups    xmm5, [edi+4*5*4]  
  74.     // 平移行乘上255  
  75.     mov       ebx, 255  
  76.     cvtsi2ss  xmm6, ebx  
  77.     pshufd    xmm6, xmm6, 0  
  78.     mulps     xmm5, xmm6  
  79.     // 交换每行的红与蓝位置  
  80.     pshufd    xmm1, xmm1, 11000110b  
  81.     pshufd    xmm2, xmm2, 11000110b  
  82.     pshufd    xmm3, xmm3, 11000110b  
  83.     pshufd    xmm4, xmm4, 11000110b  
  84.     pshufd    xmm5, xmm5, 11000110b  
  85.     // 平移行保存在栈中  
  86.     movaps    [ebp], xmm5  
  87.     pxor      xmm7, xmm7  
  88.     call      _SetCopyRegs  
  89. @@yLoop:  
  90.     push      ecx  
  91. @@xLoop:  
  92.     movd      xmm0, [esi]  
  93.     punpcklbw xmm0, xmm7  
  94.     punpcklwd xmm0, xmm7  
  95.     cvtdq2ps  xmm0, xmm0  
  96.     pshufd    xmm5, xmm0, 0  
  97.     pshufd    xmm6, xmm0, 01010101b  
  98.     mulps     xmm5, xmm3      // vb = blue * m3  
  99.     mulps     xmm6, xmm2      // vg = green * m2  
  100.     addps     xmm5, [ebp]     // vb += m5  
  101.     addps     xmm5, xmm6      // vb += vg  
  102.     pshufd    xmm6, xmm0, 10101010b  
  103.     pshufd    xmm0, xmm0, 11111111b  
  104.     mulps     xmm6, xmm1      // vr = red * m1  
  105.     mulps     xmm0, xmm4      // va = alpha * m4  
  106.     addps     xmm0, xmm6      // v = va + vr  
  107.     addps     xmm0, xmm5      // v += vb  
  108.     cvtps2dq  xmm0, xmm0  
  109.     packssdw  xmm0, xmm7  
  110.     packuswb  xmm0, xmm7  
  111.     movd      [edi], xmm0  
  112.     add       esi, 4  
  113.     add       edi, 4  
  114.     loop      @@xLoop  
  115.     pop       ecx  
  116.     add       esi, eax  
  117.     add       edi, ebx  
  118.     dec       edx  
  119.     jnz       @@yLoop  
  120. @@Exit:  
  121.     add       esp, 32  
  122.     pop       ebx  
  123.     pop       edi  
  124.     pop       esi  
  125.     pop       ebp  
  126. end;  
  127.   
  128. procedure ImageSetColorMatrix(var Data: TImageData; Matrix: TColorMatrix); overload;  
  129. begin  
  130.   ImageSetColorMatrix(Data, Data, Matrix);  
  131. end;  
  132. //---------------------------------------------------------------------------  
    《Delphi图像处理》系列使用GDI+单元下载地址和说明见文章《GDI+ for VCL基础 -- GDI+ 与 VCL》。

    因水平有限,错误在所难免,欢迎指正和指导。邮箱地址:maozefa@hotmail.com

    这里可访问《Delphi图像处理 -- 文章索引》。

posted @ 2017-03-09 11:07  h2z  阅读(1233)  评论(0编辑  收藏  举报