Home | History | Annotate | Download | only in ui
      1 package com.android.hierarchyviewer.ui;
      2 
      3 import com.android.ddmlib.IDevice;
      4 import com.android.ddmlib.RawImage;
      5 import com.android.hierarchyviewer.scene.ViewNode;
      6 import com.android.hierarchyviewer.ui.util.IconLoader;
      7 import com.android.hierarchyviewer.ui.util.PngFileFilter;
      8 import com.android.hierarchyviewer.util.WorkerThread;
      9 
     10 import java.awt.AlphaComposite;
     11 import java.awt.BorderLayout;
     12 import java.awt.Color;
     13 import java.awt.Dimension;
     14 import java.awt.FlowLayout;
     15 import java.awt.Graphics;
     16 import java.awt.Graphics2D;
     17 import java.awt.GridBagConstraints;
     18 import java.awt.GridBagLayout;
     19 import java.awt.Insets;
     20 import java.awt.Point;
     21 import java.awt.Rectangle;
     22 import java.awt.RenderingHints;
     23 import java.awt.event.ActionEvent;
     24 import java.awt.event.ActionListener;
     25 import java.awt.event.MouseAdapter;
     26 import java.awt.event.MouseEvent;
     27 import java.awt.event.MouseMotionAdapter;
     28 import java.awt.event.MouseWheelEvent;
     29 import java.awt.event.MouseWheelListener;
     30 import java.awt.image.BufferedImage;
     31 import java.io.File;
     32 import java.io.IOException;
     33 import java.util.concurrent.ExecutionException;
     34 
     35 import javax.imageio.ImageIO;
     36 import javax.swing.BorderFactory;
     37 import javax.swing.Box;
     38 import javax.swing.JButton;
     39 import javax.swing.JCheckBox;
     40 import javax.swing.JComponent;
     41 import javax.swing.JFileChooser;
     42 import javax.swing.JLabel;
     43 import javax.swing.JPanel;
     44 import javax.swing.JScrollPane;
     45 import javax.swing.JSlider;
     46 import javax.swing.SwingUtilities;
     47 import javax.swing.SwingWorker;
     48 import javax.swing.Timer;
     49 import javax.swing.event.ChangeEvent;
     50 import javax.swing.event.ChangeListener;
     51 
     52 class ScreenViewer extends JPanel implements ActionListener {
     53     private final Workspace workspace;
     54     private final IDevice device;
     55 
     56     private GetScreenshotTask task;
     57     private BufferedImage image;
     58     private int[] scanline;
     59     private volatile boolean isLoading;
     60 
     61     private BufferedImage overlay;
     62     private AlphaComposite overlayAlpha = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.3f);
     63 
     64     private ScreenViewer.LoupeStatus status;
     65     private ScreenViewer.LoupeViewer loupe;
     66     private ScreenViewer.Crosshair crosshair;
     67 
     68     private int zoom = 8;
     69     private int y = 0;
     70 
     71     private Timer timer;
     72     private ViewNode node;
     73 
     74     private JSlider zoomSlider;
     75 
     76     ScreenViewer(Workspace workspace, IDevice device, int spacing) {
     77         setLayout(new GridBagLayout());
     78         setOpaque(false);
     79 
     80         this.workspace = workspace;
     81         this.device = device;
     82 
     83         timer = new Timer(5000, this);
     84         timer.setInitialDelay(0);
     85         timer.setRepeats(true);
     86 
     87         JPanel panel = buildViewerAndControls();
     88         add(panel, new GridBagConstraints(0, 0, 1, 1, 0.3f, 1.0f,
     89                 GridBagConstraints.FIRST_LINE_START, GridBagConstraints.BOTH,
     90                 new Insets(0, 0, 0, 0), 0, 0));
     91 
     92         JPanel loupePanel = buildLoupePanel(spacing);
     93         add(loupePanel, new GridBagConstraints(1, 0, 1, 1, 0.7f, 1.0f,
     94                 GridBagConstraints.FIRST_LINE_START, GridBagConstraints.BOTH,
     95                 new Insets(0, 0, 0, 0), 0, 0));
     96 
     97         SwingUtilities.invokeLater(new Runnable() {
     98             public void run() {
     99                 timer.start();
    100             }
    101         });
    102     }
    103 
    104     private JPanel buildLoupePanel(int spacing) {
    105         loupe = new LoupeViewer();
    106         loupe.addMouseWheelListener(new WheelZoomListener());
    107         CrosshairPanel crosshairPanel = new CrosshairPanel(loupe);
    108 
    109         JPanel loupePanel = new JPanel(new BorderLayout());
    110         loupePanel.add(crosshairPanel);
    111         status = new LoupeStatus();
    112         loupePanel.add(status, BorderLayout.SOUTH);
    113 
    114         loupePanel.setBorder(BorderFactory.createEmptyBorder(0, spacing, 0, 0));
    115         return loupePanel;
    116     }
    117 
    118     private class WheelZoomListener implements MouseWheelListener {
    119         public void mouseWheelMoved(MouseWheelEvent e) {
    120             if (zoomSlider != null) {
    121                 int val = zoomSlider.getValue();
    122                 val -= e.getWheelRotation() * 2;
    123                 zoomSlider.setValue(val);
    124             }
    125         }
    126     }
    127 
    128     private JPanel buildViewerAndControls() {
    129         JPanel panel = new JPanel(new GridBagLayout());
    130         crosshair = new Crosshair(new ScreenshotViewer());
    131         crosshair.addMouseWheelListener(new WheelZoomListener());
    132         JScrollPane scroller = new JScrollPane(crosshair);
    133         scroller.setPreferredSize(new Dimension(320, 480));
    134         scroller.setBorder(null);
    135         panel.add(scroller,
    136                 new GridBagConstraints(0, y++, 2, 1, 1.0f, 1.0f,
    137                     GridBagConstraints.FIRST_LINE_START, GridBagConstraints.BOTH,
    138                     new Insets(0, 0, 0, 0), 0, 0));
    139         buildSlider(panel, "Overlay:", "0%", "100%", 0, 100, 30, 1).addChangeListener(
    140                 new ChangeListener() {
    141                     public void stateChanged(ChangeEvent event) {
    142                         float opacity = ((JSlider) event.getSource()).getValue() / 100.0f;
    143                         overlayAlpha = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, opacity);
    144                         repaint();
    145                     }
    146         });
    147         buildOverlayExtraControls(panel);
    148         buildSlider(panel, "Refresh Rate:", "1s", "40s", 1, 40, 5, 1).addChangeListener(
    149                 new ChangeListener() {
    150                     public void stateChanged(ChangeEvent event) {
    151                         int rate = ((JSlider) event.getSource()).getValue() * 1000;
    152                         timer.setDelay(rate);
    153                         timer.setInitialDelay(0);
    154                         timer.restart();
    155                     }
    156         });
    157         zoomSlider = buildSlider(panel, "Zoom:", "2x", "24x", 2, 24, 8, 2);
    158         zoomSlider.addChangeListener(
    159                 new ChangeListener() {
    160                     public void stateChanged(ChangeEvent event) {
    161                         zoom = ((JSlider) event.getSource()).getValue();
    162                         loupe.clearGrid = true;
    163                         loupe.moveToPoint(crosshair.crosshair.x, crosshair.crosshair.y);
    164                         repaint();
    165                     }
    166         });
    167         panel.add(Box.createVerticalGlue(),
    168                 new GridBagConstraints(0, y++, 2, 1, 1.0f, 1.0f,
    169                     GridBagConstraints.FIRST_LINE_START, GridBagConstraints.NONE,
    170                     new Insets(0, 0, 0, 0), 0, 0));
    171         return panel;
    172     }
    173 
    174     private void buildOverlayExtraControls(JPanel panel) {
    175         JPanel extras = new JPanel(new FlowLayout(FlowLayout.LEFT, 0, 0));
    176 
    177         JButton loadOverlay = new JButton("Load...");
    178         loadOverlay.addActionListener(new ActionListener() {
    179             public void actionPerformed(ActionEvent event) {
    180                 SwingWorker<?, ?> worker = openOverlay();
    181                 if (worker != null) {
    182                     worker.execute();
    183                 }
    184             }
    185         });
    186         extras.add(loadOverlay);
    187 
    188         JCheckBox showInLoupe = new JCheckBox("Show in Loupe");
    189         showInLoupe.setSelected(false);
    190         showInLoupe.addActionListener(new ActionListener() {
    191             public void actionPerformed(ActionEvent event) {
    192                 loupe.showOverlay = ((JCheckBox) event.getSource()).isSelected();
    193                 loupe.repaint();
    194             }
    195         });
    196         extras.add(showInLoupe);
    197 
    198         panel.add(extras, new GridBagConstraints(1, y++, 1, 1, 1.0f, 0.0f,
    199                     GridBagConstraints.LINE_START, GridBagConstraints.NONE,
    200                         new Insets(0, 0, 0, 0), 0, 0));
    201     }
    202 
    203     public SwingWorker<?, ?> openOverlay() {
    204         JFileChooser chooser = new JFileChooser();
    205         chooser.setFileFilter(new PngFileFilter());
    206         int choice = chooser.showOpenDialog(this);
    207         if (choice == JFileChooser.APPROVE_OPTION) {
    208             return new OpenOverlayTask(chooser.getSelectedFile());
    209         } else {
    210             return null;
    211         }
    212     }
    213 
    214     private JSlider buildSlider(JPanel panel, String title, String minName, String maxName,
    215             int min, int max, int value, int tick) {
    216         panel.add(new JLabel(title), new GridBagConstraints(0, y, 1, 1, 1.0f, 0.0f,
    217                     GridBagConstraints.LINE_END, GridBagConstraints.NONE,
    218                     new Insets(0, 0, 0, 6), 0, 0));
    219         JPanel sliderPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 0, 0));
    220         sliderPanel.add(new JLabel(minName));
    221         JSlider slider = new JSlider(min, max, value);
    222         slider.setMinorTickSpacing(tick);
    223         slider.setMajorTickSpacing(tick);
    224         slider.setSnapToTicks(true);
    225         sliderPanel.add(slider);
    226         sliderPanel.add(new JLabel(maxName));
    227         panel.add(sliderPanel, new GridBagConstraints(1, y++, 1, 1, 1.0f, 0.0f,
    228                     GridBagConstraints.FIRST_LINE_START, GridBagConstraints.NONE,
    229                         new Insets(0, 0, 0, 0), 0, 0));
    230         return slider;
    231     }
    232 
    233     void stop() {
    234         timer.stop();
    235     }
    236 
    237     void start() {
    238         timer.start();
    239     }
    240 
    241     void select(ViewNode node) {
    242         this.node = node;
    243         repaint();
    244     }
    245 
    246     class LoupeViewer extends JComponent {
    247         private final Color lineColor = new Color(1.0f, 1.0f, 1.0f, 0.3f);
    248 
    249         private int width;
    250         private int height;
    251         private BufferedImage grid;
    252         private int left;
    253         private int top;
    254         public boolean clearGrid;
    255 
    256         private final Rectangle clip = new Rectangle();
    257         private boolean showOverlay = false;
    258 
    259         LoupeViewer() {
    260             addMouseListener(new MouseAdapter() {
    261                 @Override
    262                 public void mousePressed(MouseEvent event) {
    263                     moveToPoint(event);
    264                 }
    265             });
    266             addMouseMotionListener(new MouseMotionAdapter() {
    267                 @Override
    268                 public void mouseDragged(MouseEvent event) {
    269                     moveToPoint(event);
    270                 }
    271             });
    272         }
    273 
    274         @Override
    275         protected void paintComponent(Graphics g) {
    276             if (isLoading) {
    277                 return;
    278             }
    279 
    280             g.translate(-left, -top);
    281 
    282             if (image != null) {
    283                 Graphics2D g2 = (Graphics2D) g.create();
    284                 g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
    285                         RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);
    286                 g2.scale(zoom, zoom);
    287                 g2.drawImage(image, 0, 0, null);
    288                 if (overlay != null && showOverlay) {
    289                     g2.setComposite(overlayAlpha);
    290                     g2.drawImage(overlay, 0, image.getHeight() - overlay.getHeight(), null);
    291                 }
    292                 g2.dispose();
    293             }
    294 
    295             int width = getWidth();
    296             int height = getHeight();
    297 
    298             Graphics2D g2 = null;
    299             if (width != this.width || height != this.height) {
    300                 this.width = width;
    301                 this.height = height;
    302 
    303                 grid = new BufferedImage(width + zoom + 1, height + zoom + 1,
    304                         BufferedImage.TYPE_INT_ARGB);
    305                 clearGrid = true;
    306                 g2 = grid.createGraphics();
    307             } else if (clearGrid) {
    308                 g2 = grid.createGraphics();
    309                 g2.setComposite(AlphaComposite.Clear);
    310                 g2.fillRect(0, 0, grid.getWidth(), grid.getHeight());
    311                 g2.setComposite(AlphaComposite.SrcOver);
    312             }
    313 
    314             if (clearGrid) {
    315                 clearGrid = false;
    316 
    317                 g2.setColor(lineColor);
    318                 width += zoom;
    319                 height += zoom;
    320 
    321                 for (int x = zoom; x <= width; x += zoom) {
    322                     g2.drawLine(x, 0, x, height);
    323                 }
    324 
    325                 for (int y = 0; y <= height; y += zoom) {
    326                     g2.drawLine(0, y, width, y);
    327                 }
    328 
    329                 g2.dispose();
    330             }
    331 
    332             if (image != null) {
    333                 g.getClipBounds(clip);
    334                 g.clipRect(0, 0, image.getWidth() * zoom + 1, image.getHeight() * zoom + 1);
    335                 g.drawImage(grid, clip.x - clip.x % zoom, clip.y - clip.y % zoom, null);
    336             }
    337 
    338             g.translate(left, top);
    339         }
    340 
    341         void moveToPoint(MouseEvent event) {
    342             int x = Math.max(0, Math.min((event.getX() + left) / zoom, image.getWidth() - 1));
    343             int y = Math.max(0, Math.min((event.getY() + top) / zoom, image.getHeight() - 1));
    344             moveToPoint(x, y);
    345             crosshair.moveToPoint(x, y);
    346         }
    347 
    348         void moveToPoint(int x, int y) {
    349             left = x * zoom - width / 2 + zoom / 2;
    350             top = y * zoom - height / 2 + zoom / 2;
    351             repaint();
    352         }
    353     }
    354 
    355     class LoupeStatus extends JPanel {
    356         private JLabel xLabel;
    357         private JLabel yLabel;
    358         private JLabel rLabel;
    359         private JLabel gLabel;
    360         private JLabel bLabel;
    361         private JLabel hLabel;
    362         private ScreenViewer.LoupeStatus.ColoredSquare square;
    363         private Color color;
    364 
    365         LoupeStatus() {
    366             setOpaque(true);
    367             setLayout(new GridBagLayout());
    368             setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4));
    369 
    370             square = new ColoredSquare();
    371             add(square, new GridBagConstraints(0, 0, 1, 2, 0.0f, 0.0f,
    372                     GridBagConstraints.LINE_START, GridBagConstraints.NONE,
    373                     new Insets(0, 0, 0, 12), 0, 0 ));
    374 
    375             JLabel label;
    376 
    377             add(label = new JLabel("#ffffff"), new GridBagConstraints(0, 2, 1, 1, 0.0f, 0.0f,
    378                     GridBagConstraints.LINE_START, GridBagConstraints.NONE,
    379                     new Insets(0, 0, 0, 12), 0, 0 ));
    380             label.setForeground(Color.WHITE);
    381             hLabel = label;
    382 
    383             add(label = new JLabel("R:"), new GridBagConstraints(1, 0, 1, 1, 0.0f, 0.0f,
    384                     GridBagConstraints.LINE_START, GridBagConstraints.NONE,
    385                     new Insets(0, 6, 0, 6), 0, 0 ));
    386             label.setForeground(Color.WHITE);
    387             add(label = new JLabel("255"), new GridBagConstraints(2, 0, 1, 1, 0.0f, 0.0f,
    388                     GridBagConstraints.LINE_START, GridBagConstraints.NONE,
    389                     new Insets(0, 0, 0, 12), 0, 0 ));
    390             label.setForeground(Color.WHITE);
    391             rLabel = label;
    392 
    393             add(label = new JLabel("G:"), new GridBagConstraints(1, 1, 1, 1, 0.0f, 0.0f,
    394                     GridBagConstraints.LINE_START, GridBagConstraints.NONE,
    395                     new Insets(0, 6, 0, 6), 0, 0 ));
    396             label.setForeground(Color.WHITE);
    397             add(label = new JLabel("255"), new GridBagConstraints(2, 1, 1, 1, 0.0f, 0.0f,
    398                     GridBagConstraints.LINE_START, GridBagConstraints.NONE,
    399                     new Insets(0, 0, 0, 12), 0, 0 ));
    400             label.setForeground(Color.WHITE);
    401             gLabel = label;
    402 
    403             add(label = new JLabel("B:"), new GridBagConstraints(1, 2, 1, 1, 0.0f, 0.0f,
    404                     GridBagConstraints.LINE_START, GridBagConstraints.NONE,
    405                     new Insets(0, 6, 0, 6), 0, 0 ));
    406             label.setForeground(Color.WHITE);
    407             add(label = new JLabel("255"), new GridBagConstraints(2, 2, 1, 1, 0.0f, 0.0f,
    408                     GridBagConstraints.LINE_START, GridBagConstraints.NONE,
    409                     new Insets(0, 0, 0, 12), 0, 0 ));
    410             label.setForeground(Color.WHITE);
    411             bLabel = label;
    412 
    413             add(label = new JLabel("X:"), new GridBagConstraints(3, 0, 1, 1, 0.0f, 0.0f,
    414                     GridBagConstraints.LINE_START, GridBagConstraints.NONE,
    415                     new Insets(0, 6, 0, 6), 0, 0 ));
    416             label.setForeground(Color.WHITE);
    417             add(label = new JLabel("0 px"), new GridBagConstraints(4, 0, 1, 1, 0.0f, 0.0f,
    418                     GridBagConstraints.LINE_START, GridBagConstraints.NONE,
    419                     new Insets(0, 0, 0, 12), 0, 0 ));
    420             label.setForeground(Color.WHITE);
    421             xLabel = label;
    422 
    423             add(label = new JLabel("Y:"), new GridBagConstraints(3, 1, 1, 1, 0.0f, 0.0f,
    424                     GridBagConstraints.LINE_START, GridBagConstraints.NONE,
    425                     new Insets(0, 6, 0, 6), 0, 0 ));
    426             label.setForeground(Color.WHITE);
    427             add(label = new JLabel("0 px"), new GridBagConstraints(4, 1, 1, 1, 0.0f, 0.0f,
    428                     GridBagConstraints.LINE_START, GridBagConstraints.NONE,
    429                     new Insets(0, 0, 0, 12), 0, 0 ));
    430             label.setForeground(Color.WHITE);
    431             yLabel = label;
    432 
    433             add(Box.createHorizontalGlue(), new GridBagConstraints(5, 0, 1, 1, 1.0f, 0.0f,
    434                     GridBagConstraints.LINE_START, GridBagConstraints.BOTH,
    435                     new Insets(0, 0, 0, 0), 0, 0 ));
    436         }
    437 
    438         @Override
    439         protected void paintComponent(Graphics g) {
    440             g.setColor(Color.BLACK);
    441             g.fillRect(0, 0, getWidth(), getHeight());
    442         }
    443 
    444         void showPixel(int x, int y) {
    445             xLabel.setText(x + " px");
    446             yLabel.setText(y + " px");
    447 
    448             int pixel = image.getRGB(x, y);
    449             color = new Color(pixel);
    450             hLabel.setText("#" + Integer.toHexString(pixel));
    451             rLabel.setText(String.valueOf((pixel >> 16) & 0xff));
    452             gLabel.setText(String.valueOf((pixel >>  8) & 0xff));
    453             bLabel.setText(String.valueOf((pixel      ) & 0xff));
    454 
    455             square.repaint();
    456         }
    457 
    458         private class ColoredSquare extends JComponent {
    459             @Override
    460             public Dimension getPreferredSize() {
    461                 Dimension d = super.getPreferredSize();
    462                 d.width = 60;
    463                 d.height = 30;
    464                 return d;
    465             }
    466 
    467             @Override
    468             protected void paintComponent(Graphics g) {
    469                 g.setColor(color);
    470                 g.fillRect(0, 0, getWidth(), getHeight());
    471 
    472                 g.setColor(Color.WHITE);
    473                 g.drawRect(0, 0, getWidth() - 1, getHeight() - 1);
    474             }
    475         }
    476     }
    477 
    478     class Crosshair extends JPanel {
    479         // magenta = 0xff5efe
    480         private final Color crosshairColor = new Color(0x00ffff);
    481         Point crosshair = new Point();
    482         private int width;
    483         private int height;
    484         private final ScreenshotViewer screenshotViewer;
    485 
    486         Crosshair(ScreenshotViewer screenshotViewer) {
    487             this.screenshotViewer = screenshotViewer;
    488             setOpaque(true);
    489             setLayout(new BorderLayout());
    490             add(screenshotViewer);
    491             addMouseListener(new MouseAdapter() {
    492                 @Override
    493                 public void mousePressed(MouseEvent event) {
    494                     moveToPoint(event);
    495                 }
    496             });
    497             addMouseMotionListener(new MouseMotionAdapter() {
    498                 @Override
    499                 public void mouseDragged(MouseEvent event) {
    500                     moveToPoint(event);
    501                 }
    502             });
    503         }
    504 
    505         void moveToPoint(int x, int y) {
    506             crosshair.x = x;
    507             crosshair.y = y;
    508             status.showPixel(crosshair.x, crosshair.y);
    509             repaint();
    510         }
    511 
    512         private void moveToPoint(MouseEvent event) {
    513             crosshair.x = Math.max(0, Math.min(image.getWidth() - 1, event.getX()));
    514             crosshair.y = Math.max(0, Math.min(image.getHeight() - 1, event.getY()));
    515             loupe.moveToPoint(crosshair.x, crosshair.y);
    516             status.showPixel(crosshair.x, crosshair.y);
    517 
    518             repaint();
    519         }
    520 
    521         @Override
    522         public Dimension getPreferredSize() {
    523             return screenshotViewer.getPreferredSize();
    524         }
    525 
    526         @Override
    527         public Dimension getMaximumSize() {
    528             return screenshotViewer.getPreferredSize();
    529         }
    530 
    531         @Override
    532         public void paint(Graphics g) {
    533             super.paint(g);
    534 
    535             if (crosshair == null || width != getWidth() || height != getHeight()) {
    536                 width = getWidth();
    537                 height = getHeight();
    538                 crosshair = new Point(width / 2, height / 2);
    539             }
    540 
    541             g.setColor(crosshairColor);
    542 
    543             g.drawLine(crosshair.x, 0, crosshair.x, height);
    544             g.drawLine(0, crosshair.y, width, crosshair.y);
    545         }
    546 
    547         @Override
    548         protected void paintComponent(Graphics g) {
    549             super.paintComponent(g);
    550             g.setColor(Color.BLACK);
    551             g.fillRect(0, 0, getWidth(), getHeight());
    552         }
    553     }
    554 
    555     class ScreenshotViewer extends JComponent {
    556         private final Color boundsColor = new Color(0xff5efe);
    557 
    558         ScreenshotViewer() {
    559             setOpaque(true);
    560         }
    561 
    562         @Override
    563         protected void paintComponent(Graphics g) {
    564             g.setColor(Color.BLACK);
    565             g.fillRect(0, 0, getWidth(), getHeight());
    566 
    567             if (isLoading) {
    568                 return;
    569             }
    570 
    571             if (image != null) {
    572                 g.drawImage(image, 0, 0, null);
    573                 if (overlay != null) {
    574                     Graphics2D g2 = (Graphics2D) g.create();
    575                     g2.setComposite(overlayAlpha);
    576                     g2.drawImage(overlay, 0, image.getHeight() - overlay.getHeight(), null);
    577                 }
    578             }
    579 
    580             if (node != null) {
    581                 Graphics s = g.create();
    582                 s.setColor(boundsColor);
    583                 ViewNode p = node.parent;
    584                 while (p != null) {
    585                     s.translate(p.left - p.scrollX, p.top - p.scrollY);
    586                     p = p.parent;
    587                 }
    588                 s.drawRect(node.left, node.top, node.width - 1, node.height - 1);
    589                 s.translate(node.left, node.top);
    590 
    591                 s.setXORMode(Color.WHITE);
    592                 if ((node.paddingBottom | node.paddingLeft |
    593                         node.paddingTop | node.paddingRight) != 0) {
    594                     s.setColor(Color.BLACK);
    595                     s.drawRect(node.paddingLeft, node.paddingTop,
    596                             node.width - node.paddingRight - node.paddingLeft - 1,
    597                             node.height - node.paddingBottom - node.paddingTop - 1);
    598                 }
    599                 if (node.hasMargins && (node.marginLeft | node.marginBottom |
    600                         node.marginRight | node.marginRight) != 0) {
    601                     s.setColor(Color.BLACK);
    602                     s.drawRect(-node.marginLeft, -node.marginTop,
    603                             node.marginLeft + node.width + node.marginRight - 1,
    604                             node.marginTop + node.height + node.marginBottom - 1);
    605                 }
    606 
    607                 s.dispose();
    608             }
    609         }
    610 
    611         @Override
    612         public Dimension getPreferredSize() {
    613             if (image == null) {
    614                 return new Dimension(320, 480);
    615             }
    616             return new Dimension(image.getWidth(), image.getHeight());
    617         }
    618     }
    619 
    620     private class CrosshairPanel extends JPanel {
    621         private final Color crosshairColor = new Color(0xff5efe);
    622         private final Insets insets = new Insets(0, 0, 0, 0);
    623 
    624         CrosshairPanel(LoupeViewer loupe) {
    625             setLayout(new BorderLayout());
    626             add(loupe);
    627         }
    628 
    629         @Override
    630         public void paint(Graphics g) {
    631             super.paint(g);
    632 
    633             g.setColor(crosshairColor);
    634 
    635             int width = getWidth();
    636             int height = getHeight();
    637 
    638             getInsets(insets);
    639 
    640             int x = (width - insets.left - insets.right) / 2;
    641             int y = (height - insets.top - insets.bottom) / 2;
    642 
    643             g.drawLine(insets.left + x, insets.top, insets.left + x, height - insets.bottom);
    644             g.drawLine(insets.left, insets.top + y, width - insets.right, insets.top + y);
    645         }
    646 
    647         @Override
    648         protected void paintComponent(Graphics g) {
    649             g.setColor(Color.BLACK);
    650             Insets insets = getInsets();
    651             g.fillRect(insets.left, insets.top, getWidth() - insets.left - insets.right,
    652                     getHeight() - insets.top - insets.bottom);
    653         }
    654     }
    655 
    656     public void actionPerformed(ActionEvent event) {
    657         if (task != null && !task.isDone()) {
    658             return;
    659         }
    660         task = new GetScreenshotTask();
    661         task.execute();
    662     }
    663 
    664     private class GetScreenshotTask extends SwingWorker<Boolean, Void> {
    665         private GetScreenshotTask() {
    666             workspace.beginTask();
    667         }
    668 
    669         @Override
    670         @WorkerThread
    671         protected Boolean doInBackground() throws Exception {
    672             RawImage rawImage;
    673             try {
    674                 rawImage = device.getScreenshot();
    675             } catch (IOException ioe) {
    676                 return false;
    677             }
    678 
    679             boolean resize = false;
    680             isLoading = true;
    681             try {
    682                 if (rawImage != null) {
    683                     if (image == null || rawImage.width != image.getWidth() ||
    684                             rawImage.height != image.getHeight()) {
    685                         image = new BufferedImage(rawImage.width, rawImage.height,
    686                                 BufferedImage.TYPE_INT_ARGB);
    687                         scanline = new int[rawImage.width];
    688                         resize = true;
    689                     }
    690 
    691                     switch (rawImage.bpp) {
    692                         case 16:
    693                             rawImage16toARGB(rawImage);
    694                             break;
    695                         case 32:
    696                             rawImage32toARGB(rawImage);
    697                             break;
    698                     }
    699                 }
    700             } finally {
    701                 isLoading = false;
    702             }
    703 
    704             return resize;
    705         }
    706 
    707         private int getMask(int length) {
    708             int res = 0;
    709             for (int i = 0 ; i < length ; i++) {
    710                 res = (res << 1) + 1;
    711             }
    712 
    713             return res;
    714         }
    715 
    716         private void rawImage32toARGB(RawImage rawImage) {
    717             byte[] buffer = rawImage.data;
    718             int index = 0;
    719 
    720             final int redOffset = rawImage.red_offset;
    721             final int redLength = rawImage.red_length;
    722             final int redMask = getMask(redLength);
    723             final int greenOffset = rawImage.green_offset;
    724             final int greenLength = rawImage.green_length;
    725             final int greenMask = getMask(greenLength);
    726             final int blueOffset = rawImage.blue_offset;
    727             final int blueLength = rawImage.blue_length;
    728             final int blueMask = getMask(blueLength);
    729             final int alphaLength = rawImage.alpha_length;
    730             final int alphaOffset = rawImage.alpha_offset;
    731             final int alphaMask = getMask(alphaLength);
    732 
    733             for (int y = 0 ; y < rawImage.height ; y++) {
    734                 for (int x = 0 ; x < rawImage.width ; x++) {
    735                     int value = buffer[index++] & 0x00FF;
    736                     value |= (buffer[index++] & 0x00FF) << 8;
    737                     value |= (buffer[index++] & 0x00FF) << 16;
    738                     value |= (buffer[index++] & 0x00FF) << 24;
    739 
    740                     int r = ((value >>> redOffset) & redMask) << (8 - redLength);
    741                     int g = ((value >>> greenOffset) & greenMask) << (8 - greenLength);
    742                     int b = ((value >>> blueOffset) & blueMask) << (8 - blueLength);
    743                     int a = 0xFF;
    744 
    745                     if (alphaLength != 0) {
    746                         a = ((value >>> alphaOffset) & alphaMask) << (8 - alphaLength);
    747                     }
    748 
    749                     scanline[x] = a << 24 | r << 16 | g << 8 | b;
    750                 }
    751 
    752                 image.setRGB(0, y, rawImage.width, 1, scanline,
    753                         0, rawImage.width);
    754             }
    755         }
    756 
    757         private void rawImage16toARGB(RawImage rawImage) {
    758             byte[] buffer = rawImage.data;
    759             int index = 0;
    760 
    761             for (int y = 0 ; y < rawImage.height ; y++) {
    762                 for (int x = 0 ; x < rawImage.width ; x++) {
    763                     int value = buffer[index++] & 0x00FF;
    764                     value |= (buffer[index++] << 8) & 0x0FF00;
    765 
    766                     int r = ((value >> 11) & 0x01F) << 3;
    767                     int g = ((value >> 5) & 0x03F) << 2;
    768                     int b = ((value     ) & 0x01F) << 3;
    769 
    770                     scanline[x] = 0xFF << 24 | r << 16 | g << 8 | b;
    771                 }
    772 
    773                 image.setRGB(0, y, rawImage.width, 1, scanline,
    774                         0, rawImage.width);
    775             }
    776         }
    777 
    778         @Override
    779         protected void done() {
    780             workspace.endTask();
    781             try {
    782                 if (get()) {
    783                     validate();
    784                     crosshair.crosshair = new Point(image.getWidth() / 2,
    785                             image.getHeight() / 2);
    786                     status.showPixel(image.getWidth() / 2, image.getHeight() / 2);
    787                     loupe.moveToPoint(image.getWidth() / 2, image.getHeight() / 2);
    788                 }
    789             } catch (InterruptedException e) {
    790                 e.printStackTrace();
    791             } catch (ExecutionException e) {
    792                 e.printStackTrace();
    793             }
    794             repaint();
    795         }
    796     }
    797 
    798     private class OpenOverlayTask extends SwingWorker<BufferedImage, Void> {
    799         private File file;
    800 
    801         private OpenOverlayTask(File file) {
    802             this.file = file;
    803             workspace.beginTask();
    804         }
    805 
    806         @Override
    807         @WorkerThread
    808         protected BufferedImage doInBackground() {
    809             try {
    810                 return IconLoader.toCompatibleImage(ImageIO.read(file));
    811             } catch (IOException ex) {
    812                 ex.printStackTrace();
    813             }
    814             return null;
    815         }
    816 
    817         @Override
    818         protected void done() {
    819             try {
    820                 overlay = get();
    821                 repaint();
    822             } catch (InterruptedException e) {
    823                 e.printStackTrace();
    824             } catch (ExecutionException e) {
    825                 e.printStackTrace();
    826             } finally {
    827                 workspace.endTask();
    828             }
    829         }
    830     }
    831 }
    832