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




body DrawDecomposedString      ;, .pImage, .pArray, .x, .y, .font, .color

.startX  dd ?

.old_font    dd ?
.old_bmp     dd ?

.img    dd ?

begin
        pushad

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

        stdcall GetFontMetrics, [.font]
        sub     [.y], ebx

        mov     ebx, [.pImage]
        stdcall CreateImage, 100, 100
        mov     [.img], eax

        invoke  CreateCompatibleDC, 0
        mov     ebx, eax

        mov     eax, [.img]
        invoke  SelectObject, ebx, [eax+TImage.hBitmap]
        mov     [.old_bmp], eax

        invoke  SelectObject, ebx, [.font]
        mov     [.old_font], eax

        invoke  SetBkMode, ebx, TRANSPARENT
        invoke  SelectClipRgn, ebx, 0
        invoke  SetTextColor, ebx, $ffffff
        invoke  SetTextAlign, ebx, TA_TOP or TA_LEFT

        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

        movzx   ecx, word [esi+TArray.array+8*edi+TTextChar.width]
        stdcall DrawSolidRect, [.img], 0, 0, ecx, [esi+TArray.lparam], 0

        lea     eax, [esi+TArray.array+8*edi+TTextChar.code]
        movzx   ecx, word [esi+TArray.array+8*edi+TTextChar.width]
        invoke  ExtTextOutW, ebx, 0, 0, 0, 0, eax, 1, 0

        movzx   ecx, word [esi+TArray.array+8*edi+TTextChar.width]
        stdcall __BlendAlphaMask, [.pImage], [.x], [.y], [.img], 0, 0, ecx, [esi+TArray.lparam], [.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:
        invoke  SelectObject, ebx, [.old_font]
        invoke  SelectObject, ebx, [.old_bmp]
        invoke  DeleteDC, ebx

        stdcall DestroyImage, [.img]

        popad
        return
endp




body TextDecompose      ;, .hString, .font

  .miny    dd ?
  .maxy    dd ?

  .more    dd ?
  .string  rd 2
  .code    dd ?

  .size SIZE

  .old_font dd ?

begin
        pushad

        invoke  CreateCompatibleDC, 0
        mov     ebx, eax

        invoke  SelectClipRgn, ebx, 0
        invoke  SelectObject, ebx, [.font]
        mov     [.old_font], eax

        xor     ecx, ecx

        mov     [.string], ecx
        mov     [.string+4], ecx

        inc     ecx

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


        mov     word [.string], '|'
        mov     word [.string+2], '|'

        lea     ecx, [.string]
        lea     eax, [.size]
        invoke  GetTextExtentPoint32W, ebx, ecx, 1, eax

        mov     eax, [.size.cx]
        mov     [.more], eax

        stdcall StrPtr, [.hString]
        mov     esi, eax

        stdcall CreateArray, sizeof.TTextChar
        mov     edi, eax

.char_loop:

        stdcall DecodeUtf8, [esi]
        jc      .finish

        add     esi, edx

        test    eax, eax
        jz      .end

        mov     [.code], eax
        mov     word [.string], ax

        lea     ecx, [.string]
        lea     eax, [.size]
        invoke  GetTextExtentPoint32W, ebx, ecx, 2, eax

        stdcall AddArrayItems, edi, 1
        mov     edi, edx

        mov     ecx, [.size.cx]
        sub     ecx, [.more]

        mov     dword [eax+TTextChar.width], ecx
        mov     ecx, [.code]
        mov     [eax+TTextChar.code],  ecx

        jmp     .char_loop

.end:
        clc

.finish:
        pushf
        invoke  SelectObject, ebx, [.old_font] ; old font from the stack.
        invoke  DeleteDC, ebx

        stdcall GetFontMetrics, [.font]
        popf

        mov     [esp+4*regEDX], ebx             ; font ascent
        mov     [esp+4*regECX], edx             ; font descent
        mov     [edi+TArray.lparam], eax        ; line height

        mov     [esp+4*regEAX], edi

        popad
        return
endp




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

body DrawString ;, .pImage, .pString, .len, .x, .y, .font, .color
.pwc dd ?
.charlen dd ?

.img  dd ?
.size SIZE

.tm TEXTMETRIC

begin
        push    eax ebx ecx edx

        invoke  CreateCompatibleDC, 0
        mov     ebx, eax

        invoke  SelectObject, ebx, [.font]
        push    eax

        invoke  SetBkMode,    ebx, TRANSPARENT
        invoke  SetTextAlign, ebx, TA_TOP or TA_LEFT
        invoke  SelectClipRgn, ebx, 0
        invoke  SetTextColor, ebx, $ffffff

        lea     eax, [.tm]
        invoke  GetTextMetricsW, ebx, eax

        mov     eax, [.tm.tmAscent]
        sub     [.y], eax

; convert the string:

        mov     ecx, [.len]
        shl     ecx, 2
        stdcall GetMem, ecx
        mov     [.pwc], eax
        shr     ecx, 1

        invoke  MultiByteToWideChar, CP_UTF8, 0, [.pString], [.len], [.pwc], ecx
        mov     [.charlen], eax

        lea     eax, [.size]
        invoke  GetTextExtentPoint32W, ebx, [.pwc], [.charlen], eax

        stdcall CreateImage, [.size.cx], [.size.cy]
        mov     [.img], eax

        invoke  SelectObject, ebx, [eax+TImage.hBitmap]
        push    eax

        xor     eax, eax
        invoke  ExtTextOutW, ebx, eax, eax, eax, eax, [.pwc], [.charlen], eax

        invoke  SelectObject, ebx ; old bitmap from the stack.
        invoke  SelectObject, ebx ; old font from the stack.
        invoke  DeleteDC, ebx

        stdcall __BlendAlphaMask, [.pImage], [.x], [.y], [.img], 0, 0, [.size.cx], [.size.cy], [.color]

        stdcall DestroyImage, [.img]
        stdcall FreeMem, [.pwc]

        pop     edx ecx ebx eax

        mov     eax, [.size.cx]
        mov     ebx, [.tm.tmAscent]
        mov     edx, [.size.cy]

        return
endp





proc __BlendAlphaMask, .pDstImage, .xDst, .yDst, .pSrcImage, .xSrc, .ySrc, .width, .height, .color
.srect RECT
.drect RECT
.rect  RECT
begin
        pushad

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

        mov     esi, [.pSrcImage]
        mov     edi, [.pDstImage]

        xor     eax, eax
        mov     ecx, [esi+TImage.width]
        mov     edx, [esi+TImage.height]

        mov     [.srect.left], eax
        mov     [.srect.top], eax
        mov     [.srect.right], ecx
        mov     [.srect.bottom], edx

        mov     ecx, [edi+TImage.width]
        mov     edx, [edi+TImage.height]

        mov     [.drect.left], eax
        mov     [.drect.top], eax
        mov     [.drect.right], ecx
        mov     [.drect.bottom], edx

        mov     ecx, [.xSrc]
        mov     edx, [.ySrc]

        mov     [.rect.left], ecx
        mov     [.rect.top], edx

        add     ecx, [.width]
        add     edx, [.height]

        mov     [.rect.right], ecx
        mov     [.rect.bottom], edx

        lea     ecx, [.srect]
        lea     edx, [.rect]
        stdcall RectIntersect, ecx, ecx, edx
        jc      .finish

        mov     ecx, [.xSrc]
        mov     edx, [.ySrc]
        sub     ecx, [.xDst]
        sub     edx, [.yDst]

; convert the rect to the destination coordinates.
        sub     [.srect.left], ecx
        sub     [.srect.top], edx
        sub     [.srect.right], ecx
        sub     [.srect.bottom], edx

        lea     eax, [.srect]
        lea     ebx, [.drect]
        stdcall RectIntersect, ebx, ebx, eax
        jc      .finish

        stdcall RectCopy, eax, ebx      ; srect = drect

;convert back to source coordinates.
        add     [.srect.left], ecx
        add     [.srect.top], edx
        add     [.srect.right], ecx
        add     [.srect.bottom], edx

; here [.srect] contains the source rectange
; [.drect] contains the destination rectangle
; the width and height of the rectangles  is the same.

        mov     ecx, [.srect.left]
        mov     edx, [.srect.top]
        sub     [.srect.right], ecx
        sub     [.srect.bottom], edx

        mov     ecx, [.srect.top]
        mov     edx, [.drect.top]

        imul    ecx, [esi+TImage.width]
        imul    edx, [edi+TImage.width]

        add     ecx, [.srect.left]
        add     edx, [.drect.left]

        shl     ecx, 2
        shl     edx, 2

        add     ecx, [esi+TImage.pPixels]
        add     edx, [edi+TImage.pPixels]

        mov     esi, [esi+TImage.width]
        mov     edi, [edi+TImage.width]
        lea     esi, [4*esi]
        lea     edi, [4*edi]

; 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

.loopY:
        mov     ebx, [.srect.right]     ; the width of the source rectangle

.loopX:
        dec     ebx                     ; The ZF will not be changed later in the inner loop.
        js      .nextY

        push    edx

        movzx   eax, byte [ecx+4*ebx]
        movzx   edx, byte [ecx+4*ebx+1]

        lea     eax, [eax+2*edx]
        movzx   edx, byte [ecx+4*ebx+2]

        add     eax, edx
        shr     eax, 2          ; grayscale.

        pop     edx

        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, [edx+4*ebx]      ; 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     [edx+4*ebx], mm6

        jmp     .loopX


.nextY:
        add     ecx, esi
        add     edx, edi

        dec     [.srect.bottom]
        jnz     .loopY

.finish:
        emms
        popad
        return
endp





;interface GetTextBounds, .pString, .len, .font

body GetTextBounds      ;, .pString, .len, .font
.size SIZE
.pwc dd ?
.charlen dd ?
begin
        push    ebx ecx

        invoke  CreateCompatibleDC, 0
        mov     ebx, eax

; first convert the string:
        mov     ecx, [.len]
        shl     ecx, 3
        stdcall GetMem, ecx
        mov     [.pwc], eax
        invoke  MultiByteToWideChar, CP_UTF8, 0, [.pString], [.len], [.pwc], ecx
        mov     [.charlen], eax

        invoke  SelectClipRgn, ebx, 0
        invoke  SelectObject, ebx, [.font]
        push    eax

        lea     eax, [.size]
        invoke  GetTextExtentPoint32W, ebx, [.pwc], [.charlen], eax
        invoke  SelectObject, ebx ; from the stack.
        invoke  DeleteDC, ebx

        mov     eax, [.size.cx]
        mov     edx, [.size.cy]

        stdcall FreeMem, [.pwc]
        pop     ecx ebx
        return
endp


; returns:
;       eax - x offset of the baseline of the string.
;       edx - y offset of the baseline of the string.

proc GetTextOffset, .context, .ptrString, .len, .font
.metrics TEXTMETRIC
.pwc dd ?
.charlen dd ?
begin
        push    ebx ecx

; first convert the string:

        mov     ecx, [.len]
        shl     ecx, 3
        stdcall GetMem, ecx
        mov     [.pwc], eax
        invoke  MultiByteToWideChar, CP_UTF8, 0, [.ptrString], [.len], [.pwc], ecx
        mov     [.charlen], eax

        mov     ebx, [.context]

        cmp     [.font], 0
        jne     @f
        invoke  GetStockObject, DEFAULT_GUI_FONT
        mov     [.font], eax

@@:
        invoke  SelectObject, [ebx+TContext.handle], [.font]
        push    eax

        lea     eax, [.metrics]
        invoke  GetTextMetricsW, [ebx+TContext.handle], eax
        invoke  SelectObject, [ebx+TContext.handle] ; from the stack.

        xor     eax, eax
        mov     edx, [.metrics.tmAscent]
        sub     edx, [.metrics.tmInternalLeading]

        stdcall FreeMem, [.pwc]
        pop     ecx ebx
        return
endp