パソコンでプログラミングしよう ウィンドウズC++プログラミング環境の構築
1.6.3.6(15)
GIMPとアプリケーションアイコン

MSYS2によるGIMP導入の注意点を説明し、Code::BlocksにおけるwxWidgetsプロジェクトのアイコンを考察する。

MSYS2へのGIMPインストール

GIMPはフリーの高機能なグラフィック編集ソフトウェアだが、本サイトはたかがアプリケーションのアイコン編集に用いる(鶏を割くに焉んぞ牛刀を用いん)。本サイトはMSYS2へGIMPをインストールする(MSYS2版)。

ウィンドウズインストーラ版との比較

GIMPは通常はウィンドウズインストーラで配布される(インストーラ版)。インストーラ版は「箱から出して(out-of-the-box)」すぐ使えるが、MSYS2版はそうならない。インストーラ版はスタートメニューへの登録を自動で行うが、MSYS2版は手動で行う。MSYS2版のアンインストールはMSYS2から行うが、スタートメニューやレジストリの消去は手動で行う必要がある。

どちらのGIMPも本体は同じで、インストールディレクトリがウィンドウズ準拠かMSYS2準拠かの違いでしかない。インストーラ版は例えばC:\Program Files\Gimp 2をインストールディレクトリとするが、binサブディレクトリに実行形式ファイルとDLLファイルを配置し、etc、lib、shareサブディレクトリに様々なファイルを配置し、これらはMSYS2版とほぼ同じ構成となる。従ってMSYS2がインストール済みのパソコンにインストーラ版のGIMPを導入すると多くのDLLファイルが重複する。Git for Windowsと異なりコマンドサーチパスが混乱する可能性は無いがディスク容量は消費する。

ff-load.dll/ff-save.dllエラー

GIMPはオリジナルのユニックスライク版ではターミナルへ標準出力(stdout)する。しかしウィンドウズではデスクトップアプリケーションとしてビルドされコマンドプロンプトへの標準出力を持たず、代わりに独自ウィンドウを表示して出力する。これは--verboseオプションで起動すれば確認できる。

コマンドプロンプトからのリダイレクションによるファイル保存もできない。一方でAPIレベルのパイプは機能し、mingw64ターミナルから起動すれば独自ウィンドウは表示されずターミナルへ標準出力される。Code::Blocksから外部ツールとして起動して[Logs & others]あるいは[Tool Output]ウィンドウへ標準出力することもできる。

GIMPはエラー出力(stderr)もするがリンク先記述と異なり独自ウィンドウへは出力しない一方、mingw64ターミナルや先のCode::Blocksウィンドウへは出力する。本サイトセットアップでmingw64ターミナルから起動すれば以下のエラーを表示するが、ウィンドウズから通常起動する限りは出力先が無いため全く気付かないであろう。

user@THINKPAD-L430 MINGW64 ~
$ gimp
GEGL-Message: 09:29:18.467: Module 'C:\msys64\mingw64\lib\gegl-0.4\ff-load.dll' load error: 'C:\msys64\mingw64\lib\gegl-0.4\ff-load.dll': 指定されたモジュールが見つかりません。
GEGL-Message: 09:29:18.467: Module 'C:\msys64\mingw64\lib\gegl-0.4\ff-save.dll' load error: 'C:\msys64\mingw64\lib\gegl-0.4\ff-save.dll': 指定されたモジュールが見つかりません。

ff-load.dll/ff-save.dllのロード失敗をエラーレポートするがアプリケーションは起動するので実行時動的リンク失敗であろう。両ファイルは存在するものの以下の存在しないDLLファイルのロード時動的リンクを試み起動に失敗する。

  • avcodec-58.dll
  • avformat-58.dll
  • avutil-56.dll
  • swscale-5.dll
覚え書き
上記調査においてMSYS2ツールlddでは十分な情報を得る事ができず、マイクロソフトDependency Walkerを用いている。

これら本サイトセットアップで存在しないDLLファイルはmingw64/mingw-w64-x86_64-ffmpegパッケージで供給される。GIMPのMSYS2へのインストール時に追加を提案されたパッケージの一つで本サイトはその提案を無視した。ffmpegはフリーのマルチメディアフレームワークでff-load.dll/ff-save.dllがそのファイルのロード/セーブを行うであろうことは容易に想像できる。本サイトはマルチメディアファイルを扱わずこのエラーは無視する事に決める。

