2014年8月29日金曜日

AppMethodに挑戦(16)TPanelの色を変えて配置する件。

appmethod(C++Builder/Delphi)の、最初に迷うところのひとつとして、
Colorプロパティが無いことが上げられる。
で、どうやら、『スタイル』なるもので設定するようだということがわかる。
しかし、そもそもスタイルをどうやって変更するのかというと、
これがなかなか面倒なのだ。
で、TPanelを3つ配置して、それぞれの色を変えることをやってみる。
いやいや。。。。3時間くらいかかりましたよ^^;^^;
信じられん!
まぁ、こういうことです。


まず、スタイルを変更するために、TStyleBookなるコントロールを貼り付けないとダメなのだ。
次に、FormのStyleBookプロパティにそれを設定するのだ。
しかし、貼り付けたスタイルをどうするかというと、スタイルファイルを読み込ませる必要があるのだ。
で、結論だけ順を追うと、、

(1)C:\Program Files (x86)\Embarcadero\Studio\14.0\Redist\styles\Fmxから、*.styleをひとつCopyする。
(2)色を追加するために、そのファイルにスタイルを追加する(後述)
(3)TStyleBookをFormに貼り付ける
(4)FormのStyleBookプロパティにそれを設定する。
(5)はりつけたStyleBookコントロールをダブルクリックする
(6)『読み込み』ボタンを押して、Copy&修正したスタイルファイルを読み込む
(7)『適用して閉じる』ボタンで戻る
(8)コントロールのstyleLookUpで追加したスタイルを設定する。

である。
なぜか、(5)の段階でスタイルを追加する方法がわからなかったため、(1)(2)の方法をとった。
本当はやり方があるのかもしれない。
※"コントロールを右クリックして『カスタムスタイルの編集』を選択"という情報が
WEB上にありますが、そもそもそのメニューが出てこないのです^^;^^;


で、スタイルの追加について。。
まず、TPanelで選択できるスタイルは2つしかない。
『panelstyle』と『calloutpanelstyle』である。
そして、Copyしたスタイルファイルを見ると、これらが定義されている。
『calloutpanelstyle』については、こんな記述だった。
うん。Fill.Colorとか設定してあるので、なんかちょっと安心だ。
  object TCalloutRectangle
    StyleName = 'calloutpanelstyle'
    Fill.Color = xFF0000f0
    Height = 50.000000000000000000
    HitTest = False
    Stroke.Color = xFF085F30
    Width = 50.000000000000000000
    CalloutWidth = 23.000000000000000000
    CalloutLength = 11.000000000000000000
  end
そこでこれごとコピペする。
そんでもって、色とStyleNameを変える。
こんな感じ。
  object TCalloutRectangle
    StyleName = 'calloutpanelstyle2'
    Fill.Color = xFFf00000
    Height = 50.000000000000000000
    HitTest = False
    Stroke.Color = xFF085F30
    Width = 50.000000000000000000
    CalloutWidth = 23.000000000000000000
    CalloutLength = 11.000000000000000000
  end

そうすると、styleLookUpプロパティを設定するとき、
これも一覧に出てくる。



めでたしめでたし(o^^o)


2014年8月22日金曜日

AppMethodに挑戦(14)簡単に画像加工してみる

