2012-02-15 15 views
14

Dalla domanda iniziale (sotto), ora sto offrendo una taglia per la seguente:Java - Pannello angolo arrotondato con compositing in paintComponent

Una soluzione basata AlphaComposite per gli angoli arrotondati.

  • Si prega di dimostrare con un JPanel.
  • Gli angoli devono essere completamente trasparenti.
  • deve essere in grado di supportare la pittura JPG, ma gli angoli ancora sono arrotondati
  • non devono utilizzare SetClip (o qualsiasi clipping)
  • Deve avere prestazioni decenti

Speriamo che qualcuno prende questo in fretta, sembra facile.

Inoltre assegnerò la taglia se c'è una ragione ben spiegata per cui questo non può mai essere fatto, che altri sono d'accordo.

Ecco un'immagine campione di quello che ho in mente (ma usando AlphaComposite) enter image description here


domanda originale

Ho cercato di capire un modo per fare gli angoli arrotondati utilizzando il compositing, molto simile a How to make a rounded corner image in Java o http://weblogs.java.net/blog/campbell/archive/2006/07/java_2d_tricker.html.

Tuttavia, i miei tentativi senza una BufferedImage intermedia non funzionano: il composito di destinazione arrotondato a quanto pare non influenza la sorgente. Ho provato cose diverse ma niente funziona. Dovrei ottenere un rettangolo rosso arrotondato, invece ne otterrò uno quadrato.

Così, ho due domande, davvero:

1) C'è un modo per fare questo lavoro?

2) Un'immagine intermedia genererà effettivamente prestazioni migliori?

SSCCE:

pannello

il test TPanel

import java.awt.AlphaComposite; 
import java.awt.Color; 
import java.awt.Dimension; 
import java.awt.Graphics; 
import java.awt.Graphics2D; 

import javax.swing.JLabel; 

public class TPanel extends JLabel { 
int w = 300; 
int h = 200; 

public TPanel() { 
    setOpaque(false); 
    setPreferredSize(new Dimension(w, h)); 
     setMaximumSize(new Dimension(w, h)); 
     setMinimumSize(new Dimension(w, h)); 
} 

@Override 
public void paintComponent(Graphics g) { 
    super.paintComponent(g); 
    Graphics2D g2d = (Graphics2D) g.create(); 

    // Yellow is the clipped area. 
    g2d.setColor(Color.yellow); 
    g2d.fillRoundRect(0, 0, w, h, 20, 20); 
    g2d.setComposite(AlphaComposite.Src); 

    // Red simulates the image. 
    g2d.setColor(Color.red); 
    g2d.setComposite(AlphaComposite.SrcAtop); 

    g2d.fillRect(0, 0, w, h); 
    } 
} 

e la sua Sandbox

import java.awt.Dimension; 
import java.awt.FlowLayout; 

import javax.swing.JFrame; 

public class Sandbox { 
public static void main(String[] args) { 
    JFrame f = new JFrame(); 
     f.setMinimumSize(new Dimension(800, 600)); 
     f.setLocationRelativeTo(null); 
     f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
     f.setLayout(new FlowLayout()); 

     TPanel pnl = new TPanel(); 
     f.getContentPane().add(pnl); 

     f.setVisible(true); 
    } 
} 
+0

