Questo sito utilizza cookies solo per scopi di autenticazione sul sito e nient'altro. Nessuna informazione personale viene tracciata. Leggi l'informativa sui cookies.
Username: Password: oppure
Windows - Linee semitrasparenti
Forum - Windows - Linee semitrasparenti "difettose" con GDI+

Avatar
()
Newbie


Messaggi:
Iscritto:

Segnala al moderatore
Postato alle 1:00
Giovedì, 01/01/1970
Avrei un quesito per chi si fosse già trovato a dover risolvere questo "rompicapo". Vediamo se riesco a spiegarmi a dovere...

Sto tentando di realizzare un programmino di grafica nel quale mi piacerebbe inserire una funzione che impieghi una "penna" semitrasparente. Il programmino, realizzato in C (con sprazzi di C++) sfruttando le API di GDI/GDI+, è già a buon punto e funziona perfettamente con penne opache. Il problema sorge quando cerco di usare penne semitrasparenti.

Il tracciamento avviene memorizzando in una variabile statica la posizione del mouse in un dato momento, quindi usando la funzione membro LineTo() di un oggetto Pen per unire il punto memorizzato alla posizione corrente del cursore. Tutto bene, se non che con la penna semitrasparente ogni punto viene inevitabilmente tracciato due volte provocando uno scurimento del tratto che assume l'aspetto di una serie di "pallini" in corrispondenza dei punti occupati dal cursore nel tempo.

Un esempio? Poniamo che il primo punto sia in 5,3 e il secondo in 27,41. La penna traccia la riga semitrasparente da 5,3 a 27,41 esattamente come ci si aspetta, con un bel tratto uniforme. Nel frattempo, il mouse è stato spostato in 52,60. La penna, diligentemente traccia un secondo tratto che unisce 27,41 a 52,60. Ne consegue che nel punto 27,41 sono state tracciate due parti di linea, una che appartiene al primo passaggio e l'altra che appartiene al secondo. Effetto, le opacità parziali della penna si sommano in 27,41 dando un'opacità doppia solo in quel punto. Ovviamente la cosa si ripete ad ogni spostamento del mouse.

Ho pensato e provato alcune soluzioni per aggirare l'ostacolo, alcune decisamente cervellotiche, ma nessuna si è dimostrata efficace alla prova dei fatti. C'è qualche esperto, qui, che mi sa dare qualche dritta a prova di imbecille? (e che vuole farlo, ovviamente)

PM Quote
Avatar
mattia1481 (Member)
Pro


Messaggi: 84
Iscritto: 03/11/2008

Segnala al moderatore
Postato alle 8:14
Giovedì, 03/07/2014
Il problema non è da cercarsi nel grado di opacità della penna, sicuramente si verifica anche nella condizione di completa opacità, è che in tal caso non ti accorgi della sovrapposizione del colore.
Probabilmente l'errore sta nell'utilizzo non corretto della funzione LineTo(), ma per confermartelo averei bisogno di vedere la parte di codice in questione.

Buona giornata.

Ciao.

PM Quote
Avatar
()
Newbie


Messaggi:
Iscritto:

Segnala al moderatore
Postato alle 1:52
Venerdì, 04/07/2014
Innanzi tutto, grazie per esserti preso la briga di rispondermi.

La funzione di tracciamento è banale (probabilmente più del dovuto):

Codice sorgente - presumibilmente C++

  1. void Traccia( LONG x, LONG y ) {
  2.     if( !g.gomma ) {
  3.         // scrive
  4.         g.gfx->DrawLine( g.penna, (INT)g.exPos.x, (INT)g.exPos.y, (INT)x, (INT)y );
  5.     }
  6.     else {
  7.         // cancella
  8.         g.gfx->DrawLine( g.penGomma, (INT)g.exPos.x, (INT)g.exPos.y, (INT)x, (INT)y );
  9.     }
  10.  
  11.     g.os->aggiorna(); // trasferisce la grafica dal buffer offscreen al video
  12.  
  13.     // memorizza la posizione del mouse appena trattata
  14.     // per usarla nella prossima chiamata a Traccia()
  15.     g.exPos.x = x;
  16.     g.exPos.y = y;
  17. }



