閱讀提示:
《Delphi影像處理》系列以效率為側重點,一般代碼為PASCAL,核心代碼採用BASM。
《C++影像處理》系列以代碼清晰,可讀性為主,全部使用C++代碼。
儘可能保持二者內容一致,可相互對照。
本文代碼必須包括文章《Delphi影像處理 -- 資料類型及公用過程》中的ImageData.pas單元。
Photoshop提供了豐富的圖象圖層混合模式,其中的顏色混合模式是用層圖象的亮度與層填充顏色或者圖象色彩進行混合,形成的結果既有著層的色彩,又保留了下層圖象的灰階,基於這種混合特性,顏色混合模式常用來對灰階圖象進行著色。
在《C++影像處理 -- 映像顏色混合(上)》、《C++影像處理 -- 映像顏色混合(中)》和《C++影像處理 -- 映像顏色混合(下)》等多篇文章中,介紹了映像顏色混合的原理、演算法最佳化和代碼完善方法,因此這裡不再介紹,直接貼出Delpha實現代碼(BASM)和例子。由於BASM彙編碼較長,在C++版的顏色混合文章的映像黑白調整和灰階圖象染色功能,準備另文發表。
映像顏色混合實現代碼:
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;const BWDefault: array[0..5] of Integer = (410, 614, 410, 819, 205, 614); GrayConst: array[0..2] of Integer = (113, 604, 307);// eax,edx,ecx=r,g,b esi,sdi,ebx=rIndex,gIndex,bIndexprocedure CompareRgb;asm cmp eax, ecx jae @@1 xchg eax, ecx xchg esi, ebx@@1: cmp eax, edx jae @@2 xchg eax, edx xchg esi, edi@@2: cmp ecx, edx jbe @@3 xchg ecx, edx xchg ebx, edi@@3:end;// in: edi=pixel// out: eax=bwGrayprocedure GetBWGray;asm push esi push edi movzx ecx, [edi].TARGBQuad.Blue movzx edx, [edi].TARGBQuad.Green movzx eax, [edi].TARGBQuad.Red mov ebx, 4 // blue index mov edi, 2 // green index xor esi, esi // red index call CompareRgb // CompareRgb(red, green, blue) sub eax, edx // max - mid sub edx, ecx // mid - min add edi, esi dec edi imul eax, BWDefault[esi*4].Integer imul edx, BWDefault[edi*4].Integer add eax, edx // gray = (((max - mid) * params[maxIndex] + add eax, 512 // (mid - min) * params[maxIndex + midIndex - 1] + sar eax, 10 // 512) >> 10) + min add eax, ecx pop edi pop esiend;// in: esi=srcPixel,edi=dstPixel,eax=gray// out: [edi]=mixerColorprocedure ColorMix;var gray, max_min: LongWord;asm push esi push edi mov gray, eax movzx ecx, [esi].TARGBQuad.Blue movzx edx, [esi].TARGBQuad.Green movzx eax, [esi].TARGBQuad.Red xor ebx, ebx // blue index mov edi, 1 // green index mov esi, 2 // red index call CompareRgb // CompareRgb(red, green, blue) sub eax, ecx // max - min jnz @@4 pop edi mov eax, gray mov [edi].TARGBQuad.Blue, al mov [edi].TARGBQuad.Green, al mov [edi].TARGBQuad.Red, al jmp @@Exit@@4: sub edx, ecx // mid - min mov max_min, eax mov ecx, eax sub eax, edx imul eax, GrayConst[edi*4].Integer imul ecx, GrayConst[ebx*4].Integer add eax, ecx add eax, 512 // nMax = gray + shr eax, 10 // (max_min - mid_min) * grayConst[midIndex] + add eax, gray // max_min * grayConst[minIndex] cmp eax, 255 ja @@5 mov ecx, eax sub ecx, max_min // nMin = nMax - max_min js @@6 add edx, ecx // nMid = nMin + mid_min jmp @@8@@5:// nMax > 255 shl edx, 10 // hueCoef = (mid_min << 10) / max_min mov eax, max_min xchg eax, edx mul DivTab[edx*4].Integer push edx mov ecx, GrayConst[edi*4].Integer imul edx, ecx shr edx, 10 // v0 = (ys[mid.index] * hueCoef) >> 10 add ecx, GrayConst[ebx*4].Integer sub ecx, edx // v1 = ys[mid.index] + ys[min.index] - v0 add edx, GrayConst[esi*4].Integer mov eax, edx shl edx, 8 sub edx, eax mov eax, gray shl eax, 10 sub eax, edx mov edx, ecx shr edx, 1 add eax, edx mul DivTab[ecx*4].Integer mov ecx, edx // nMin = ((gray << 10) - (ys[max.index] + v0) * pop eax // 255 + (v1 >> 1)) / v1 xor edx, 255 imul edx, eax add edx, 512 shr edx, 10 add edx, ecx // nMid = nMin + (((255 ^ newMin) * hueCoef + 512) >> 10) mov eax, 255 // nMax = 255 jmp @@8@@6:// nMin < 0 shl edx, 10 // hueCoef = (mid_min << 10) / max_min mov eax, max_min xchg eax, edx mul DivTab[edx*4].Integer push edx imul edx, GrayConst[edi*4].Integer add edx, 512 shr edx, 10 // tmp = ys[max.index] + ((ys[mid.index] * hueCoef + 512) >> 10) add edx, GrayConst[esi*4].Integer mov eax, gray shl eax, 10 mov ecx, edx shr edx, 1 add eax, edx mul DivTab[ecx*4].Integer mov eax, edx // nMax = ((gray << 10) + (tmp >> 1)) / tmp pop edx imul edx, eax add edx, 512 shr edx, 10 // nMid = (nMax * hueCoef + 512) >> 10 mov ecx, 1 // nMin = 1@@8: mov ah, dl pop edx mov [edx+esi], al mov [edx+edi], ah mov [edx+ebx], cl mov edi, edx@@Exit: pop esiend;procedure _DoColorMixer(var Dest: TImageData; const Source: TImageData; Alpha: Integer);var width, height, dstOffset, srcOffset: Integer; dst, src: LongWord; alphaI: Integer;asm push esi push edi push ebx mov alphaI, ecx mov cl, [eax].TImageData.AlphaFlag mov ch, [edx].TImageData.AlphaFlag and [edx].TImageData.AlphaFlag, cl push ecx call _SetCopyRegs mov width, ecx mov height, edx mov srcOffset, eax mov dstOffset, ebx pop ecx cmp alphaI, 256 jne @@MixerA cmp ch, TRUE je @@MixerA cmp cl, TRUE je @@MixerB@@yLoop: push width@@xLoop: call GetBWGray call ColorMix add esi, 4 add edi, 4 dec width jnz @@xLoop pop width add esi, srcOffset add edi, dstOffset dec height jnz @@yLoop jmp @@End@@MixerA: cmp cl, True je @@MixerB pxor mm7, mm7@@yLoopA: push width@@xLoopA: movzx eax, [esi].TARGBQuad.Alpha mul alphaI shr eax, 8 jz @@NextA push esi push edi push eax mov eax, [edi] mov dst, eax lea edi, dst call GetBWGray // get dest BlackWhite gray call ColorMix // dst = source.HS mixer dest.gray mov esi, edi pop eax // mixAlpha = source.Alpha * alpha / 256 pop edi call _ARGBMixer // dest.rgb = dst.rgb mixer dest.rgb movd [edi], mm0 mov [edi].TARGBQuad.Alpha, 255 pop esi@@NextA: add esi, 4 add edi, 4 dec width jnz @@xLoopA pop width add esi, srcOffset add edi, dstOffset dec height jnz @@yLoopA emms jmp @@End@@MixerB: call _SetMixerMM@@yLoopB: push width@@xLoopB: movzx eax, [esi].TARGBQuad.Alpha mul alphaI shr eax, 8 jz @@NextB test [edi].TARGBQuad.Alpha, 0ffh jnz @@B_1 mov eax, [esi] mov [edi], eax jmp @@NextB@@B_1: push esi push edi push edi push eax mov eax, [esi] mov src, eax mov eax, [edi] mov dst, eax lea edi, dst call GetBWGray // get dest BlackWhite gray call ColorMix // dst = source.HS mixer dest.gray mov esi, edi pop eax // mixAlpha = source.Alpha * alpha / 256 pop edi call _PARGBMixer // dst.rgb = dst.rgb mixer dest.rgb movd [esi], mm0 movzx eax, [edi].TARGBQuad.Alpha// mixAlpha = dest.Alpha lea edi, src call _PARGBMixer // dest.rgb = dst.rgb mixer source.rgb pop edi movd [edi], mm0 mov [edi].TARGBQuad.Alpha, al pop esi@@NextB: add esi, 4 add edi, 4 dec width jnz @@xLoopB pop width add esi, srcOffset add edi, dstOffset dec height jnz @@yLoopB emms@@End: pop ebx pop edi pop esiend;// 映像顏色模式混合procedure ImageColorMixer(var Dest: TImageData; const Source: TImageData; Alpha: Single = 1);var alphaI: Integer;begin alphaI := Round(Alpha * 256); if alphaI < 0 then Exit; if alphaI > 256 then alphaI := 256; _DoColorMixer(Dest, Source, alphaI);end;
例子一:
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); ImageColorMixer(dst, src);// 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;
例子一運行:
例子二:
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); ImageColorMixer(dst, src);// ImageMixer(dst, src, 1); UnlockGpBitmap(source, src); Canvas.Draw(dst.Width + src.Width, 0, dest); g.Free; dest.Free; source.Free;end;
例子二:
本文只提供了基本的顏色混合代碼,要實現顏色混合的幾何變換,可參見本系列有關幾何變換的文章。
《Delphi影像處理》系列使用GDI+單元和說明見文章《GDI+ for VCL基礎 -- GDI+ 與 VCL》。
因水平有限,錯誤在所難免,歡迎指正和指導。郵箱地址:maozefa@hotmail.com
這裡可訪問《Delphi影像處理 -- 文章索引》。