びぼうろく

北海道の人。適当にもっさりまったり書きます。さくら荘のましろが好きです。アニメとかパソコンとか

組み込みLinuxでスクリーンショットを撮影する

組み込みでスクリーンショット

はじめに

組み込みLinuxはデスクトップ環境が無いので
スクリーンショットを取るアプリも無い。
でも操作画面(LCD)はあるからドキュメントを作るのにスクリーンショットが欲しい!

という上司からの要望があった。
半日でスクリーンショット撮影するツールを作らされたのでここにメモっておく。

こちらのサイトを参考にさせていただきました。
非常に助かりました。ありがとうございます。
ラズパイでフレームバッファ(/dev/fb0)を使用して、直接ディスプレイ画像を入出力する - Qiita
Bitmapファイルフォーマット

結論

フレームバッファを読みだしたRawファイルに、
ビットマップのヘッダをつけたらビットマップファイルとして開けた。

作ったものはgithubに置いてあります。
github.com

内容

結論までの経緯とやったことを書いておく。

フレームバッファとは

Linuxフレームバッファにデータを書き込むとカーネルがいい感じに勝手に描画してくれる。
つまりディスプレイの情報はフレームバッファに入っている。
カーネルが書き込んだフレームバッファをどう処理しているのかはわからない(そのうち理解したい)。

手元のUbuntuマシンでは/dev/fb0があった。

フレームバッファの情報は以下のコマンドで確認できるらしい。

$ cat /sys/class/graphics/fb0/bits_per_pixel
32

$ cat /sys/class/graphics/fb0/virtual_size 
1920,1080
フレームバッファを取得して見てみる

以下のコマンドでフレームバッファを取得する。

$ cat /dev/fb0 > temp.raw

IrfanViewでrawファイルを開く。
ソフトを起動して、ドラッグ&ドロップでrawファイルをぶち込むとrawファイルを開く設定ができる。

今回は以下の設定でrawファイルを正常に表示できた。

  • Image width:1920
  • Image heigh:1080
  • BitsPerPixel:32 BPP[4 byte per pixel]
  • Options for 24 and 32 BPP:Color order BGR(32bit BGRA)
rawファイルは扱いにくい

rawファイルでスクリーンショットを撮影することはできた。
でもrawファイルは一般的な画像ビューアで表示できないし、ドキュメントに貼り付けることもできない。

...と参ってたら、チームのすごい人から
「昔、bitmapでスクリーンショット撮るツールあった気がする。ツール見つけられないけど確かbitmapヘッダをくっつけてただけだった気がする。」
と、神の導きを頂いたので、とりあえずbitmapヘッダを作ってみることにした。

bitmapヘッダを作る

bitmapのヘッダは、ファイルヘッダ(14byte)と情報ヘッダ(40byte)の54byteでできているらしい。
とりあえず空のバイナリファイルを作る。

dd if=/dev/zero of=bmp_head.bin bs=54 count=1

vscode拡張機能vscode-hexdump」でヘッダにデータを入れていく。

ファイルタイプ以外はすべてリトルエンディアンでデータを入れる。
以下は実際に入れたデータ

種類 オフセット サイズ メモ
ファイルタイプ 0 2byte 0x42、0x4D 固定値
ファイルサイズ(byte) 2 4byte 0x00 1F A4 36 ヘッダサイズ(54byte)+1920*1080
予約領域1 6 2byte 0x00 固定値
予約領域2 8 2byte 0x00 固定値
ヘッダサイズ 10 4byte 0x00 00 00 36 固定値
情報ヘッダサイズ 14 4byte 0x00 00 00 28 固定値
画像の横幅(ピクセル) 18 4byte 0x00 00 07 80 1920
画像の縦幅(ピクセル) 22 4byte 0x FF FF FB C8 -1080(縦幅の2の補数)
プレーン数 26 2byte 0x00 01 固定値(プレーンとは?)
1画素の色数 28 2byte 0x00 20 bits_per_pixelの値
圧縮形式 30 4byte 0x00 00 00 00 rawファイルなので0固定
画像サイズ 34 4byte 0x00 1F A4 00 1920*1080
水平解像度(ppm) 38 4byte 0x00 00 00 00 固定値
垂直解像度(ppm) 42 4byte 0x00 00 00 00 固定値
色数 46 4byte 0x00 00 00 00 固定値
重要色数 50 4byte 0x00 00 00 00 固定値
rawファイルにbitmapヘッダをくっつける

必死こいてポチポチ計算してヘッダファイル作ったらあとは、rawファイルにくっつけるだけ。

cat bmp_head.bin temp.raw > test.bmp
何故かできた

これで何故かbitmapファイルでスクリーンショットが作れてしまった。
f:id:gari30:20210505033140p:plain

まとめ

フレームバッファにbitmapヘッダをつけたら上手くいく理由がいまいち理解できていない。
けど何故かできてしまった。(絶対良くないやつ)
ggっても関連する話が全く出てこなかったので、理由をご存じの方いらっしゃったら教えて下さい。。


とりあえず動けばいいツールだし、ユーザ空間でデバイスファイルのReadしかしていないので
OSに影響も無いだろうから一旦これで良いことにしておく。
理由の理解は今後の課題。