Home | History | Annotate | Download | only in awt
      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 
     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
     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
     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
     96     public void paint(Graphics g){
     97         Graphics2D g2d = (Graphics2D) g;
     98         synchronized (lock){
     99             g2d.drawImage(img, transformOp, 0, 0);
    100         }
    101     }
    102 
    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 
    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 
    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 
    180     public boolean isActiveDrawing(){
    181         return paintMode != PaintMode.OnRequest && showing.get();
    182     }
    183 
    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 
    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 
    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 
    244     public boolean isInitialized() {
    245         return fb != null;
    246     }
    247 
    248     public void preFrame(float tpf) {
    249     }
    250 
    251     public void postQueue(RenderQueue rq) {
    252     }
    253 
    254     @Override
    255     public void invalidate(){
    256         // For "PaintMode.OnDemand" only.
    257         repaintRequest.set(true);
    258     }
    259 
    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 
    288     public void reshape(ViewPort vp, int w, int h) {
    289     }
    290 
    291     public void cleanup() {
    292     }
    293 }
    294