SimpleCap ユーザの方から Snow Leopard でスクリーンキャプチャを実行した時、タイマーが0になった後に固まってしまうとの報告をうけた。コンソール.app のログをもらったところ下記のエラーが出ているのがわかった。
SimpleCap[1199]: CGImageCreate: invalid image bits/pixel or bytes/row.
自分の所の Snow Leopard では発生しないので何らかの条件が揃うと起きるようだ。
ソースを見たところ特に問題が見つらなかったので、"snow leopard CGWindowListCreateImage" でネットを検索したところ下記の記事が見つかった。
Re: CGWindowListCreateImage fails on Snow Leopard with kCGNullWindowID
CGWindowListCreateImage() は CGRectInfinite を渡すと、自動的にスクリーン全体の大きさを割り出して CGImage を作ってくれる。この情報によると、本来 CGRectInfinite はその名の通りシステム内で無限相当の矩形範囲なのだが、Snow Leopardの 64bit化に伴い 32bitアプリでこの値を使うと「無限相当」ではなく「非常に大きな範囲」として認識されてしまうのが原因とのこと。つまり 32bitでは無限と見なせていた大きな値が、64bit化によってそこまで大きな値ではなくなったということらしい。その結果 CGWindowListCreateImage() はその巨大な矩形範囲の CGImage を生成してしまおうとする。先のエラーの場合、巨大すぎるが為に CGImage の生成に失敗したようだ。
確かに自分のMacではキャプチャで固まることは無いのだが(Leopardの時よりも)長い時間がかかる。この場合は、時間はかかったが運良く(運悪く?)画像の生成に成功していた為に問題が出ていなかったようだ。
対策は先の記事にも掲載してあった通り CGRectInfinite を使わず明示的に範囲指定すること。
(例)
NSRect desktopRect = NSZeroRect;
for (NSScreen *screen in [NSScreen screens])
{
desktopRect = NSUnionRect(desktopRect, [screen frame]);
}
SimpleCap の場合はマルチスクリーンに対応するため、専用のクラス Screen を用意しており、そこから上記と同じ範囲値を取得できる。
(旧)
CGImageRef cgimage = CGWindowListCreateImage(CGRectInfinite,
option,
[_capture_controller windowID],
kCGWindowImageDefault);
(新)
CGRect cgrect = NSRectToCGRect([[Screen defaultScreen] frame]);
CGImageRef cgimage = CGWindowListCreateImage(cgrect,
option,
[_capture_controller windowID],
kCGWindowImageDefault);
32bit->64bit移行期に発生する不具合はまだまだありそうな気もする。
※上記修正を加えた 1.1.0 は今月リリースの予定です。少しお待ちください。
- - - -
(Special Thanks )
今回の件は 感じ通信 さんからの報告で発覚しました。ありがとうございました!