; _______________________________________________________________________________________
;|                                                                                       |
;| ..::FreshLib::..  Free, open source. Licensed under "BSD 2-clause" license."          |
;|_______________________________________________________________________________________|
;
;  Description: Text drawing library.
;
;  Target OS: KolibriOS
;
;  Dependencies:
;
;  Notes:
;_________________________________________________________________________________________


iglobal
  var flagFreeTypeLoadFlags = FT_LOAD_RENDER or FT_LOAD_TARGET_LCD or FT_LOAD_NO_AUTOHINT or FT_LOAD_COLOR
endg


uglobal
  var FTC_CMapCache_Lookup = 0
  var FTC_ImageCache_LookupScaler = 0

endg



body DrawDecomposedString      ;, .pImage, .pArray, .x, .y, .font, .color
.scaler  FTC_ScalerRec
.glyph   dd ?

.startX  dd ?
begin
        pushad

        mov     edx, [.font]
        xor     ecx, ecx

        mov     [.scaler.face_id], edx
        mov     [.scaler.width], ecx

        mov     eax, [edx+__TFont.height]
        inc     ecx

        mov     [.scaler.height], eax
        mov     [.scaler.pixel], ecx    ; ecx = 1

        mov     eax, [.x]
        mov     [.startX], eax

        mov     esi, [.pArray]
        xor     edi, edi
        dec     edi

.char_loop:
        inc     edi
        cmp     edi, [esi+TArray.count]
        jae     .finish

        cmp     word [esi+TArray.array+8*edi+TTextChar.width], 0
        je      .char_ok

        cinvoke FTC_CMapCache_Lookup, [FTCCMapCache], [.font], -1, [esi+TArray.array+8*edi+TTextChar.code]

        lea     ecx, [.scaler]
        lea     edx, [.glyph]
        cinvoke FTC_ImageCache_LookupScaler, [FTCImageCache], ecx, [flagFreeTypeLoadFlags], eax, edx, 0

        test    eax, eax
        jnz     .char_ok

        mov     ebx, [.glyph]
        mov     ecx, [.x]
        mov     edx, [.y]

        add     ecx, [ebx+FT_BitmapGlyphRec.left]
        sub     edx, [ebx+FT_BitmapGlyphRec.top]

        lea     eax, [ebx+FT_BitmapGlyphRec.bitmap]
        stdcall __DrawGlyph, [.pImage], eax, ecx, edx, [.color]

        movzx   eax, word [esi+TArray.array+8*edi+TTextChar.width]
        add     [.x], eax

.char_ok:
; check for new line.
        movzx   eax, word [esi+TArray.array+8*edi+TTextChar.height]
        test    eax, eax
        jz      .char_loop

        add     [.y], eax
        mov     eax, [.startX]
        mov     [.x], eax
        jmp     .char_loop


.finish:
        popad
        return
endp




; Draws the string on the image using the specified font and color.
; The string is drawn in one line. The control symbols are not
; processed. The tabs are not expanded or replaced with spaces.
;
; Arguments:
;    .pImage  - pointer to the image where to draw the text.
;    .pString - pointer to a UTF-8 string.
;    .len     - how many bytes from the string to be drawn. If -1, the whole string will be drawn.
;    .x       - X coordinate, where to start drawing.
;    .y       - Y coordinate of the baseline where to draw the string.
;    .font    - the font to be used.
;    .color   - ARGB color to draw the glyphs.
;
;;;;;;;; Returns:
;;;;;;;;    EAX - the width of the string in pixels.
;;;;;;;;    EBX - the ascender of the string. It is negative.
;;;;;;;;    EDX - the height of the string in pixels.


body DrawString ;, .pImage, .pString, .len, .x, .y, .font, .color

.scaler  FTC_ScalerRec
.glyph   dd ?

.miny    dd ?
.maxy    dd ?
.startX  dd ?

begin
        pushad

        mov     edx, [.font]
        xor     ecx, ecx

        mov     [.scaler.face_id], edx
        mov     [.scaler.width], ecx

        mov     eax, [edx+__TFont.height]
        inc     ecx

        mov     [.scaler.height], eax
        mov     [.scaler.pixel], ecx    ; ecx = 1

        ror     ecx, 1                  ; ecx = $80000000
        mov     [.maxy], ecx
        dec     ecx
        mov     [.miny], ecx            ; ecx = $7fffffff

        mov     eax, [.x]
        mov     [.startX], eax

        mov     esi, [.pString]