Appare evidente come, pirla che sono, nel mio messaggio precedente ho chiamato LineTo() la funzione membro di GDI+ che invece si chiama DrawLine() (andando a memoria, mi sono evidentemente confuso con il GDI "ordinario").

Il colore e la trasparenza della penna vengono impostati in un'altra funzione, "attivata" tramite una apposita finestra di dialogo. Comunque è vero quel che dici: il procedimento è identico sia con la penna completamente opaca sia con la penna parzialmente trasparente, però con la penna opaca l'effetto di sovrapposizione non è visibile perché il nuovo tratto copre completamente quello vecchio nell'area dove avviene la sovrapposizione.

PM Quote
Avatar
mattia1481 (Member)
Pro


Messaggi: 84
Iscritto: 03/11/2008

Segnala al moderatore
Postato alle 11:35
Venerdì, 04/07/2014
Prova a rimuovere la chiamata alla procedura g.os->aggiorna(), se la tua procedura Traccia (come suppongo) è invocata dall'evento Paint di un controllo/form non necessita di richiedere l'invalidamento del video, credo sia questo il problema, la chiamata a g.os->aggiorna() fa si che venga richiamata nuovamente la tua procedura Traccia e la conseguente sovrapposizione delle linee.

Prova, poi fammi sapere, diversamente bisogna spulciare il resto del codice, in particolar modo quello che invoca la procedura Traccia.

Ciao.

PM Quote
Avatar
()
Newbie


Messaggi:
Iscritto:

Segnala al moderatore
Postato alle 22:33
Domenica, 06/07/2014
Traccia() risponde agli eventi WM_LBUTTONDOWN, WM_MOUSEMOVE e WM_LBUTTONUP nella loro combinazione più "classica" abbinata ad una comune finestra WS_POPUP creata con CreateWindowEx(), non a un controllo nè a un "form":

Codice sorgente - presumibilmente Windows

  1. LRESULT CALLBACK MainWndProc( HWND hwnd, UINT msg, WPARAM wPar, LPARAM lPar ) {
  2.     UINT errore;
  3.     POINT pt;
  4.  
  5.     switch( msg ) {
  6.        
  7.         // [...] altri eventi rimossi per brevita'
  8.  
  9.         case WM_LBUTTONDOWN:
  10.             pt.x = LOWORD(lPar);
  11.             pt.y = HIWORD(lPar);
  12.  
  13.             if( PtInRect(&g.rDisegno,pt) ) {
  14.                 SetCapture( hwnd );
  15.                 g.exPos = pt;
  16.  
  17.                 if( !g.gomma )
  18.                     g.scrivi = kScrivi;
  19.                 else g.scrivi = kCancella;
  20.  
  21.                 Traccia( pt.x, pt.y );
  22.             }
  23.             break;
  24.  
  25.         case WM_MOUSEMOVE:
  26.             if( g.scrivi != kNonScrivere ) {
  27.                 SHORT x = LOWORD(lPar);
  28.                 SHORT y = HIWORD(lPar);
  29.                 Traccia( x, y );
  30.             }
  31.             break;
  32.  
  33.         case WM_LBUTTONUP:
  34.             g.exPos.x = g.exPos.y = 0;
  35.             g.scrivi = kNonScrivere;
  36.             ReleaseCapture();
  37.             break;
  38.                
  39.         // [...] altri eventi rimossi per brevita'
  40.  
  41.         default:
  42.             return DefWindowProc( hwnd, msg, wPar, lPar );
  43.     }
  44.  
  45.     return 0;
  46. }




Nella funzione Traccia(), g.gfx è un oggetto della classe Graphics di GDI+, creato a partire da un Device Context in memoria abbinato a una HBITMAP che funge da buffer (offscreen). Dunque, g.gfx scrive in una HBITMAP offscreen, non sullo schermo, e serve solo ed unicamente per poter usufruire degli antialias e delle trasparenze tipiche di GDI+ (il GDI "puro", come ben sai, non ha nè trasparenze nè antialias).

La HBITMAP e l'HDC dell'offscreen sono a loro volta gestiti da g.os, che è un oggetto d'una classe autocostruita OFFSCREEN. La sua funzione membro aggiorna() "trascrive" la bitmap del buffer offscreen sul video con il comunissimo BitBlt() tra il DC dell'offscreen e il DC dello schermo.

