スクリプトの文字列処理
まず、スクリプトは改行で区切られるコマンド行から構成されているので、改行ごとに分離しましょう。そして、「そのコマンド行内のコマンドと引数を取得し、実際にコマンドを処理する関数に渡す」というここまでの処理を、関数ExecScript()
に記述します。
// 文字列を行ごとに区切り、コマンド名と引数に分離 public void ExecScript(String script) { if (script == null) { return; } // 改行を\nに統一 script = script.Replace("\r\n", "\n"); // スクリプトを改行で分離し行ごとに配列に格納 String[] astLine = script.Split("\n".ToCharArray()); // 各行を処理 foreach (String stLine in astLine) { // 「文字列(文字列、または空)」の形式なら、コマンド行として処理 if (stLine.IndexOf("(") > 0 && stLine.EndsWith(")")) { // (の前までをコマンド名として取得 String stCommand = stLine.Substring(0, stLine.IndexOf("(")); String stArg = null; // ()内に文字列があれば、引数として取得 if (stLine.Length > stLine.IndexOf("(") + 2) { stArg = stLine.Substring(stLine.IndexOf("(") + 1, stLine.Length - 1 - (stLine.IndexOf("(") + 1)); } // コマンドを処理 ExecCommand(stCommand, stArg); } } }
関数内の処理は、まず改行\n
でスクリプトを各コマンド行に分離し、分離した各コマンド行からコマンド名と引数を取得して、実際にコマンドを処理するExecCommand()
に渡していく、という流れになっています。今回のコマンドは
コマンド名(引数)
という形式なので、コマンド行からコマンド名と引数の文字列を取得する処理は、次のようになります。
取得対象 | 識別方法 |
コマンド名 | 最初の( の前まで |
引数 | 最初の( の次からコマンド行末尾の) の前まで |
コマンドを処理するExecCommand()
では、まず引数文字列として渡された文字列を「,」で区切り、各引数を分離します。ただし、文字列内(""
の中)の「,」では区切ってはいけないので、""
の内側の「,」は区切り文字と見なさないようにしました。今回引数を文字列にとるのはload()
のファイルパスのみで、ファイルパスに「,」は使えないので、""
内の判定はなくても(単純に「,」でSplit()
し引数を分離しても)動作に支障はありません。
引数を「,」で区切って配列astArg
に格納したら、次にコマンド名を見て各コマンドの処理に振り分けます。load
であれば渡されたファイルパスをBitmap
クラスのコンストラクタに渡す、mirror/upset
ならBitmap
クラスのRotateFlip()
を呼び出す……とほとんど.NETのライブラリに「丸投げ」して行います。
画像に対して独自の処理を行っている箇所は、各ピクセルのRGBを加算するaddRGB
くらいです。addRGB
の処理では、まずBitmap
のGetPixel()
で各ピクセルの色情報(Color
構造体)を取得し、その各RGB成分に指定値を加算して新しいRGBを算出します。そして、RGBの各値を0-255に丸めてから、そのRGBでColor
構造体を作成し、ピクセルの値としてSetPixel()
する、という流れになっています。
ピクセルのRGBを取得し、またピクセルに書き戻すことができれば、画像処理のアルゴリズム次第で何でもできるので、各種フィルタなど自分でいろいろなコマンドを追加してみるのも良いでしょう。
アプリケーション
実行するとフォームが作成されるので、下の方にある白いテキスト欄に
load("d:\image.jpg")
など、適当な画像を読み込むコマンド行を書いて[実行]ボタンをクリックしてみてください。画像が読み込まれ表示されたら、
addRGB(100,-100,100) mirror() upset()
などと入力し、コマンドを一通り試してみましょう。[実行]ボタンをクリックすると、スクリプトを入力したテキスト入力欄の下にあるテキスト欄にExecCommand()
内で認識されたコマンド名や各引数が表示されます。
ただし、resize()
の引数にあまり大きな値を指定すると、画像の作成に失敗する可能性があるので注意してください。
まとめ
今回のように「コマンドのみ」を実行するスクリプト実行系の実装は、意外に簡単です。そして、この種の簡易スクリプト実行系は、アプリケーションでちょっとした定型処理を行えるようにしたり、また手持ちの高度なライブラリの機能を、プログラムを新たに作ることなくテキストファイルから簡単に利用できるインターフェイス(テキストファイルのコマンドをライブラリを呼び出して処理するプログラム)を作ってみたり、と応用範囲もかなり広いと言えるでしょう。
実際、私も数年前に簡易スクリプト実行系を試作してみてからは、自作アプリケーションにちょっとしたスクリプト実行系を組み込む機会が多くなりました。一定の「手順」を踏む処理を行うアプリケーションを開発する際は、その手順を自動化するスクリプト実行系を組み込んで利便性を高められないか、検討してみる価値はあると思います。
簡易スクリプト実行系の実装に慣れてきたら、簡易スクリプトから変数や制御構文を持つ本格的なスクリプト実行系へと発展させていくのも、興味深い挑戦になるでしょう。