.char_loop:

        stdcall DecodeUtf8, [esi]
        jc      .finish

        add     esi, edx

        sub     [.len], edx
        jb      .finish

        test    eax, eax
        jz      .finish

        cinvoke FTC_CMapCache_Lookup, [FTCCMapCache], [.font], -1, eax

        lea     ecx, [.scaler]
        lea     edx, [.glyph]
        cinvoke FTC_ImageCache_LookupScaler, [FTCImageCache], ecx, [flagFreeTypeLoadFlags], eax, edx, 0

        test    eax, eax
        jnz     .char_loop

        mov     ebx, [.glyph]

        mov     ecx, [.x]
        mov     edx, [.y]

        add     ecx, [ebx+FT_BitmapGlyphRec.left]
        sub     edx, [ebx+FT_BitmapGlyphRec.top]

        cmp     edx, [.miny]
        jge     @f
        mov     [.miny], edx
@@:
        mov     eax, edx
        add     eax, [ebx+FT_BitmapGlyphRec.bitmap.rows]
        cmp     eax, [.maxy]
        jle     @f
        mov     [.maxy], eax
@@:
        lea     eax, [ebx+FT_BitmapGlyphRec.bitmap]
        stdcall __DrawGlyph, [.pImage], eax, ecx, edx, [.color]

        mov     eax, [ebx+FT_BitmapGlyphRec.root.advance.x]
        sar     eax, 16        ; format 16.16 fixed decimal point.
        add     [.x], eax

        jmp     .char_loop


.finish:
;        mov     eax, [.x]
;        mov     edx, [.maxy]
;        mov     ebx, [.miny]
;        sub     eax, [.startX]
;        sub     edx, ebx
;        sub     ebx, [.y]

;        mov     [esp+4*regEAX], eax
;        mov     [esp+4*regEBX], ebx
;        mov     [esp+4*regEDX], edx

        popad
        return
endp



; returns:
;   EAX - array with decomposed string.
;   EDX - maximal bearing_Y of the string.
;   ECX - maximal descender of the string.

body TextDecompose      ;, .hString, .font

  .scaler  FTC_ScalerRec
  .glyph   dd ?

  .miny    dd ?
  .maxy    dd ?

begin
        pushad

        mov     edx, [.font]
        xor     ecx, ecx

        mov     [.scaler.face_id], edx
        mov     [.scaler.width], ecx

        mov     eax, [edx+__TFont.height]
        inc     ecx

        mov     [.scaler.height], eax
        mov     [.scaler.pixel], ecx    ; ecx = 1

        ror     ecx, 1                  ; ecx = $80000000
        mov     [.maxy], ecx
        dec     ecx
        mov     [.miny], ecx            ; ecx = $7fffffff

        stdcall StrPtr, [.hString]
        mov     esi, eax

        stdcall CreateArray, sizeof.TTextChar
        mov     edi, eax

.char_loop:
        stdcall DecodeUtf8, [esi]
        jc      .finish

        add     esi, edx
        mov     ebx, eax

        test    eax, eax
        jz      .end

        cinvoke FTC_CMapCache_Lookup, [FTCCMapCache], [.font], -1, eax

        lea     ecx, [.scaler]
        lea     edx, [.glyph]
        cinvoke FTC_ImageCache_LookupScaler, [FTCImageCache], ecx, [flagFreeTypeLoadFlags], eax, edx, 0

        xor     ecx, ecx

        test    eax, eax
        jnz     .glyph_size_ok  ; zero width glyph ????

        mov     ecx, [.glyph]

        mov     eax, [ecx+FT_BitmapGlyphRec.top]
        neg     eax

        cmp     eax, [.miny]
        jge     @f
        mov     [.miny], eax
@@:

        add     eax, [ecx+FT_BitmapGlyphRec.bitmap.rows]
        cmp     eax, [.maxy]
        jle     @f
        mov     [.maxy], eax
@@:
        mov     ecx, [ecx+FT_BitmapGlyphRec.root.advance.x]  ; format 16.16 fixed decimal point.
        sar     ecx, 16   ; format 16.16


.glyph_size_ok:

        stdcall AddArrayItems, edi, 1
        mov     edi, edx
        mov     dword [eax+TTextChar.width], ecx
        mov     [eax+TTextChar.code],  ebx
        jmp     .char_loop

