Закадровый рендеринг Java/Swing (Cobra HTMLPanel -> BufferedImage) Проблема: сначала компонент не перерисовывается

Я пытаюсь отобразить содержимое средства визуализации 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 до установки нового документа; иногда полу-визуализированная версия нового документа. Я понимаю, это потому, что метод setDocument в Cobra просто планирует документ для повторного рендеринга, но я вхожу в отладчик и не вижу второго потока для повторного рендеринга. У кого-нибудь есть понимание того, что здесь может происходить?

4 ответа

Решение

Когда страница была проанализирована, вам нужно дождаться загрузки всех изображений, прежде чем выложить компонент.

Перехватите всю загрузку изображений, чтобы убедиться, что все изображения полностью загружены перед разметкой компонента. Я сделал это, переопределив 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. Может быть, есть более простой способ.

Попробуйте позвонить g.dispose()

Вам придется подождать, пока компонент не станет видимым, чтобы получить изображение. Попробуйте с этим:

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

Собственно, окружите ваш '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 {
    }
Другие вопросы по тегам