Python Consoleウィンドウの対策

本サイトセットアップ直後のGIMPは[フィルター|Python-Fu|コンソール](英語表示なら[Filters|Python-Fu|Console])で[Python Console]ウィンドウの起動に失敗する。mingw64ターミナルから起動していれば以下のエラーが報告される。

Traceback (most recent call last):
File "C:/msys64/mingw64/lib/gimp/2.0/python/gimpfu.py", line 857, in _run
res = apply(func, params[1:])
File "C:\msys64\mingw64\lib\gimp\2.0\plug-ins\python-console\python-console.py", line 32, in do_console
import sys, gobject, gtk, gimpenums, gimpshelf, gimpui, pyconsole
ImportError: No module named pyconsole

モジュールpyconsole.pyはpython-console.pyと同じC:\msys64\mingw64\lib\gimp\2.0\plug-ins\python-consoleディレクトリに存在するが、Pythonのsys.pathがこのディレクトリを含まず発見できない。C:\msys64\mingw64\lib\gimp\2.0\environ\default.envを以下に書き換えれば解決する。設定ファイルの書き換えは本来避けるべきだが、これはインストーラ版の初期設定と同じで危険性は少ない。

# Example entry in files like these
# FOOPATH=/path/to/foo/stuff
PYTHONPATH=${gimp_installation_dir}/lib/gimp/2.0/python;${gimp_plug_in_dir}/plug-ins/python-console

GIMPによるアイコン作成

GIMPの使用法はリンクに譲り、開発アプリケーション用アイコン編集として用いる注意点のみ記述する。GIMPは[File|Open](日本語表示なら[ファイル|開く/インポート])などで扱う任意の画像ファイル形式を読み込むが、[File|Save]([ファイル|保存])などはGIMP独自形式(*.xcfなど)に固定され、それ以外の形式は[File|Export]などでエクスポートする。本サイトwxWidgetsプロジェクトはアプリケーションアイコンにICO形式、メニュー項目やツールボタンに表示されるビットマップにXPM形式を用いる。

ICO形式(*.ico)

ICOは主にアイコンで使用される画像ファイル形式。複数の画像格納が可能で、主にサイズの異なる画像を格納する。GIMPはこの複数画像をレイヤーとして扱う。

例えば本サイトのファビコンは以下全てを格納するICOファイルである。

16x16 24x24 32x32 48x48 64x64 96x96 128x128

wxWidgetsプロジェクトウィザード(K2)によるプロジェクトはアプリケーションアイコンのテンプレートファイルとしてiconimages\favicon.icoを用意し、アプリケーションアイコン、メインウィンドウアイコン、ソースコード解析ドキュメントHTMLファイルファビコンなどに共用する。

XPM形式(*.xpm)

XPMはテキストデータによる画像ファイル形式。2次元文字配列定式型式で記述されC++ソースコードに埋め込むことができる。

GIMPの生成するXPMを例示するが、ソースコードに埋め込むには以下の修正を必要とする。

/* XPM */
static char * C:\Users\user\MinGW\TestDrive_Work\Desktop_XPM_Test\iconimages\verinfo_xpm[] = {
"16 16 16 1",
" c None",
". c #013D64",
"+ c #0C567C",
"@ c #217296",
"# c #0082B9",
"$ c #0499BA",
"% c #1694C6",
"& c #35ACDB",
"* c #32B0D0",
"= c #5BC4D0",
"- c #6AC7EB",
"; c #89D0EA",
"> c #93E2F7",
", c #A2EBEB",
"' c #CCE9F5",
") c #FDFFFC",
" @+..+@ ",
" .@,>;>>=@. ",
" @,'-&;-&',=@ ",
" .,>&&')'&&;'=. ",
" =;&&&')'&&&&,= ",
"@=*--&&%%&;-&-,@",
"+--&%&'''&#%---+",
"+-&%##')'%#%%--.",
"+%%%##')'%#%%#-+",
"+$%%##')'%#%%%&+",
"@*%%##')'%#%%%*@",
" *$%##')'%#%%&* ",
" @*%##')'$#%%*@ ",
" **%#;;;#%%** ",
" $**%%%%$*$ ",
" @$$$$@ "};
  • 変数名がファイルフルパスから作成され不正なので適当なものに変更する。
  • 変数型static char *をstatic const char*に変更する。