では、画像を読み込んでフィルタリングすることを考える。
ここでは、以前ロジックを考えた『漫画カメラ加工』をしてみる。
で、調査。
だいたい私のやりたいことって、HELPとか見てもググってもダメな場合が多い。
また、
こんなとこ(http://docwiki.embarcadero.com/Libraries/XE6/ja/メインページ)
なんて見ても、検索も表示も遅すぎてだれてしまうだけでなく、結局、

【このトピックには現在ドキュメントが存在しません。「ノート」を利用してこのトピックの改良について話しあうことができます。】

なんていう表示をシレっとしてくるのだ。
で、とりあえず、fmx/FMX.Graphics.hppを眺める。
だいたい、GetPixelとかSetPixelなんていう単語を検索するのだ。
うん。どうやら、【TBitmapData】あたりだな。。。
で、これが引数になるやつは、、、
【TBitmap::Map】、【TBitmap::Unmap】あたりかな。。

ということで、
(1)Timage::Bitmap::LoadFromFileでファイルをLOAD。
(2)Timage::Bitmap::MapでTBitmapDataをセット
(3)TBitmapData::Dataがどうやらデータへのポインタ。

で、テストコード。
TBitmapData B;
TI->Bitmap->Map(TMapAccess::maReadWrite,B);
unsigned char* cp = (unsigned char*)B.Data;
DIB.setSize(B.Width,B.Height);//DIBは自前の画像処理クラス
memcpy(DIB.getCp(),cp,B.Width*B.Height*4);//4は本来はBytesPerPixel
DIB.goManga();//goMangaは漫画カメラフィルタ
memcpy(cp,DIB.getCp(),B.Width*B.Height*4);
TI->Bitmap->Unmap(B);

で、テスト。→アクセス例外!
そこであれこれやってみて、、TBitmapDataを見直す。
うん。【GetScanline】という関数がある。
ってことは、、画像データが全てメモリ上にあるわけでは無いと予測。
だからmemcpyがオーバーランして例外に違いない。
きっと、使うとこ周辺だけマッピングされてるんだろね。
だって、関数が【Map】だったし!

ということで、コードを改修。1行ずつメモリ内容をCOPYして戻す。
※DIBクラスは全てOnMemory。

TBitmapData B;
TI->Bitmap->Map(TMapAccess::maReadWrite,B);
DIB.setSize(B.Width,B.Height);
for(int y=0;y<B.Height;y++) DIB.setLineVal(y,B.GetScanline(y));
DIB.goManga2();
for(int y=0;y<B.Height;y++) DIB.copLineVal(y,B.GetScanline(y));
TI->Bitmap->Unmap(B);

で、結果。
うん。大丈夫ですね。
★変換前                 ★変換後
   

2014年8月21日木曜日

AppMethodに挑戦(13) 画像を扱う第一歩→Oneソースマルチプラットフォームの道は険しい。→いや、そうでもないか。

これはまぁバグじゃないんでしょう。きっと。
仕様としてはこの方が美しいと言える部分もあります。
こうなってる理由もわかります。
でも、やっぱいただけないかなー。
だって#ifdefかかなきゃなんないもんね。。。^^;

ということで、画像を扱う第一歩として、
画像を読み込んで表示させて、メモリを操作して線を描画して表示させるなんてことをやってみました。
●TBitmapに読み込む
●TImageにわりつける
TImage::TBitmapのデータポイントを取得する
●直書きしてみる
っとまぁ、そんなとこです。
で、ですね。。。例によって、WIN32とAndroidの結果ですが。。。。

★WIN32       ★Android
  

そうです。線の色が違う。
そしてデータを取ってきたときの内容が違う。
WIN32の方は、B-G-R-Aの順。
Andridの方は、R-G-B-Aの順。
もちろん、理由はなんとなくわかりますけどねー。
でもねーー。。せっかくOneソースなんだから。。。
なんとかして欲しかったなぁーー。


TBitmap* bmp = new TBitmap;

String gfile = TPath::Combine(TPath::GetDocumentsPath(), "gazou.png");
bmp->LoadFromFile(gfile);
TI->Bitmap = bmp;
TBitmapData B;
TI->Bitmap->Map(TMapAccess::maReadWrite,B);
unsigned char* cp = (unsigned char*)B.Data;
AnsiString as;
for(int w=0;w<100;w+=4){
 as.printf("[%02x][%02x][%02x][%02x]",cp[w*4+0],cp[w*4+1],cp[w*4+2],cp[w*4+3]);
 M1->Lines->Add(as);
}
for(int w=0;w<100;w++){
 char* ccp = (char*)B.GetPixelAddr(w,w);
 ccp[0] = 0x0;  
 ccp[1] = 0x0;  
 ccp[2] = 0xff;  
 ccp[3] = 0xff;
 ccp[0+4] = 0x0;  
 ccp[1+4] = 0x0; 
 ccp[2+4] = 0xff;
 ccp[3+4] = 0xff;
 ccp[0+8] = 0x0; 
 ccp[1+8] = 0x0; 
 ccp[2+8] = 0xff;
 ccp[3+8] = 0xff;
}
TI->Bitmap->Unmap(B);

AppMethodに挑戦(12)UTF8Stringというのを使ってみる。

UTF8Stringというのを使ってみる。

UTF8のcharの配列を作り、
それをUTF8Stringで代入して、
それをWideCharに変換。
それをデバッガにかけて内容をみます。
おー!意外にも、サロゲートペアまで含めて、ちゃんとなってます。
以下、SRCとデバッグ時画像。
参考:
12402→0x3072→【ひ】
12425→0x3089→【ら】


char utf8er[] = {0x41,0x42,0xe3,0x81,0xb2,0xe3,0x82,0x89,0xf0,0xa0,0x80,0x8b,0};//ABひら丈(点あり)

UTF8String u8 = utf8er;
UnicodeString us0 = u8;
WideChar* wcptr = us0.c_str();
ShowMessage(wcptr);

●WIN32の方


●Androidの方


文字はなんとなくわかりました。
次は画像ですね(o^^o)




2014年8月20日水曜日

WIN32でGetPrivateProfileStringでダブルクォーテーションの件。

超・今さらなのですが、初めて気付いたこと。
Win32に、
GetPrivateProfileString 
というAPIがありまして。
今まで知らなかったのですが、このAPI、
中途半端にダブルクォーテーションを削除してしまう
ということを初めて知りました。
要するに、
--------------
key1="a","b"
--------------
なんていうiniファイルを読ませると、
【a","b】
という、最初と最後の【"】を削除してしまうのですよ。

当然のように、【a","b】をパースするので思った結果にならない。

今回は、
--------------
key1="abc,def","123"
--------------
みたいな感じだったので、
読み込む  →【abc,def","123】
カンマで分離→【abc】&【def","123】
という最悪の結果(>_<)

で、結論から言うと、
--------------
key1=""abc,def","123""
--------------
みたいに、最初と最後の【"】を重ねればよいことがわかりましたとさ。

どんとはらい。(o^^o)



2014年8月19日火曜日

AppMethodに挑戦(11)UnicodeStringのバグ?→仕様だそうです。

20140821追記
表題の件、embarcaderoさまより回答をいただいており、『仕様』のようです。
以下、参考URLです。
C++ におけるマルチデバイス アプリケーションについての考慮事項
文字列定数
またその他の情報として、
『C++ではAnsiString型の一種であり、UTF-8形式で文字列を処理するUTF8String型をお勧め』
だそうなので、UTF8Stringというのをためしてみます。
以下は、元の記事です。

文字コードの謎です。
まぁ謎というほどのことでもないのですが、
appmethod(Delpji/C++Builder含む)
の大きな特徴のひとつとして、ワンソース、マルチプラットフォームがあるわけです。
そして、文字コードは(正確には文字のEncode)、UTF16のようで、
文字列はとにかくUnicodeStringというものを使うべきだと。
ということで、SRCと結果を。
 M1->Lines->Clear();
 UnicodeString us1;
 us1 = "AnsiString";
 M1->Lines->Add(us1);
 us1 = "AnsiString日本語";
 M1->Lines->Add(us1);
 us1 = L"WideString";
 M1->Lines->Add(us1);
 us1 = L"WideString日本語";
 M1->Lines->Add(us1);
 WideChar wc[] = { 0xd840,0xdc0b,0x6075,0x4ecb,0};
 us1 = wc;
 M1->Lines->Add(us1);
 M1->Lines->Add(wc);

●結果
左から、設計時画面、WIN32実行画面、Android実行画面

    

●ということで、2点あります。
■ひとつは、もちろん期待はしていませんでしたが、
サロゲートペアの文字は当然のようにWindowsでしか表示されませんでした。
まぁこれはしかたないところでしょう。スマフォ側のフォントの問題もありますし、
スマフォのブラウザでも表示できなかったので。。。(>_<)

■しかし、問題はこれです。
us1 = L"WideString日本語"; はOKですが、
us1 = "AnsiString日本語"; はNGだということです。
ここを見ると、こんな記述があります。
UnicodeString は、名前こそ Unicode となっていますが、ANSI 、Unicode 両方の文字セットの文字列を表すことができます(ANSI 文字列はまず変換されます)。
ということは、
us1 = "AnsiString日本語"; としたときに内部的にはUnicodeに変換されてると予想されますが、変換される方法が間違っているということですよね。
で、ここでテスト。
us1 = "あAnsiString日本語";
WideChar* wcptr = us1.c_str();
と書いて、デバッガで見てみます。

●win32だとこうである。


●arm(Android)だとこうである。


もーハッキリ言ってぐでぐでですわ~(o^^o)。
まぁ、日本人が作ってないですからね。。。
バグ報告とか無いんでしょうか?

まぁ、、でも、、このような使い方をしなければよいのでしょうけど。。。(>_<)!

その他。。
【配置】において、日本語ファイルを追加するとエラーとなりますね。
まぁこれも仕方ないのかな。。。(>_<)


その他2
→こんなのも書かざるを得ませんでした。。。(>_<)
#ifdef __arm__
#define THandle int
#endif



AppMethodに挑戦(10) defineされているもの。


まぁいわゆる定義済みマクロというやつですね。
Windows/Linuxさらに32BIT/64BITをいままでdefineで判断して
SRCを書いていたわけですが、さらにスマフォ用としていろいろ判断しなけりゃならなくなりました。
とりあえず、以下のようなdefineで場合わけします。
なぜか、

【__BORLANDC__】とか
【__linux__】あたりもdefineされてます。
あとは、
【__arm__】
【__ANDROID__】
【__APPLE__】
あたりで判断しますかね~。


■まとめ。

●Android用のコンパイル時にdefineされてない
_WIN32 NOT define
_WIN64 NOT define
linux NOT define
__i386__ NOT define
__x86_64__ NOT define
__LP64__ NOT define
XP_WIN NOT define
CROSS_COMPILE NOT define
WINCE NOT define
__APPLE__ NOT define

●Android用のコンパイル時にdefineされている
__linux define
__linux__ define
__arm__ define
__ANDROID__ define
__BORLANDC__ define

2014年8月14日木曜日

AppMethodに挑戦(9) いよいよアプリ作成開始!

アプリ作成開始です。
ま、とりあえず作りたいものはなんとなく決まってますから、
それに向けての、クラス類の作成です。

まず、アプリとは言っても、私の場合、サーバ連携ものが必須なので、
気軽にGET/POSTできるものが必要です。

日頃のHTTPとかはCURLでやってるのですが、、
どうもCURLのソースをappmethodで通すのは結構手間隙かかりそうな感じだったので
素直に、準備されてるIndyを使うことにします。
まぁ、これです。
でも、使い方としては、
dHttp* p = new dHttp;
const char* s = p->getStr("http://***.***.***/**.php");
みたいにしたいので、
TIdHttpと
TMemoStreamとかを含んだ形のクラスを作成してみます。
作り方は、、

クラスを作る

win32としてテストプログラムを作って実行

androidで実行

ですね。
appmethodはテストのためのビルドやテスト環境として、
win32ができないと、どうしようも開発効率が悪いです。
細かいクラスまでAndroid端末でテスト実行なんて、時間がかかりすぎで
現実的じゃぁないよなぁー。

ちなみに、appmethodは、どういうライセンスのものを買うかは決めてませんが、
1ヶ月過ぎてしまったので、とりあえず、RadStudio(c++bulder)の評価版を使ってやってみます。
まぁこれが1ヶ月すぎるころには、何かしら買ってることでしょう。。^^;

というこどで、とりあえず要点だけ。
●getしてくる
 TIdHTTP *IdHTTP1 = new TIdHTTP;
 IdHTTP1->HandleRedirects = true;
 IdHTTP1->Head(url);
 if(IdHTTP1->ResponseCode == 302){
  IdHTTP1->Head(IdHTTP1->Response->Location);
 }
 WideString SourceFile = IdHTTP1->URL->URI;
 TMemoryStream* mstr = new TMemoryStream;
 IdHTTP1->Get(SourceFile, mstr);
 IdHTTP1->Disconnect();

まぁあとは、TMemoryStreamの部分をクラスのメンバ変数にしたり、
メモリから画像、メモリからファイル、メモリから文字列みたいな関数を作ります。

あとは、、getのほかにpostとfileのUPLOADくらいはつけますですね。