X-Git-Url: https://gerrit.simantics.org/r/gitweb?a=blobdiff_plain;f=bundles%2Forg.simantics.diagram%2Fsrc%2Forg%2Fsimantics%2Fdiagram%2Fexport%2FImagePrinter.java;h=0e073be653a8d6c324eef1bec67a247833981120;hb=e3f46ffc9d4a6930adc83ebb8e6730f19708cc94;hp=0b3e6c7237ecb2c87dd74377a280c07aaa34bf78;hpb=969bd23cab98a79ca9101af33334000879fb60c5;p=simantics%2Fplatform.git diff --git a/bundles/org.simantics.diagram/src/org/simantics/diagram/export/ImagePrinter.java b/bundles/org.simantics.diagram/src/org/simantics/diagram/export/ImagePrinter.java index 0b3e6c723..0e073be65 100644 --- a/bundles/org.simantics.diagram/src/org/simantics/diagram/export/ImagePrinter.java +++ b/bundles/org.simantics.diagram/src/org/simantics/diagram/export/ImagePrinter.java @@ -1,249 +1,249 @@ -package org.simantics.diagram.export; - -import java.awt.Point; -import java.awt.geom.AffineTransform; -import java.awt.image.BufferedImage; -import java.io.File; -import java.util.concurrent.Semaphore; -import java.util.concurrent.atomic.AtomicReference; - -import org.eclipse.core.runtime.IProgressMonitor; -import org.eclipse.core.runtime.SubMonitor; -import org.simantics.Simantics; -import org.simantics.db.ReadGraph; -import org.simantics.db.Resource; -import org.simantics.db.common.ResourceArray; -import org.simantics.db.common.request.PossibleIndexRoot; -import org.simantics.db.common.request.UniqueRead; -import org.simantics.db.common.utils.NameUtils; -import org.simantics.db.exception.DatabaseException; -import org.simantics.db.exception.ValidationException; -import org.simantics.db.management.ISessionContext; -import org.simantics.diagram.elements.DiagramNodeUtil; -import org.simantics.g2d.canvas.ICanvasContext; -import org.simantics.g2d.canvas.impl.CanvasContext; -import org.simantics.g2d.chassis.ICanvasChassis; -import org.simantics.g2d.chassis.SWTChassis; -import org.simantics.g2d.participant.TransformUtil; -import org.simantics.g2d.scenegraph.ICanvasSceneGraphProvider; -import org.simantics.g2d.scenegraph.SceneGraphConstants; -import org.simantics.modeling.ModelingResources; -import org.simantics.scenegraph.g2d.nodes.NavigationNode; -import org.simantics.scenegraph.utils.NodeUtil; -import org.simantics.structural2.StructuralVariables; -import org.simantics.utils.DataContainer; -import org.simantics.utils.datastructures.Pair; -import org.simantics.utils.threads.AWTThread; -import org.simantics.utils.threads.IThreadWorkQueue; -import org.simantics.utils.threads.ThreadUtils; -import org.simantics.utils.threads.WorkerThread; - -public class ImagePrinter { - - public static class ImageExportPlan { - public File exportLocation; - public String name; - public Resource diagram; - - // use dpi or size, not both. - public Double dpi; - public Point size; - public double margin; - } - - /** - * @param monitor - * the progress monitor to use for reporting progress to the - * user. It is the caller's responsibility to call done() on the - * given monitor. Accepts null, indicating that no - * progress should be reported and that the operation cannot be - * cancelled. - * - * @param exportPath - * @throws Exception - */ - public static BufferedImage printToImage(IProgressMonitor monitor, ImageExportPlan exportPlan) throws Exception { - - - SubMonitor progress = SubMonitor.convert(monitor, "Export to Image", 1); - - WorkerThread workerThread = new WorkerThread("Diagram Image Painter"); - workerThread.start(); - String loc; - if (exportPlan.exportLocation == null) - loc = "clipboard"; - else - loc = exportPlan.exportLocation.getAbsolutePath(); - progress.beginTask("Writing " + exportPlan.name + " to " + loc, 1); - - return render(workerThread, Simantics.getSessionContext(), exportPlan); - - - } - - /** - * Renders diagram to BufferedImage - * - * @param thread - * @param sessionContext - * @param exportPlan - * @return - * @throws Exception - */ - public static BufferedImage render( - final IThreadWorkQueue thread, - final ISessionContext sessionContext, - final ImageExportPlan exportPlan) - throws Exception - { - final DataContainer result = new DataContainer(null); - final DataContainer exception = new DataContainer(); - - final CanvasContext ctx = new CanvasContext(thread); - final AtomicReference sgProvider = new AtomicReference(); - - try { - final Semaphore done = new Semaphore(0); - // IMPORTANT: Load diagram in a different thread than the canvas context thread! - ThreadUtils.getBlockingWorkExecutor().execute(new Runnable() { - @Override - public void run() { - try { - Pair modelAndRVI = sessionContext.getSession().syncRequest(new UniqueRead>() { - @Override - public Pair perform(ReadGraph graph) throws DatabaseException { - return new Pair( resolveModel(graph, exportPlan.diagram ), resolveRVI(graph, exportPlan.diagram) ); - } - }); - - ICanvasSceneGraphProvider provider = DiagramNodeUtil.loadSceneGraphProvider(ctx, modelAndRVI.first, exportPlan.diagram, modelAndRVI.second); - sgProvider.set( provider ); - - ThreadUtils.asyncExec(thread, new Runnable() { - @Override - public void run() { - try { - ImageBuilder chassis = new ImageBuilder(exportPlan.exportLocation,exportPlan.dpi,exportPlan.size,exportPlan.margin); - - result.set(chassis.paint(ctx)); - } catch (Exception e) { - exception.set(e); - } finally { - done.release(); - } - } - }); - } catch (DatabaseException e) { - done.release(); - exception.set(e); - } catch (Throwable e) { - done.release(); - exception.set(new DatabaseException(e)); - } finally { - done.release(); - } - } - }); - - done.acquire(2); - if (exception.get() != null) - throw exception.get(); - return result.get(); - } finally { - if (sgProvider.get() != null) - sgProvider.get().dispose(); - ctx.dispose(); - } - } - - /** - * Renders diagram to BufferedImage using existing CanvasContext. - * - * Note: While the method tries to restore setting as they were, there may be visible side effects in the diagram. - * - * @param context - * @param chassis - * @param exportPlan - * @return - * @throws Exception - */ - public static BufferedImage renderLocal(final ICanvasContext context, final ICanvasChassis chassis, final ImageExportPlan exportPlan) throws Exception { - final Semaphore done = new Semaphore(0); - final DataContainer result = new DataContainer(null); - final DataContainer exception = new DataContainer(null); - ThreadUtils.asyncExec(AWTThread.getThreadAccess(), new Runnable() { - - @Override - public void run() { - try { - result.set(renderLocal(context, exportPlan, chassis)); - } catch (Exception e) { - exception.set(e); - } finally { - done.release(); - } - } - }); - done.acquire(); - if (exception.get() != null) - throw exception.get(); - return result.get(); - } - - private static BufferedImage renderLocal(ICanvasContext context, ImagePrinter.ImageExportPlan exportPlan, ICanvasChassis chassis) throws Exception { - TransformUtil util = context.getSingleItem(TransformUtil.class); - AffineTransform at = util.getTransform(); - final NavigationNode node = NodeUtil.findNodeById(context.getSceneGraph(), SceneGraphConstants.NAVIGATION_NODE_PATH); - boolean adapt = true; - if (node != null) { - adapt = node.getAdaptViewportToResizedControl(); - // prevent NavigationNode from re-scaling when it detects canvas size change. - node.setAdaptViewportToResizedControl(false); - } - - ImageBuilder builder = new ImageBuilder(exportPlan.exportLocation,exportPlan.dpi,exportPlan.size,exportPlan.margin); - BufferedImage image = builder.paint(context); - - util.setTransform(at); - - - if (node != null && adapt != false) { - if (chassis instanceof SWTChassis) { - ((SWTChassis) chassis).getAWTComponent().repaint(); - } - // restore re-scaling setting (need to be scheduled, so that Diagram canvas is re-painted once. - final boolean b = adapt; - ThreadUtils.asyncExec(AWTThread.getThreadAccess(), new Runnable() { - @Override - public void run() { - node.setAdaptViewportToResizedControl(b); - } - }); - } - - return image; - } - - - - private static Resource resolveModel(ReadGraph graph, Resource diagram) throws DatabaseException { - ModelingResources mod = ModelingResources.getInstance(graph); - Resource composite = graph.getSingleObject(diagram, mod.DiagramToComposite); - Resource model = graph.syncRequest(new PossibleIndexRoot(composite)); - if (model == null) - throw new ValidationException("no model found for composite " + NameUtils.getSafeName(graph, composite)); - return model; - } - - - - private static String resolveRVI(ReadGraph graph, Resource diagram) throws DatabaseException { - ModelingResources mod = ModelingResources.getInstance(graph); - Resource composite = graph.getSingleObject(diagram, mod.DiagramToComposite); - final ResourceArray compositePath = StructuralVariables.getCompositeArray(graph, composite); - final ResourceArray variablePath = compositePath.removeFromBeginning(1); - return StructuralVariables.getRVI(graph, variablePath); - } - - -} +package org.simantics.diagram.export; + +import java.awt.Point; +import java.awt.geom.AffineTransform; +import java.awt.image.BufferedImage; +import java.io.File; +import java.util.concurrent.Semaphore; +import java.util.concurrent.atomic.AtomicReference; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.SubMonitor; +import org.simantics.Simantics; +import org.simantics.db.ReadGraph; +import org.simantics.db.Resource; +import org.simantics.db.common.ResourceArray; +import org.simantics.db.common.request.PossibleIndexRoot; +import org.simantics.db.common.request.UniqueRead; +import org.simantics.db.common.utils.NameUtils; +import org.simantics.db.exception.DatabaseException; +import org.simantics.db.exception.ValidationException; +import org.simantics.db.management.ISessionContext; +import org.simantics.diagram.elements.DiagramNodeUtil; +import org.simantics.g2d.canvas.ICanvasContext; +import org.simantics.g2d.canvas.impl.CanvasContext; +import org.simantics.g2d.chassis.ICanvasChassis; +import org.simantics.g2d.chassis.SWTChassis; +import org.simantics.g2d.participant.TransformUtil; +import org.simantics.g2d.scenegraph.ICanvasSceneGraphProvider; +import org.simantics.g2d.scenegraph.SceneGraphConstants; +import org.simantics.modeling.ModelingResources; +import org.simantics.scenegraph.g2d.nodes.NavigationNode; +import org.simantics.scenegraph.utils.NodeUtil; +import org.simantics.structural2.StructuralVariables; +import org.simantics.utils.DataContainer; +import org.simantics.utils.datastructures.Pair; +import org.simantics.utils.threads.AWTThread; +import org.simantics.utils.threads.IThreadWorkQueue; +import org.simantics.utils.threads.ThreadUtils; +import org.simantics.utils.threads.WorkerThread; + +public class ImagePrinter { + + public static class ImageExportPlan { + public File exportLocation; + public String name; + public Resource diagram; + + // use dpi or size, not both. + public Double dpi; + public Point size; + public double margin; + } + + /** + * @param monitor + * the progress monitor to use for reporting progress to the + * user. It is the caller's responsibility to call done() on the + * given monitor. Accepts null, indicating that no + * progress should be reported and that the operation cannot be + * cancelled. + * + * @param exportPath + * @throws Exception + */ + public static BufferedImage printToImage(IProgressMonitor monitor, ImageExportPlan exportPlan) throws Exception { + + + SubMonitor progress = SubMonitor.convert(monitor, "Export to Image", 1); + + WorkerThread workerThread = new WorkerThread("Diagram Image Painter"); + workerThread.start(); + String loc; + if (exportPlan.exportLocation == null) + loc = "clipboard"; + else + loc = exportPlan.exportLocation.getAbsolutePath(); + progress.beginTask("Writing " + exportPlan.name + " to " + loc, 1); + + return render(workerThread, Simantics.getSessionContext(), exportPlan); + + + } + + /** + * Renders diagram to BufferedImage + * + * @param thread + * @param sessionContext + * @param exportPlan + * @return + * @throws Exception + */ + public static BufferedImage render( + final IThreadWorkQueue thread, + final ISessionContext sessionContext, + final ImageExportPlan exportPlan) + throws Exception + { + final DataContainer result = new DataContainer(null); + final DataContainer exception = new DataContainer(); + + final CanvasContext ctx = new CanvasContext(thread); + final AtomicReference sgProvider = new AtomicReference(); + + try { + final Semaphore done = new Semaphore(0); + // IMPORTANT: Load diagram in a different thread than the canvas context thread! + ThreadUtils.getBlockingWorkExecutor().execute(new Runnable() { + @Override + public void run() { + try { + Pair modelAndRVI = sessionContext.getSession().syncRequest(new UniqueRead>() { + @Override + public Pair perform(ReadGraph graph) throws DatabaseException { + return new Pair( resolveModel(graph, exportPlan.diagram ), resolveRVI(graph, exportPlan.diagram) ); + } + }); + + ICanvasSceneGraphProvider provider = DiagramNodeUtil.loadSceneGraphProvider(ctx, modelAndRVI.first, exportPlan.diagram, modelAndRVI.second); + sgProvider.set( provider ); + + ThreadUtils.asyncExec(thread, new Runnable() { + @Override + public void run() { + try { + ImageBuilder chassis = new ImageBuilder(exportPlan.exportLocation,exportPlan.dpi,exportPlan.size,exportPlan.margin); + + result.set(chassis.paint(ctx)); + } catch (Exception e) { + exception.set(e); + } finally { + done.release(); + } + } + }); + } catch (DatabaseException e) { + done.release(); + exception.set(e); + } catch (Throwable e) { + done.release(); + exception.set(new DatabaseException(e)); + } finally { + done.release(); + } + } + }); + + done.acquire(2); + if (exception.get() != null) + throw exception.get(); + return result.get(); + } finally { + if (sgProvider.get() != null) + sgProvider.get().dispose(); + ctx.dispose(); + } + } + + /** + * Renders diagram to BufferedImage using existing CanvasContext. + * + * Note: While the method tries to restore setting as they were, there may be visible side effects in the diagram. + * + * @param context + * @param chassis + * @param exportPlan + * @return + * @throws Exception + */ + public static BufferedImage renderLocal(final ICanvasContext context, final ICanvasChassis chassis, final ImageExportPlan exportPlan) throws Exception { + final Semaphore done = new Semaphore(0); + final DataContainer result = new DataContainer(null); + final DataContainer exception = new DataContainer(null); + ThreadUtils.asyncExec(AWTThread.getThreadAccess(), new Runnable() { + + @Override + public void run() { + try { + result.set(renderLocal(context, exportPlan, chassis)); + } catch (Exception e) { + exception.set(e); + } finally { + done.release(); + } + } + }); + done.acquire(); + if (exception.get() != null) + throw exception.get(); + return result.get(); + } + + private static BufferedImage renderLocal(ICanvasContext context, ImagePrinter.ImageExportPlan exportPlan, ICanvasChassis chassis) throws Exception { + TransformUtil util = context.getSingleItem(TransformUtil.class); + AffineTransform at = util.getTransform(); + final NavigationNode node = NodeUtil.findNodeById(context.getSceneGraph(), SceneGraphConstants.NAVIGATION_NODE_PATH); + boolean adapt = true; + if (node != null) { + adapt = node.getAdaptViewportToResizedControl(); + // prevent NavigationNode from re-scaling when it detects canvas size change. + node.setAdaptViewportToResizedControl(false); + } + + ImageBuilder builder = new ImageBuilder(exportPlan.exportLocation,exportPlan.dpi,exportPlan.size,exportPlan.margin); + BufferedImage image = builder.paint(context); + + util.setTransform(at); + + + if (node != null && adapt != false) { + if (chassis instanceof SWTChassis) { + ((SWTChassis) chassis).getAWTComponent().repaint(); + } + // restore re-scaling setting (need to be scheduled, so that Diagram canvas is re-painted once. + final boolean b = adapt; + ThreadUtils.asyncExec(AWTThread.getThreadAccess(), new Runnable() { + @Override + public void run() { + node.setAdaptViewportToResizedControl(b); + } + }); + } + + return image; + } + + + + private static Resource resolveModel(ReadGraph graph, Resource diagram) throws DatabaseException { + ModelingResources mod = ModelingResources.getInstance(graph); + Resource composite = graph.getSingleObject(diagram, mod.DiagramToComposite); + Resource model = graph.syncRequest(new PossibleIndexRoot(composite)); + if (model == null) + throw new ValidationException("no model found for composite " + NameUtils.getSafeName(graph, composite)); + return model; + } + + + + private static String resolveRVI(ReadGraph graph, Resource diagram) throws DatabaseException { + ModelingResources mod = ModelingResources.getInstance(graph); + Resource composite = graph.getSingleObject(diagram, mod.DiagramToComposite); + final ResourceArray compositePath = StructuralVariables.getCompositeArray(graph, composite); + final ResourceArray variablePath = compositePath.removeFromBeginning(1); + return StructuralVariables.getRVI(graph, variablePath); + } + + +}