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