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