View Full Version : [JAVA] paintComponent() repaint() e prestazioni grafiche
JohnMarston
21-03-2016, 12:54
Ciao!!! Ho un pannello che disegna un piano cartesiano. Oltre a questo esistono dei metodi che, ad ogni evento, chiamano il repaint(). Gli eventi sono molto frequenti. Immaginate una enorme serie di dati, oggetti grafici ed ogni volta il repaint cancella e ridisegna tutto. Ovviamente questo crea dei rallentamenti non indifferenti.
Prima disegnavo tutto su paintComponent(). Faceva il suo lavoro, però pensando di migliorare il codice e la relativa prestazione grafica ho creato della classi a parte che si occupano di disegnare ogni singolo oggetto attraverso il bufferedImage che richiamo nel paintComponent() del pannello.
Con mio stupore, ho peggiorato le cose di parecchio. Senza ombra di dubbio ho sbagliato completamente la logica di rappresentazione grafica. Come posso fare per migliorare la performance grafica?
Grazie!
banryu79
21-03-2016, 14:37
Due utili risorse per dipanare tutti i dubbi:
Painting in AWT and Swing (http://www.oracle.com/technetwork/java/painting-140037.html#smart)
Lesson: Performing Custom Painting (http://docs.oracle.com/javase/tutorial/uiswing/painting/index.html)
JohnMarston
21-03-2016, 15:14
Piccolo aggiornamento. Ho provato a dividere gli oggetti per classi con estensione jPanel per aggiungerli al costruttore del pannello principale. Ovviamente li sto sovrapponendo con sfondo trasparente. Un miglioramento c'è stato, ma sto notando una cosa. Il metodo repaint() richiamato dalla classe principale, ridisegna completamente tutto (pannelli e oggetti sovrapposti compresi). A me non interessa cancellare e ridisegnare gli oggetti che ho già disegnato, perché devo modificare la graficazione di alcuni oggetti. Quindi è un lavoro inutile e volevo evitarlo! Perché sostanzialmente mi rallenta.
Cosa devo fare per far disegnare solo alcuni oggetti ed evitare che quelli già disegnati vengano cancellati e nuovamente ridisegnati? A questo punto credo che repaint non vada bene... :mc:
banryu79
21-03-2016, 16:12
Ti posto il link a un vecchio thread dove sono stati individuati due problemi nell'implementazione del metodo paintComponent di un'utente poco che tentava di fare custom painting in Swing/AWT (controlla anche tu se nel tuo paintComponent fai qualcosa di "strano"):
[JAVA] Dubbi su PaintComponent (http://www.hwupgrade.it/forum/showthread.php?t=2173165)
Per il resto, è difficile risponderti nel merito senza vedere il codice.
JohnMarston
22-03-2016, 08:52
Bravo! Hai centrato in pieno il mio problema! Per l'esattezza questo:
Meravigliam tutto sembrava funzionare, e in effetti funziona. Ho trovato dei limiti che credo insormontabili, nel senso che per gestire le immagini che voglio visualizzare all'interno del jPanel è stato necessario definire parecchie variabili globali che ne controllano i parametri e all'interno della PaintComponent realizzare i disegni in base a questi parametri.
Infatti... PaintComponent ridisegna continuamente e completamente l'area e non solo "a bisogno" e questo porta la CPU al 100%.
L'unica differenza è che l'utente usa un JComponent per disegnare mentre io sto usando un JPanel. In questo caso, quale dei due offre migliori prestazioni? Considera che i pannelli devono sovrapporsi in maniera trasparente questo per permettere di vedere le altre immagini... Con un JComponent si può fare??
Nel frattempo faccio una ricerca per capire meglio come implementare il JComponent. Ottimo anche il suggerimento di PGI-Bis sul passaggio di riferimento... Non ci avevo pensato!!!
Vediamo se me la cavo..
P.S.: Purtroppo il codice non riesco a postarlo per mancanza di tempo perché è "zozzo", e minimo mi parte mezza giornata per pulirlo solo per far capire ad un esterno tutti i passaggi. Ad ogni modo, direi che il problema è stato centrato! Qualora non riuscissi a cavarmela, sicuramente investirò del tempo per postare il codice.
JohnMarston
23-03-2016, 08:49
Ho fatto un mega lavorone e mi aspettavo qualcosa di meglio. Certamente ho visto un leggero miglioramento, ma sottolineo leggero e non ne sono per nulla soddisfatto!
Tutti gli oggetti che venivano disegnati nel pannello principale, li ho trasferiti in classi con estensione JPanel.
Pertanto la struttura ora è:
JFrame incorpora JPanel A (principale),
JPanel A incorpora JPanel B,
JPanel B incorpora JPanel C,
JPanel C incorpora JPanel D,
JPanel D incorpora JPanel E,
JPanel E incorpora JPanel F.
È importante far notare che da A a F c'è un passaggio di parametri attraverso metodi che serviranno per graficare gli oggetti delle rispettive classi.
Ogni classe ha il proprio paintComponet() che si occupa di disegnare l'oggetto corrispondente.
L'oggetto che viene ridisegnato frequentemente si trova in JPanel F. Quindi l'ho messo in ultima posizione pensando che, il repaint veniva effettuato solo su quest'ultima classe. Invece, se metto una print nel paintComponent() di JPanel A, che in realtà non disegna nulla, perché è vuoto, me la stampa ogni qualvolta si verifica l'evento repaint() di JPanel F!!! :confused: :confused:
E questo cosa significa? Che tutte le classi JPanel cancellano e ridisegnano, ad ogni repaint di F, tutti gli oggetti, che in realtà non dovrebbero essere ridisegnati. Quindi mi sono ammazzato di lavoro per niente, perché fa la stessa cosa di prima, cioè quando il paintComponent() di classe A si occupava di disegnare tutti gli oggetti. :muro: :muro: :muro:
Ma come devo fare!? :cry: :cry: :cry:
banryu79
23-03-2016, 09:29
E questo cosa significa? Che tutte le classi JPanel cancellano e ridisegnano, ad ogni repaint di F, tutti gli oggetti, che in realtà non dovrebbero essere ridisegnati. Quindi mi sono ammazzato di lavoro per niente, perché fa la stessa cosa di prima, cioè quando il paintComponent() di classe A si occupava di disegnare tutti gli oggetti. :muro: :muro: :muro:
Ma come devo fare!? :cry: :cry: :cry:
Si infatti, il framework funziona così!
Pensavo lo sapessi, ma leggendo questo tuo messaggio ho come l'impressione che tu non conosca bene le meccaniche di base. Quindi io personalmente ti invito di nuovo a documentarti meglio: puoi partire dai due link che avevo inserito nella mia prima risposta. Secondo me ci metti meno tempo a studiare un po' meglio come funziona il framework e capirlo, che non andando così a tentativi.
EDIT: comunque bisogna sempre vedere come sono implementati i vari paintComponent dei tuoi pannelli. Nel senso: non è tanto importante il fatto che venga invocato il paintComponent di un pannello che poi non disegna nulla (e quindi non "pesa" niente) quanto quali sono le operazioni che vengono eseguite nei paintComponent che "fanno qualcosa".
Soprattuto, lo ripeto, controlla che non ci siano:
1) chiamate a repaint()
2) letture/scritture su disco - e in generale operazioni molto onerose.
JohnMarston
23-03-2016, 11:23
Escludiamo letture/scritture su disco...
Cosa intendi per chiamate al repaint()?
Non posso fare a meno del repaint(), mi serve anche negli altri pannelli!
Ogni oggetto è in un pannello e quest'ultimo dovrà ridisegnare in base ad un evento riferito a quell'oggetto.
Senza il repaint() come faccio a ridisegnarli? Non posso di certo lavorare con variabili booleane.
banryu79
23-03-2016, 16:11
Escludiamo letture/scritture su disco...
Cosa intendi per chiamate al repaint()?
Non posso fare a meno del repaint(), mi serve anche negli altri pannelli!
Ogni oggetto è in un pannello e quest'ultimo dovrà ridisegnare in base ad un evento riferito a quell'oggetto.
Senza il repaint() come faccio a ridisegnarli? Non posso di certo lavorare con variabili booleane.
Intendevo chiamate a repaint DENTRO il metodo paintComponent. Se è così hai un problema.
banryu79
24-03-2016, 09:31
Comunque, se devi ridisegnare solo l'oggetto nell'ultimo componente/panello (F) quando chiami repaint invece di invocare la versione no-arg invoca la versione con parmetri che ti permette di definire la regione che necessita di un aggiornamento.
Vedi il primo link che ti ho girato, che spiega il Paint Mechanism di AWT/Swing.
E' tutto spiegato lì.
Leggi anche questo:
Solving Common Painting Problems (http://docs.oracle.com/javase/tutorial/uiswing/painting/problems.html)
Magari fatti il tutorial o almeno leggilo, compreso il codice di esempio.
Sono sciuro che se lo farai capirai molte cose e ne verrai fuori tranquillamente.
JohnMarston
24-03-2016, 13:57
Scolta, forse c'è un fraintendimento. Nel 3d sto chiedendo come evitare che un evento dell'ultimo pannello mi va a cancellare per poi ridisegnare tutto ciò che è stato disegnato dal primo al penultimo pannello. Questo è il mio problema. Apprezzo il tuo interesse e i tuoi interventi ma i link che mi hai passato non risolvono il mio problema.
Sto googolando da più giorni e tutto ciò che ho trovato compreso ciò che stai lincando l'ho già visto e non offre soluzioni al mio caso. Anzi, son proprio tutti i vari tutorial e letture che ho seguito che mi hanno portato ad avere questo problema di graficazione.
Se riesci a trovare casi simili al mio son ben lieto di approfondire, altrimenti è anche inutile postare cose che ho già visto perché non risolvono il mio caso specifico. Sono letture e modelli troppo generali che non approfondiscono con esempi concreti la soluzione al mio problema.
Ovviamente non sono un esperto, ma non mi sognerei mai di andare a mettere un repaint nel paintComponent(). Da nabbo quale sono, ti dico che, nel mio caso, non puoi neanche mettere un repaint() che identifica un area precisa dell'area che vuoi ridisegnare semplicemente perché andando per logica non escludi gli altri pannelli. Farai ridisegnare nuovamente gli altri pannelli con la differenza che verrà ridisegnata una zona precisa. E questo non risolve nulla.
Ogni pannello è un oggetto grafico che ha "vita propria" messo all'interno di un pannello generale ed io devo fare in modo che, nel rappresentarli graficamente, siano indipendenti l'uno dall'altro in modo tale da evitare che un repaint di un oggetto si trasformi in un repaint generale. Spero di essere chiaro.
In ogni caso ti ringrazio per l'interesse. In un modo o nell'altro ne usciremo vivi... ;)
banryu79
25-03-2016, 08:29
Tanto per essere chiari: in Swing, per una gerarchia di componenti, il Graphic che viene passato ai vari figli di quella gerarchia è uno solo. Ed è quello della top level window (JFrame, JDialog o JDesktopPane, se ricordo bene).
Quindi nel tuo caso, è il graphic del JFrame, se ho capito la tua situazione.
Tu, se vuoi limitare l'area di questo Graphic che viene ridisegnata, devi circoscriverla in qualche modo.
Ma, ripeto: senza vedere il codice e/o vedere una rappresentazione dell'interfaccia, è difficile capire cosa sia più opportuno fare (almeno per me).
Detto questo, in bocca al lupo e buon lavoro.
JohnMarston
29-03-2016, 10:05
Dunque ho abbozzato un progetto simile alla struttura del mio codice.
Mi sono limitato a creare solo 3 pannelli. Il pannello A è lo sfondo grigio, B disegna un rettangolo rosso, C una sfera che rimbalza.
Immagine (https://gyazo.com/cc3bf96ee87a0286fcd336530ea6eccc)
Il Frame comunica attraverso un metodo, le coordinate del movimento dell'oggetto C (sfera) per tutta l'area. Quest'ultime vengono passate fra i vari pannelli attraverso il metodo riceviMovimento().
Il pannello C a differenza di altri ha il famoso repaint(). Come detto precedentemente questo repaint() cancella e ridisegna tutti gli oggetti dei vari pannelli. Io devo evitare questo. In pratica ogni pannello che rappresenta l'oggetto non deve essere ridisegnato ogni istante che la sfera, ossia l'oggetto del pannello C, si muove.
Questo è il mio problema.. Attendo lumi!!!
vBulletin® v3.6.4, Copyright ©2000-2025, Jelsoft Enterprises Ltd.