Java/Swing offscreen rendering (Cobra HTMLPanel -> BufferedImage) Problem: Component doesn't finish redrawing first

馋奶兔 提交于 2019-12-24 16:27:49

问题


I'm trying to render the contents of a the Java/Swing Cobra HTML renderer to an offscreen BufferedImage, for use elsewhere in my app:

 slideViewPanel.setDocument(document, rendererContext);
 BufferedImage test = new BufferedImage(300,300,BufferedImage.TYPE_INT_RGB);
 Graphics g = test.getGraphics();
 slideViewPanel.paint(g);

The resulting image in g shows a partially rendered page -- sometimes the contents of the HTMLFrame before the new document was set; sometimes a half-rendered version of the new document. I gather this is because Cobra's setDocument method just schedules the document for re-rendering, but I'm stepping through in the debugger and I don't see a second thread to do re-rendering. Anyone have any insight into what might be happening here?


回答1:


When the page has been parsed, you need to wait until all images are loaded before laying out the component.

Intercept all image loading to make sure all images are completely loaded before laying out the component. I did this by overiding HtmlDocumentImpl.loadImage. (Had to subclass DocumentBuilderImpl and overriding createDocument to make it work.) I used a semaphore to wait until the result of a image.getWItdh is available.

I had to set up a timer around the parsing, as some scripts may loop and never return. I have no idea if there is a better way of solving this.

Regarding the layout, there is a a deal of cargo cult and experimentation that lead to the snipped below , so maybe you can try to remove some things.

            htmlPanel.setVisible(true); 
    htmlPanel.setPreferredWidth(DEFAULT_PAGE_WIDTH);
    logger.info("Calculating preferred size");

    // Get the preferred heigth for the current width.
    Dimension psvz = htmlPanel.getPreferredSize();
    Dimension min = htmlPanel.getMinimumSize();

    logger.info("prf :" + psvz);
    logger.info("min :" + min);

    // Enlarge to the minimum width (with a limit)
    int width = Math.min(MAX_PAGE_WIDTH, Math.max(DEFAULT_PAGE_WIDTH,
            psvz.width));
    int height = psvz.height;

    logger.info("width :" + width);
    logger.info("heigth :" + height);

    htmlPanel.setSize(width, height);

            // actually, htmlPanel is a subclass, and this method exposes validateTree. It may work without it.
    htmlPanel.forceValidateTree(); 

    htmlPanel.doLayout();

    setImageSize(width);
    logger.info("actual size:" + htmlPanel.getSize());

I couldn't figure out what a JFrame does with the HtmlPanel so that it paints its children properly. I had to paint using the children component; Ie either the htmlPanel.getBlockRenderable() or the frameset.

Images are painted asychronous, (and the paints might abort), so before the BufferedImage can be used, all the image paints must be completed.

I used a delegating graphics2d object overriding all the image paint methods to wait until the painting is completed.

After that, i found that the html might benefit from a re-layout as all images sizes are known, so i invalidate the component and do the layout and painting again (or actually, I just call the code twice...)

After that, the BufferedImage can be used. Maybe there is a simpler way.




回答2:


Try calling g.dispose()




回答3:


You'll have to wait until the component is visible to get the image. Try with that:

   htmlPanel.setDocument(document, rendererContext);
   EventQueue.invokeLater(new Runnable() {
   public void run() {
    if (!captureImage(htmlPanel, "test.jpg")){
     EventQueue.invokeLater(this);
     System.out.println("Encolado");
    }else {
     System.out.println("capturado");
    }
   }
  });

Having captureImage as:

  public static boolean captureImage(Component component, String fileName) {
  boolean captured = false;
  if (component.isVisible()) {
   Dimension size = component.getSize();
   BufferedImage image = new BufferedImage(size.width, size.height,
     BufferedImage.TYPE_INT_RGB);

   component.paint(image.getGraphics());
   captured = true;
   try {
    ImageIO.write(image, "JPEG", new File(fileName));
   } catch (IOException e) {
    e.printStackTrace();
   }
  }
  return captured;
 }



回答4:


Actually, surround your 'component.paint(image.getGraphics());' with a 'SwingUtilities.invokeAndWait()' as in:

    try {
    SwingUtilities.invokeAndWait(new Runnable() {
        public void run() {
            component.paint(image.getGraphics());
        }
    });
    ImageIO.write(image, "png", file);
} catch (Exception e) {
    } finally {
    }


来源:https://stackoverflow.com/questions/2783377/java-swing-offscreen-rendering-cobra-htmlpanel-bufferedimage-problem-compo

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!