• 追加された行はこの色です。
  • 削除された行はこの色です。
---
category: swing
folder: ScreenCapture
title: Robotを使用してスクリーンショットを取得する
tags: [Robot, JFrame, BufferedImage, ConvolveOp]
author: aterai
pubdate: 2021-12-13T00:03:40+09:00
description: Robotを使用してスクリーンショット画像を取得して背景画像として描画することで`JFrame`を半透明に見せかけます。
description: Robotを使用してスクリーンショット画像を取得して背景画像として描画することでJFrameを半透明に見せかけます。
image: https://drive.google.com/uc?id=1e9aL18WI4SjJp5iHgshLy4Kx0XGw0nMD
---
* 概要 [#summary]
Robotを使用してスクリーンショット画像を取得して背景画像として描画することで`JFrame`を半透明に見せかけます。
`Robot`を使用してスクリーンショット画像を取得して背景画像として描画することで`JFrame`を半透明に見せかけます。

#download(https://drive.google.com/uc?id=1e9aL18WI4SjJp5iHgshLy4Kx0XGw0nMD)

* サンプルコード [#sourcecode]
#code(link){{
private final transient Robot robot;
private final Rectangle screenRect;
private final Rectangle buf = new Rectangle();
private transient BufferedImage backgroundImage;
private final float[] data = {
    .1f, .1f, .1f,
    .1f, .2f, .1f,
    .1f, .1f, .1f,
};
private Kernel kernel = new Kernel(3, 3, data);
private ConvolveOp op = new ConvolveOp(kernel, ConvolveOp.EDGE_NO_OP, null);
private Color bgc = new Color(255, 255, 255, 100);

@Override protected void paintComponent(Graphics g) {
  super.paintComponent(g);
  Graphics2D g2 = (Graphics2D) g.create();
  Point pt = getLocationOnScreen();
  buf.setBounds(screenRect);
  SwingUtilities.computeIntersection(pt.x, pt.y, getWidth(), getHeight(), buf);
  Image img = backgroundImage.getSubimage(buf.x, buf.y, buf.width, buf.height);
  g2.drawImage(img, -Math.min(pt.x, 0), -Math.min(pt.y, 0), this);
  g2.setPaint(bgc);
  g2.fillRect(0, 0, getWidth(), getHeight());
  g2.dispose();
}

public void updateBackground() {
  backgroundImage = op.filter(robot.createScreenCapture(screenRect), null);
}
}}

* 解説 [#explanation]
上記のサンプルでは`Robot#createScreenCapture(...)`メソッドを使用してデスクトップのスクリーンショットを取得して`JPanel`の背景として描画することで`JFrame`が半透明であるかのように表示しています。

- `Robot#createScreenCapture(...)`で取得した画像に`ConvolveOp`でぼかし効果を設定
- `JPanel#getLocationOnScreen()`で取得した位置を左上にして`JPanel`と同じ幅と高さの矩形`Rectangle`を作成
- `JFrame`がスクリーンの外に配置されている場合などを除く必要があるため、`SwingUtilities.computeIntersection(...)`でこの矩形とスクリーンサイズとの共通矩形を取得
- スクリーン画像から`getSubimage(...)`で共通矩形の画像を取得
- 共通矩形の画像を`JPanel`に背景として描画
-- たとえば`JFrame`の左辺がスクリーンの左辺の外に存在する(`JPanel#getLocationOnScreen()`で取得した位置がマイナスになる)場合は共通矩形の画像は`JPanel`より幅が短くなるので注意が必要
- `JFrame`に`ComponentListener`を追加して移動やリサイズで背景画像を再描画
- `JFrame`に`WindowListener`を追加してアイコン化が解除されるとスクリーン画像を更新
- たとえば別ウィンドウにフォーカス移動してその位置を変更したりマウスホイールなどでバックグラウンドのウィンドウを更新してもスクリーン画像は更新していないので`JFrame`に描画している背景画像とずれが発生する

* 参考リンク [#reference]
- [[JFrameを半透明化 - Java Swing Tips>https://ateraimemo.com/Swing/WindowOpacity]]
-- `JFrame.setDefaultLookAndFeelDecorated(true); JFrame#setBackground(new Color(0x0, true));`などで`JFrame`を半透明化する場合は`SystemLookAndFeel`などで使用不可、また`ConvolveOp`などを適用してぼかし効果なども不可となる
- [https://stackoverflow.com/questions/70234904/how-to-make-the-jframe-contentpane-transparent-but-the-jframe-visible java - How to make the JFrame contentPane transparent but the JFrame visible? - Stack Overflow]

* コメント [#comment]
#comment
#comment