aboutsummaryrefslogtreecommitdiff
path: root/hierarchyviewer/src/com/android/hierarchyviewer/ui/ScreenViewer.java
diff options
context:
space:
mode:
Diffstat (limited to 'hierarchyviewer/src/com/android/hierarchyviewer/ui/ScreenViewer.java')
-rw-r--r--hierarchyviewer/src/com/android/hierarchyviewer/ui/ScreenViewer.java831
1 files changed, 831 insertions, 0 deletions
diff --git a/hierarchyviewer/src/com/android/hierarchyviewer/ui/ScreenViewer.java b/hierarchyviewer/src/com/android/hierarchyviewer/ui/ScreenViewer.java
new file mode 100644
index 000000000..bae1270cc
--- /dev/null
+++ b/hierarchyviewer/src/com/android/hierarchyviewer/ui/ScreenViewer.java
@@ -0,0 +1,831 @@
+package com.android.hierarchyviewer.ui;
+
+import com.android.ddmlib.IDevice;
+import com.android.ddmlib.RawImage;
+import com.android.hierarchyviewer.scene.ViewNode;
+import com.android.hierarchyviewer.ui.util.IconLoader;
+import com.android.hierarchyviewer.ui.util.PngFileFilter;
+import com.android.hierarchyviewer.util.WorkerThread;
+
+import java.awt.AlphaComposite;
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.FlowLayout;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.RenderingHints;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseMotionAdapter;
+import java.awt.event.MouseWheelEvent;
+import java.awt.event.MouseWheelListener;
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.IOException;
+import java.util.concurrent.ExecutionException;
+
+import javax.imageio.ImageIO;
+import javax.swing.BorderFactory;
+import javax.swing.Box;
+import javax.swing.JButton;
+import javax.swing.JCheckBox;
+import javax.swing.JComponent;
+import javax.swing.JFileChooser;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JSlider;
+import javax.swing.SwingUtilities;
+import javax.swing.SwingWorker;
+import javax.swing.Timer;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+
+class ScreenViewer extends JPanel implements ActionListener {
+ private final Workspace workspace;
+ private final IDevice device;
+
+ private GetScreenshotTask task;
+ private BufferedImage image;
+ private int[] scanline;
+ private volatile boolean isLoading;
+
+ private BufferedImage overlay;
+ private AlphaComposite overlayAlpha = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.3f);
+
+ private ScreenViewer.LoupeStatus status;
+ private ScreenViewer.LoupeViewer loupe;
+ private ScreenViewer.Crosshair crosshair;
+
+ private int zoom = 8;
+ private int y = 0;
+
+ private Timer timer;
+ private ViewNode node;
+
+ private JSlider zoomSlider;
+
+ ScreenViewer(Workspace workspace, IDevice device, int spacing) {
+ setLayout(new GridBagLayout());
+ setOpaque(false);
+
+ this.workspace = workspace;
+ this.device = device;
+
+ timer = new Timer(5000, this);
+ timer.setInitialDelay(0);
+ timer.setRepeats(true);
+
+ JPanel panel = buildViewerAndControls();
+ add(panel, new GridBagConstraints(0, 0, 1, 1, 0.3f, 1.0f,
+ GridBagConstraints.FIRST_LINE_START, GridBagConstraints.BOTH,
+ new Insets(0, 0, 0, 0), 0, 0));
+
+ JPanel loupePanel = buildLoupePanel(spacing);
+ add(loupePanel, new GridBagConstraints(1, 0, 1, 1, 0.7f, 1.0f,
+ GridBagConstraints.FIRST_LINE_START, GridBagConstraints.BOTH,
+ new Insets(0, 0, 0, 0), 0, 0));
+
+ SwingUtilities.invokeLater(new Runnable() {
+ public void run() {
+ timer.start();
+ }
+ });
+ }
+
+ private JPanel buildLoupePanel(int spacing) {
+ loupe = new LoupeViewer();
+ loupe.addMouseWheelListener(new WheelZoomListener());
+ CrosshairPanel crosshairPanel = new CrosshairPanel(loupe);
+
+ JPanel loupePanel = new JPanel(new BorderLayout());
+ loupePanel.add(crosshairPanel);
+ status = new LoupeStatus();
+ loupePanel.add(status, BorderLayout.SOUTH);
+
+ loupePanel.setBorder(BorderFactory.createEmptyBorder(0, spacing, 0, 0));
+ return loupePanel;
+ }
+
+ private class WheelZoomListener implements MouseWheelListener {
+ public void mouseWheelMoved(MouseWheelEvent e) {
+ if (zoomSlider != null) {
+ int val = zoomSlider.getValue();
+ val -= e.getWheelRotation() * 2;
+ zoomSlider.setValue(val);
+ }
+ }
+ }
+
+ private JPanel buildViewerAndControls() {
+ JPanel panel = new JPanel(new GridBagLayout());
+ crosshair = new Crosshair(new ScreenshotViewer());
+ crosshair.addMouseWheelListener(new WheelZoomListener());
+ JScrollPane scroller = new JScrollPane(crosshair);
+ scroller.setPreferredSize(new Dimension(320, 480));
+ scroller.setBorder(null);
+ panel.add(scroller,
+ new GridBagConstraints(0, y++, 2, 1, 1.0f, 1.0f,
+ GridBagConstraints.FIRST_LINE_START, GridBagConstraints.BOTH,
+ new Insets(0, 0, 0, 0), 0, 0));
+ buildSlider(panel, "Overlay:", "0%", "100%", 0, 100, 30, 1).addChangeListener(
+ new ChangeListener() {
+ public void stateChanged(ChangeEvent event) {
+ float opacity = ((JSlider) event.getSource()).getValue() / 100.0f;
+ overlayAlpha = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, opacity);
+ repaint();
+ }
+ });
+ buildOverlayExtraControls(panel);
+ buildSlider(panel, "Refresh Rate:", "1s", "40s", 1, 40, 5, 1).addChangeListener(
+ new ChangeListener() {
+ public void stateChanged(ChangeEvent event) {
+ int rate = ((JSlider) event.getSource()).getValue() * 1000;
+ timer.setDelay(rate);
+ timer.setInitialDelay(0);
+ timer.restart();
+ }
+ });
+ zoomSlider = buildSlider(panel, "Zoom:", "2x", "24x", 2, 24, 8, 2);
+ zoomSlider.addChangeListener(
+ new ChangeListener() {
+ public void stateChanged(ChangeEvent event) {
+ zoom = ((JSlider) event.getSource()).getValue();
+ loupe.clearGrid = true;
+ loupe.moveToPoint(crosshair.crosshair.x, crosshair.crosshair.y);
+ repaint();
+ }
+ });
+ panel.add(Box.createVerticalGlue(),
+ new GridBagConstraints(0, y++, 2, 1, 1.0f, 1.0f,
+ GridBagConstraints.FIRST_LINE_START, GridBagConstraints.NONE,
+ new Insets(0, 0, 0, 0), 0, 0));
+ return panel;
+ }
+
+ private void buildOverlayExtraControls(JPanel panel) {
+ JPanel extras = new JPanel(new FlowLayout(FlowLayout.LEFT, 0, 0));
+
+ JButton loadOverlay = new JButton("Load...");
+ loadOverlay.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent event) {
+ SwingWorker<?, ?> worker = openOverlay();
+ if (worker != null) {
+ worker.execute();
+ }
+ }
+ });
+ extras.add(loadOverlay);
+
+ JCheckBox showInLoupe = new JCheckBox("Show in Loupe");
+ showInLoupe.setSelected(false);
+ showInLoupe.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent event) {
+ loupe.showOverlay = ((JCheckBox) event.getSource()).isSelected();
+ loupe.repaint();
+ }
+ });
+ extras.add(showInLoupe);
+
+ panel.add(extras, new GridBagConstraints(1, y++, 1, 1, 1.0f, 0.0f,
+ GridBagConstraints.LINE_START, GridBagConstraints.NONE,
+ new Insets(0, 0, 0, 0), 0, 0));
+ }
+
+ public SwingWorker<?, ?> openOverlay() {
+ JFileChooser chooser = new JFileChooser();
+ chooser.setFileFilter(new PngFileFilter());
+ int choice = chooser.showOpenDialog(this);
+ if (choice == JFileChooser.APPROVE_OPTION) {
+ return new OpenOverlayTask(chooser.getSelectedFile());
+ } else {
+ return null;
+ }
+ }
+
+ private JSlider buildSlider(JPanel panel, String title, String minName, String maxName,
+ int min, int max, int value, int tick) {
+ panel.add(new JLabel(title), new GridBagConstraints(0, y, 1, 1, 1.0f, 0.0f,
+ GridBagConstraints.LINE_END, GridBagConstraints.NONE,
+ new Insets(0, 0, 0, 6), 0, 0));
+ JPanel sliderPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 0, 0));
+ sliderPanel.add(new JLabel(minName));
+ JSlider slider = new JSlider(min, max, value);
+ slider.setMinorTickSpacing(tick);
+ slider.setMajorTickSpacing(tick);
+ slider.setSnapToTicks(true);
+ sliderPanel.add(slider);
+ sliderPanel.add(new JLabel(maxName));
+ panel.add(sliderPanel, new GridBagConstraints(1, y++, 1, 1, 1.0f, 0.0f,
+ GridBagConstraints.FIRST_LINE_START, GridBagConstraints.NONE,
+ new Insets(0, 0, 0, 0), 0, 0));
+ return slider;
+ }
+
+ void stop() {
+ timer.stop();
+ }
+
+ void start() {
+ timer.start();
+ }
+
+ void select(ViewNode node) {
+ this.node = node;
+ repaint();
+ }
+
+ class LoupeViewer extends JComponent {
+ private final Color lineColor = new Color(1.0f, 1.0f, 1.0f, 0.3f);
+
+ private int width;
+ private int height;
+ private BufferedImage grid;
+ private int left;
+ private int top;
+ public boolean clearGrid;
+
+ private final Rectangle clip = new Rectangle();
+ private boolean showOverlay = false;
+
+ LoupeViewer() {
+ addMouseListener(new MouseAdapter() {
+ @Override
+ public void mousePressed(MouseEvent event) {
+ moveToPoint(event);
+ }
+ });
+ addMouseMotionListener(new MouseMotionAdapter() {
+ @Override
+ public void mouseDragged(MouseEvent event) {
+ moveToPoint(event);
+ }
+ });
+ }
+
+ @Override
+ protected void paintComponent(Graphics g) {
+ if (isLoading) {
+ return;
+ }
+
+ g.translate(-left, -top);
+
+ if (image != null) {
+ Graphics2D g2 = (Graphics2D) g.create();
+ g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
+ RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);
+ g2.scale(zoom, zoom);
+ g2.drawImage(image, 0, 0, null);
+ if (overlay != null && showOverlay) {
+ g2.setComposite(overlayAlpha);
+ g2.drawImage(overlay, 0, image.getHeight() - overlay.getHeight(), null);
+ }
+ g2.dispose();
+ }
+
+ int width = getWidth();
+ int height = getHeight();
+
+ Graphics2D g2 = null;
+ if (width != this.width || height != this.height) {
+ this.width = width;
+ this.height = height;
+
+ grid = new BufferedImage(width + zoom + 1, height + zoom + 1,
+ BufferedImage.TYPE_INT_ARGB);
+ clearGrid = true;
+ g2 = grid.createGraphics();
+ } else if (clearGrid) {
+ g2 = grid.createGraphics();
+ g2.setComposite(AlphaComposite.Clear);
+ g2.fillRect(0, 0, grid.getWidth(), grid.getHeight());
+ g2.setComposite(AlphaComposite.SrcOver);
+ }
+
+ if (clearGrid) {
+ clearGrid = false;
+
+ g2.setColor(lineColor);
+ width += zoom;
+ height += zoom;
+
+ for (int x = zoom; x <= width; x += zoom) {
+ g2.drawLine(x, 0, x, height);
+ }
+
+ for (int y = 0; y <= height; y += zoom) {
+ g2.drawLine(0, y, width, y);
+ }
+
+ g2.dispose();
+ }
+
+ if (image != null) {
+ g.getClipBounds(clip);
+ g.clipRect(0, 0, image.getWidth() * zoom + 1, image.getHeight() * zoom + 1);
+ g.drawImage(grid, clip.x - clip.x % zoom, clip.y - clip.y % zoom, null);
+ }
+
+ g.translate(left, top);
+ }
+
+ void moveToPoint(MouseEvent event) {
+ int x = Math.max(0, Math.min((event.getX() + left) / zoom, image.getWidth() - 1));
+ int y = Math.max(0, Math.min((event.getY() + top) / zoom, image.getHeight() - 1));
+ moveToPoint(x, y);
+ crosshair.moveToPoint(x, y);
+ }
+
+ void moveToPoint(int x, int y) {
+ left = x * zoom - width / 2 + zoom / 2;
+ top = y * zoom - height / 2 + zoom / 2;
+ repaint();
+ }
+ }
+
+ class LoupeStatus extends JPanel {
+ private JLabel xLabel;
+ private JLabel yLabel;
+ private JLabel rLabel;
+ private JLabel gLabel;
+ private JLabel bLabel;
+ private JLabel hLabel;
+ private ScreenViewer.LoupeStatus.ColoredSquare square;
+ private Color color;
+
+ LoupeStatus() {
+ setOpaque(true);
+ setLayout(new GridBagLayout());
+ setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4));
+
+ square = new ColoredSquare();
+ add(square, new GridBagConstraints(0, 0, 1, 2, 0.0f, 0.0f,
+ GridBagConstraints.LINE_START, GridBagConstraints.NONE,
+ new Insets(0, 0, 0, 12), 0, 0 ));
+
+ JLabel label;
+
+ add(label = new JLabel("#ffffff"), new GridBagConstraints(0, 2, 1, 1, 0.0f, 0.0f,
+ GridBagConstraints.LINE_START, GridBagConstraints.NONE,
+ new Insets(0, 0, 0, 12), 0, 0 ));
+ label.setForeground(Color.WHITE);
+ hLabel = label;
+
+ add(label = new JLabel("R:"), new GridBagConstraints(1, 0, 1, 1, 0.0f, 0.0f,
+ GridBagConstraints.LINE_START, GridBagConstraints.NONE,
+ new Insets(0, 6, 0, 6), 0, 0 ));
+ label.setForeground(Color.WHITE);
+ add(label = new JLabel("255"), new GridBagConstraints(2, 0, 1, 1, 0.0f, 0.0f,
+ GridBagConstraints.LINE_START, GridBagConstraints.NONE,
+ new Insets(0, 0, 0, 12), 0, 0 ));
+ label.setForeground(Color.WHITE);
+ rLabel = label;
+
+ add(label = new JLabel("G:"), new GridBagConstraints(1, 1, 1, 1, 0.0f, 0.0f,
+ GridBagConstraints.LINE_START, GridBagConstraints.NONE,
+ new Insets(0, 6, 0, 6), 0, 0 ));
+ label.setForeground(Color.WHITE);
+ add(label = new JLabel("255"), new GridBagConstraints(2, 1, 1, 1, 0.0f, 0.0f,
+ GridBagConstraints.LINE_START, GridBagConstraints.NONE,
+ new Insets(0, 0, 0, 12), 0, 0 ));
+ label.setForeground(Color.WHITE);
+ gLabel = label;
+
+ add(label = new JLabel("B:"), new GridBagConstraints(1, 2, 1, 1, 0.0f, 0.0f,
+ GridBagConstraints.LINE_START, GridBagConstraints.NONE,
+ new Insets(0, 6, 0, 6), 0, 0 ));
+ label.setForeground(Color.WHITE);
+ add(label = new JLabel("255"), new GridBagConstraints(2, 2, 1, 1, 0.0f, 0.0f,
+ GridBagConstraints.LINE_START, GridBagConstraints.NONE,
+ new Insets(0, 0, 0, 12), 0, 0 ));
+ label.setForeground(Color.WHITE);
+ bLabel = label;
+
+ add(label = new JLabel("X:"), new GridBagConstraints(3, 0, 1, 1, 0.0f, 0.0f,
+ GridBagConstraints.LINE_START, GridBagConstraints.NONE,
+ new Insets(0, 6, 0, 6), 0, 0 ));
+ label.setForeground(Color.WHITE);
+ add(label = new JLabel("0 px"), new GridBagConstraints(4, 0, 1, 1, 0.0f, 0.0f,
+ GridBagConstraints.LINE_START, GridBagConstraints.NONE,
+ new Insets(0, 0, 0, 12), 0, 0 ));
+ label.setForeground(Color.WHITE);
+ xLabel = label;
+
+ add(label = new JLabel("Y:"), new GridBagConstraints(3, 1, 1, 1, 0.0f, 0.0f,
+ GridBagConstraints.LINE_START, GridBagConstraints.NONE,
+ new Insets(0, 6, 0, 6), 0, 0 ));
+ label.setForeground(Color.WHITE);
+ add(label = new JLabel("0 px"), new GridBagConstraints(4, 1, 1, 1, 0.0f, 0.0f,
+ GridBagConstraints.LINE_START, GridBagConstraints.NONE,
+ new Insets(0, 0, 0, 12), 0, 0 ));
+ label.setForeground(Color.WHITE);
+ yLabel = label;
+
+ add(Box.createHorizontalGlue(), new GridBagConstraints(5, 0, 1, 1, 1.0f, 0.0f,
+ GridBagConstraints.LINE_START, GridBagConstraints.BOTH,
+ new Insets(0, 0, 0, 0), 0, 0 ));
+ }
+
+ @Override
+ protected void paintComponent(Graphics g) {
+ g.setColor(Color.BLACK);
+ g.fillRect(0, 0, getWidth(), getHeight());
+ }
+
+ void showPixel(int x, int y) {
+ xLabel.setText(x + " px");
+ yLabel.setText(y + " px");
+
+ int pixel = image.getRGB(x, y);
+ color = new Color(pixel);
+ hLabel.setText("#" + Integer.toHexString(pixel));
+ rLabel.setText(String.valueOf((pixel >> 16) & 0xff));
+ gLabel.setText(String.valueOf((pixel >> 8) & 0xff));
+ bLabel.setText(String.valueOf((pixel ) & 0xff));
+
+ square.repaint();
+ }
+
+ private class ColoredSquare extends JComponent {
+ @Override
+ public Dimension getPreferredSize() {
+ Dimension d = super.getPreferredSize();
+ d.width = 60;
+ d.height = 30;
+ return d;
+ }
+
+ @Override
+ protected void paintComponent(Graphics g) {
+ g.setColor(color);
+ g.fillRect(0, 0, getWidth(), getHeight());
+
+ g.setColor(Color.WHITE);
+ g.drawRect(0, 0, getWidth() - 1, getHeight() - 1);
+ }
+ }
+ }
+
+ class Crosshair extends JPanel {
+ // magenta = 0xff5efe
+ private final Color crosshairColor = new Color(0x00ffff);
+ Point crosshair = new Point();
+ private int width;
+ private int height;
+ private final ScreenshotViewer screenshotViewer;
+
+ Crosshair(ScreenshotViewer screenshotViewer) {
+ this.screenshotViewer = screenshotViewer;
+ setOpaque(true);
+ setLayout(new BorderLayout());
+ add(screenshotViewer);
+ addMouseListener(new MouseAdapter() {
+ @Override
+ public void mousePressed(MouseEvent event) {
+ moveToPoint(event);
+ }
+ });
+ addMouseMotionListener(new MouseMotionAdapter() {
+ @Override
+ public void mouseDragged(MouseEvent event) {
+ moveToPoint(event);
+ }
+ });
+ }
+
+ void moveToPoint(int x, int y) {
+ crosshair.x = x;
+ crosshair.y = y;
+ status.showPixel(crosshair.x, crosshair.y);
+ repaint();
+ }
+
+ private void moveToPoint(MouseEvent event) {
+ crosshair.x = Math.max(0, Math.min(image.getWidth() - 1, event.getX()));
+ crosshair.y = Math.max(0, Math.min(image.getHeight() - 1, event.getY()));
+ loupe.moveToPoint(crosshair.x, crosshair.y);
+ status.showPixel(crosshair.x, crosshair.y);
+
+ repaint();
+ }
+
+ @Override
+ public Dimension getPreferredSize() {
+ return screenshotViewer.getPreferredSize();
+ }
+
+ @Override
+ public Dimension getMaximumSize() {
+ return screenshotViewer.getPreferredSize();
+ }
+
+ @Override
+ public void paint(Graphics g) {
+ super.paint(g);
+
+ if (crosshair == null || width != getWidth() || height != getHeight()) {
+ width = getWidth();
+ height = getHeight();
+ crosshair = new Point(width / 2, height / 2);
+ }
+
+ g.setColor(crosshairColor);
+
+ g.drawLine(crosshair.x, 0, crosshair.x, height);
+ g.drawLine(0, crosshair.y, width, crosshair.y);
+ }
+
+ @Override
+ protected void paintComponent(Graphics g) {
+ super.paintComponent(g);
+ g.setColor(Color.BLACK);
+ g.fillRect(0, 0, getWidth(), getHeight());
+ }
+ }
+
+ class ScreenshotViewer extends JComponent {
+ private final Color boundsColor = new Color(0xff5efe);
+
+ ScreenshotViewer() {
+ setOpaque(true);
+ }
+
+ @Override
+ protected void paintComponent(Graphics g) {
+ g.setColor(Color.BLACK);
+ g.fillRect(0, 0, getWidth(), getHeight());
+
+ if (isLoading) {
+ return;
+ }
+
+ if (image != null) {
+ g.drawImage(image, 0, 0, null);
+ if (overlay != null) {
+ Graphics2D g2 = (Graphics2D) g.create();
+ g2.setComposite(overlayAlpha);
+ g2.drawImage(overlay, 0, image.getHeight() - overlay.getHeight(), null);
+ }
+ }
+
+ if (node != null) {
+ Graphics s = g.create();
+ s.setColor(boundsColor);
+ ViewNode p = node.parent;
+ while (p != null) {
+ s.translate(p.left - p.scrollX, p.top - p.scrollY);
+ p = p.parent;
+ }
+ s.drawRect(node.left, node.top, node.width - 1, node.height - 1);
+ s.translate(node.left, node.top);
+
+ s.setXORMode(Color.WHITE);
+ if ((node.paddingBottom | node.paddingLeft |
+ node.paddingTop | node.paddingRight) != 0) {
+ s.setColor(Color.BLACK);
+ s.drawRect(node.paddingLeft, node.paddingTop,
+ node.width - node.paddingRight - node.paddingLeft - 1,
+ node.height - node.paddingBottom - node.paddingTop - 1);
+ }
+ if (node.hasMargins && (node.marginLeft | node.marginBottom |
+ node.marginRight | node.marginRight) != 0) {
+ s.setColor(Color.BLACK);
+ s.drawRect(-node.marginLeft, -node.marginTop,
+ node.marginLeft + node.width + node.marginRight - 1,
+ node.marginTop + node.height + node.marginBottom - 1);
+ }
+
+ s.dispose();
+ }
+ }
+
+ @Override
+ public Dimension getPreferredSize() {
+ if (image == null) {
+ return new Dimension(320, 480);
+ }
+ return new Dimension(image.getWidth(), image.getHeight());
+ }
+ }
+
+ private class CrosshairPanel extends JPanel {
+ private final Color crosshairColor = new Color(0xff5efe);
+ private final Insets insets = new Insets(0, 0, 0, 0);
+
+ CrosshairPanel(LoupeViewer loupe) {
+ setLayout(new BorderLayout());
+ add(loupe);
+ }
+
+ @Override
+ public void paint(Graphics g) {
+ super.paint(g);
+
+ g.setColor(crosshairColor);
+
+ int width = getWidth();
+ int height = getHeight();
+
+ getInsets(insets);
+
+ int x = (width - insets.left - insets.right) / 2;
+ int y = (height - insets.top - insets.bottom) / 2;
+
+ g.drawLine(insets.left + x, insets.top, insets.left + x, height - insets.bottom);
+ g.drawLine(insets.left, insets.top + y, width - insets.right, insets.top + y);
+ }
+
+ @Override
+ protected void paintComponent(Graphics g) {
+ g.setColor(Color.BLACK);
+ Insets insets = getInsets();
+ g.fillRect(insets.left, insets.top, getWidth() - insets.left - insets.right,
+ getHeight() - insets.top - insets.bottom);
+ }
+ }
+
+ public void actionPerformed(ActionEvent event) {
+ if (task != null && !task.isDone()) {
+ return;
+ }
+ task = new GetScreenshotTask();
+ task.execute();
+ }
+
+ private class GetScreenshotTask extends SwingWorker<Boolean, Void> {
+ private GetScreenshotTask() {
+ workspace.beginTask();
+ }
+
+ @Override
+ @WorkerThread
+ protected Boolean doInBackground() throws Exception {
+ RawImage rawImage;
+ try {
+ rawImage = device.getScreenshot();
+ } catch (IOException ioe) {
+ return false;
+ }
+
+ boolean resize = false;
+ isLoading = true;
+ try {
+ if (rawImage != null) {
+ if (image == null || rawImage.width != image.getWidth() ||
+ rawImage.height != image.getHeight()) {
+ image = new BufferedImage(rawImage.width, rawImage.height,
+ BufferedImage.TYPE_INT_ARGB);
+ scanline = new int[rawImage.width];
+ resize = true;
+ }
+
+ switch (rawImage.bpp) {
+ case 16:
+ rawImage16toARGB(rawImage);
+ break;
+ case 32:
+ rawImage32toARGB(rawImage);
+ break;
+ }
+ }
+ } finally {
+ isLoading = false;
+ }
+
+ return resize;
+ }
+
+ private int getMask(int length) {
+ int res = 0;
+ for (int i = 0 ; i < length ; i++) {
+ res = (res << 1) + 1;
+ }
+
+ return res;
+ }
+
+ private void rawImage32toARGB(RawImage rawImage) {
+ byte[] buffer = rawImage.data;
+ int index = 0;
+
+ final int redOffset = rawImage.red_offset;
+ final int redLength = rawImage.red_length;
+ final int redMask = getMask(redLength);
+ final int greenOffset = rawImage.green_offset;
+ final int greenLength = rawImage.green_length;
+ final int greenMask = getMask(greenLength);
+ final int blueOffset = rawImage.blue_offset;
+ final int blueLength = rawImage.blue_length;
+ final int blueMask = getMask(blueLength);
+ final int alphaLength = rawImage.alpha_length;
+ final int alphaOffset = rawImage.alpha_offset;
+ final int alphaMask = getMask(alphaLength);
+
+ for (int y = 0 ; y < rawImage.height ; y++) {
+ for (int x = 0 ; x < rawImage.width ; x++) {
+ int value = buffer[index++] & 0x00FF;
+ value |= (buffer[index++] & 0x00FF) << 8;
+ value |= (buffer[index++] & 0x00FF) << 16;
+ value |= (buffer[index++] & 0x00FF) << 24;
+
+ int r = ((value >>> redOffset) & redMask) << (8 - redLength);
+ int g = ((value >>> greenOffset) & greenMask) << (8 - greenLength);
+ int b = ((value >>> blueOffset) & blueMask) << (8 - blueLength);
+ int a = 0xFF;
+
+ if (alphaLength != 0) {
+ a = ((value >>> alphaOffset) & alphaMask) << (8 - alphaLength);
+ }
+
+ scanline[x] = a << 24 | r << 16 | g << 8 | b;
+ }
+
+ image.setRGB(0, y, rawImage.width, 1, scanline,
+ 0, rawImage.width);
+ }
+ }
+
+ private void rawImage16toARGB(RawImage rawImage) {
+ byte[] buffer = rawImage.data;
+ int index = 0;
+
+ for (int y = 0 ; y < rawImage.height ; y++) {
+ for (int x = 0 ; x < rawImage.width ; x++) {
+ int value = buffer[index++] & 0x00FF;
+ value |= (buffer[index++] << 8) & 0x0FF00;
+
+ int r = ((value >> 11) & 0x01F) << 3;
+ int g = ((value >> 5) & 0x03F) << 2;
+ int b = ((value ) & 0x01F) << 3;
+
+ scanline[x] = 0xFF << 24 | r << 16 | g << 8 | b;
+ }
+
+ image.setRGB(0, y, rawImage.width, 1, scanline,
+ 0, rawImage.width);
+ }
+ }
+
+ @Override
+ protected void done() {
+ workspace.endTask();
+ try {
+ if (get()) {
+ validate();
+ crosshair.crosshair = new Point(image.getWidth() / 2,
+ image.getHeight() / 2);
+ status.showPixel(image.getWidth() / 2, image.getHeight() / 2);
+ loupe.moveToPoint(image.getWidth() / 2, image.getHeight() / 2);
+ }
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ } catch (ExecutionException e) {
+ e.printStackTrace();
+ }
+ repaint();
+ }
+ }
+
+ private class OpenOverlayTask extends SwingWorker<BufferedImage, Void> {
+ private File file;
+
+ private OpenOverlayTask(File file) {
+ this.file = file;
+ workspace.beginTask();
+ }
+
+ @Override
+ @WorkerThread
+ protected BufferedImage doInBackground() {
+ try {
+ return IconLoader.toCompatibleImage(ImageIO.read(file));
+ } catch (IOException ex) {
+ ex.printStackTrace();
+ }
+ return null;
+ }
+
+ @Override
+ protected void done() {
+ try {
+ overlay = get();
+ repaint();
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ } catch (ExecutionException e) {
+ e.printStackTrace();
+ } finally {
+ workspace.endTask();
+ }
+ }
+ }
+}