Al dibujar un componente en BufferedImage, se causa daños en la pantalla

Estoy utilizando el componente JScrollNavigator que se describe aquí , para proporcionar una ventana de navegación sobre un gran componente CAD tipo “canvas” que he incrustado en un JScrollPane .

He intentado adaptar el JScrollNavigator para dibujar una imagen en miniatura del canvas para proporcionar un contexto adicional para el usuario. Sin embargo, la acción de hacer esto hace que se dañe el renderizado del marco principal de mi aplicación. Específicamente, es la acción de llamar paint(Graphics) en el componente de la vista (es decir, mi canvas principal), pasando el objeto de Graphics creado por la BufferedImage que causa daños en la visualización posterior; si comento esta línea todo funciona bien.

A continuación se muestra el método paintComponent reemplazado por paintComponent :

 @Override protected void paintComponent(Graphics g) { Component view = jScrollPane.getViewport().getView(); BufferedImage img = new BufferedImage(view.getWidth(), view.getHeight(), BufferedImage.TYPE_INT_ARGB); Graphics2D g2d = img.createGraphics(); // Paint JScrollPane view to off-screen image and then scale. // It is this action that causes the display corruption! view.paint(g2d); g2d.drawImage(img, 0, 0, null); Image scaled = img.getScaledInstance(getWidth(), getHeight(), 0); super.paintComponent(g); g.drawImage(scaled, 0, 0, null); } 

¿Alguien tiene alguna sugerencia sobre la causa de la corrupción? Hubiera pensado que pintar con una imagen fuera de pantalla no debería tener ningún efecto en las operaciones de pintura existentes.

EDITAR

Para proporcionar algunos detalles adicionales: JScrollNavigator forma un JScrollNavigator en el lado izquierdo de un JSplitPane . El JScrollPane asociado con el navegador está en el lado derecho. La “corrupción” hace que el divisor ya no se represente y las barras de desplazamiento no estén visibles (aparecen en blanco). Si cambio el tamaño del JFrame , la sección JMenu también se vuelve blanca. Si bash usar el navegador o interactuar con las barras de desplazamiento, se vuelven visibles, pero el divisor permanece en blanco. Es como si la configuración opaca de los diversos componentes se hubiera visto afectada por la representación de la vista de la ventana gráfica como una imagen fuera de pantalla.

Además, si hago que JScrollNavigator aparezca en un JDialog completamente separado, todo funciona correctamente.

EDIT 2

Puedo reproducir el problema constantemente haciendo lo siguiente:

Agregue una JMenuBar al mFrame :

 JMenuBar bar = new JMenuBar(); bar.add(new JMenu("File")); mFrame.setJMenuBar(bar); 

En el método main() de JScrollNavigator replace:

 jsp.setViewportView(textArea); 

… con:

 jsp.setViewportView(new JPanel() { { setBackground(Color.GREEN); setBorder(BorderFactory.createLineBorder(Color.BLACK, 5)); } }); 

Asegúrese de que el JScrollNavigator esté incrustado como un panel dentro de mFrame , en lugar de aparecer como un JDialog separado:

 mFrame.add(jsp, BorderLayout.CENTER); mFrame.add(nav, BorderLayout.NORTH); 

Ahora cuando se ejecuta la aplicación, JMenuBar ya no es visible ; el acto de pintar la vista (es decir, un JPanel verde con un borde negro grueso) en el Graphics2D devuelto por BufferedImage.createGraphics() parece estar apareciendo en pantalla , posiblemente desde la esquina superior izquierda del JFrame, oscureciendo así otros componentes. Esto solo parece suceder si se utiliza un JPanel como vista de vista y no otro componente como JTextArea , JTable , etc.

EDIT 3

Parece que esta persona estaba teniendo el mismo problema (sin embargo, no se publicó ninguna solución): http://www.javaworld.com/community/node/2894/

EDIT 4

Aquí están los métodos main y paintComponent que dan como resultado el error reproducible descrito en Edit 2:

 public static void main(String[] args) { JScrollPane jsp = new JScrollPane(); jsp.setViewportView(new JPanel() { { setBackground(Color.GREEN); setBorder(BorderFactory.createLineBorder(Color.BLACK, 5)); } }); JScrollNavigator nav = new JScrollNavigator(); nav.setJScrollPane(jsp); JFrame mFrame = new JFrame(); JMenuBar bar = new JMenuBar(); bar.add(new JMenu("File")); mFrame.setJMenuBar(bar); mFrame.setTitle("JScrollNavigator Test"); mFrame.setSize(800, 600); mFrame.setLayout(new GridLayout(1, 2)); mFrame.add(jsp); mFrame.add(nav); Dimension screenDim = Toolkit.getDefaultToolkit().getScreenSize(); mFrame.setLocation((screenDim.width - mFrame.getSize().width) / 2, (screenDim.height - mFrame.getSize().height) / 2); mFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); mFrame.setVisible(true); } @Override protected void paintComponent(Graphics g) { super.paintComponent(g); Component view = jScrollPane.getViewport().getView(); if (img == null) { GraphicsConfiguration gfConf = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration(); img = new BufferedImage(view.getWidth(), view.getHeight(), BufferedImage.TYPE_INT_ARGB); } Graphics2D g2d = img.createGraphics(); view.paint(g2d); Image scaled = img.getScaledInstance(getWidth(), getHeight(), 0); g.drawImage(scaled, 0, 0, null); } 

EDIT 5

