BBC BASIC
Programming >> Graphics and Games >> Using GDIP with assembler
http://bbcbasic.conforums.com/index.cgi?board=graphics&action=display&num=1508893577

Using GDIP with assembler
Post by svein on Oct 25th, 2017, 01:06am

Hello Richard.
I hope you can help with a little problem.

It's about drawing a line in GDI+ from assembler code.

In the supplied demo, two figures are drawn, one by assembler and one by the regular library call, using the same pen for both.
With a width of one, they look the same, but as the width increases, the assembler drawn figure seems to use a different pen style.
Any idea why ?

The @memhdc% parameter causes a crash if included in the assembler call to 'GdipDrawLine'.
Any idea why ?

In fact, one can remove or replace @memhdc% in SYS 'GdipDrawLine' .... from the library.
And it still works, how come ??

This is for windows 10.
Svein

Code:
      INSTALL @lib$+"GDIPLIB" : PROC_gdipinit      ON CLOSE PROCexit : QUIT      Pen%=FN_gdipcreatepen(&FF000000,0,1)      PROC_asm : OFF      ON TIME PROCnewpen : RETURN      *REFRESH OFF      REPEAT : CLS        Posx=500 : Posy=500        A%+=1 : Angle=A%        FOR I%=1 TO 12          Angle+=30          FOR J%=1 TO 6            Posx+=100*SINRAD(Angle)            Posy+=100*COSRAD(Angle)            CALL antiline            PROC_gdipline(Pen%,oldx+400,oldy,Posx+400,Posy)            oldx=Posx : oldy=Posy            Angle-=60          NEXT        NEXT        *REFRESH        WAIT 0      UNTIL 0      DEF PROCnewpen      PRIVATE width,adj      IF width=0 THEN width=2 : adj=0.25      width+=adj      IF width>5 OR width<1 THEN adj=-adj      PROC_gdipdeletepen(Pen%)      Pen%=FN_gdipcreatepen(&FF204080,0,width)      ENDPROC      DEF PROC_gdipline(P%, x1, y1, x2, y2)      LOCAL rc{}      DIM rc{l%,t%,r%,b%}      PROC_bbc2api(x1,y1)      PROC_bbc2api(x2,y2)      SYS "SetBoundsRect", @memhdc%, 0, 5      SYS `GdipDrawLine`, FN_gdipg, P%, FN_f4(x1), FN_f4(y1), FN_f4(x2), FN_f4(y2), @memhdc%      SYS "GetBoundsRect", @memhdc%, rc{}, 0      SYS "OffsetRect", rc{}, -@ox%, -@oy%      SYS "InvalidateRect", @hwnd%, rc{}, 0      ENDPROC      DEF PROC_asm      LOCAL var%,code%,pass%,P%,L%,size%      size%=2048      DIM var% NOTEND AND 3, var% 1023      DIM code% NOTEND AND 2047, code% size%-1      FOR pass%=8 TO 10 STEP 2        P%=var% : L%=code%-1        [OPT pass%        .temprc        dd 0 : dd 0 : dd 0 : dd 0        .tempx        dd 0        .tempy        dd 0        .grhdc        dd 0        .two        dd 2        ]          P%=code% : L%=code%+size%-1        [OPT pass%        .antiline        cld        finit        ;SYS "SetBoundsRect", @memhdc%, 0, 5        push 5 ; DCB_ENABLE OR DCB_RESET        push 0        push @memhdc%        call "SetBoundsRect"        ;        ;SYS `GdipDrawLine`, FN_gdipg, P%, FN_f4(x1), FN_f4(y1), FN_f4(x2), FN_f4(y2), @memhdc%        ; push @memhdc%        fld tbyte [^Posy] : call y2api : fstp dword [tempy] : push [tempy]  ;real80 to real32 for gdi+        fld tbyte [^Posx] : call x2api : fstp dword [tempx] : push [tempx]        fld tbyte [^oldy] : call y2api : fstp dword [tempy] : push [tempy]        fld tbyte [^oldx] : call x2api : fstp dword [tempx] : push [tempx]        push Pen%        push [grhdc]        call `GdipDrawLine`        ;        ;SYS "GetBoundsRect", @memhdc%, rc{}, 0        push 0        push temprc        push @memhdc%        call "GetBoundsRect"        ;        ;SYS "OffsetRect", rc{}, -@ox%, -@oy%        push -@oy%        push -@ox%        push temprc        call "OffsetRect"        ;        ;SYS "InvalidateRect", @hwnd%, rc{}, 0        push 0        push temprc        push @hwnd%        call "InvalidateRect"        ret        ;        .y2api        ;y=@vdu%!212-(y+@vdu%!4)/2        fiadd dword [@vdu%+4]        fidiv dword [two]        fild dword [@vdu%+212]        fsub st0,st1        ret        ;        .x2api        ;x=(x+@vdu%!0)/2        fiadd dword [@vdu%]        fidiv dword [two]        ret        ]      NEXT pass%      !grhdc=FN_gdipg      ENDPROC      DEF PROCexit      ON TIME OFF : WAIT 30      PROC_gdipdeletepen(Pen%)      PROC_gdipexit      ENDPROC 

Re: Using GDIP with assembler
Post by Richard Russell on Oct 25th, 2017, 10:20am

on Oct 25th, 2017, 01:06am, svein wrote:
Any idea why ?

As far as I can see, there are two principal faults in your code:
  1. Pen% is a variable (it's modified in PROCnewpen) but your assembler code treats it as a constant. To fix this:

    Change this Code:
            push Pen% 

    To this Code:
            push [^Pen%] 

  2. Your y2api subroutine corrupts the floating-point stack (there are more 'pushes' than 'pops'). To fix this:

    Change this Code:
            fsub st0,st1 

    To this Code:
            fsubr 
Quote:
In fact, one can remove or replace @memhdc% in SYS 'GdipDrawLine' .... from the library. And it still works, how come ??

It doesn't still work, or at least not reliably. The @memhdc% activates the Critical Section that protects against concurrent access to the DC from two different threads (Windows DCs are not thread-safe and must be accessed from only one thread at a time). If you omit the @memhdc% you are trusting to luck that the interpreter thread and the GUI thread will not try to access the DC at the same time, which would fail.

Richard.

Re: Using GDIP with assembler
Post by svein on Oct 26th, 2017, 06:59am

Thank you !
Went over the code a million times without spotting the errors.
Svein