Page 1 of 1

StartDrawing() in a loop

Posted: Wed Jan 12, 2011 2:07 am
by charvista
Hello all
When I perform a StartDrawing() .. StopDrawing() in a long loop, it takes a very loooong time.
Except when the window is hidden, then it is really instantaneous.
That's why we can create the window invisible, and when ready we show the window.
But how to draw fast when we are in the middle of the program?
If I hide the window temporarily like in the fragment below, there is an unpleasant flash, this is obvious.

Code: Select all

    HideWindow(Win,1)
    For i=1 To MadoH-9
        zDraw(ImA(),40,i+2,zNumMask(i,"##0"))
    Next i
    HideWindow(Win,0)   
Thanks,
Cheers.

Re: StartDrawing() in a loop

Posted: Wed Jan 12, 2011 3:45 am
by netmaestro
Firstly, there is nothing to be gained by making a post of this kind without runnable code that can be assessed for efficiency. StartDrawing() in and of itself uses less than one microsecond on the average processor. I have code with StartDrawing() -draw a lot of stuff- StopDrawing() in the WM_MOUSEMOVE event with no discernible delay. Download and run my maestro: a chess gui (wip) from Applications: Feedback for an example. The board and all pieces are being redrawn at every pixel of mousemove, yet the user would never guess it.

Re: StartDrawing() in a loop

Posted: Wed Jan 12, 2011 9:57 pm
by charvista
Netmaestro,
I understand that it is sometimes hard to understand without fully workable example. I hoped it was sufficient, but here I have worked for making an executable example.
You will notice that the two first columns are displayed in no time when the window was hidden.
The third and fourth columns are very slow. If you uncomment the HideWindow() lines, it will be fast, but a flash will occur. Do you have an idea how to circumvent this?
Thanks.
PS: please netmaestro, where is your chess gui that I can download and check its speed? EDIT: Found it. thanks!

Code: Select all

