Delphi图像处理 -- 图像颜色混合

阅读提示:

    《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, 0
end;

// --> 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 pixel
procedure _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       edx
end;

// --> 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, mm0
end;

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,bIndex
procedure 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=bwGray
procedure 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     esi
end;

// in: esi=srcPixel,edi=dstPixel,eax=gray
// out: [edi]=mixerColor
procedure 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     esi
end;

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     esi
end;

// 图像颜色模式混合
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图像处理 -- 文章索引》。

 

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值