Newsgroup: it.comp.lang.visual-basic
Titolo: [LUNGO] [API] Line vs LineTo

Salve a tutti!
Nel programma che sto scrivendo, carico dei dati da un file ASCII e poi li
grafico in un picture box.
Come mi fu consigliato qualche tempo fa (ciao Zanna!) ho provato a
modificare il grafico usando l'API LineTo (e la MoveToEx) al posto del
metodo line del pb.
Ho scritto una piccola routine per verificare l'efficienza dei due metodi a
confronto, e sul mio processore (Athlon 800) i tempi necessari a graficare
il tutto non sono molto diversi (si parla di un 15% di differenza a
vantaggio del LineTo). Sono io che implemento male il metodo di ridisegno, o
questi sono gli effettivi guadagni in termini di prestazioni?
Per sicurezza vi allego il codice della routine di prova.
'--------------------------------------------
Option Explicit
Private Type POINTAPI
        x As Long
        y As Long
End Type
Private Declare Function MoveToEx Lib "gdi32" (ByVal hdc As Long, ByVal x As
Long, ByVal y As Long, lpPoint As Any) As Long
Private Declare Function LineTo Lib "gdi32" (ByVal hdc As Long, ByVal x As
Long, ByVal y As Long) As Long
Private Declare Function timeGetTime Lib "winmm.dll" () As Long
Private Declare Function CreatePen Lib "gdi32" (ByVal nPenStyle As Long,
ByVal nWidth As Long, ByVal crColor As Long) As Long
Private Declare Function DeleteObject Lib "gdi32" (ByVal hObject As Long) As
Long
Private Declare Function SelectObject Lib "gdi32" (ByVal hdc As Long, ByVal
hObject As Long) As Long
Dim Dati() As POINTAPI, DatiLine() As POINTAPI
Dim dMIN#, dMAX#
Dim Tempo As Long, hRPen As Long
Const PS_SOLID = 0
Const PS_DASH = 1
Const PS_DOT = 2
Const PS_DASHDOT = 3
Const PS_DASHDOTDOT = 4
Const PS_NULL = 5
Const PS_INSIDEFRAME = 6
Sub DatiCasuali(nDati As Long, FM As Double, Tipo As Integer, vMax As Long,
vMin As Long, mSiNo As Boolean)
Dim i&, Dummy#, SpTraDati#, id As Long
Randomize
ReDim Dati(nDati), DatiLine(nDati)
SpTraDati = Form1.Picture1.ScaleWidth / nDati
For i = 0 To nDati
    Dati(i).x = i * SpTraDati
    Dati(i).y = (vMax - vMin) * Rnd + vMin
Next i
Dummy = Media(nDati)
dMIN = Dummy - Dati(0).y: dMAX = Dummy - Dati(0).y
For i = 0 To nDati
    Dati(i).y = Dummy - Dati(i).y
    If Dati(i).y <= dMIN Then dMIN = Dati(i).y
    If Dati(i).y >= dMAX Then dMAX = Dati(i).y
    DatiLine(i).x = Dati(i).x
    DatiLine(i).y = ((Dati(i).y * FM) + Form1.Picture1.ScaleHeight / 2)
    Dati(i).x = DatiLine(i).x / Screen.TwipsPerPixelX
    Dati(i).y = DatiLine(i).y / Screen.TwipsPerPixelY
Next i
Tempo = timeGetTime
Select Case Tipo
Case 0 'Line
    Form1.Picture1.ForeColor = vbBlue
    Form1.Picture1.DrawStyle = 0
    Form1.Picture1.DrawWidth = 3
    For i = 1 To nDati
        Form1.Picture1.Line (DatiLine(i - 1).x, DatiLine(i -
1).y)-(DatiLine(i).x, DatiLine(i).y)
    Next i
Case Else 'LineTo API
    hRPen = CreatePen(PS_SOLID, 3, vbBlue)
    DeleteObject SelectObject(Form1.Picture1.hdc, hRPen)
    MoveToEx Form1.Picture1.hdc, Dati(0).x, Dati(0).y, ByVal 0&
    For i = 0 To nDati
        LineTo Form1.Picture1.hdc, Dati(i).x, Dati(i).y
    Next i
End Select
If mSiNo Then MsgBox (timeGetTime - Tempo)
End Sub
Function Media(nDati As Long) As Double
Dim Dummy#, i&
Dummy# = 0
For i = 0 To nDati
    Dummy = Dummy + Dati(i).y
Next i
Media = Dummy / nDati
End Function
Private Sub Form_Load()
DatiCasuali 100000, 10, 2, 130, -130, True
'           ^       ^   ^  ^    ^     ^
'           |       |   |  |    |     Messaggio tempo impiegato(T/F)
'           |       |   |  |    Minimo
'           |       |   |  Massimo
'           |       |   0=Line; 1=LineTo API; Else=Polygon API
'           |       Fattore Moltiplicativo
'           N. punti graficati - nb. ne grafica (n+1)
End Sub
'--------------------------------------------
Perdonate per la lunghezza del testo.
Ogni idea e' bene accetta!
- SonicBoom -

    Next i
