試験運用中なLinux備忘録・旧記事

はてなダイアリーで公開していた2007年5月-2015年3月の記事を保存しています。

openBVEがTao.Sdl.Sdl.SDL_MapRGB()で落ちる件のその後

openBVEのバージョン0.9.6.0を試すが...」で路線と車両のデータを読み込んだ後シミュレータのウィンドウが出る(あるいはフルスクリーンに切り替わる)ところで落ちてしまった件は、正式リリースされたバージョン1.0.0.0でも同様*1だったものの、回避はできるということが分かった。

  1. 該当する行と関連した行をコメントにしたところ...
  2. 詳しく見てみると
  3. パッチ

該当する行と関連した行をコメントにしたところ...

下が該当部分とその周辺なのだが
[引用]ファイル名: openBVE/OpenBve/Program.cs より

            // icon
            {
                string File = Interface.GetCombinedFileName(Interface.GetDataFolder(), "icon.bmp");
                if (System.IO.File.Exists(File)) {
                    try {
                        IntPtr Bitmap = Sdl.SDL_LoadBMP(File);
                        if (Bitmap != null) {
                            Sdl.SDL_Surface Surface = (Sdl.SDL_Surface)System.Runtime.InteropServices.Marshal.PtrToStructure(Bitmap, typeof(Sdl.SDL_Surface));
                            int ColorKey = Sdl.SDL_MapRGB(Surface.format, 0, 0, 255);
                            Sdl.SDL_SetColorKey(Bitmap, Sdl.SDL_SRCCOLORKEY, ColorKey);
                            Sdl.SDL_WM_SetIcon(Bitmap, null);
                        }
                    } catch { }
                }
            }

Tao.Sdl.Sdl.SDL_MapRGB()のある行と、その戻り値(ColorKey)を使用している次のTao.Sdl.Sdl.SDL_SetColorKey()の行をコメントにしたらどのようなメッセージに変わるかを試してみたところ、落ちることがなくなってしまった。シミュレータモードに入った後の動作はバージョン0.9.5系までのように正常に見える。

詳しく見てみると

ここではTao.Sdl.Sdl.SDL_LoadBMP()で読み込んだアイコン画Data/icon.bmpに対して青を透明色として設定していたようで、修正後ビルドしたアセンブリを使用してシミュレータモードに入るとウィンドウマネージャのアイコンの四隅は青くなっている*2ことが分かった。ちなみに落ちる問題の出なかったバージョン0.9.5.5までではシミュレータモード時のウィンドウに対するウィンドウマネージャアイコンは設定されておらず、各ウィンドウマネージャが用意している「既定のアイコン」になっていた。
最終的にはTao FrameworkのTao.Sdl.Sdl.SDL_WM_SetIcon()を用いてこの透明部分がうまく無くなるようにしてやればよいのだが、この関数についてのリファレンスを見ると、引数で画像の「マスク」を渡すことによる透明部分の指定があることを発見した。これは画像データ(32x32ピクセル)をビットの並び*3に見立てて

  • 表示されない(透明な)部分を0
  • 表示される(不透明な)部分を1

として処理するようで、今回のアイコン画像の場合

0,0,1,1,1,1,1,1,  1,1,1,1,1,1,1,1,  1,1,1,1,1,1,1,1,  1,1,1,1,1,1,0,0,
0,1,1,1,1,1,1,1,  1,1,1,1,1,1,1,1,  1,1,1,1,1,1,1,1,  1,1,1,1,1,1,1,0,
1,1,1,1,1,1,1,1,  1,1,1,1,1,1,1,1,  1,1,1,1,1,1,1,1,  1,1,1,1,1,1,1,1,
(26行同じものが続く)
1,1,1,1,1,1,1,1,  1,1,1,1,1,1,1,1,  1,1,1,1,1,1,1,1,  1,1,1,1,1,1,1,1,
0,1,1,1,1,1,1,1,  1,1,1,1,1,1,1,1,  1,1,1,1,1,1,1,1,  1,1,1,1,1,1,1,0,
0,0,1,1,1,1,1,1,  1,1,1,1,1,1,1,1,  1,1,1,1,1,1,1,1,  1,1,1,1,1,1,0,0,

となる(「0」のピクセルが青い部分となる)ようにしたいので、8ビットのまとまりごとに2進数の足し算をすると

 63, 255, 255, 252,