si prega di leggere domande su [JButton e grafica] (http://stackoverflow.com/users/584862/mre?tab=questions) di @mre – mKorbel

+0

ti riferisci http://stackoverflow.com/questions/8416295/component-pittura-fuori-custom-border? Questo riguarda i colori pieni riempiti nella grafica, non le immagini ... Sto cercando un modo per usare una forma composita per impostare una maschera, quindi dipingere un'immagine sopra di essa. – Ben

risposta

7

Per quanto riguarda la tua prestazione, l'articolo Java 2D di Trickery contiene un link ad una spiegazione molto buona di Chet Haase sull'uso di Intermediate Images.

penso che il seguente estratto da Java Foundation Classes di O'Reilly in a Nutshell potrebbe essere utile a voi, al fine di dare un senso al comportamento AlphaComposite e perché le immagini intermedie possono essere la tecnica necessaria per usare.

L'AlphaComposite Regole Compositing

Lo SRC_OVER regola compositing disegna un possibile traslucido colore di origine sul colore di destinazione. Questo è ciò che in genere vogliamo fare quando eseguiamo un'operazione grafica. Ma l'oggetto AlphaComposite consente effettivamente di combinare i colori in base alle altre sette regole .

Prima di considerare le regole di compositing in dettaglio, c'è un punto importante che è necessario comprendere. I colori visualizzati sullo schermo non hanno mai un canale alfa. Se è possibile vedere un colore, è un colore opaco . Il preciso valore del colore potrebbe essere stato scelto in base a un calcolo della trasparenza , ma, una volta scelto il colore, il colore risiede nella memoria di una scheda video da qualche parte e non ha un valore alpha associato. In altre parole, con il disegno su schermo , i pixel di destinazione hanno sempre valori alfa di 1.0.

La situazione è diversa quando si disegna in un'immagine fuori schermo . Come vedremo quando consideriamo la classe Java 2D BufferedImage più avanti in questo capitolo, puoi specificare la rappresentazione colore desiderata quando crei un'immagine fuori schermo. Per impostazione predefinita, un oggetto BufferedImage rappresenta un'immagine come una matrice di colori RGB, ma è anche possibile creare un'immagine che è una matrice di colori ARGB. Ad esempio, a un'immagine sono associati valori alfa e quando si disegna nelle immagini , i valori alfa rimangono associati ai pixel che si designa .

Questa distinzione tra sullo schermo e off-schermo di disegno è importante perché alcune delle regole di composizione eseguire composizioni in base ai valori alfa dei pixel di destinazione, piuttosto che l'alfa valori dei pixel sorgente.Con il disegno su schermo, i pixel di destinazione sono sempre opachi (con valori alfa di 1.0), ma con il disegno fuori schermo , questo non deve essere il caso. Pertanto, alcune delle regole di compositing sono utili solo quando si disegna in immagini fuori schermo che ha un canale alfa.

Per overgeneralize un po ', possiamo dire che quando si sta disegnando sullo schermo, in genere rimanere con lo SRC_OVER predefinito composizione regola, utilizza colori opachi, e variare il valore alfa utilizzato dall'oggetto AlphaComposite. Tuttavia, quando si lavora con immagini fuori schermo con canali alfa , è possibile utilizzare altre regole di composizione. In questo caso, si utilizzano in genere colori traslucidi e immagini traslucide e un oggetto AlphaComposite con un valore alfa di 1.0.

+0

'I colori visualizzati sullo schermo non hanno mai un canale alfa. FTW. Non sono sicuro del motivo per cui nessun altro l'ha inchiodato nelle ultime due settimane. Premiato, dal momento che hai affrontato i problemi di rendimento e la fonte del problema (da "fonti credibili e ufficiali" non meno). Saluti. – Ben

+0

BTW ha letto quell'articolo in "Filthy Rich Clients", che mi ha interessato in primo luogo a questo problema: ridacchiare: in particolare le cose di compositing - è piuttosto eccitato per i compositi. – Ben

4

Ho esaminato questo problema e non si può vedere come fare questo in un chiamata singola alle classi di sistema.

Graphics2D è un'istanza astratta, implementata come SunGraphics2D. Il codice sorgente è disponibile per esempio docjar e quindi potremmo potenzialmente "fare lo stesso, ma diverso" copiando un po 'di codice. Tuttavia, i metodi per dipingere un'immagine dipendono da alcune classi "pipe" che non sono disponibili. Anche se fai roba con il caricamento di classe, probabilmente colpirai una classe nativa ottimizzata che non può essere manipolata per fare l'approccio teoricamente ottimale; tutto quello che ottieni è dipingere le immagini come quadrati.

Tuttavia possiamo creare un approccio in cui il nostro codice, non nativo (leggi: lento?), Esegue il codice il meno possibile, e non in base alla dimensione dell'immagine ma piuttosto alla (relativamente) bassa area del round rett. Inoltre, senza copiare le immagini in memoria, consumando molta memoria. Ma se hai molta memoria, ovviamente, un'immagine cache è più veloce dopo che l'istanza è stata creata.

Alternativa 1:

import java.awt.Composite; 
import java.awt.CompositeContext; 
import java.awt.Dimension; 
import java.awt.Graphics; 
import java.awt.Graphics2D; 
import java.awt.RenderingHints; 
import java.awt.image.BufferedImage; 
import java.awt.image.ColorModel; 
import java.awt.image.Raster; 
import java.awt.image.WritableRaster; 

import javax.swing.JLabel; 

public class TPanel2 extends JLabel implements Composite, CompositeContext { 
private int w = 300; 
private int h = 200; 

private int cornerRadius = 20; 
private int[] roundRect; // first quadrant 
private BufferedImage image; 
private int[][] first = new int[cornerRadius][]; 
private int[][] second = new int[cornerRadius][]; 
private int[][] third = new int[cornerRadius][]; 
private int[][] forth = new int[cornerRadius][]; 

public TPanel2() { 
    setOpaque(false); 
    setPreferredSize(new Dimension(w, h)); 
    setMaximumSize(new Dimension(w, h)); 
    setMinimumSize(new Dimension(w, h)); 

    // calculate round rect  
    roundRect = new int[cornerRadius]; 
    for(int i = 0; i < roundRect.length; i++) { 
     roundRect[i] = (int)(Math.cos(Math.asin(1 - ((double)i)/20))*20); // x for y 
    } 

    image = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB); // all black 
} 

@Override 
public void paintComponent(Graphics g) { 
    // discussion: 
    // We have to work with the passed Graphics object. 

    if(g instanceof Graphics2D) { 

     Graphics2D g2d = (Graphics2D) g; 

     // draw the whole image and save the corners 
     g2d.setComposite(this); 
     g2d.drawImage(image, 0, 0, null); 
    } else { 
     super.paintComponent(g); 
    } 
} 

@Override 
public CompositeContext createContext(ColorModel srcColorModel, 
     ColorModel dstColorModel, RenderingHints hints) { 
    return this; 
} 

@Override 
public void dispose() { 

} 

@Override 
public void compose(Raster src, Raster dstIn, WritableRaster dstOut) { 
    // lets assume image pixels >> round rect pixels 
    // lets also assume bulk operations are optimized 

    // copy current pixels 
    for(int i = 0; i < cornerRadius; i++) { 
     // quadrants 

     // from top to buttom 
     // first 
     first[i] = (int[]) dstOut.getDataElements(src.getWidth() - (cornerRadius - roundRect[i]), i, cornerRadius - roundRect[i], 1, first[i]); 

     // second 
     second[i] = (int[]) dstOut.getDataElements(0, i, cornerRadius - roundRect[i], 1, second[i]); 

     // from buttom to top 
     // third 
     third[i] = (int[]) dstOut.getDataElements(0, src.getHeight() - i - 1, cornerRadius - roundRect[i], 1, third[i]); 

     // forth 
     forth[i] = (int[]) dstOut.getDataElements(src.getWidth() - cornerRadius + roundRect[i], src.getHeight() - i - 1, cornerRadius - roundRect[i], 1, forth[i]); 
    } 

    // overwrite entire image as a square 
    dstOut.setRect(src); 

    // copy previous pixels back in corners 
    for(int i = 0; i < cornerRadius; i++) { 
     // first 
     dstOut.setDataElements(src.getWidth() - cornerRadius + roundRect[i], i, first[i].length, 1, second[i]); 

     // second 
     dstOut.setDataElements(0, i, second[i].length, 1, second[i]); 

     // third 
     dstOut.setDataElements(0, src.getHeight() - i - 1, third[i].length, 1, third[i]); 

     // forth 
     dstOut.setDataElements(src.getWidth() - cornerRadius + roundRect[i], src.getHeight() - i - 1, forth[i].length, 1, forth[i]); 
    } 
} 

} 

