• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package com.jme3.system.awt;
2 
3 import com.jme3.post.SceneProcessor;
4 import com.jme3.renderer.RenderManager;
5 import com.jme3.renderer.ViewPort;
6 import com.jme3.renderer.queue.RenderQueue;
7 import com.jme3.texture.FrameBuffer;
8 import com.jme3.texture.Image.Format;
9 import com.jme3.util.BufferUtils;
10 import com.jme3.util.Screenshots;
11 import java.awt.*;
12 import java.awt.event.ComponentAdapter;
13 import java.awt.event.ComponentEvent;
14 import java.awt.geom.AffineTransform;
15 import java.awt.image.AffineTransformOp;
16 import java.awt.image.BufferStrategy;
17 import java.awt.image.BufferedImage;
18 import java.nio.ByteBuffer;
19 import java.nio.IntBuffer;
20 import java.util.ArrayList;
21 import java.util.Arrays;
22 import java.util.concurrent.atomic.AtomicBoolean;
23 
24 public class AwtPanel extends Canvas implements SceneProcessor {
25 
26     private boolean attachAsMain = false;
27 
28     private BufferedImage img;
29     private FrameBuffer fb;
30     private ByteBuffer byteBuf;
31     private IntBuffer intBuf;
32     private RenderManager rm;
33     private PaintMode paintMode;
34     private ArrayList<ViewPort> viewPorts = new ArrayList<ViewPort>();
35 
36     // Visibility/drawing vars
37     private BufferStrategy strategy;
38     private AffineTransformOp transformOp;
39     private AtomicBoolean hasNativePeer = new AtomicBoolean(false);
40     private AtomicBoolean showing = new AtomicBoolean(false);
41     private AtomicBoolean repaintRequest = new AtomicBoolean(false);
42 
43     // Reshape vars
44     private int newWidth  = 1;
45     private int newHeight = 1;
46     private AtomicBoolean reshapeNeeded  = new AtomicBoolean(false);
47     private final Object lock = new Object();
48 
AwtPanel(PaintMode paintMode)49     public AwtPanel(PaintMode paintMode){
50         this.paintMode = paintMode;
51 
52         if (paintMode == PaintMode.Accelerated){
53             setIgnoreRepaint(true);
54         }
55 
56         addComponentListener(new ComponentAdapter(){
57             @Override
58             public void componentResized(ComponentEvent e) {
59                 synchronized (lock){
60                     int newWidth2 = Math.max(getWidth(), 1);
61                     int newHeight2 = Math.max(getHeight(), 1);
62                     if (newWidth != newWidth2 || newHeight != newHeight2){
63                         newWidth = newWidth2;
64                         newHeight = newHeight2;
65                         reshapeNeeded.set(true);
66                         System.out.println("EDT: componentResized " + newWidth + ", " + newHeight);
67                     }
68                 }
69             }
70         });
71     }
72 
73     @Override
addNotify()74     public void addNotify(){
75         super.addNotify();
76 
77         synchronized (lock){
78             hasNativePeer.set(true);
79             System.out.println("EDT: addNotify");
80         }
81 
82         requestFocusInWindow();
83     }
84 
85     @Override
removeNotify()86     public void removeNotify(){
87         synchronized (lock){
88             hasNativePeer.set(false);
89             System.out.println("EDT: removeNotify");
90         }
91 
92         super.removeNotify();
93     }
94 
95     @Override
paint(Graphics g)96     public void paint(Graphics g){
97         Graphics2D g2d = (Graphics2D) g;
98         synchronized (lock){
99             g2d.drawImage(img, transformOp, 0, 0);
100         }
101     }
102 
checkVisibilityState()103     public boolean checkVisibilityState(){
104         if (!hasNativePeer.get()){
105             if (strategy != null){
106 //                strategy.dispose();
107                 strategy = null;
108                 System.out.println("OGL: Not visible. Destroy strategy.");
109             }
110             return false;
111         }
112 
113         boolean currentShowing = isShowing();
114         if (showing.getAndSet(currentShowing) != currentShowing){
115             if (currentShowing){
116                 System.out.println("OGL: Enter showing state.");
117             }else{
118                 System.out.println("OGL: Exit showing state.");
119             }
120         }
121         return currentShowing;
122     }
123 
repaintInThread()124     public void repaintInThread(){
125         // Convert screenshot.
126         byteBuf.clear();
127         rm.getRenderer().readFrameBuffer(fb, byteBuf);
128 
129         synchronized (lock){
130             // All operations on img must be synchronized
131             // as it is accessed from EDT.
132             Screenshots.convertScreenShot2(intBuf, img);
133             repaint();
134         }
135     }
136 
drawFrameInThread()137     public void drawFrameInThread(){
138         // Convert screenshot.
139         byteBuf.clear();
140         rm.getRenderer().readFrameBuffer(fb, byteBuf);
141         Screenshots.convertScreenShot2(intBuf, img);
142 
143         synchronized (lock){
144             // All operations on strategy should be synchronized (?)
145             if (strategy == null){
146                 try {
147                     createBufferStrategy(1,
148                             new BufferCapabilities(
149                                 new ImageCapabilities(true),
150                                 new ImageCapabilities(true),
151                                 BufferCapabilities.FlipContents.UNDEFINED)
152                                         );
153                 } catch (AWTException ex) {
154                     ex.printStackTrace();
155                 }
156                 strategy = getBufferStrategy();
157                 System.out.println("OGL: Visible. Create strategy.");
158             }
159 
160             // Draw screenshot.
161             do {
162                 do {
163                     Graphics2D g2d = (Graphics2D) strategy.getDrawGraphics();
164                     if (g2d == null){
165                         System.out.println("OGL: DrawGraphics was null.");
166                         return;
167                     }
168 
169                     g2d.setRenderingHint(RenderingHints.KEY_RENDERING,
170                                          RenderingHints.VALUE_RENDER_SPEED);
171 
172                     g2d.drawImage(img, transformOp, 0, 0);
173                     g2d.dispose();
174                     strategy.show();
175                 } while (strategy.contentsRestored());
176             } while (strategy.contentsLost());
177         }
178     }
179 
isActiveDrawing()180     public boolean isActiveDrawing(){
181         return paintMode != PaintMode.OnRequest && showing.get();
182     }
183 
attachTo(boolean overrideMainFramebuffer, ViewPort ... vps)184     public void attachTo(boolean overrideMainFramebuffer, ViewPort ... vps){
185         if (viewPorts.size() > 0){
186             for (ViewPort vp : viewPorts){
187                 vp.setOutputFrameBuffer(null);
188             }
189             viewPorts.get(viewPorts.size()-1).removeProcessor(this);
190         }
191 
192         viewPorts.addAll(Arrays.asList(vps));
193         viewPorts.get(viewPorts.size()-1).addProcessor(this);
194 
195         this.attachAsMain = overrideMainFramebuffer;
196     }
197 
initialize(RenderManager rm, ViewPort vp)198     public void initialize(RenderManager rm, ViewPort vp) {
199         if (this.rm == null){
200             // First time called in OGL thread
201             this.rm = rm;
202             reshapeInThread(1, 1);
203         }
204     }
205 
reshapeInThread(int width, int height)206     private void reshapeInThread(int width, int height) {
207         byteBuf = BufferUtils.ensureLargeEnough(byteBuf, width * height * 4);
208         intBuf = byteBuf.asIntBuffer();
209 
210         fb = new FrameBuffer(width, height, 1);
211         fb.setDepthBuffer(Format.Depth);
212         fb.setColorBuffer(Format.RGB8);
213 
214         if (attachAsMain){
215             rm.getRenderer().setMainFrameBufferOverride(fb);
216         }
217 
218         synchronized (lock){
219             img = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
220         }
221 
222 //        synchronized (lock){
223 //            img = (BufferedImage) getGraphicsConfiguration().createCompatibleImage(width, height);
224 //        }
225 
226         AffineTransform tx = AffineTransform.getScaleInstance(1, -1);
227         tx.translate(0, -img.getHeight());
228         transformOp = new AffineTransformOp(tx, AffineTransformOp.TYPE_NEAREST_NEIGHBOR);
229 
230         for (ViewPort vp : viewPorts){
231             if (!attachAsMain){
232                 vp.setOutputFrameBuffer(fb);
233             }
234             vp.getCamera().resize(width, height, true);
235 
236             // NOTE: Hack alert. This is done ONLY for custom framebuffers.
237             // Main framebuffer should use RenderManager.notifyReshape().
238             for (SceneProcessor sp : vp.getProcessors()){
239                 sp.reshape(vp, width, height);
240             }
241         }
242     }
243 
isInitialized()244     public boolean isInitialized() {
245         return fb != null;
246     }
247 
preFrame(float tpf)248     public void preFrame(float tpf) {
249     }
250 
postQueue(RenderQueue rq)251     public void postQueue(RenderQueue rq) {
252     }
253 
254     @Override
invalidate()255     public void invalidate(){
256         // For "PaintMode.OnDemand" only.
257         repaintRequest.set(true);
258     }
259 
postFrame(FrameBuffer out)260     public void postFrame(FrameBuffer out) {
261         if (!attachAsMain && out != fb){
262             throw new IllegalStateException("Why did you change the output framebuffer?");
263         }
264 
265         if (reshapeNeeded.getAndSet(false)){
266             reshapeInThread(newWidth, newHeight);
267         }else{
268             if (!checkVisibilityState()){
269                 return;
270             }
271 
272             switch (paintMode){
273                 case Accelerated:
274                     drawFrameInThread();
275                     break;
276                 case Repaint:
277                     repaintInThread();
278                     break;
279                 case OnRequest:
280                     if (repaintRequest.getAndSet(false)){
281                         repaintInThread();
282                     }
283                     break;
284             }
285         }
286     }
287 
reshape(ViewPort vp, int w, int h)288     public void reshape(ViewPort vp, int w, int h) {
289     }
290 
cleanup()291     public void cleanup() {
292     }
293 }
294