Reading Tips:
《Delphi Image ProcessingThe series focuses on efficiency. The general code is Pascal, and the core code is BaSm.
《C ++ Image ProcessingThe series focuses on code clarity and readability, and all uses C ++ code.
Make sure that the two items are consistent and can be compared with each other.
The code in this article must include the imagedata. Pas unit in "Delphi Image Processing-data type and public process.
During image processing, image synthesis is the most frequently used, such as display, image copy, image stitching, and layer combination and superposition.
Image synthesis is actually a mixture of pixel colors. In Photoshop, color mixing is a very complicated thing. different blending modes will produce different synthesis effects, if you thoroughly study all of this, you may have to write a book. Therefore, this article only talks about the most basic image synthesis, that is, the normal mixing mode in Photoshop.
As long as you have been involved in image processing, we all know that there is an image pixel mixing formula:
1) dstrgb = srcrgb * Alpha + dstrgb * (1-alpha)
Dstrgb indicates the pixel value of the target image, srcrgb indicates the pixel value of the source image, and Alpha indicates the blending ratio of the pixel value of the source image (opacity, range: 0-1 ).
In fact, this pixel mixing formula has great limitations and is only suitable for images without Alpha information.
To process a mix of images (layers) with Alpha channels, the complete formula should be:
2-1) srcrgb = srcrgb * srcalpha * alpha/255 (source image pre-multiplication to pargb)
2-2) dstrgb = dstrgb * dstalpha/255 (pre-multiplication of target image pixels to pargb)
2-3) dstrgb = dstrgb + srcrgb-dstrgb * srcalpha * alpha/255 (the source image pixel value is mixed with the target image pixel value)
2-4) dstalpha = dstalpha + srcalpha * alpha-dstalpha * srcalpha * alpha/255 (the alpha channel value of the target image after mixing)
2-5) dstrgb = dstrgb * 255/dstalpha (after blending, the target image pixel is converted to argb)
Dstrgb indicates the pixel value of the target image, srcrgb indicates the pixel value of the source image, dstalpha indicates the alpha channel value of the target image, srcalpha indicates the alpha channel value of the source image, and dstargb indicates the pixel value of the alpha target image; alpha is the blending ratio of the source image pixel values (opacity, range: 0-1 ).
2-1 in formula 2 is substituted into the 2-3 formula to simplify the process:
3-1) dstrgb = dstrgb * dstalpha/255
3-2) dstrgb = dstrgb + (srcrgb-dstrgb) * srcalpha * alpha/255
3-3) dstalpha = dstalpha + srcalpha * alpha-dstalpha * srcalpha * alpha/255
3-4) dstrgb = dstrgb * 255/dstalpha
When dstalpha = srcalpha = 255, the 3-1, 3-3, and 3-4 types in Formula 3 are meaningless, and the 3-2 type also changes:
4) dstrgb = dstrgb + (srcrgb-dstrgb) * alpha
It is not hard to see that formula 4 is the deformation of Formula 1. Therefore, Formula 1 is only a special case when Formula 3 (or formula 2) does not contain Alpha information (or alpha = 255) in the target image and source image.
When alpha = 1 in formula 4, the target image pixel is equal to the source image pixel. Therefore, image copying is also a field of image synthesis.
The above detailed analysis shows that even the most basic normal image mixing mode is complex. In fact, the above is not a complete analysis, because it is a complete arrangement and combination of three elements, namely the target image Alpha information, the source image Alpha information, and the source image synthesis ratio, A maximum of 8 formulas can be derived.
The following describes the code implementation in all eight cases of the normal hybrid mode (two items overlap, but the actual situation is 7), and the above text description can also be improved and supplemented:
unit main;interfaceuses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls;type TForm1 = class(TForm) Button1: TButton; Button2: TButton; procedure Button1Click(Sender: TObject); procedure Button2Click(Sender: TObject); private { Private declarations } public { Public declarations } end;var Form1: TForm1;implementationuses Gdiplus, ImageData, Jpeg;{$R *.dfm}procedure _SetMixerMM;asm pxor mm7, mm7 mov eax, 1011h movd mm6, eax pshufw mm6, mm6, 0 mov eax, 8 movd mm5, eax pshufw mm5, mm5, 0end;// --> mm7 4 * word = 0// --> mm6 4 * word = 0x1101// --> mm5 4 * word = 4// --> eax source alpha// --> esi source pixel (ARGB)// --> edi dest pixel (ARGB)// <-- eax dest alpha !!!// <-- mm0 dest pixelprocedure _PARGBMixer(srcAlpha: Integer);asm push edx movd mm0, [esi] movd mm1, [edi] punpcklbw mm0, mm7 punpcklbw mm1, mm7 // dest.rgb = dest.rgb * dest.alpha / 255 movzx edx, [edi].TARGBQuad.Alpha pmullw mm1, qword ptr ArgbTab[edx*8] pmulhuw mm1, mm6 paddusw mm1, mm5 psrlw mm1, 4 // dest.rgb = (dest.rgb * 255 + (source.rgb - dest.rgb) * sourec.alpha) / 255 psubw mm0, mm1 pmullw mm0, qword ptr ArgbTab[eax*8] pmullw mm1, qword ptr ArgbTab[255*8] paddw mm0, mm1 pmulhuw mm0, mm6 paddusw mm0, mm5 psrlw mm0, 4 // dest.alpha += (source.alpha - (dest.alpha * source.alpha + 127) / 255) push eax add [esp], edx imul eax, edx add eax, 127 mul dword ptr DivTab[255*4] pop eax sub eax, edx // dest.rgb = dest.rgb * 255 / dest.alpha movq mm1, mm0 psllw mm0, 8 psubw mm0, mm1 pmulhuw mm0, qword ptr MMDivTab[eax*8] packuswb mm0, mm7 pop edxend;// --> mm7 4 * word = 0// --> eax source alpha// --> esi source pixel (ARGB)// --> edi dest pixel (ARGB)// <-- mm0 dest pixel (RGB)procedure _ARGBMixer(srcAlpha: Integer);asm movd mm0, [esi] movd mm1, [edi] punpcklbw mm0, mm7 punpcklbw mm1, mm7 psubw mm0, mm1 pmullw mm0, qword ptr ArgbTab[eax*8] psllw mm1, 8 paddw mm0, mm1 psrlw mm0, 8 packuswb mm0, mm0end;procedure _DoMixer(var Dest: TImageData; const Source: TImageData; Alpha: Integer);var dstOffset, srcOffset:Integer; alphaI: Integer; dst: PImageData;asm push esi push edi push ebx mov dst, eax mov alphaI, ecx shr ecx, 8 movzx ebx, [eax].TImageData.AlphaFlag shl ebx, 1 or ecx, ebx movzx ebx, [edx].TImageData.AlphaFlag shl ebx, 2 or ecx, ebx push ecx call _SetCopyRegs mov srcOffset, eax pop eax jmp dword ptr [@@jmpTable+eax*4]@@jmpTable: dd @@mixer0, @@mixer1, @@mixer2, @@mixer3, @@mixer4, @@mixer5, @@mixer6, @@mixer7// src = 0, dst = 0, alpha = 0@@mixer0: mov eax, alphaI movq mm2, qword ptr ArgbTab[eax*8] mov eax, srcOffset pxor mm7, mm7@@yLoop0: push ecx@@xLoop0: movd mm0, [esi] movd mm1, [edi] punpcklbw mm0, mm7 punpcklbw mm1, mm7 psubw mm0, mm1 pmullw mm0, mm2 psllw mm1, 8 paddw mm0, mm1 psrlw mm0, 8 packuswb mm0, mm0 movd [edi], mm0 mov [edi].TARGBQuad.Alpha, 255 add esi, 4 add edi, 4 loop @@xLoop0 add esi, eax add edi, ebx pop ecx dec edx jnz @@yLoop0 jmp @@End// src = 0, dst = 0, alpha = 1@@mixer1:// src = 0, dst = 1, alpha = 1@@mixer3: mov eax, srcOffset@@yLoop1: push ecx rep movsd pop ecx add esi, eax add edi, ebx dec edx jnz @@yLoop1 jmp @@End mov eax, dst mov [eax].TImageData.AlphaFlag, False jmp @@Exit// src = 0, dst = 1, alpha = 0@@mixer2: call _SetMixerMM mov eax, alphaI imul eax, 255 shr eax, 8@@yLoop2: push ecx@@xLoop2: push eax call _PARGBMixer movd [edi], mm0 mov [edi].TARGBQuad.Alpha, al pop eax add esi, 4 add edi, 4 loop @@xLoop2 add esi, srcOffset add edi, ebx pop ecx dec edx jnz @@yLoop2 jmp @@End// src = 1, dst = 0, alpha = 0@@mixer4: mov dstOffset, ebx mov ebx, alphaI pxor mm7, mm7@@yLoop4: push ecx@@xLoop4: movzx eax, [esi].TARGBQuad.Alpha imul eax, ebx shr eax, 8 jz @@Next4 call _ARGBMixer movd [edi], mm0 mov [edi].TARGBQuad.Alpha, 255@@Next4: add esi, 4 add edi, 4 loop @@xLoop4 add esi, srcOffset add edi, dstOffset pop ecx dec edx jnz @@yLoop4 jmp @@End// src = 1, dst = 0, alpha = 1@@mixer5: pxor mm7, mm7@@yLoop5: push ecx@@xLoop5: movzx eax, [esi].TARGBQuad.Alpha call _ARGBMixer movd [edi], mm0 mov [edi].TARGBQuad.Alpha, 255 add esi, 4 add edi, 4 loop @@xLoop5 add esi, srcOffset add edi, ebx pop ecx dec edx jnz @@yLoop5 jmp @@End// src = 1, dst = 1, alpha = 0@@mixer6: mov dstOffset, ebx mov ebx, alphaI call _SetMixerMM@@yLoop6: push ecx@@xLoop6: movzx eax, [esi].TARGBQuad.Alpha imul eax, ebx shr eax, 8 jz @@Next6 call _PARGBMixer movd [edi], mm0 mov [edi].TARGBQuad.Alpha, al@@Next6: add esi, 4 add edi, 4 loop @@xLoop6 add esi, srcOffset add edi, dstOffset pop ecx dec edx jnz @@yLoop6 jmp @@End// src = 1, dst = 1, alpha = 1@@mixer7: call _SetMixerMM@@yLoop7: push ecx@@xLoop7: movzx eax, [esi].TARGBQuad.Alpha call _PARGBMixer movd [edi], mm0 mov [edi].TARGBQuad.Alpha, al add esi, 4 add edi, 4 loop @@xLoop7 add esi, srcOffset add edi, ebx pop ecx dec edx jnz @@yLoop7@@End: emms@@Exit: pop ebx pop edi pop esiend;procedure ImageMixer(var Dest: TImageData; const Source: TImageData; Alpha: Single);var alphaI: Integer;begin alphaI := Round(Alpha * 256); if alphaI < 0 then Exit; if alphaI > 256 then alphaI := 256; _DoMixer(Dest, Source, alphaI);end;procedure TForm1.Button1Click(Sender: TObject);var source, dest: TGpBitmap; g: TGpGraphics; src, dst: TImageData;begin source := TGpBitmap.Create('..\..\media\Apple.png'); dest := TGpBitmap.Create('..\..\media\xmas_011.png'); g := TGpGraphics.Create(Canvas.Handle); g.DrawImage(dest, 0, 0); g.DrawImage(source, dest.Width, 0); src := LockGpBitmap(source); dst := LockGpBitmap(dest); ImageMixer(dst, src, 0.75); UnlockGpBitmap(dest, dst); UnlockGpBitmap(source, src); g.DrawImage(dest, dst.Width + src.Width, 0); g.Free; dest.Free; source.Free;end;procedure TForm1.Button2Click(Sender: TObject);var source: TGpBitmap; dest: TBitmap; tmp: TJpegImage; g: TGpGraphics; src, dst: TImageData;begin dest := TBitmap.Create; tmp := TJpegImage.Create; tmp.LoadFromFile('..\..\media\IMG_9440_mf.jpg'); dest.Assign(tmp); tmp.Free; source := TGpBitmap.Create('..\..\media\xmas_011.png'); g := TGpGraphics.Create(Canvas.Handle); Canvas.Draw(0, 0, dest); g.DrawImage(source, dest.Width, 0); dst := GetBitmapData(dest); src := LockGpBitmap(source); ImageMixer(dst, src, 1); UnlockGpBitmap(source, src); Canvas.Draw(dst.Width + src.Width, 0, dest); g.Free; dest.Free; source.Free;end;end.
The preceding is a complete Delphi program. In the _ domixer process, seven image synthesis sub-processes are defined to meet the needs of image merging in all eight permutation and combination modes. In addition to the subprocesses @ mixer0 and @ mixer1, other subprocesses call the _ argbmixer and _ pargbmixer processes respectively, in fact, the processing code in @ mixer0 is basically the same as the _ argbmixer process. That is to say, the seven sub-processes use three processing methods respectively:
First, the full copy method of @ mixer1;
Second, the target image does not contain Alpha information. The source image is mixed to the _ argbmixer process of the target image by Pixel Alpha information or given opacity, this process uses the simplified formula of the previous Formula 1 or 4.
The third is the Processing Form of the target image with Alpha Information _ pargbmixer. This process uses the previous Formula 3.
If there are too many seven sub-processes, you can also simplify the code and retain only the sub-processes that meet the preceding three conditions, that is, @ mixer1, @ mixer4, and @ mixer6, it has little impact on processing efficiency.
The above program contains two examples. The first one is to use the GDI + bitmap to open two PNG images for processing. This processing calls @ mixer6. The running effect is as follows:
The next example is a hybrid processing of the GDI + bitmap and VCL bitmap. The VCL bitmap is a jpeg file without Alpha information. The running effect of this example is as follows:
For details about the use of GDI + units and descriptions in the "Delphi image processing" series, see the article 《GDI + for VCL basics-GDI + and VCL.
Due to limited levels, errors are inevitable. Correction and guidance are welcome. Email Address:Maozefa@hotmail.com
Here, you can access "Delphi Image Processing-Article Index".