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