Se tolgo g.os->aggiorna(), l'immagine nel buffer resta nel buffer e lo schermo non viene mai aggiornato, per cui si trascina il mouse di qua e di là senza che si veda niente (i disegni finiscono nel buffer offscreen e lì rimangono fino al primo evento di tipo WM_UPDATE, quando viene chiamata g.os->mostra()).

Tutto questo per dire che il problema non è nella classe OFFSCREEN e nel suo metodo aggiorna().

Per rendere più chiaro il difetto che incontro, allego uno screenshot in png.


ha allegato un file: screenshot.png (93628 bytes)
Clicca qui per guardare l'immagine
PM Quote
Avatar
mattia1481 (Member)
Pro


Messaggi: 84
Iscritto: 03/11/2008

Segnala al moderatore
Postato alle 8:27
Lunedì, 07/07/2014
A livello di MainWndProc non v'è alcuna differenza tra quella che tu chiami finestra di popup e un controllo/form ... comunque: il tuo problema è che la procedura LineTo o DrawLine riprende a disegnare dall'ultimo punto da cui si è interrotta, sovrascivendo appunto quest'ultimo punto.
Il problema lo puoi risolvere aggiungendo ad una matrice di punti (ogni qual volta si verificano i messaggi WM_LBUTTONDOWN e WM_MOUSEMOVE) il valore della posizione del mouse e allo stesso tempo invocare una procedura (che svilupperai tu) che crea un percorso grafico (costruito sui punti della matrice) e lo riempe con la procedura FillPath (a tal proposito guarda qui http://msdn.microsoft.com/en-us/library/windows/desktop/ms ....

Buon lavoro.

Ciao.

Ultima modifica effettuata da mattia1481 il 07/07/2014 alle 9:55
PM Quote
Avatar
()
Newbie


Messaggi:
Iscritto:

Segnala al moderatore
Postato alle 12:21
Lunedì, 07/07/2014
Grazie per il fatto che continui a rispondermi con una bella pazienza! La cosa non è comune e si fa piacevolmente notare.

In effetti sia in GDI che in GDI+ esistono funzioni che fanno quel che di ci tu, ricevendo in ingresso un array di punti e tracciando un'unica linea che li tocca tutti (tra l'altro, non limitandosi alle linee rette, perché volendo sono disponibili anche diverse opzioni per ottenere curve di vario genere). Ma... c'è un ma (e ti pareva!).

Se muovo il cursore memorizzando i punti che tocca in un array (fattibilissimo e neppure tanto difficile) posso alla fine del "tratto" tracciare l'intera figura che ne deriva, ma non riesco a immaginare come offrire un supporto visuale adeguato nel corso del tracciamento. In altre parole, l'utente farebbe click (WM_LBUTTONDOWN), trascinerebbe in qua e in là il mouse (WM_MOUSEMOVE) senza vedere alcun segno sullo schermo (mentre vengono memorizzati i punti nell'array) fino al momento in cui, rilasciando il pulsante del mouse (WM_LBUTTONUP), verrebbe invocata la funzione per il tracciamento dell'intero tratto. Insomma, non riesco a immaginare come avere l'effetto ottimo delle funzioni che suggerisci E, nello stesso tempo, il dovuto "feedback" in tempo reale.

Immagino che con questa serie di difficoltà farò la figura dello sprovveduto, ma non me ne lagno, perché quando si parla di programmare SONO uno sprovveduto!! :)

Se hai qualche idea e hai voglia di darmi spunto, apprezzo. In caso contrario, apprezzo lo stesso per il tempo e l'attenzione che mi hai già dedicato.

PM Quote
Avatar
mattia1481 (Member)
Pro


Messaggi: 84
Iscritto: 03/11/2008

Segnala al moderatore
Postato alle 12:59
Lunedì, 07/07/2014
La soluzione te l'ho già scritta, comunque te la ripeto: invoca la tua procedura Traccia anche durante il messaggio WM_MOUSEMOVE.

Credo che su questo argomento abbiamo scritto abbastanza, ora sta a te sperimentare.

Buon lavoro, Ciao!

PM Quote
Avatar
()
Newbie


Messaggi:
Iscritto:

Segnala al moderatore
Postato alle 20:06
Lunedì, 07/07/2014
Sì, concordo. Grazie ancora per la disponibilità.

PM Quote