127, 255, 255, 254,
255, 255, 255, 255,
(26行同じものが続く)
255, 255, 255, 255,
127, 255, 255, 254,
 63, 255, 255, 252,

という「バイト」の並びとなり、これをC#のbyte型(1バイトを用いて0から255のデータを表現できるデータ型)*4の配列としてTao.Sdl.Sdl.SDL_WM_SetIcon()へ渡すことになる。
(2009/3/26)値の1つが間違っていたのを修正

パッチ

修正としては下のようになった(改行コードはCR+LF)。
[任意]ファイル名: openbve-1.0.0.0-wmicon.patch

(this patch is public domain)
diff -ur openbve-1.0.0.0.orig/openBVE/OpenBve/Program.cs openbve-1.0.0.0/openBVE/OpenBve/Program.cs
--- openbve-1.0.0.0.orig/openBVE/OpenBve/Program.cs
+++ openbve-1.0.0.0/openBVE/OpenBve/Program.cs
@@ -251,10 +251,39 @@
                     try {
                         IntPtr Bitmap = Sdl.SDL_LoadBMP(File);
                         if (Bitmap != null) {
-                            Sdl.SDL_Surface Surface = (Sdl.SDL_Surface)System.Runtime.InteropServices.Marshal.PtrToStructure(Bitmap, typeof(Sdl.SDL_Surface));
-                            int ColorKey = Sdl.SDL_MapRGB(Surface.format, 0, 0, 255);
-                            Sdl.SDL_SetColorKey(Bitmap, Sdl.SDL_SRCCOLORKEY, ColorKey);
-                            Sdl.SDL_WM_SetIcon(Bitmap, null);
+                            byte[] mask = {  63, 255, 255, 252,
+                                            127, 255, 255, 254,
+                                            255, 255, 255, 255,
+                                            255, 255, 255, 255,
+                                            255, 255, 255, 255,
+                                            255, 255, 255, 255,
+                                            255, 255, 255, 255,
+                                            255, 255, 255, 255,
+                                            255, 255, 255, 255,
+                                            255, 255, 255, 255,
+                                            255, 255, 255, 255,
+                                            255, 255, 255, 255,
+                                            255, 255, 255, 255,
+                                            255, 255, 255, 255,
+                                            255, 255, 255, 255,
+                                            255, 255, 255, 255,
+                                            255, 255, 255, 255,
+                                            255, 255, 255, 255,
+                                            255, 255, 255, 255,
+                                            255, 255, 255, 255,
+                                            255, 255, 255, 255,
+                                            255, 255, 255, 255,
+                                            255, 255, 255, 255,
+                                            255, 255, 255, 255,
+                                            255, 255, 255, 255,
+                                            255, 255, 255, 255,
+                                            255, 255, 255, 255,
+                                            255, 255, 255, 255,
+                                            255, 255, 255, 255,
+                                            255, 255, 255, 255,
+                                            127, 255, 255, 254,
+                                             63, 255, 255, 252, };
+                            Sdl.SDL_WM_SetIcon(Bitmap, mask);
                         }
                     } catch { }
                 }

(2009/3/26)値の1つが間違っていたのを修正
確認してみると

このようにマスクがうまく処理されて四隅が透明になっていることが分かる(画像はGNOME上のMetacityウィンドウマネージャでAlt+Tabを押しているところ)。
この問題にうまく対処できたため、この修正を含んだMandriva Linux 2009.0向けのパッケージを別館の配布ページに公開した。
(2009/3/26)修正に合わせて画像も更新し、別館のパッケージも更新した。
(2014/10/12)配布ページは削除済み。

参考URL:

  • docs.taoframework.com/Tao.Sdl/Tao.Sdl.Sdl.SDL_WM_SetIcon.html (リンク切れ) - Tao Framework リファレンス: Sdl.SDL_WM_SetIcon()

使用したバージョン:

  • openBVE 1.0.0.0
  • Tao Framework 2.1.0
  • Mono 1.9.1

*1:一時的にMonoのバージョンを2.2にもしてみたが変わらなかったので1.9.1に戻した

*2:つまり、透過してほしいのにされていない状態

*3:左からそれぞれ128,64,32,16,8,4,2,1の位と見る

*4:符号無しの8ビットということでC言語SDLではUint8型(へのポインタ)となっている