;"Web" demo for KolibriOS, version 0.3
;Copyright Alexander Meshcheryakov (Self-Perfection), 2009
;Contact me: alexander.s.m@gmail.com
;distributed under BSD license

;Used assumptions:
;   1) Screen resolution does not change while app is running
;   2) Screen width bigger than height
;   3) Screen width and height are even (2*k)

include '../../../macros.inc'

background_cl = 0x000000
foreground_cl = 0xFFFFFF

delay = 4

; debug = 1

MEOS_APP_START

CODE
    ;Preinit. Randomize start counter
    mcall 3
    mov     [initial_counter], eax          ;init with system time

    ;Query screen size
    mcall   14
    add     eax, 0x00010001
    mov     dword [y_max], eax      ;store x_max and y_max
    shr     eax, 1
    mov     dword [radius], eax     ;store radius and x_center
    
    ;Calc line_number
    mov     ax, [y_max]
    mov     dx, 0
    mov     bx, 5
    div     bx
    mov     word [half_line_number], ax
    movzx   edx, ax         ;edx = half_line_number
    imul    edx, 2 * line_coords_element_size      ;Space needed for line_coords_array

    ;Demand memory
    mcall   68, 11      ; Init heap
    test    eax, eax    ; Is heap successfully inited?
    jnz     @f
    mcall   -1          ;   Netu pamjati?! Nu i nahuj vas
@@:
    movzx   ecx, [y_max]
    inc     ecx
    movzx   eax, [x_max]
    imul    ecx, eax        ;[Remember to left here space for 1-2 emergency lines at bottom]
    add     ecx, edx        ;And add space for line_coords_array
    mcall   68, 12,
    test    eax, eax    ; Did we get something non zero?
    jnz     @f
    mcall   -1
@@:
    mov     [line_coords_array_pointer], eax
    add     eax, edx
    mov     [image_pointer], eax


    call    clear_offscreen_bitmap



;Calc fixed line ends coords
    fninit
    fldpi
    fidiv word [half_line_number]    ;Now st0 contains angle step of line start points
    
    mov eax, [line_coords_array_pointer]          ;cleanup: comment
    movzx   ecx, word [half_line_number]
    shl     ecx, 1
    fld     st      ;skip zero angle to avoid 1px fixed line at right side


calculate_next_line_start_point:
    fld st

    ;Calculate line start points coords
    fsincos
    fimul [radius]
    fiadd [x_center]
    fistp word [eax+start_x_offset]
;     fchs                              ;affects direction, uncomment with corresponding line below
    fimul [radius]
    fiadd [radius]
    fistp word [eax+start_y_offset]

    ;Calculate line start point pointer
    movzx   ebx, word [eax+start_y_offset]
    movzx   edx, word [x_max]
    imul    ebx, edx
    movzx   edx, word [eax+start_x_offset]
    add     ebx, edx
    add     ebx, [image_pointer]

    mov     [eax+line_start_pointer_offset], ebx


    fadd st0, st1
    add eax, line_coords_element_size    ;Move to next element in line_coords_array

    loop calculate_next_line_start_point

    fstp    st0          ;drop current angle
    fidiv   [divider]   ;change angle step


draw_window:

;Start line coords calculation

    fld     st      ;skip zero angle to avoid 1px fixed line at right side

    ;Use time since start to get current counter value
    mcall   26, 9
    add     eax, [initial_counter]
    mov     [current_counter], eax

    mov eax, [line_coords_array_pointer]          ;cleanup: comment
    movzx   ecx, word [half_line_number]
    shl     ecx, 1

calculate_next_line_end_point:
    fld st

    ;Calculate line end points
    fimul [current_counter]
    fsincos
    fimul [radius]
    fiadd [x_center]
    fistp word [eax+2]
;     fchs                              ;affects direction, uncomment with corresponding line above
    fimul [radius]
    fiadd [radius]
    fistp word [eax+6]

    fadd st0, st1

    add eax, line_coords_element_size    ;Move to next element in line_coords_array
    loop calculate_next_line_end_point

    fstp    st0     ;drop current angle

    inc dword [initial_counter]


;   *********************************************
;   *******Draw lines on offscreen bitmap********
;   *********************************************

    ;draw end points
    movzx   edi, [half_line_number]
    shl     edi, 1
    mov esi, [line_coords_array_pointer]

  draw_next_line:

    if defined debug    ;Draw red points next to line ends in debug mode
        movzx   ebx, word [esi+start_y_offset]
        movzx   eax, word [x_max]
        imul    eax, ebx
        movzx   ebx, word [esi+start_x_offset]
        add     eax, ebx
        add     eax, dword [image_pointer]
        inc     eax
        mov     [eax], byte red_cl_index
        
        movzx   ebx, word [esi+end_y_offset]
        movzx   eax, word [x_max]
        imul    eax, ebx
        movzx   ebx, word [esi+end_x_offset]
        add     eax, ebx
        add     eax, dword [image_pointer]
        inc     eax                             ;Move one right to make more visible
        mov     [eax], byte red_cl_index
    end if


;Drawing lines. Need to keep esi and edi values in process

    mov     eax, [esi+line_start_pointer_offset]

  check_horizontal_line:

    mov     bx, word [esi+start_y_offset]
    cmp     bx, word [esi+end_y_offset]
    jnz     general_draw_line      ;Jump to next test if dy!=0
    
    pusha
    
    movzx   ecx, word [esi+end_x_offset]
    sub     cx, word [esi+start_x_offset]

    jnc     @f
    neg     cx
    sub     eax, ecx
  @@:

    cld
    inc     cx

    mov     edi, eax
    mov     al, foreground_cl_index
    rep stos byte [edi]

    popa

    jmp     line_drawing_end



    ;General line draw algorithm. Based on Bresenham's algorithm (below) but heavily optimized