Parece que otros están teniendo problemas para volver a crear el problema exacto. Le pediría a las personas que ejecuten el código pegado aquí . Cuando ejecuto este ejemplo por primera vez, veo lo siguiente:

Imagen corrupta 1

Ni el JScrollNavigator ni el JMenuBar han sido pintados; estas áreas del marco son transparentes.

Después de cambiar el tamaño, veo lo siguiente:

Imagen corrupta 2

La JMenuBar todavía no se ha pintado y parece que el JPanel se representó en algún punto en (0,0) (donde debe estar la JMenuBar ). La llamada view.paint dentro de paintComponent es la causa directa de esto.

Resumen: el JScrollNavigator original usa la propiedad de opacity Swing para renderizar un conveniente NavBox verde sobre una miniatura escalada del componente en un JScrollPane adyacente. Debido a que amplía JPanel , el uso de opacity de los delegates de la interfaz de usuario (compartida) entra en conflicto con el del componente desplazable. Las imágenes que se ven en la edición 5 anterior tipifican el artefacto de representación asociado, que también se muestra aquí . La solución es permitir que NavBox , JScrollNavigator y el componente desplazable extiendan JComponent , como se sugiere en el segundo apéndice a continuación. Cada componente puede administrar sus propias propiedades individualmente.

enter image description here

No veo ningún artefacto de representación inusual con su código publicado en mi plataforma, Mac OS X, Java 1.6. Lo sentimos, no veo ninguna violación evidente de portabilidad.

imagen uno

Algunas observaciones probablemente irrelevantes, pero quizás útiles.

  • Incluso si usa setSize() , apropiadamente en este caso, aún debe pack() la Window adjunta.

     f.pack(); f.setSize(300, 200); 
  • Para su comodidad, add() reenvía el componente al panel de contenido.

     f.add(nav, BorderLayout.WEST); 
  • Prefiero StringBuilder a StringBuffer .

  • Considere ComponentAdapter en lugar de ComponentListener .

Adición: Como se sugiere aquí , obtuve resultados algo más flexibles usando RenderingHints lugar de getScaledInstance() como se muestra a continuación. Agregar algunos icons hace que sea más fácil ver el efecto dispar en las imágenes y el texto.

imagen dos

 editPane.insertIcon(UIManager.getIcon("OptionPane.errorIcon")); editPane.insertIcon(UIManager.getIcon("OptionPane.warningIcon")); ... @Override protected void paintComponent(Graphics g) { super.paintComponent(g); Component view = jScrollPane.getViewport().getView(); BufferedImage img = new BufferedImage(view.getWidth(), view.getHeight(), BufferedImage.TYPE_INT_ARGB); Graphics2D off = img.createGraphics(); off.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); off.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC); view.paint(off); Graphics2D on = (Graphics2D)g; on.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); on.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC); on.drawImage(img, 0, 0, getWidth(), getHeight(), null); } 

JPanel del JPanel : parece que el delegado de UI de JPanel no está cooperando. Una solución es extender JComponent para que pueda controlar la opacidad . Solo es un poco más de trabajo administrar el backgroundColor . NavBox y JScrollNavigator también son candidatos para un tratamiento similar.

enter image description here

 jsp.setViewportView(new JComponent() { { setBackground(Color.red); setBorder(BorderFactory.createLineBorder(Color.BLACK, 16)); } @Override protected void paintComponent(Graphics g) { super.paintComponent(g); g.setColor(getBackground()); g.fillRect(0, 0, getWidth(), getHeight()); } @Override public Dimension getPreferredSize() { return new Dimension(300, 300); } }); 

Tampoco estoy seguro de lo que quiere decir con corrupción, pero noté que la imagen muestreada es mucho mejor si especifica Image.SCALE_SMOOTH como la sugerencia de cambio de escala:

 Image scaled = img.getScaledInstance(getWidth(), getHeight(), Image.SCALE_SMOOTH); 

Tal vez esto es lo que estás buscando …

Pude reproducir tu problema y obtener el resultado que buscas. El problema es que el dibujo de la imagen no estaba completo cuando volvía a pintar, por lo que solo se pintaban partes de la imagen. Para solucionar esto, agregue este campo a su clase JScrollNavigator (como un locking):

 /** Lock to prevent trying to repaint too many times */ private boolean blockRepaint = false; 

Cuando repintamos el componente, este locking se activará. No se lanzará hasta que hayamos podido pintar el panel con éxito, entonces se puede ejecutar otra pintura.

paintComponent necesita ser modificado para cumplir con el locking y usar un ImageObserver al pintar su panel de navegación.

 @Override protected void paintComponent(final Graphics g) { super.paintComponent(g); if(!blockRepaint){ final Component view = (Component)jScrollPane.getViewport().getView(); BufferedImage img = new BufferedImage(view.getWidth(), view.getHeight(), BufferedImage.TYPE_INT_ARGB); Graphics2D g2d = img.createGraphics(); g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); // Paint JScrollPane view to off-screen image and then scale. // It is this action that causes the display corruption! view.paint(g2d); ImageObserver io = new ImageObserver() { @Override public boolean imageUpdate(Image img, int infoflags, int x, int y,int width, int height) { boolean result = true; g.drawImage(img, 0, 0, null); if((infoflags & ImageObserver.FRAMEBITS) == ImageObserver.FRAMEBITS){ blockRepaint = false; result = false; } return result; } }; Image scaled = img.getScaledInstance(getWidth(), getHeight(), 0); blockRepaint = g.drawImage(scaled, 0, 0, io); } } 
Intereting Posts