2012-02-15 32 views
14

A partir de la pregunta original (abajo), ahora estoy ofreciendo una recompensa por lo siguiente:Java - panel de esquina redondeada con la composición en paintComponent

Una solución basada AlphaComposite para las esquinas redondeadas.

  • Demuestre con un JPanel.
  • Las esquinas deben ser completamente transparentes.
  • Debe ser capaz de soportar la pintura JPG, pero esquinas todavía han redondeado
  • no debe utilizar setClip (o cualquier recorte)
  • Debe tener un rendimiento decente

Esperemos que alguien recoge esto rápido, parece fácil.

También otorgaré la recompensa si hay una razón bien explicada por la que esto nunca se puede hacer, con el que otros estén de acuerdo.

Aquí está una imagen de muestra de lo que tengo en mente (pero usando AlphaComposite) enter image description here


pregunta original

He estado tratando de encontrar una manera de hacer las esquinas redondeadas usando composición, muy similar a How to make a rounded corner image in Java o http://weblogs.java.net/blog/campbell/archive/2006/07/java_2d_tricker.html.

Sin embargo, mis intentos sin una Imagen Buffered intermedia no funcionan: el compuesto de destino redondeado aparentemente no afecta la fuente. Probé cosas diferentes, pero nada funciona. Debería obtener un rectángulo rojo redondeado, en lugar de eso, obtendré uno cuadrado.

lo tanto, tengo dos preguntas, en realidad:

1) ¿Hay una manera de hacer este trabajo?

2) ¿Una imagen intermedia realmente generará un mejor rendimiento?

SSCCE:

panel de

la prueba 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); 
    } 
} 

y su 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

lea preguntas sobre [JButton y gráficos] (http://stackoverflow.com/users/584862/mre?tab=questions) por @mre – mKorbel

+0

¿Se refiere a http://stackoverflow.com/questions/8416295/componente-pintura-fuera-borde-personalizado? Eso se aplica a los colores sólidos que se incluyen en los gráficos, no en las imágenes ... Estoy buscando una forma de usar una forma compuesta para establecer una máscara, luego pinto una imagen sobre ella. – Ben

Respuesta

7

Con respecto a sus preocupaciones sobre el rendimiento, el artículo de Java 2D Trickery contiene un enlace a una muy buena explicación de Chet Haase sobre el uso de Intermediate Images.

creo que el siguiente extracto de Java Foundation Classes de O'Reilly en una cáscara de nuez podría ser útil para usted con el fin de dar sentido a la conducta AlphaComposite y por qué las imágenes intermedias pueden ser la técnica necesaria para su uso.

El AlphaComposite Reglas de Composición regla de composición

El SRC_OVER dibuja un translúcida de color de origen posiblemente sobre el color de destino. Esto es lo que generalmente deseamos que ocurra al cuando realizamos una operación de gráficos. Pero el objeto AlphaComposite realmente permite que los colores se combinen de acuerdo con otras siete reglas también.

Antes de considerar las reglas de composición en detalle, hay un punto importante que necesita comprender. Los colores mostrados en la pantalla nunca tienen un canal alfa. Si puede ver un color, es un color opaco. El valor de color preciso puede haber sido elegido basado en un cálculo de transparencia , pero, una vez que se elige ese color, el color reside en la memoria de una tarjeta de video en algún lugar y no tiene un valor alfa asociado. En otras palabras, con el dibujo en la pantalla , los píxeles de destino siempre tienen valores alfa de 1.0.

La situación es diferente cuando se dibuja en una imagen fuera de pantalla , sin embargo. Como verá cuando consideremos la clase 2D 2D de BufferedImage más adelante en este capítulo, puede especificar la representación de color deseada cuando crea una imagen fuera de la pantalla. De forma predeterminada, un objeto BufferedImage representa una imagen como una matriz de colores RGB, pero también puede crear una imagen que es una matriz de colores ARGB. Tal una imagen tiene valores alfa asociados a ella, y cuando dibuja en las imágenes, los valores alfa permanecen asociados con los píxeles que dibuja .

Esta distinción entre en pantalla y fuera de pantalla de dibujo es importante porque algunas de las reglas de composición cabo la composición basada en las valores alfa de los píxeles de destino, en lugar de la alfa valora de los píxeles de origen.Con el dibujo en pantalla, los píxeles de destino son siempre opacos (con valores alfa de 1.0), pero con el dibujo fuera de la pantalla , este no tiene que ser el caso. Por lo tanto, algunas de las reglas de composición solo son útiles cuando se dibujan imágenes fuera de pantalla que tienen un canal alfa.

Para generalizar un poco, podemos decir que cuando se está dibujando en la pantalla, por lo general se pega con la SRC_OVER predeterminado de composición regla, utiliza colores opacos, y variar el valor alfa utilizado por el objeto AlphaComposite. Sin embargo, cuando se trabaja con imágenes fuera de pantalla que tienen canales alfa, puede hacer uso de otras reglas de composición. En este caso, normalmente usa colores translúcidos y translúcidos imágenes y un objeto AlphaComposite con un valor alfa de 1.0.

+0

'Los colores mostrados en la pantalla nunca tienen un canal alfa. FTW. No estoy seguro de por qué nadie más clavó esto en las últimas dos semanas. Otorgado, ya que no abordó las inquietudes sobre el rendimiento y la fuente del problema (a partir de "fuentes creíbles y oficiales"). Aclamaciones. – Ben

+0

BTW leyó ese artículo en "Filthy Rich Clients", que me interesó en primer lugar en este problema: reírse entre dientes: específicamente sobre la composición: está muy entusiasmado con los materiales compuestos. – Ben

4

He mirado en este tema y no se puede ver cómo se hace esto en una llamada única a las clases del sistema.

Graphics2D es una instancia abstracta, implementada como SunGraphics2D. El código fuente está disponible en, por ejemplo, docjar, por lo que podríamos simplemente 'hacer lo mismo, pero diferente' copiando algún código. Sin embargo, los métodos para pintar una imagen dependen de algunas clases 'pipe' que no están disponibles. Aunque hagas cosas con la carga de clases, probablemente toques con alguna clase nativa optimizada que no pueda ser manipulada para hacer el enfoque teóricamente óptimo; todo lo que obtienes es pintura de imágenes como cuadrados.

Sin embargo, podemos hacer un enfoque en el que nuestro código no nativo (leer: ¿lento?) Se ejecute lo menos posible y no según el tamaño de la imagen sino el área (relativamente) baja de la ronda rect. Además, sin copiar las imágenes en la memoria, consume mucha memoria. Pero si tiene mucha memoria, obviamente, una imagen en caché es más rápida después de que se haya creado la instancia.

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]); 
    } 
} 

} 

Espero que esto ayude, esto es algo así como una segunda mejor solución, pero así es la vida (esto es cuando viene algún gráficos-guru y me demuestra que está equivocado (??) ...) ;-)

+0

+1, pero [lado oscuro de la luna] (http://stackoverflow.com/a/8420566/714968) – mKorbel

+0

Gracias Thomas, un +1 por su problema, pero tengo que ir con mis entrañas aquí, que dice @Mark McLaren es el "más correcto". – Ben

+0

Perdóname por no usar AlphaComposite.SrcATop directamente, pensaba que en su mayoría estarías interesado en UNA SOLUCIÓN DE TRABAJO EQUIVALENTE. Lo anterior parece cumplir 5 de 5 criterios para un rect redondo, y usted podría agregar bordes antialiasing/desvanecidos con poco esfuerzo. Mucho por ser pragmático. – ThomasRS

Cuestiones relacionadas