Java/Swing 离屏渲染(Cobra HTMLPanel -> BufferedImage)问题:组件未首先完成重绘

发布于 2024-08-31 18:21:58 字数 521 浏览 8 评论 0原文

我正在尝试将 Java/Swing Cobra HTML 渲染器 的内容渲染到屏幕外的 BufferedImage,在我的应用程序的其他地方使用:

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

g 中的结果图像显示部分渲染的页面——有时是设置新文档之前 HTMLFrame 的内容;有时是新文档的半渲染版本。我认为这是因为 Cobra 的 setDocument 方法只是安排文档重新渲染,但我在调试器中单步调试,没有看到第二个线程来重新渲染。有人知道这里可能发生什么吗?

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

感性不性感 2024-09-07 18:21:58

当页面被解析后,您需要等待所有图像加载完毕才能布局组件。

拦截所有图像加载,以确保在布局组件之前所有图像均已完全加载。我通过覆盖 HtmlDocumentImpl.loadImage 来做到这一点。 (必须子类化 DocumentBuilderImpl 并重写 createDocument 才能使其工作。)我使用信号量来等待 image.getWItdh 的结果可用。

我必须围绕解析设置一个计时器,因为某些脚本可能会循环并且永远不会返回。我不知道是否有更好的方法来解决这个问题。

关于布局,有大量的货物崇拜和实验导致了下面的截图,所以也许你可以尝试删除一些东西。

            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());

我无法弄清楚 JFrame 对 HtmlPanel 有何作用,以便正确绘制其子项。
我必须使用儿童组件进行绘画;即 htmlPanel.getBlockRenderable() 或框架集。

图像是异步绘制的(并且绘制可能会中止),因此在使用 BufferedImage 之前,必须完成所有图像绘制。

我使用委托的 Graphics2d 对象覆盖所有图像绘制方法来等待绘制完成。

,我发现 html 可能会受益于重新布局,因为所有图像尺寸都是已知的,所以我使组件无效并再次进行布局和绘制(或者实际上,我只是调用代码两次......)

之后 ,可以使用BufferedImage。也许有更简单的方法。

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.

ヅ她的身影、若隐若现 2024-09-07 18:21:58

尝试调用g.dispose()

Try calling g.dispose()

梦巷 2024-09-07 18:21:58

您必须等到组件可见才能获取图像。
尝试一下:

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

将 captureImage 设为:

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

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;
 }
无尽的现实 2024-09-07 18:21:58

实际上,围绕你的 'component.paint(image.getGraphics());'带有“SwingUtilities.invokeAndWait()”,如下所示:

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

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 {
    }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文