Non ho verificato, comunque prova così:
    Dim hDC As Long
    hDC = Form1.Picture1.hdc
    MoveToEx hDC, Dati(0).x, Dati(0).y, ByVal 0&
    For i = 0 To nDati
        LineTo hDC, Dati(i).x, Dati(i).y
    Next i
L'accesso a una proprietà semplice anzichè a una variabile è circa 300/400
volte più lento.
Questo non significa che il tuo prog avrà prestazioni 300 volte superiori
(probabilmente nemmeno 30), perchè queste dipendono da un mucchio di
fattori, però senz'altro aiuta.
Ciao


Ti ringrazio per la risposta. Un solo dubbio: non esistono metodi ancora
piu' rapidi? Avevo pensato ad una chiamata a Polygon, ma mi sembra che con
molti dati mandi in palla il computer... e parlo di WinXP, che non mi aveva
_MAI_ crashato!!!!
In ogni caso, grazie ancora... provero' e magari ti faccio sapere...
Ciao,
- SonicBoom -

Giusto per la precisione, ho ottenuto i seguenti risultati, graficando
100000 punti con un penna di spessore 3:
metodo line: 3200ms
LineTo come avevo postato io: 2350ms
LineTo con le correzioni di Zanna: 2200ms
Un secondo in meno rispetto al metodo line mi pare un buon risultato! Grazie
ancora.
- SonicBoom -

 che con molti dati mandi in palla il computer...
Io consiglio la PolyPolyLine. Ho provato sul tuo sorgente, ed è circa 7
volte + veloce del ciclo con la LineTo.
In effetti anche lei crasha il computer (io ho Win2000, e anche sul mio
portatile si freeza il SO), ma solo se setti il DrawWidth = 3. Essendo così
+ veloce, male che vada ridisegna la sequenza 3 volte con offset spostati di
un pixel, e saresti comunque sempre il doppio più veloce.
Devi dichiarare l'api in questione così:
Private Declare Function PolyPolyline Lib "gdi32.dll" (ByVal hdc As Long,
lppt As POINTAPI, lpdwPolyPoints As Long, ByVal cCount As Long) As Long
E poi nel tuo case, togli il Case Else dalla 'LineTo API e sostituiscilo con
un case 1. A questo punto aggiungi:
Case Else
   Dim NumPoints(0) As Long
   Dim retval As Long
   Form1.Picture1.ForeColor = vbBlue
   NumPoints(0) = nDati
   retval = PolyPolyline(Picture1.hdc, Dati(0), NumPoints(0), 1)
Ovviamente mi devi una birra. ;-)
Ciao
*Roby*

 Ovviamente mi devi una birra. ;-)
Naturalmente! :) Quando ti trovi a passare per Salerno? :) Oppure ci vediamo
da te? (di dove sei?)
Ciao,
- SonicBoom -

 Oppure ci vediamo da te? (di dove sei?)
Ehm. San Benedetto del Tronto. Non siamo proprio a due passi.
Vabbè, mi accontenterò di una birra virtuale:
http://www.virtualbeer.net/beer_get.cgi?drink=Budweiser
Ciao
*Roby*

> Comunque facci sapere se risulta più veloce anche a te.
Risultati Ufficiali Misurazione (RUM - che fa molto alkoolico):
Metodo Line -> 642ms
API LineTo -> 280,5ms
API LineTo con la modifica proposta da Zanna -> 283,6ms
API PolyPolyline proposta da Roby -> 43,2ms
API PolyLineTo -> 660ms
API Polygon -> 660ms
Le misurazioni sono state effettuate usando il mio codice con 100000 punti
casuali, ponendo in tutti i casi la width del line pari a 1 ed effettuando
una media su 10 misure (quindi creando ogni volta un nuovo set di dati). Mi
pare che non ci siano dubbi su quale metodo sia il "vincitore" della
competizione! :)
Qualche appunto a proposito dei vari metodi grafici (magari non frega niente
a nessuno, oppure sono cose gia' note ed io sto sfondando una serie di porte
aperte...):
1. l'API Polygon, come dice il nome, crea un poligono chiuso, quindi
generera' un'ulteriore linea per congiungere il primo con l'ultimo punto. Da
evitare come la peste se si vogliono graficare dei dati sperimentali!!!
2. La PolyLineTo e' del tutto inutile! Secondo la documentazione delle API
dovrebbe essere l'equivalente di un MoveToEx + LineTo, ma e' lento come il
metodo line... cui prodest?
3. Ovviamente, LineTo e' sempre preceduto da un MoveToEx.
4. [BUG?] Tutte le API Poly* crashano o freezano il sistema se usati in
congiunzione con DrawWidth>1, o con un numero di dati piuttosto "grande"
(con un milione di dati solo le API LineTo funzionano...)! Perche'?
Soprattutto mi secca l'inaffidabilita' con il DrawWidth! Nel mio programma
avrei bisogno di graficare linee di un certo "spessore"! :)
Con questo vi saluto (specialmente Zanna & Roby che hanno partecipato al
thread attivamente!)!
Al prossimo post,
- SonicBoom -

 che hanno partecipato al thread attivamente!)!
Ah, ho anche ricevuto la birra. Grazie. ;-)
Ciao
*Roby*

 Ah, ho anche ricevuto la birra. Grazie. ;-)
Dovere! ;) A quando una _reale_? :)
- SonicBoom -