.end:
        clc

.finish:
        mov     [esp+4*regEAX], edi

        mov     eax, [.miny]
        neg     eax
        mov     ecx, [.maxy]

        mov     [esp+4*regEDX], eax
        mov     [esp+4*regECX], ecx

        add     ecx, eax
        mov     [edi+TArray.lparam], ecx

        popad
        return
endp



; Returns an array with the widths from the begining of the text to the every character inside the string.


proc GetTextWidthsArray, .hString, .font

  .scaler  FTC_ScalerRec
  .glyph   dd ?

  .x       dd ?

begin
        pushad

        mov     edx, [.font]
        xor     ecx, ecx

        mov     [.scaler.face_id], edx
        mov     [.scaler.width], ecx

        mov     eax, [edx+__TFont.height]
        inc     ecx

        mov     [.scaler.height], eax
        mov     [.scaler.pixel], ecx    ; ecx = 1


        stdcall StrPtr, [.hString]
        mov     esi, eax

        stdcall CreateArray, 4
        mov     edi, eax

        xor     ebx, ebx

.char_loop:
        stdcall DecodeUtf8, [esi]
        jc      .finish

        add     esi, edx
        test    eax, eax
        jz      .end

        cinvoke FTC_CMapCache_Lookup, [FTCCMapCache], [.font], -1, eax

        lea     ecx, [.scaler]
        lea     edx, [.glyph]
        cinvoke FTC_ImageCache_LookupScaler, [FTCImageCache], ecx, [flagFreeTypeLoadFlags], eax, edx, 0

        test    eax, eax
        jnz     .glyph_size_ok  ; zero width glyph ????


        mov     ecx, [.glyph]
        mov     ecx, [ecx+FT_BitmapGlyphRec.root.advance.x]  ; format 16.16 fixed decimal point.

        add     ebx, ecx


.glyph_size_ok:

        stdcall AddArrayItems, edi, 1
        mov     edi, edx

        mov     edx, ebx
        sar     edx, 16         ; format 16.16
        mov     [eax], edx
        jmp     .char_loop

.end:
        clc

.finish:
        mov     [esp+4*regEAX], edi
        popad
        return

.error:
        stc
        popad
        return
endp











; Computes the width and height of the text.
;
; Arguments:
;    .pString - pointer to a UTF-8 string.
;    .len     - how many bytes from the string to be used. If -1 the whole string will be scanned.
;    .font    - the font to be used for the computations.
;
; Returns:
;    EAX - the width of the string in pixels.
;    EBX - the ascender of the string. It is negative.
;    EDX - the height of the string in pixels.


body GetTextBounds      ;, .pString, .len, .font

.scaler  FTC_ScalerRec
.glyph   dd ?

.x       dd ?

.miny    dd ?
.maxy    dd ?

.charlen dd ?

begin
        pushad

        mov     edx, [.font]
        xor     ecx, ecx

        mov     [.scaler.face_id], edx
        mov     [.scaler.width], ecx

        mov     [.x], ecx

        mov     eax, [edx+__TFont.height]
        inc     ecx

        mov     [.scaler.height], eax
        mov     [.scaler.pixel], ecx    ; ecx = 1

        ror     ecx, 1                  ; ecx = $80000000
        mov     [.maxy], ecx
        dec     ecx
        mov     [.miny], ecx            ; ecx = $7fffffff

        mov     esi, [.pString]

.char_loop:
        stdcall DecodeUtf8, [esi]
        jc      .finish

        add     esi, edx
        mov     [.charlen], edx

        test    eax, eax
        jz      .finish

        cinvoke FTC_CMapCache_Lookup, [FTCCMapCache], [.font], -1, eax

        lea     ecx, [.scaler]
        lea     edx, [.glyph]
        cinvoke FTC_ImageCache_LookupScaler, [FTCImageCache], ecx, [flagFreeTypeLoadFlags],  eax, edx, 0

        test    eax, eax
        jnz     .char_loop

        mov     ebx, [.glyph]

        sub     eax, [ebx+FT_BitmapGlyphRec.top]                ; eax = 0 here
        mov     edx, eax
        add     edx, [ebx+FT_BitmapGlyphRec.bitmap.rows]

        cmp     eax, [.miny]
        jge     @f
        mov     [.miny], eax
@@:
        cmp     edx, [.maxy]
        jle     @f
        mov     [.maxy], edx
