液晶モジュールでグラフィック
今回も前回に続いて「1.8inch TFT 液晶モジュール」を使います。
・micro:bit用液晶モジュール(www.waveshare.comでの製品紹介) https://www.waveshare.com/1.8inch-lcd-for-micro-bit.htm
(Amazonでの販売) https://www.amazon.co.jp/dp/B07HD2TXQZ/
・GitHub https://github.com/waveshare/WSLCD1in8
この液晶モジュールは160×128ドット65536色の表示が可能ですが、マイクロビットでピクセル画像を表示しようとするとかなり大変です。1つ1つブロックを組み上げるのも、JavaScriptプログラムを入力していくのも手間がかかります。液晶モジュールの性能がよくても開発環境や用意されている命令によって、その性能をフルに発揮できないことがあります。
今回は液晶モジュールの性能を活かすためにAdobe PhotoshopとAdobe Illustratorを使ってマイクロビットのプログラムを生成することにします。なお、PhotoshopはCS3以降、IllustratorはCS6以降が対象です。
・Adobe Photoshop
https://www.adobe.com/jp/products/photoshop.html
・Adobe Illustrator
https://www.adobe.com/jp/products/illustrator.html
マイクロビットのプログラムは直接バイナリコードを生成するのは難しいので開発環境で使われているJavaScriptプログラムを生成することにします。
Photoshopを利用してピクセル画像を表示する
表示するピクセル画像は160×128ドットのサイズですので、Photoshopで用意する画像もそのサイズにしておきます。ただ、マイクロビット側の容量の関係で160×128サイズで表示できる画像は限界があります。このため、以後に説明する変換プログラムは160×128サイズのものと80×64サイズのものを用意してあります。
80×64サイズでもおおよそ700程度のプログラム行数を超えるとコンパイルエラーでバイナリデータを生成できなくなります。
画像データは160×128または80×64ピクセルサイズで、カラーモードはRGBカラー(8bit) にします。画像を用意したらPhotoshopで動作するスクリプトを実行します。なお、スクリプトについての詳しい説明は省きますが、処理としては以下のようになります。
まず、画像の左上から1ピクセルずつ色を取得します。次に右側のピクセルの色を取得します。1つ前の色と同じならさらに右側のピクセルを読み込みます。違う色か右端を超えた場合には以下のLCD1IN8.DrawLine()で線を描画します。
LCD1IN8.DrawLine(x1,y1,x2,y2,カラー,ピクセルサイズ,形状)
同じ色が続いた場合は線を描画することで生成するプログラムを短くしています。データ圧縮の手法で言えばもっとも簡単なランレングス法になります。
あとは、縦のサイズ分だけ繰り返せばプログラムが生成されます。
Photoshopで160×128ピクセルの画像を用意し以下のプログラムを実行するとマイクロビットのJavaScriptコードが生成されます。生成されたコードをコピーしてマイクロビットのJavaScriptコードエディタにペーストします。あとは、通常通りにマイクロビットのバイナリデータを生成します。
以下のプログラムを実行する前に、あらかじめテキストエディタ等でコピー&ペーストした後で保存しておく必要があります。保存する際に拡張子を.jsxにしてください。
Photoshop用のプログラム(160x128)
// micro:bit LCD image converter (160x128)
// RRRRRGGGGGBBBBB1
(function (){
var basefile=new File("~/Desktop/00mbit.txt");
var savefile = basefile.saveDlg("保存ファイル名を指定してください(*.txt)")
if (!savefile){ return; } // キャンセルされた場合
var flag = savefile.open("w");
if (!flag){
alert("ファイルが保存できません");
return;
}
savefile.encoding = "ASCII";
savefile.writeln('LCD1IN8.LCD_Init()');
savefile.writeln('LCD1IN8.LCD_Clear()');
for(var y=0; y<128; y++){
var txt = "";
var x1=0;
var cc=getPixel(0, 0);
for(var x=0; x<160; x++){
var c=getPixel(x, y);
if ((c==cc) && (x<159)){ continue; }
txt += 'LCD1IN8.DrawLine('+x1+','+y+','+(x-1)+','+y+','+cc+',DOT_PIXEL.DOT_PIXEL_1,LINE_STYLE.LINE_SOLID)\n';
x1=x;
cc=c;
}
savefile.writeln(txt);
$.writeln("line : "+y);
}
activeDocument.selection.deselect();
savefile.writeln('LCD1IN8.LCD_Display()');
savefile.close();
//---------------------------------------------
// ピクセル値(RGB)を取得する(8bitモード専用)
//---------------------------------------------
function getPixel(x,y){
var R = G= B= 0,data,i;
var docObj = activeDocument;
docObj.selection.select([[x,y],[x+1,y],[x+1,y+1],[x,y+1],[x,y]]);
data = docObj.channels[0].histogram;
for (i=0; i<256; i++) if (data[i] > 0) { R = i; break; }
data = docObj.channels[1].histogram;
for (i=0; i<256; i++) if (data[i] > 0) { G = i; break; }
data = docObj.channels[2].histogram;
for (i=0; i<256; i++) if (data[i] > 0) { B = i; break; }
R=R>>3;
G=G>>3;
B=B>>3;
return (R<<11)|(G<<6)|(B<<1)|1;
}
})();
実際には160×128ではデータサイズが大きくなりマイクロビットのバイナリデータを生成することができません。
そこで80×64ピクセルサイズにして、なるべく横に同じ色が続くように画像を作成します。
ここでは以下の富士山の画像を用意しました。
次に以下のプログラムをPhotoshopから実行します。
スクリプトの変換が終わるとテキストファイルにプログラムが生成されます。
Photoshop用のプログラム(80x64)
// micro:bit LCD image comverter (80x64)
// RRRRRGGGGGBBBBB1
(function (){
var basefile=new File("~/Desktop/00mbit.txt");
var savefile = basefile.saveDlg("保存ファイル名を指定してください(*.txt)")
if (!savefile){ return; } // キャンセルされた場合
var flag = savefile.open("w");
if (!flag){
alert("ファイルが保存できません");
return;
}
savefile.encoding = "ASCII";
savefile.writeln('LCD1IN8.LCD_Init()');
savefile.writeln('LCD1IN8.LCD_Clear()');
for(var y=0; y<64; y++){
var txt = "";
var x1=0;
var cc=getPixel(0, 0);
for(var x=0; x<80; x++){
var c=getPixel(x, y);
if ((c==cc) && (x<79)){ continue; }
txt += 'LCD1IN8.DrawLine('+(x1*2)+','+(y*2)+','+((x-1)*2)+','+(y*2)+','+cc+',DOT_PIXEL.DOT_PIXEL_2,LINE_STYLE.LINE_SOLID)\n';
x1=x;
cc=c;
}
savefile.writeln(txt);
//$.writeln("line : "+y);
}
activeDocument.selection.deselect();
savefile.writeln('LCD1IN8.LCD_Display()');
savefile.close();
//---------------------------------------------
// ピクセル値(RGB)を取得する(8bitモード専用)
//---------------------------------------------
function getPixel(x,y){
var R = G= B= 0,data,i;
var docObj = activeDocument;
docObj.selection.select([[x,y],[x+1,y],[x+1,y+1],[x,y+1],[x,y]]);
data = docObj.channels[0].histogram;
for (i=0; i<256; i++) if (data[i] > 0) { R = i; break; }
data = docObj.channels[1].histogram;
for (i=0; i<256; i++) if (data[i] > 0) { G = i; break; }
data = docObj.channels[2].histogram;
for (i=0; i<256; i++) if (data[i] > 0) { B = i; break; }
R=R>>3;
G=G>>3;
B=B>>3;
return (R<<11)|(G<<6)|(B<<1)|1;
}
})();
生成されたコードをマイクロビットのJavaScriptコードエディタにペーストします。
マイクロビットに生成されたプログラムを転送すると画像が表示されます。
カラー値についての補足説明
生成するプログラム内で表示するカラー値の処理があります。液晶モジュールはRGB各色5ビットで表現されています。このビットの並びは以下のようになっています。
RRRRRGGGGGBBBBB1
全部で16ビットの値になります。最下位ビットは輝度ビットで0より1にすると明るくなります。 PhotoshopのRGBモードは8ビットなので5ビットにする必要があります。5ビットにするにはシフト演算を行う>>を使い、Red>>3のようにします。これで3ビット分ずれるので5ビットカラーに調整されます。緑や青も同様に処理します。
次にRGB各色5ビットを16ビットに収めるようにします。ここでもシフト演算を使います。今度は<<を使います。Red<<11のように演算を行います。他の色とミックスするにはビット演算の論理和を使います。論理和は|記号を使います。これをRGBそれぞれに適用します。最下位ビットを0にするか1にするかは好みですが、ここでは明るく表示するために1としています。
Illustratorを利用してベクトル画像を表示する
次にピクセル画像ではなくベクトル画像を表示してみます。液晶モジュールに用意されている命令はベクトルベースでは、やりやすいものばかりです。ここでは線を描く機能だけを使います。
Illustratorで新規にドキュメントを作成する際、Webで160×128のカスタムサイズにします。また、カラーモードはRGBにしておきます。
あとは、好きな色と1〜4ポイントまでの線を描きます。おおよそ700本程度までの線であれば処理することができます。
以下のプログラムを実行する前に、あらかじめテキストエディタ等でコピー&ペーストした後で保存しておく必要があります。保存する際に拡張子を.jsxにしてください。
描画する図を描いたらスクリプトを実行します。
変換が終わるとマイクロビットのプログラムが生成されます。
// Adobe Illustrator -> micro:bit液晶モジュールコンバーター(直線のみ)
// 線の太さとカラーを反映
(function(){
var basefile=new File("~/Desktop/00mbitv.txt");
var savefile = basefile.saveDlg("保存ファイル名を指定してください(*.txt)")
if (!savefile){ return; } // キャンセルされた場合
var flag = savefile.open("w");
if (!flag){
alert("ファイルが保存できません");
return;
}
savefile.encoding = "ASCII";
savefile.writeln('LCD1IN8.LCD_Init()');
savefile.writeln('LCD1IN8.LCD_Clear()');
// 変換処理
var doc = app.activeDocument;
var pageHeight = app.activeDocument.height;
var pathList = doc.pathItems;
var txt = "";
for(var i=0; i<pathList.length; i++){
var pItem = pathList[i];
var x1 = pItem.pathPoints[0].anchor[0]|0;
var y1 = Math.abs(pItem.pathPoints[0].anchor[1])|0;
var x2 = pItem.pathPoints[1].anchor[0]|0;
var y2 = Math.abs(pItem.pathPoints[1].anchor[1])|0;
var color = pItem.strokeColor; // RGB Color
var R=color.red>>3;
var G=color.green>>3;
var B=color.blue>>3;
var c=(R<<11)|(G<<6)|(B<<1)|1;
var sw=pItem.strokeWidth|0;
if(sw<1){ sw=1; }
if(sw>3){ sw=4; }
txt += 'LCD1IN8.DrawLine('+x1+','+y1+','+x2+','+y2+','+c+',DOT_PIXEL.DOT_PIXEL_'+sw+',LINE_STYLE.LINE_SOLID)\n';
}
savefile.writeln(txt);
savefile.writeln('LCD1IN8.LCD_Display()');
savefile.close();
alert("変換終了");
})();
このプログラムをコピーしてマイクロビットのJavaScriptコードエディタにペーストします。
マイクロビットに生成されたプログラムを転送すると画像が表示されます。
マイクロビットは他のアプリケーションでプログラムを生成することで、より性能を発揮することもできます。
今回はPhotoshopやIllustratorのスクリプトについては説明しませんでしたが、興味のある方はWebサイトや書籍等で勉強してみてください。
著者 古籏一浩
プログラミングをベースにして面白そうなものはとりあえずやってみるというスタンス。複雑なものよりシンプルで楽しめるものが好み。最近は30年前に移植したゲーム(mz-700版 SPACE HARRIER)の話などを書いたりしています。
著者サイト:http://www.openspc2.org/