Global F13=LoadFont(#PB_Any,"DOSLike",14)
; The font DOSLike can be found at http://www.uwe-sieber.de/dosfon_e.html, go to the Download Section at the bottom of the page,
; and choose the ANSI Codepage 1252 which is DOSLike.ZIP or DOSLike.EXE. The font is not freeware, but you can download it and test it.

Global _ChrW=10; Chui Character Width (in pixels)
Global _ChrH=20; Chui Character Height (in pixels)
ExamineDesktops() ;necessary to get the Desktop width & height
Global _DW.i = DesktopWidth(0); Desktop resolution width
Global _DH.i = DesktopHeight(0); Desktop resolution height
Global _ChuiScreenW=Int(_DW/_ChrW); Chui Screen Width
Global _ChuiScreenH=Int(_DH/_ChrH); Chui Screen Height
Global _ChuiMadoX=2; X-pos (left margin) standard dialogue
Global _ChuiMadoY=1; Y-pos (top margin) standard dialogue
Global _ChuiMadoW=_ChuiScreenW-(2*_ChuiMadoX); Width standard dialogue
Global _ChuiMadoH=_ChuiScreenH-(2*_ChuiMadoY); Height standard dialogue

Procedure zChuiPrint(Array ImA.i(1),X.i,Y.i,String.s,BgColor.i=$000000,Color.i=$FFFFFF,Filling.s="")
    StartDrawing(ImageOutput(ImA(0)))
    DrawingMode(#PB_2DDrawing_Transparent)
    FrontColor(Color.i)
    DrawingFont(FontID(F13))
    Box(X*_ChrW,Y*_ChrH,Len(String.s+Filling.s)*_ChrW,_ChrH,BgColor.i)
    DrawText(X*_ChrW,Y*_ChrH,String.s+Filling.s)
    StopDrawing()
    SetGadgetState(ImA(1),ImageID(ImA(0)))
EndProcedure

Procedure.i zBaseImage(Array ImA.i(1),X.i,Y.i,W.i,H.i)
    ImCre=CreateImage(#PB_Any,W,H)
    ImGad=ImageGadget(#PB_Any,X,Y,W,H,ImageID(ImCre))
    DisableGadget(ImGad,#True)
    ;InvalidateRect_(GadgetID(ImGad),0,1)
    ImA(0)=ImCre
    ImA(1)=ImGad
    ImA(2)=X
    ImA(3)=Y
    ImA(4)=W
    ImA(5)=H
EndProcedure

Procedure zLoop(ParentWin)
    DisableWindow(ParentWin,1)
    Win=OpenWindow(#PB_Any,_ChuiMadoX*_ChrW-2,_ChuiMadoY*_ChrH-2,_ChuiMadoW*_ChrW+4,_ChuiMadoH*_ChrH+4,"",#PB_Window_BorderLess|#PB_Window_Invisible,WindowID(ParentWin))
    SetWindowColor(Win,$0000FF); this will make the red border
    Dim ImA(5)
    zBaseImage(ImA(),2,2,_ChuiMadoW*_ChrW,_ChuiMadoH*_ChrH)
    
    zChuiPrint(ImA(),9,1,"Fast")
    zChuiPrint(ImA(),19,1,"Fast")
    zChuiPrint(ImA(),29,1,"Slow")
    zChuiPrint(ImA(),39,1,"Slow")
    
    For i=1 To _ChuiMadoH-9; <============================== FAST ================
        zChuiPrint(ImA(),10,i+2,Str(i))
    Next i
    For i=1 To _ChuiMadoH-9; <============================== FAST ================
        zChuiPrint(ImA(),20,i+2,Str(i))
    Next i
    HideWindow(Win,0)
    
    ;HideWindow(Win,1); Comment this line and it will be slow <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
    For i=1 To _ChuiMadoH-9; <============================== SLOW ================
        zChuiPrint(ImA(),30,i+2,Str(i))
    Next i
    For i=1 To _ChuiMadoH-9; <============================== SLOW ================
        zChuiPrint(ImA(),40,i+2,Str(i))
    Next i
    ;HideWindow(Win,0); Comment this line and it will be slow <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
    
    zChuiPrint(ImA(),60,10,"Press ALT+F4 to close this window")
    
    Repeat : Until WaitWindowEvent()=#PB_Event_CloseWindow
    CloseWindow(Win)
    DisableWindow(ParentWin,0)
EndProcedure



MainWin=OpenWindow(#PB_Any,0,0,_DW,_DH,"",#PB_Window_BorderLess)
zLoop(Mainwin)
End


Re: StartDrawing() in a loop

Posted: Wed Jan 12, 2011 10:47 pm
by Trond
Place Start/StopDrawing() outside the loop.

Re: StartDrawing() in a loop

Posted: Wed Jan 12, 2011 11:07 pm
by charvista
This is precisely the problem, Trond.
zChuiPrint() is a procedure, which is called only when needed. When needed, it will start/stop drawing. If it is needed several times, like in a loop, it will start/stop many times, that's obvious. zChuiPrint() is like SetGadgetText(), you use it in your program only when necessary.
As hiding/unhiding the window is a fast way, I was wondering if we can "freeze" the window while waiting for the drawing be finished, in other words, a command like HideWindow() but that does NOT actually hide the window.

Re: StartDrawing() in a loop

Posted: Thu Jan 13, 2011 11:42 am
by Trond
If it is needed several times, like in a loop, it will start/stop many times, that's obvious.
So recode it so that this doesn't happen. You could use two procedures, one with start/stopdrawing() and one without.

Re: StartDrawing() in a loop

Posted: Thu Jan 13, 2011 12:24 pm
by charvista
Thank you Trond, that's a very clever solution! :o I was thinking and thinking for a possible way to speed up the display, but not thought about a nested procedure!
I was thinking of the opportunity of using CopyImage(), change it, then replace my image with the newly created one...
Okay, I am going to see tonight which one fits me the best.
Best regards,
Richard

Re: StartDrawing() in a loop

Posted: Thu Jan 13, 2011 12:33 pm
by C64
charvista wrote:I was wondering if we can "freeze" the window while waiting for the drawing be finished, in other words, a command like HideWindow() but that does NOT actually hide the window.
For Windows, try the LockWindowUpdate_() API command.

Re: StartDrawing() in a loop

Posted: Thu Jan 13, 2011 6:43 pm
by charvista
MAGICAL! C64, this solves very easily my problem! Thank you so much! :D
By inserting your API, the two slow columns became ultra fast.

Code: Select all

Global F13=LoadFont(#PB_Any,"DOSLike",14)
; The font DOSLike can be found at http://www.uwe-sieber.de/dosfon_e.html, go to the Download Section at the bottom of the page,
; and choose the ANSI Codepage 1252 which is DOSLike.ZIP or DOSLike.EXE. The font is not freeware, but you can download it and test it.

Global _ChrW=10; Chui Character Width (in pixels)
Global _ChrH=20; Chui Character Height (in pixels)
ExamineDesktops() ;necessary to get the Desktop width & height
Global _DW.i = DesktopWidth(0); Desktop resolution width
Global _DH.i = DesktopHeight(0); Desktop resolution height
Global _ChuiScreenW=Int(_DW/_ChrW); Chui Screen Width
Global _ChuiScreenH=Int(_DH/_ChrH); Chui Screen Height
Global _ChuiMadoX=2; X-pos (left margin) standard dialogue
Global _ChuiMadoY=1; Y-pos (top margin) standard dialogue
Global _ChuiMadoW=_ChuiScreenW-(2*_ChuiMadoX); Width standard dialogue
Global _ChuiMadoH=_ChuiScreenH-(2*_ChuiMadoY); Height standard dialogue

Procedure zChuiPrint(Array ImA.i(1),X.i,Y.i,String.s,BgColor.i=$000000,Color.i=$FFFFFF,Filling.s="")
    StartDrawing(ImageOutput(ImA(0)))
    DrawingMode(#PB_2DDrawing_Transparent)
    FrontColor(Color.i)
    DrawingFont(FontID(F13))
    Box(X*_ChrW,Y*_ChrH,Len(String.s+Filling.s)*_ChrW,_ChrH,BgColor.i)
    DrawText(X*_ChrW,Y*_ChrH,String.s+Filling.s)
    StopDrawing()
    SetGadgetState(ImA(1),ImageID(ImA(0)))
EndProcedure

Procedure.i zBaseImage(Array ImA.i(1),X.i,Y.i,W.i,H.i)
    ImCre=CreateImage(#PB_Any,W,H)
    ImGad=ImageGadget(#PB_Any,X,Y,W,H,ImageID(ImCre))
    DisableGadget(ImGad,#True)
    ;InvalidateRect_(GadgetID(ImGad),0,1)
    ImA(0)=ImCre
    ImA(1)=ImGad
    ImA(2)=X
    ImA(3)=Y
    ImA(4)=W
    ImA(5)=H
EndProcedure

Procedure zLoop(ParentWin)
    DisableWindow(ParentWin,1)
    Win=OpenWindow(#PB_Any,_ChuiMadoX*_ChrW-2,_ChuiMadoY*_ChrH-2,_ChuiMadoW*_ChrW+4,_ChuiMadoH*_ChrH+4,"",#PB_Window_BorderLess|#PB_Window_Invisible,WindowID(ParentWin))
    SetWindowColor(Win,$0000FF); this will make the red border
    Dim ImA(5)
    zBaseImage(ImA(),2,2,_ChuiMadoW*_ChrW,_ChuiMadoH*_ChrH)
    
    zChuiPrint(ImA(),9,1,"Fast")
    zChuiPrint(ImA(),19,1,"Fast")
    zChuiPrint(ImA(),29,1,"Slow")
    zChuiPrint(ImA(),39,1,"Slow")
    
    For i=1 To _ChuiMadoH-9; <============================== FAST ================
        zChuiPrint(ImA(),10,i+2,Str(i))
    Next i
    For i=1 To _ChuiMadoH-9; <============================== FAST ================
        zChuiPrint(ImA(),20,i+2,Str(i))
    Next i
    HideWindow(Win,0)
    
    LockWindowUpdate_(WindowID(Win)); disable window drawing - drawings will be done invisibly

    For i=1 To _ChuiMadoH-9; <============================== SLOW ================
        zChuiPrint(ImA(),30,i+2,Str(i))
    Next i
    For i=1 To _ChuiMadoH-9; <============================== SLOW ================
        zChuiPrint(ImA(),40,i+2,Str(i))
    Next i
    
    zChuiPrint(ImA(),60,10,"Press ALT+F4 to close this window")
    LockWindowUpdate_(0); enable window drawing back and shows what has been drawn in the meantime. Mandatory, otherwise you'll get in trouble! So, don't forget this!
    
    
    
    Repeat : Until WaitWindowEvent()=#PB_Event_CloseWindow
    CloseWindow(Win)
    DisableWindow(ParentWin,0)
EndProcedure



MainWin=OpenWindow(#PB_Any,0,0,_DW,_DH,"",#PB_Window_BorderLess)
zLoop(Mainwin)
End

As I'm Windows-only, it is perfect to me. It is not portable, I know. Those who need portability should try Trond's solution.

PB-Administrators, why not make of this a native command? You see that it is very useful to speed up drawings in some cases like mine, and PB is seemingly always seeking for highest speed, so a function like LockWindow(#Window, State) could be the ideal brother of HideWindow(#Window, State). True or not?

Re: StartDrawing() in a loop

Posted: Thu Jan 13, 2011 7:09 pm
by freak
charvista wrote:PB-Administrators, why not make of this a native command? You see that it is very useful to speed up drawings in some cases like mine, and PB is seemingly always seeking for highest speed, so a function like LockWindow(#Window, State) could be the ideal brother of HideWindow(#Window, State). True or not?
We are not adding it because this is not the purpose of the LockWindowUpdate API. If you want to get into it:
http://blogs.msdn.com/b/oldnewthing/arc ... 16211.aspx
http://blogs.msdn.com/b/oldnewthing/arc ... 26880.aspx
http://blogs.msdn.com/b/oldnewthing/arc ... 42084.aspx
http://blogs.msdn.com/b/oldnewthing/arc ... 47713.aspx

If you read all that, you will notice that your scenario above is exactly the wrong use of this function.

The redrawing of the window is not your real problem anyway. Your problem is that you call SetGadgetState() to refresh the ImageGadget content in a loop, which causes the window to be redrawn all the time. When you lock the window update, you speed up that redraw, but you still pay the price of the SetGadgetState() function, which is quite substantial if your images are 32bit (try that and you will see).

The solution to your problem is to _not_ call SetGadgetState() with images that the user will never see because you will change it again in the next iteration of your loop anyway. This is like painting your house in 10 different colors in a loop. The efficient solution is to paint only the final color ;)

Do your drawing loop, _then_ update the gadget. Don't update the gadget every time and then look for a solution to make this update not take effect (which is what the LockWindowUpdate does)

Re: StartDrawing() in a loop

Posted: Thu Jan 13, 2011 9:33 pm
by charvista
The efficient solution is to paint only the final color
Very true! :o
Interesting article on the blog. Understanding what a function exactly does, and be aware of the side effects is about the same as taking a medicine that seems to work, but which is not intended for this particular disease, and knowing the possible side effects leads to more thinking. Medical Doctors are in a way also programmers, and they need to know how the Nature has programmed all the ingredients and how the humans have changed their structure... An error can be fatal. Total failure. System crash. Blue screen. Or blue blood of the death.
Freak is right, calling SetGadgetState() for updating each element in the loop slows down enormously. In the code I removed the LockWindowUpdate_() and moved SetGadgetState() where it will be called only once. The result is also MAGICAL! And... it is portable!
In practice, I think I will now need to add a parameter to zChuiPrint() to either update the image or not. If I say no, it is my responsibility to ensure it will be done separately after the loop.
Thank you Freak.

PS: When you are sick, call the correct procedure from your own Memory so it can program (give orders to) the soldiers of your body (red/white cells, etc.) and you will feel already better. This is called auto-suggestion :wink:

Code: Select all

Global F13=LoadFont(#PB_Any,"DOSLike",14)
; The font DOSLike can be found at http://www.uwe-sieber.de/dosfon_e.html, go to the Download Section at the bottom of the page,
; and choose the ANSI Codepage 1252 which is DOSLike.ZIP or DOSLike.EXE. The font is not freeware, but you can download it and test it.

Global _ChrW=10; Chui Character Width (in pixels)
Global _ChrH=20; Chui Character Height (in pixels)
ExamineDesktops() ;necessary to get the Desktop width & height
Global _DW.i = DesktopWidth(0); Desktop resolution width
Global _DH.i = DesktopHeight(0); Desktop resolution height
Global _ChuiScreenW=Int(_DW/_ChrW); Chui Screen Width
Global _ChuiScreenH=Int(_DH/_ChrH); Chui Screen Height
Global _ChuiMadoX=2; X-pos (left margin) standard dialogue
Global _ChuiMadoY=1; Y-pos (top margin) standard dialogue
Global _ChuiMadoW=_ChuiScreenW-(2*_ChuiMadoX); Width standard dialogue
Global _ChuiMadoH=_ChuiScreenH-(2*_ChuiMadoY); Height standard dialogue

Procedure zChuiPrint(Array ImA.i(1),X.i,Y.i,String.s,BgColor.i=$000000,Color.i=$FFFFFF,Filling.s="")
    StartDrawing(ImageOutput(ImA(0)))
    DrawingMode(#PB_2DDrawing_Transparent)
    FrontColor(Color.i)
    DrawingFont(FontID(F13))
    Box(X*_ChrW,Y*_ChrH,Len(String.s+Filling.s)*_ChrW,_ChrH,BgColor.i)
    DrawText(X*_ChrW,Y*_ChrH,String.s+Filling.s)
    StopDrawing()
    ;SetGadgetState(ImA(1),ImageID(ImA(0)))
EndProcedure

Procedure.i zBaseImage(Array ImA.i(1),X.i,Y.i,W.i,H.i)
    ImCre=CreateImage(#PB_Any,W,H)
    ImGad=ImageGadget(#PB_Any,X,Y,W,H,ImageID(ImCre))
    DisableGadget(ImGad,#True)
    ;InvalidateRect_(GadgetID(ImGad),0,1)
    ImA(0)=ImCre
    ImA(1)=ImGad
    ImA(2)=X
    ImA(3)=Y
    ImA(4)=W
    ImA(5)=H
EndProcedure

Procedure zLoop(ParentWin)
    DisableWindow(ParentWin,1)
    Win=OpenWindow(#PB_Any,_ChuiMadoX*_ChrW-2,_ChuiMadoY*_ChrH-2,_ChuiMadoW*_ChrW+4,_ChuiMadoH*_ChrH+4,"",#PB_Window_BorderLess|#PB_Window_Invisible,WindowID(ParentWin))
    SetWindowColor(Win,$0000FF); this will make the red border
    Dim ImA(5)
    zBaseImage(ImA(),2,2,_ChuiMadoW*_ChrW,_ChuiMadoH*_ChrH)
    
    zChuiPrint(ImA(),9,1,"Fast")
    zChuiPrint(ImA(),19,1,"Fast")
    zChuiPrint(ImA(),29,1,"Slow")
    zChuiPrint(ImA(),39,1,"Slow")
    
    For i=1 To _ChuiMadoH-9; <============================== FAST ================
        zChuiPrint(ImA(),10,i+2,Str(i))
    Next i
    For i=1 To _ChuiMadoH-9; <============================== FAST ================
        zChuiPrint(ImA(),20,i+2,Str(i))
    Next i
    HideWindow(Win,0)
    


    For i=1 To _ChuiMadoH-9; <============================== SLOW ================
        zChuiPrint(ImA(),30,i+2,Str(i))
    Next i
    For i=1 To _ChuiMadoH-9; <============================== SLOW ================
        zChuiPrint(ImA(),40,i+2,Str(i))
    Next i
    
    zChuiPrint(ImA(),60,10,"Press ALT+F4 to close this window")
    SetGadgetState(ImA(1),ImageID(ImA(0))); following Freak's advice, I removed the LockWindowUpdate_() function, removed the SetGadgetState() in the zChuiPrint() procedure and added the SetGadgetState() here. What is the result? FAST!

    
    
    
    Repeat : Until WaitWindowEvent()=#PB_Event_CloseWindow
    CloseWindow(Win)
    DisableWindow(ParentWin,0)
EndProcedure



MainWin=OpenWindow(#PB_Any,0,0,_DW,_DH,"",#PB_Window_BorderLess)
zLoop(Mainwin)
End