@@:

        mov     eax, [ebx+FT_GlyphRec.advance.x]
        sar     eax, 16        ; format 16.16 fixed decimal point.
        add     [.x], eax

        mov     eax, [.charlen]
        sub     [.len], eax
        ja      .char_loop


.finish:
        mov     eax, [.x]
        mov     edx, [.maxy]
        mov     ebx, [.miny]
        sub     edx, ebx

        mov     [esp+4*regEAX], eax
        mov     [esp+4*regEBX], ebx
        mov     [esp+4*regEDX], edx

        popad
        return
endp







proc __DrawGlyph, .pImage, .pFTBitmap, .x, .y, .color
.spitch   dd ?
.dpitch   dd ?

.drect   RECT
.srect   RECT

.pixel_mode dd ?

.sstart dd ?
.dstart dd ?

.xsize  dd ?

.bpp    dd ?
.bit_ofs dd ?

begin
        pushad

        stdcall __fix_color, [.color], regEAX
        mov     [.color], eax

        mov     ebx, [.pImage]
        mov     edi, [.pFTBitmap]

; the glyph pixel mode handling.
        movzx   eax, [edi+FT_Bitmap.pixel_mode]
        cmp     eax, FT_PIXEL_MODE_BGRA
        ja      .finish

        mov     [.pixel_mode], eax
        movzx   eax, [.bpp_const+eax]
        mov     [.bpp], eax

; Clip the glyph to the image surface.

        mov     eax, [ebx+TImage.width]
        mov     ecx, [ebx+TImage.height]
        mov     [.drect.right], eax
        mov     [.drect.bottom], ecx
        xor     eax, eax
        mov     [.drect.left], eax
        mov     [.drect.top], eax

        mov     eax, [.x]
        mov     ecx, [.y]
        mov     [.srect.left], eax
        mov     [.srect.top], ecx

        mov     eax, [edi+FT_Bitmap.width]
        cmp     [.pixel_mode], FT_PIXEL_MODE_LCD
        jne     @f

        cdq
        mov     ecx, 3
        div     ecx

@@:
        add     eax, [.srect.left]
        mov     [.srect.right], eax

        mov     eax, [.srect.top]
        add     eax, [edi+FT_Bitmap.rows]
        mov     [.srect.bottom], eax

        lea     eax, [.srect]
        lea     ecx, [.drect]
        stdcall RectIntersect, ecx, eax, ecx
        jc      .finish                         ; the rectangles does not intersect!

        mov     eax, [.drect.left]
        mov     ecx, [.drect.top]
        sub     eax, [.x]
        sub     ecx, [.y]
        mov     [.srect.left], eax
        mov     [.srect.top], ecx

        mov     eax, [.drect.right]
        mov     ecx, [.drect.bottom]
        sub     eax, [.drect.left]
        sub     ecx, [.drect.top]
        mov     [.srect.right], eax     ; width!
        mov     [.srect.bottom], ecx    ; height!

; End of clipping code - [.srect] contains the rectangle of the glyph that have to be drawn (left, top, width, height).
; [.drect] contains the destination rectangle. Both have the same size.

        mov     ecx, [edi+FT_Bitmap.pitch]
        mov     [.spitch], ecx

; glyph pixel pointer

        mov     esi, [edi+FT_Bitmap.buffer]
        test    esi, esi
        jz      .finish

        mov     eax, [.srect.top]
        mov     ecx, [.srect.left]

        imul    eax, [.spitch]
        imul    ecx, [.bpp]
        mov     [.bit_ofs], ecx
        shr     ecx, 3          ; byte offs
        and     [.bit_ofs], 7          ; bit offs

        add     esi, ecx
        add     esi, eax

; TImage destination pixel pointer

        mov     edi, [ebx+TImage.pPixels]

        mov     ecx, [ebx+TImage.width]
        mov     eax, [.drect.top]
        imul    eax, ecx
        add     eax, [.drect.left]
        lea     edi, [edi+4*eax]    ; in bytes.

        shl     ecx, 2
        mov     [.dpitch], ecx


; prepare MMX constants

        pxor    mm0, mm0        ; mm0 = 0000 0000 0000 0000
        pcmpeqw mm1, mm1                                        ; mm1 = ffff ffff ffff ffff
        psrlw   mm1, 15                                         ; mm1 = 0001 0001 0001 0001
        psllw   mm1, 8          ; mm1 = 0100 0100 0100 0100

        movq    mm2, mm1                                        ; mm2 = 0001 0001 0001 0001
        psllq   mm2, 48         ; mm2 = 0100 0000 0000 0000