XPMによるビットマップ指定

Code::Blocks付属wxSmithはメニュー項目やツールボタンなどのコントロールに表示するビットマップにwxArtProviderクラス利用によるwxWidgetsバンドルビットマップか、あるいはユーザー供給の画像ファイルを用いる。本サイトは主に後者の画像ファイルをXPM形式で与えるものとして、加えてソースコードへ埋め込む方法も示す。

wxSmithによるファイル指定

wxSmithでビットマップをファイル指定する方法を説明する。例として動作確認(1)および動作確認(2)で利用したDeskotp1アプリケーションの[Help|About](翻訳済みなら[ヘルプ|このアプリケーションについて])メニュー項目にビットマップを追加する。

  1. Code::BlocksでDesktop1プロジェクトを開く。
  2. プロジェクトディレクトリにiconimagesサブディレクトリを作成し、例示した未修整のXPM形式ファイルをverinfo.xpmという名称で保存する。
  3. [Management]ウィンドウ[Projects]ページのツリービューで[...|Desktop1|Resources|wxsmith|Desktop1Frame.wxs]をダブルクリックしてDesktop1Frame.wxsをwxSmithエディタに読み込む。
  4. [Management]ウィンドウ[Resources]ページ(wxSmithリソースマネージャ)のツリービューで[Resources|Desktop1|Desktop1Frame|Tools|wxMenuBar:MenuBar1|Help|About]を選択する。
  5. ツリービュー下部のプロパティペインで[Bitmap]を選択し右端[...]ボタンを押して[Image editor]ダイアログを開く。
  6. [Image options|Image From File]ラジオボタンを選択しその下のテキストボックス右端[...]ボタンを押して[Choose image file]ダイアログで先のiconimages\verinfo.xpmを選択し[開く]でダイアログを閉じる。
  7. [Image options|Image From File]テキストボックスにファイルフルパスが入力され、[Preview]にビットマップがプレビュー表示される。[OK]でダイアログを閉じる。
  8. [File|Save everything]し[Build|Build and run]すればリビルドされたDesktop1アプリケーションが起動する。[Help|About]([ヘルプ|このアプリケーションについて])メニュー項目にビットマップが表示されているのを確認してアプリケーションを終了する。

この方法はXPM形式に限定せず他の主要画像ファイル形式(*.bmp、*.gif、*.ico、*.jpg、*.pcx、*.png、*.tifなど)を用いる事もできるが、いずれも実行形式ファイルと共に画像ファイルを配布しなければならない。開発環境の絶対パスで画像ファイルを指定するので配布を念頭に相対パスに修正する必要もあり、実行形式ファイルディレクトリと実行時ワーキングディレクトリが異なるケースも考慮しなければならない。wxSmithの生成するソースコードを示す。

Desktop1Frame.cpp(ビットマップをファイル指定する)

#include "wx_pch.h"
#include "Desktop1Frame.h"
...
Desktop1Frame::Desktop1Frame(wxWindow* parent,wxWindowID id)
{
//(*Initialize(Desktop1Frame)
...
wxMenuItem* MenuItem2;
...
MenuItem2 = new wxMenuItem(Menu2, idMenuAbout, _("About\tF1"), _("Show info about this application"), wxITEM_NORMAL);
MenuItem2->SetBitmap(wxBitmap(wxImage(_T("C:\\Users\\user\\MinGW\\TestDrive\\Desktop1\\iconimages\\verinfo.xpm"))));
...
//*)
}

wxSmithによるソースコード埋め込み