Alternativa 2:

import java.awt.AlphaComposite; 
import java.awt.Composite; 
import java.awt.CompositeContext; 
import java.awt.Dimension; 
import java.awt.Graphics; 
import java.awt.Graphics2D; 
import java.awt.RenderingHints; 
import java.awt.image.BufferedImage; 
import java.awt.image.ColorModel; 
import java.awt.image.Raster; 
import java.awt.image.WritableRaster; 
import javax.swing.JLabel; 

public class TPanel extends JLabel implements Composite, CompositeContext { 
private int w = 300; 
private int h = 200; 

private int cornerRadius = 20; 
private int[] roundRect; // first quadrant 
private BufferedImage image; 

private boolean initialized = false; 
private int[][] first = new int[cornerRadius][]; 
private int[][] second = new int[cornerRadius][]; 
private int[][] third = new int[cornerRadius][]; 
private int[][] forth = new int[cornerRadius][]; 

public TPanel() { 
    setOpaque(false); 
    setPreferredSize(new Dimension(w, h)); 
    setMaximumSize(new Dimension(w, h)); 
    setMinimumSize(new Dimension(w, h)); 

    // calculate round rect  
    roundRect = new int[cornerRadius]; 
    for(int i = 0; i < roundRect.length; i++) { 
     roundRect[i] = (int)(Math.cos(Math.asin(1 - ((double)i)/20))*20); // x for y 
    } 

    image = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB); // all black 
} 