.loopy:

        mov     [.sstart], esi
        mov     [.dstart], edi

        mov     eax, [.srect.right]     ; width!
        mov     [.xsize], eax

        cmp     [.pixel_mode], FT_PIXEL_MODE_BGRA
        je      .loopx32

        cmp     [.pixel_mode], FT_PIXEL_MODE_LCD
        je      .loopx24

        cmp     [.pixel_mode], FT_PIXEL_MODE_GRAY
        je      .loopx8

        cmp     [.pixel_mode], FT_PIXEL_MODE_MONO
        jne     .finish

        lodsd
        bswap   eax
        mov     ch, 32
        mov     cl, byte [.bit_ofs]
        shl     eax, cl
        sub     ch, cl

.loopx1:
        shl     eax, 1
        dec     ch
        jnz     @f

        lodsd
        bswap   eax
        mov     ch, 32

@@:
        jnc     .next_x

        movd    mm7, [.color]        ; source pixel.
        movd    mm6, [edi]           ; destination pixel.

        movq    mm5, mm7
        psrlq   mm5, 24                 ; alpha source

        punpckldq mm5, mm5              ;
        punpcklbw mm6, mm0              ; byte to word destination pixel
        packssdw  mm5, mm5              ; mm5 = As, As, As, As
        punpcklbw mm7, mm0              ; byte to word source pixel

        movq    mm4, mm1
        psubw   mm4, mm5                ; mm4 = Bs Bs Bs Bs     (Bs = $100 - As)

        psrlq   mm5, 16                 ; mm5 = 0  As As As
        por     mm5, mm2                ; mm5 = $100 As As As

        pmullw  mm6, mm4                ; mm6 = Cd = Cd * (256-As) = Cd * Bs   (For alpha Ad = Ad*Bs)
        pmullw  mm7, mm5                ; Cs = Cs * As                   (For alpha As = $100*As )

        paddusw mm6, mm7                ; Cd = Cs + Cd                   (For alpha Ad = $100*As + Ad*Bs)
        psrlw   mm6, 8                  ; Cd = Cd/256                    (For alpha Ad = As + Ad*Bs/$100)

        packuswb mm6, mm0               ; pack words to bytes 0A 0R 0G 0B -> 0000ARGB
        movd     [edi], mm6

.next_x:
        add     edi, 4
        dec     [.xsize]
        jnz     .loopx1
        jmp     .nexty



.loopx8:
        movzx     eax, byte [esi]         ; intensity
        add       esi, 1

        movd      mm6, [.color]
        movd      mm7, eax

        punpcklbw mm6, mm0              ; byte to word color.
        punpckldq mm7, mm7              ;
        packssdw  mm7, mm7              ; mm7 = I, I, I, I

        pmullw    mm7, mm6              ; mm7 is the color to blend = Cs*256

        movd      mm6, [edi]            ; Destination color
        punpcklbw mm6, mm0              ; byte to word destination pixel

        movq      mm5, mm7
        psrlq     mm5, 56               ; alpha source
        punpckldq mm5, mm5
        packssdw  mm5, mm5              ; mm5 = As, As, As, As

        movq    mm4, mm1
        psubw   mm4, mm5                ; mm4 = Bs Bs Bs Bs     (Bs = $100 - As)

        pmullw  mm6, mm4                ; mm6 = Cd = Cd * (256-As) = Cd * Bs
        paddusw mm6, mm7                ; Cd = 256*Cs + Cd*(256-As)
        psrlw   mm6, 8                  ; Cd = Cd/256

        packuswb mm6, mm0               ; pack words to bytes 0A 0R 0G 0B -> 0000ARGB
        movd     [edi], mm6

        add     edi, 4

        dec     [.xsize]
        jnz     .loopx8
        jmp     .nexty


