; _______________________________________________________________________________________
;|                                                                                       |
;| ..::FreshLib::..  Free, open source. Licensed under "BSD 2-clause" license."          |
;|_______________________________________________________________________________________|
;
;  Description: Memory based images manipulation library.
;
;  Target OS: Any
;
;  Dependencies:
;
;  Notes: Include respective OS dependent files.
;_________________________________________________________________________________________
module "Images library"



; Creates memory image with parameters specified.
interface CreateImage, .width, .height


interface DestroyImage, .ptrImage


; Draws the image on a OS provided window surface.
interface DrawImageRect, .where, .pImage, .xDst, .yDst, .xSrc, .ySrc, .width, .height



proc LockImg, .pImage
begin
        push    eax

;        mov     eax, [.pImage]
;        lea     eax, [eax+TImage.lock]
;        stdcall WaitForMutex, eax, 100
;        jc      .error

        clc
        pop     eax
        return

.error:
        DebugMsg "Unable to lock image!"
        add      esp, 4
        return
endp


proc UnlockImg, .pImage
begin
;        push    eax
;        mov     eax, [.pImage]
;        lea     eax, [eax+TImage.lock]
;        stdcall MutexRelease, eax
;        pop     eax
        return
endp



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

        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]


; lock the images.
        stdcall LockImg, esi
        jc      .finish

        stdcall LockImg, edi
        jc      .finish2

; start the operation

        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, 8                                         ; mm1 = 00ff 00ff 00ff 00ff

;        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.

        movd    mm7, [ecx+4*ebx]        ; source pixel.
        movd    mm6, [edx+4*ebx]        ; destination pixel.

        movq    mm5, mm7
        psrlq   mm5, 24                 ; 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)

        punpcklbw mm6, mm0              ; byte to word destination pixel
        punpcklbw mm7, mm0              ; byte to word source pixel
        psllw     mm7, 8                ; Cs = Cs*256

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

        packuswb mm6, mm0               ; pack words to bytes 0A 0R 0G 0B -> 0000ARGB
        movd     [edx+4*ebx], mm6

        jnz     .loopX

.nextY:
        add     ecx, esi
        add     edx, edi

        dec     [.srect.bottom]
        jnz     .loopY

        emms

; unlock the images.
        stdcall UnlockImg, [.pDstImage]

.finish2:
        stdcall UnlockImg, [.pSrcImage]

.finish:
        popad
        return
endp






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

        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]

        stdcall LockImg, esi
        jc      .finish

        stdcall LockImg, edi
        jc      .finish2

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


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

; copy 1 dword

        dec     ebx
        mov     eax, [ecx+4*ebx]
        mov     [edx+4*ebx], eax
        jz      .nextY

.loopX:
        sub     ebx, 2

        movq    mm0, [ecx+4*ebx]
        movq    [edx+4*ebx], mm0

        jnz     .loopX

.nextY:
        add     ecx, esi
        add     edx, edi

        dec     [.srect.bottom]
        jnz     .loopY

        emms
        stdcall UnlockImg, [.pDstImage]

.finish2:
        stdcall UnlockImg, [.pSrcImage]

.finish:
        popad
        return
endp



;proc ImageMulAlpha2, .pImage, .x, .y, .width, .height
;.drect RECT
;.rect  RECT
;begin
;        pushad
;
;        mov     edi, [.pImage]
;
;        xor     eax, eax
;        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, [.x]
;        mov     edx, [.y]
;
;        mov     [.rect.left], ecx
;        mov     [.rect.top], edx
;
;        add     ecx, [.width]
;        add     edx, [.height]
;
;        mov     [.rect.right], ecx
;        mov     [.rect.bottom], edx
;
;        lea     ecx, [.drect]
;        lea     edx, [.rect]
;        stdcall RectIntersect, ecx, ecx, edx
;        jc      .finish
;
;        mov     ecx, [.drect.left]
;        mov     edx, [.drect.top]
;        sub     [.drect.right], ecx
;        sub     [.drect.bottom], edx
;
;        mov     esi, [.drect.top]
;        imul    esi, [edi+TImage.width]
;
;        add     esi, [.drect.left]
;        lea     esi, [4*esi]
;        add     esi, [edi+TImage.pPixels]
;
;        mov     edi, [edi+TImage.width]
;        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
;        psllq   mm1, 48         ; mm1 = 0100 0000 0000 0000
;
;.loopY:
;        mov     ebx, [.drect.right]     ; the width of the source rectangle
;
;.loopX:
;        dec     ebx                     ; The ZF will not be changed later in the inner loop.
;
;        movd    mm6, [esi+4*ebx]        ; destination pixel.
;
;        movq    mm5, mm6
;        psrlq   mm5, 24                 ; alpha
;
;        punpckldq mm5, mm5              ;
;        punpcklbw mm6, mm0              ; byte to word destination pixel
;        packssdw  mm5, mm5              ; mm5 = Ad, Ad, Ad, Ad
;
;        psrlq   mm5, 16                 ; mm5 = 0  Ad Ad Ad
;        por     mm5, mm1                ; mm5 = $100 Ad Ad Ad
;
;        pmullw  mm6, mm5                ; mm6 = Cd = Cd * Ad   (For alpha Ad = Ad * 256)
;        psrlw   mm6, 8                  ; Cd = Cd/256          (For alpha Ad = Ad)
;
;        packuswb mm6, mm0               ; pack words to bytes 0A 0R 0G 0B -> 0000ARGB
;        movd     [esi+4*ebx], mm6
;
;        jnz     .loopX
;
;.nextY:
;        add     esi, edi
;
;        dec     [.drect.bottom]
;        jnz     .loopY
;
;.finish:
;        emms
;        popad
;        return
;endp





include '%TargetOS%/images.asm'

endmodule