;  function line(x0, x1, y0, y1)
;      boolean steep := abs(y1 - y0) > abs(x1 - x0)
;      if steep then
;          swap(x0, y0)
;          swap(x1, y1)
;      if x0 > x1 then
;          swap(x0, x1)
;          swap(y0, y1)
;      int deltax := x1 - x0
;      int deltay := abs(y1 - y0)
;      int error := deltax / 2
;      int ystep
;      int y := y0
;      if y0 < y1 then ystep := 1 else ystep := -1
;      for x from x0 to x1
;          if steep then plot(y,x) else plot(x,y)
;          error := error - deltay
;          if error < 0 then
;              y := y + ystep
;              error := error + deltax



general_draw_line:
    pusha

    ;init step_base and step_secondary
    mov     edx, esi
    mov     esi, 1
    movzx   edi, word [x_max]

    ;calc initial delta_base & delta_secondary values
    movzx   ebx, word [edx+end_x_offset]
    sub     bx, [edx+start_x_offset]
    jnc     @f
    neg     bx
    neg     esi
  @@:
    movzx   ecx, word [edx+end_y_offset]
    sub     cx, [edx+start_y_offset]
    jnc     @f
    neg     cx
    neg     edi
  @@:
    
    ;compare abs(y1 - y0) and abs(x1 - x0)
    cmp     bx, cx
    jnc     @f
    xchg    ebx, ecx
    xchg    esi, edi
  @@:


    shl     ebx, 16
    mov     bx, cx
    rol     ebx, 16
    mov     cx, bx      ;init counter
    inc     cx
    mov     dx, bx      ;init error
    shr     dx, 1
    rol     ebx, 16


;At current point:
;eax = current point pointer
;ebx = (delta_base shl 16) + delta_secondary
;ecx = counter
;[e]dx = error
;esi = step_base
;edi = step_secondary


  brasenham_plot_point:
    mov     byte [eax], foreground_cl_index
    add     eax, esi

    sub     dx, bx

    jnc     end_loop

;              y := y + ystep
;              error := error + deltax
  change_y:
    add     eax, edi
    rol     ebx, 16
    add     dx, bx
    rol     ebx, 16

  end_loop:
    loopw   brasenham_plot_point

    end_bresenham:

    popa

line_drawing_end:

    add esi, line_coords_element_size   ;Move to next element in line_coords_array
    dec edi
    jnz draw_next_line


;   *********************************************
;   *******  WINDOW DEFINITIONS AND DRAW ********
;   *********************************************
;     mcall 18, 14    ;Wait for scanning (it seems doesn't do any good now)

    ; start redraw  (without calling 12 proc our window overwrites window that above it)
    mcall   12, 1
    xor eax,eax
    movzx ebx, [x_max]
    movzx ecx, [y_max]
    mov edx, 0x01000000     ;Window style       ;Draw nothing
;     mov edx, 0x00000000     ;Window style 
;     mov esi, 0x00000000     ;Header color (prevent odd color line on top of window in random cases)
    mcall           ;Define window


    mov ebp, 0
    mov ecx, dword [y_max]
    mcall 65, [image_pointer], , <0,0>, 8, palette


    mcall   12, 2       ; end redraw


    call    clear_offscreen_bitmap

wait_event:
    mcall 23, delay
;     mcall 10
    test eax, 0xFFFF - 1    ;Test for 0 (delay passed) or 1 (redraw) event
    jz  draw_window     ; Delay passed or redraw event
    dec eax
    dec eax
    jnz  exit           ; If not key then Alt+F4
; key pressed, read it and ignore
    mcall   2
    cmp eax, 0x1B00        ;Test for Escape key press
    jnz wait_event

; button pressed; we have only one button, close
; also seems to handle Alt+F4
exit:
    mcall   -1


clear_offscreen_bitmap:
    mov edi, [image_pointer]
    movzx   ecx, [y_max]
    movzx   eax, [x_max]
    imul    ecx, eax
    shr     ecx, 2      ;dword is 4 bytes
    mov eax, 0
    rep stos dword [edi]
    ret


DATA
divider             dw 5000

palette:
    background_cl_index = 0
    dd      background_cl
    foreground_cl_index = 1
    dd      foreground_cl

    if defined debug
        red_cl_index = 2
        dd      0x00FF0000          ;Cleanup this!
    end if

UDATA
image_pointer       dd ?

initial_counter     dd ?
current_counter     dd ?        ;counter + current time

half_line_number    dw ?

y_max            dw ?     ; screen size
x_max            dw ?

radius          dw ?
x_center        dw ?

line_coords_array_pointer       dd ?

; line_coords_array:
;     repeat 1000 ;line_number
;         dw      ?       ;start_x
;         dw      ?       ;end_x
;         dw      ?       ;start_y
;         dw      ?       ;end_y
;         dd      ?       ;line_start_pointer
;     end repeat

    start_x_offset = 0
    end_x_offset = 2
    start_y_offset = 4
    end_y_offset = 6
    line_start_pointer_offset = 8
    line_coords_element_size = 12

MEOS_APP_END