]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.diagram/src/org/simantics/diagram/export/ImagePrinter.java
Fixed all line endings of the repository
[simantics/platform.git] / bundles / org.simantics.diagram / src / org / simantics / diagram / export / ImagePrinter.java
1 package org.simantics.diagram.export;
2
3 import java.awt.Point;
4 import java.awt.geom.AffineTransform;
5 import java.awt.image.BufferedImage;
6 import java.io.File;
7 import java.util.concurrent.Semaphore;
8 import java.util.concurrent.atomic.AtomicReference;
9
10 import org.eclipse.core.runtime.IProgressMonitor;
11 import org.eclipse.core.runtime.SubMonitor;
12 import org.simantics.Simantics;
13 import org.simantics.db.ReadGraph;
14 import org.simantics.db.Resource;
15 import org.simantics.db.common.ResourceArray;
16 import org.simantics.db.common.request.PossibleIndexRoot;
17 import org.simantics.db.common.request.UniqueRead;
18 import org.simantics.db.common.utils.NameUtils;
19 import org.simantics.db.exception.DatabaseException;
20 import org.simantics.db.exception.ValidationException;
21 import org.simantics.db.management.ISessionContext;
22 import org.simantics.diagram.elements.DiagramNodeUtil;
23 import org.simantics.g2d.canvas.ICanvasContext;
24 import org.simantics.g2d.canvas.impl.CanvasContext;
25 import org.simantics.g2d.chassis.ICanvasChassis;
26 import org.simantics.g2d.chassis.SWTChassis;
27 import org.simantics.g2d.participant.TransformUtil;
28 import org.simantics.g2d.scenegraph.ICanvasSceneGraphProvider;
29 import org.simantics.g2d.scenegraph.SceneGraphConstants;
30 import org.simantics.modeling.ModelingResources;
31 import org.simantics.scenegraph.g2d.nodes.NavigationNode;
32 import org.simantics.scenegraph.utils.NodeUtil;
33 import org.simantics.structural2.StructuralVariables;
34 import org.simantics.utils.DataContainer;
35 import org.simantics.utils.datastructures.Pair;
36 import org.simantics.utils.threads.AWTThread;
37 import org.simantics.utils.threads.IThreadWorkQueue;
38 import org.simantics.utils.threads.ThreadUtils;
39 import org.simantics.utils.threads.WorkerThread;
40
41 public class ImagePrinter {
42
43         public static class ImageExportPlan {
44                 public File exportLocation;
45                 public String name;
46                 public Resource diagram;
47
48                 // use dpi or size, not both.
49                 public Double dpi;
50                 public Point size;
51                 public double margin;
52         }
53
54         /**
55          * @param monitor
56          *            the progress monitor to use for reporting progress to the
57          *            user. It is the caller's responsibility to call done() on the
58          *            given monitor. Accepts <code>null</code>, indicating that no
59          *            progress should be reported and that the operation cannot be
60          *            cancelled.
61          * 
62          * @param exportPath
63          * @throws Exception
64          */
65         public static BufferedImage printToImage(IProgressMonitor monitor, ImageExportPlan exportPlan) throws Exception {
66                 
67
68                 SubMonitor progress = SubMonitor.convert(monitor, "Export to Image", 1);
69
70                 WorkerThread workerThread = new WorkerThread("Diagram Image Painter");
71                 workerThread.start();
72                 String loc;
73                 if (exportPlan.exportLocation == null)
74                         loc = "clipboard";
75                 else
76                         loc = exportPlan.exportLocation.getAbsolutePath();
77                 progress.beginTask("Writing " + exportPlan.name + " to " + loc, 1);
78         
79                 return render(workerThread, Simantics.getSessionContext(), exportPlan);
80                 
81
82         }
83
84         /**
85          * Renders diagram to BufferedImage
86          * 
87          * @param thread
88          * @param sessionContext
89          * @param exportPlan
90          * @return
91          * @throws Exception
92          */
93         public static BufferedImage render(
94             final IThreadWorkQueue thread,
95             final ISessionContext sessionContext,
96             final ImageExportPlan exportPlan)
97     throws Exception
98     {
99         final DataContainer<BufferedImage> result = new DataContainer<BufferedImage>(null);
100         final DataContainer<Exception> exception = new DataContainer<Exception>();
101
102         final CanvasContext ctx = new CanvasContext(thread);
103         final AtomicReference<ICanvasSceneGraphProvider> sgProvider = new AtomicReference<ICanvasSceneGraphProvider>();
104
105         try {
106             final Semaphore done = new Semaphore(0);
107             // IMPORTANT: Load diagram in a different thread than the canvas context thread!
108             ThreadUtils.getBlockingWorkExecutor().execute(new Runnable() {
109                 @Override
110                 public void run() {
111                     try {
112                         Pair<Resource, String> modelAndRVI = sessionContext.getSession().syncRequest(new UniqueRead<Pair<Resource, String>>() {
113                             @Override
114                             public Pair<Resource, String> perform(ReadGraph graph) throws DatabaseException {
115                                 return new Pair<Resource, String>( resolveModel(graph, exportPlan.diagram ), resolveRVI(graph, exportPlan.diagram) );
116                             }
117                         });
118
119                         ICanvasSceneGraphProvider provider = DiagramNodeUtil.loadSceneGraphProvider(ctx, modelAndRVI.first, exportPlan.diagram, modelAndRVI.second);
120                         sgProvider.set( provider );
121                         
122                         ThreadUtils.asyncExec(thread, new Runnable() {
123                             @Override
124                             public void run() {
125                                 try {
126                                     ImageBuilder chassis = new ImageBuilder(exportPlan.exportLocation,exportPlan.dpi,exportPlan.size,exportPlan.margin);
127                                     
128                                     result.set(chassis.paint(ctx));
129                                 } catch (Exception e) {
130                                         exception.set(e);
131                                 } finally {
132                                     done.release();
133                                 }
134                             }
135                         });
136                     } catch (DatabaseException e) {
137                         done.release();
138                         exception.set(e);
139                     } catch (Throwable e) {
140                         done.release();
141                         exception.set(new DatabaseException(e));
142                     } finally {
143                         done.release();
144                     }
145                 }
146             });
147
148             done.acquire(2);
149             if (exception.get() != null)
150                 throw exception.get();
151             return result.get();
152         } finally {
153             if (sgProvider.get() != null)
154                 sgProvider.get().dispose();
155             ctx.dispose();
156         }
157     }
158         
159         /**
160          * Renders diagram to BufferedImage using existing CanvasContext.
161          * 
162          * Note: While the method tries to restore setting as they were, there may be visible side effects in the diagram. 
163          * 
164          * @param context
165          * @param chassis
166          * @param exportPlan
167          * @return
168          * @throws Exception
169          */
170         public static BufferedImage renderLocal(final ICanvasContext context, final ICanvasChassis chassis, final ImageExportPlan exportPlan) throws Exception {
171                 final Semaphore done = new Semaphore(0);
172                 final DataContainer<BufferedImage> result = new DataContainer<BufferedImage>(null);
173                 final DataContainer<Exception> exception = new DataContainer<Exception>(null);
174                 ThreadUtils.asyncExec(AWTThread.getThreadAccess(), new Runnable() {
175
176                         @Override
177                         public void run() {
178                                 try {
179                                         result.set(renderLocal(context, exportPlan, chassis));
180                                 } catch (Exception e) {
181                                         exception.set(e);
182                                 } finally {
183                                         done.release();
184                                 }
185                         }
186                 });
187                 done.acquire();
188                 if (exception.get() != null)
189                         throw exception.get();
190                 return result.get();
191         }
192         
193         private static BufferedImage renderLocal(ICanvasContext context, ImagePrinter.ImageExportPlan exportPlan, ICanvasChassis chassis) throws Exception {
194                 TransformUtil util = context.getSingleItem(TransformUtil.class);
195                 AffineTransform at = util.getTransform();
196                 final NavigationNode node = NodeUtil.findNodeById(context.getSceneGraph(), SceneGraphConstants.NAVIGATION_NODE_PATH);
197                 boolean adapt = true;
198         if (node != null) {
199                 adapt = node.getAdaptViewportToResizedControl();
200                 // prevent NavigationNode from re-scaling when it detects canvas size change. 
201                 node.setAdaptViewportToResizedControl(false);
202         }
203
204                 ImageBuilder builder = new ImageBuilder(exportPlan.exportLocation,exportPlan.dpi,exportPlan.size,exportPlan.margin);
205                 BufferedImage image = builder.paint(context);
206                 
207                 util.setTransform(at);
208
209                 
210                 if (node != null && adapt != false) {
211                         if (chassis instanceof SWTChassis) {
212                                 ((SWTChassis) chassis).getAWTComponent().repaint();
213                         }
214                         // restore re-scaling setting (need to be scheduled, so that Diagram canvas is re-painted once.
215                         final boolean b = adapt;
216                         ThreadUtils.asyncExec(AWTThread.getThreadAccess(), new Runnable() {
217                                 @Override
218                                 public void run() {
219                                         node.setAdaptViewportToResizedControl(b);
220                                 }
221                         });
222                 }
223                 
224                 return image;
225         }
226         
227         
228
229     private static Resource resolveModel(ReadGraph graph, Resource diagram) throws DatabaseException {
230         ModelingResources mod = ModelingResources.getInstance(graph);
231         Resource composite = graph.getSingleObject(diagram, mod.DiagramToComposite);
232         Resource model = graph.syncRequest(new PossibleIndexRoot(composite));
233         if (model == null)
234             throw new ValidationException("no model found for composite " + NameUtils.getSafeName(graph, composite));
235         return model;
236     }
237
238
239
240     private static String resolveRVI(ReadGraph graph, Resource diagram) throws DatabaseException {
241         ModelingResources mod = ModelingResources.getInstance(graph);
242         Resource composite = graph.getSingleObject(diagram, mod.DiagramToComposite);
243         final ResourceArray compositePath = StructuralVariables.getCompositeArray(graph, composite);
244         final ResourceArray variablePath = compositePath.removeFromBeginning(1);
245         return StructuralVariables.getRVI(graph, variablePath);
246     }
247
248
249 }