@Override 
public void paintComponent(Graphics g) { 
    if(g instanceof Graphics2D) { 

     Graphics2D g2d = (Graphics2D) g; 

     // draw 1 + 2 rectangles and copy pixels from image. could also be 1 rectangle + 4 edges 
     g2d.setComposite(AlphaComposite.Src); 

     g2d.drawImage(image, cornerRadius, 0, image.getWidth() - cornerRadius - cornerRadius, image.getHeight(), null); 
     g2d.drawImage(image, 0, cornerRadius, cornerRadius, image.getHeight() - cornerRadius - cornerRadius, null); 
     g2d.drawImage(image, image.getWidth() - cornerRadius, cornerRadius, image.getWidth(), image.getHeight() - cornerRadius, image.getWidth() - cornerRadius, cornerRadius, image.getWidth(), image.getHeight() - cornerRadius, null); 

     // draw the corners using our own logic 
     g2d.setComposite(this); 

     g2d.drawImage(image, 0, 0, null); 

    } else { 
     super.paintComponent(g); 
    } 
} 

@Override 
public CompositeContext createContext(ColorModel srcColorModel, 
     ColorModel dstColorModel, RenderingHints hints) { 
    return this; 
} 

@Override 
public void dispose() { 

} 

@Override 
public void compose(Raster src, Raster dstIn, WritableRaster dstOut) { 
    // assume only corners need painting 

    if(!initialized) { 
     // copy image pixels 
     for(int i = 0; i < cornerRadius; i++) { 
      // quadrants 

      // from top to buttom 
      // first 
      first[i] = (int[]) src.getDataElements(src.getWidth() - cornerRadius, i, roundRect[i], 1, first[i]); 

      // second 
      second[i] = (int[]) src.getDataElements(cornerRadius - roundRect[i], i, roundRect[i], 1, second[i]); 

      // from buttom to top 
      // third 
      third[i] = (int[]) src.getDataElements(cornerRadius - roundRect[i], src.getHeight() - i - 1, roundRect[i], 1, third[i]); 

      // forth 
      forth[i] = (int[]) src.getDataElements(src.getWidth() - cornerRadius, src.getHeight() - i - 1, roundRect[i], 1, forth[i]); 
     } 
     initialized = true; 
    }  

    // copy image pixels into corners 
    for(int i = 0; i < cornerRadius; i++) { 
     // first 
     dstOut.setDataElements(src.getWidth() - cornerRadius, i, first[i].length, 1, second[i]); 

     // second 
     dstOut.setDataElements(cornerRadius - roundRect[i], i, second[i].length, 1, second[i]); 

     // third 
     dstOut.setDataElements(cornerRadius - roundRect[i], src.getHeight() - i - 1, third[i].length, 1, third[i]); 

     // forth 
     dstOut.setDataElements(src.getWidth() - cornerRadius, src.getHeight() - i - 1, forth[i].length, 1, forth[i]); 
    } 
} 

} 

Spero che questo aiuti, questo è un po 'di un secondo migliore soluzione, ma questa è la vita (questo è quando un po' di grafica-guru viene e mi dimostra sbagliato (??) ..) ;-)

+0

+1, ma [lato oscuro della luna] (http://stackoverflow.com/a/8420566/714968) – mKorbel

+0

Grazie Thomas, un +1 per il tuo problema ma devo andare con il mio budello qui, che dice @ Mark McLaren è il "più corretto". – Ben

+0

Perdonami per non aver usato AlphaComposite.SrcATop direttamente, stavo pensando che ti interesserebbe principalmente una SOLUZIONE DI LAVORO EQUIVALENTE. Quanto sopra sembra soddisfare 5 criteri su 5 per un rect rotondo e si potrebbero aggiungere antialias/bordi sbiaditi con poco sforzo. Così tanto per essere pragmatico. – ThomasRS

Problemi correlati