wxSmithでビットマップをソースコードに埋め込む方法を説明する。引き続きDesktop1を用いる。

  1. iconimages\verinfo.xpmをiconimages\verinfo_corrected.xpmへコピーして変数名と変数型を修正する。
    /* XPM */
    static const char * verinfo_xpm[] = {
    "16 16 16 1",
    ...
  2. Desktop1Frame.cppにiconimages\verinfo_corrected.xpmをインクルードする。
    #include "wx_pch.h"
    #include "Desktop1Frame.h"
    #include "iconimages/verinfo_corrected.xpm"
    ...
  3. 先と同じ手順で[Help|About]メニュー項目の[Image editor]ダイアログを開く。
  4. [Image options|Code]ラジオボタンを選択し下のテキストボックスにverinfo_xpmを入力して[OK]でダイアログを閉じる。
  5. [File|Save everything]し[Build|Build and run]すればリビルドされたDesktop1アプリケーションが起動する。[Help|About]([ヘルプ|このアプリケーションについて])メニュー項目にビットマップが表示されているのを確認してアプリケーションを終了する。

この方法はXPM形式に限定されるがデータが実行形式ファイルに埋め込まれ画像ファイルを配布する必要が無い。wxSmithの生成するソースコードを示す。

Desktop1Frame.cpp(ビットマップを埋め込む)

#include "wx_pch.h"
#include "Desktop1Frame.h"
#include "iconimages/verinfo_corrected.xpm"
...
Desktop1Frame::Desktop1Frame(wxWindow* parent,wxWindowID id)
{
//(*Initialize(Desktop1Frame)
...
wxMenuItem* MenuItem2;
...
MenuItem2 = new wxMenuItem(Menu2, idMenuAbout, _("About\tF1"), _("Show info about this application"), wxITEM_NORMAL);
MenuItem2->SetBitmap(verinfo_xpm);
...
//*)
}

ファイル指定の例外的な挙動(wxImageとwxImageList)

wxSmithエディタ下部にあるコンポーネントパレットの[Tools]ページにwxImageオブジェクトとwxImageListオブジェクトが準備されている。これらはwxControlクラスを継承せずコントロールではないが、選択すればwxSmithエディタ上部にあるツールホルダーパネルにwxMenuBarと同じようにアイコンを配置してトップレベルウィンドウに追加する。例えばwxImageを選択しプロパティペイン[Image]の[...]ボタンを押せば先と全く同じ[Image editor]ダイアログが開き、[Image options|Image From File]ラジオボタンで画像ファイルを選択できる。しかし生成するソースコードは全く異なり、XPMデータに変換してソースコードに直接埋め込む。

Desktop1Frame::Desktop1Frame(wxWindow* parent,wxWindowID id)
{
//(*Initialize(Desktop1Frame)
...
static const char *Image1_XPM[] = {
"16 16 16 1",
"- c #32B0D0",
". c #217296",
": c #0082B9",
"> c #0499BA",
"= c #FDFFFC",
"@ c #89D0EA",
"; c #1694C6",
"$ c #CCE9F5",
"# c #5BC4D0",
"X c #0C567C",
"& c #6AC7EB",
"_ c None",
"* c #35ACDB",
"o c #013D64",
"O c #A2EBEB",
"+ c #93E2F7",
"_____.XooX._____",
"___o.O+@++#.o___",
"__.O$&*@&*$O#.__",
"_oO+**$=$**@$#o_",
"_#@***$=$****O#_",
".#-&&**;;*@&*&O.",
"X&&*;*$$$*:;&&&X",
"X&*;::$=$;:;;&&o",
"X;;;::$=$;:;;:&X",
"X>;;::$=$;:;;;*X",
".-;;::$=$;:;;;-.",
"_->;::$=$;:;;*-_",
"_.-;::$=$>:;;-._",
"__--;:@@@:;;--__",
"___>--;;;;>->___",
"_____.>>>>._____"
};
Image1 = new wxImage(Image1_XPM);
Image1_BMP = new wxBitmap(Image1_XPM);
...
//*)
}

変換できる画像ファイル形式はXPM形式に限定されない。XPM形式ファイルも別形式データを経由して再度XPM形式に変換していると思われ埋め込まれるテキストデータと元々のテキストデータは一致しない。この機能は一見便利なものの通常の定義と挙動が異なり混乱する。コントロールのビットマップにこのwxImageインスタンスを指定([Image editor]ダイアログ[Image options|Code]に*Image1を指定)すれば余計なインクルードファイルが不要になると考えるかもしれないが、構築順が逆だと実行時エラーを招く。さらにファイルからビットマップを取得する通常ルーチンと異なるため、同じ画像ファイルから微妙に異なるビットマップを取得するケースが存在する。

wxImageListもファイル指定までの手順が異なるものの同様に画像データをXPMに変換してソースコードへ埋め込む。