.loopx24:

        movzx   eax, byte [esi]
        movzx   edx, byte [esi+1]

        lea     eax, [eax+2*edx]
        movzx   edx, byte [esi+2]

        add     eax, edx
        shr     eax, 2          ; grayscale.

        movd      mm6, [.color]
        movd      mm7, eax

        punpcklbw mm6, mm0              ; byte to word color.
        punpckldq mm7, mm7              ;
        packssdw  mm7, mm7              ; mm7 = I, I, I, I

        pmullw    mm7, mm6              ; mm7 is the color to blend = Cs*256

        movd      mm6, [edi]            ; Destination color
        punpcklbw mm6, mm0              ; byte to word destination pixel

        movq      mm5, mm7
        psrlq     mm5, 56               ; alpha source
        punpckldq mm5, mm5
        packssdw  mm5, mm5              ; mm5 = As, As, As, As

        movq    mm4, mm1
        psubw   mm4, mm5                ; mm4 = Bs Bs Bs Bs     (Bs = $100 - As)

        pmullw  mm6, mm4                ; mm6 = Cd = Cd * (256-As) = Cd * Bs
        paddusw mm6, mm7                ; Cd = 256*Cs + Cd*(256-As)
        psrlw   mm6, 8                  ; Cd = Cd/256

        packuswb mm6, mm0               ; pack words to bytes 0A 0R 0G 0B -> 0000ARGB
        movd     [edi], mm6


        add     esi, 3
        add     edi, 4

        dec     [.xsize]
        jnz     .loopx24
        jmp     .nexty


.loopx32:
        movd    mm7, [esi]              ; source pixel.
        movd    mm6, [edi]              ; destination pixel.

        movq    mm5, mm7
        psrlq   mm5, 24                 ; alpha source

        punpckldq mm5, mm5              ;
        punpcklbw mm6, mm0              ; byte to word destination pixel
        packssdw  mm5, mm5              ; mm5 = As, As, As, As
        punpcklbw mm7, mm0              ; byte to word source pixel

        movq    mm4, mm1
        psubw   mm4, mm5                ; mm4 = Bs Bs Bs Bs     (Bs = $100 - As)

        psrlq   mm5, 16                 ; mm5 = 0  As As As
        por     mm5, mm2                ; mm5 = $100 As As As

        pmullw  mm6, mm4                ; mm6 = Cd = Cd * (256-As) = Cd * Bs   (For alpha Ad = Ad*Bs)
        pmullw  mm7, mm5                ; Cs = Cs * As                   (For alpha As = $100*As )

        paddusw mm6, mm7                ; Cd = Cs + Cd                   (For alpha Ad = $100*As + Ad*Bs)
        psrlw   mm6, 8                  ; Cd = Cd/256                    (For alpha Ad = As + Ad*Bs/$100)

        packuswb mm6, mm0               ; pack words to bytes 0A 0R 0G 0B -> 0000ARGB
        movd     [edi], mm6

        add     edi, 4
        add     esi, 4

        dec     [.xsize]
        jnz     .loopx32
        jmp     .nexty


.nexty:
        mov     esi, [.sstart]
        add     esi, [.spitch]

        mov     edi, [.dstart]
        add     edi, [.dpitch]

        dec     [.srect.bottom]         ; height
        jnz     .loopy


.finish:
        emms
        popad
        return

.bpp_const db 0, 1, 8, 2, 4, 24, 8, 32

endp



; the colors must be pre-multiplied ARGB.

proc _BlendPixel, .dstColor, .srcColor
begin
        pushad

        movzx   ecx, byte [.srcColor+3]      ; As
        mov     ebx, $ff
        mov     esi, ebx

        sub     ebx, ecx                     ; 1 - As

        movzx   eax, byte [.dstColor+3]      ; Ad
        imul    ecx, esi
        mul     ebx
        add     eax, ecx
        adc     edx, 0
        div     esi

        mov     byte [esp+4*regEAX+3], al


        movzx   eax, byte [.dstColor]       ; Bd
        movzx   ecx, byte [.srcColor]       ; Bs

        imul    ecx, esi
        mul     ebx
        add     eax, ecx
        adc     edx, 0
        div     esi

        mov     byte [esp+4*regEAX], al



        movzx   eax, byte [.dstColor+1]       ; Gd
        movzx   ecx, byte [.srcColor+1]       ; Gs

        imul    ecx, esi
        mul     ebx
        add     eax, ecx
        adc     edx, 0
        div     esi

        mov     byte [esp+4*regEAX+1], al



        movzx   eax, byte [.dstColor+2]       ; Rd
        movzx   ecx, byte [.srcColor+2]       ; Rs

        imul    ecx, esi
        mul     ebx
        add     eax, ecx
        adc     edx, 0
        div     esi

        mov     byte [esp+4*regEAX+2], al


        popad
        return
endp
