文字列をフォームファイル(DFM)形式に変換する[C++Builder]

「えーと●●●って選択肢のあるRadioGroupはどのフォームだったっけ」とか探したくなることがたまにある。
C++Builderでは(Delphiも)フォーム情報がテキスト形式(DFM)で保存されているので、ソースのあるフォルダでgrepかければ一発、のはずなのだが、英数・記号以外の文字は数値変換されているので、見つからないことがままある。

たとえば変な例だが「叱るButtonAtype」みたいなCaptionはdfmファイルに
Caption = #21489#12427'Button'#65313'type'
と保存される。(”A”も全角なので数値変換されている)

たぶん
・半角英数・記号はそのまま(シングルクオーテーション囲み)
・それ以外は文字コード番号の10進数表記(個々の先頭に”#”)
・さらにサロゲートペアは分割して変換(例の「叱(U+20B9F)」は「#21489#12427」)
というルールっぽい。

というわけで、変換関数を作った。
以下、コード。

[boost]サイト上のシリアライズデータをダウンロードせずにデシリアライズする[C++Builder]

boost::serializatioでシリアライズしたデータがweb上(例えば自分のサイト)にあり、PC上のプログラムから利用したい場合、一時的にダウンロードしてデシリアライズすれば簡単なのだが、後で一時ファイルを削除する手間が生じる。

以下、ダウンロードしないまま利用する方法をメモ。

手順は以下の3ステップ。
(1)UrlからデータをTStreamに読み込む
(2)TStreamからバイト列(char配列)に書き込む
(3)バイト列からデシリアライズ(boost::iostreamsを使う)

C++Builder10.2.3 (Boost1.55.0) / Windows10(22H2)で確認。

画像のフェードアウトいろいろ[VCL]

C++Builderで、表示した画像をじわじわ消していくのはFireMonkeyならほぼコードいらずで可能だが、VCLだと自前で処理する必要がある。いろんな方法を試してみたので暫定まとめ。使いどころはよく分からないが。

0.事前準備

TFormにTImageを置く。
ちらつき防止のためFormのDoubleBufferedプロパティをtrueにしておく。

1.ランダムにPixel操作(力業)

画像がぽつぽつと白くなっていく効果。
画像のPixel分の数値配列をシャッフルして、順番に白くしていくだけのシンプルな力業だが、実際にやってみると一手間かけないと自然な効果が出ない。

基本形

まず基本形。
手順は単純で
 ①画素数分の数値配列を作ってシャッフル
 ②シャッフルした数値のPixelをループで白くする
だけ。

多めに変化

上記のコードだと、PCの性能や画像サイズにもよるがかなり遅い。複数のPixelを白くするよう改良してみる。

実際にやると目の錯覚(たぶん)でだんだん遅くなっているように感じる。

変化量を増やしていく

白くするPixel数がどんどん増えるように改良。

だいぶ自然な感じになった。

Bitmap以外の場合

ここまで使ったImage->CanvasはBitmap以外はアクセスできない。
他の画像形式はBitmapに代入して行うのが安直だけど手軽。

まとめコード

以上のまとめコード。

2.色を薄くしていく

画像が段々薄くなるパターン。
RGBをいじるだけ。ScanLineを使えるので、ランダムなピクセル操作よりは高速。

かなりスムースになる。

3.アルファチャンネルを使う

TBitmapのアルファチャンネルを設定し、だんだん薄くしていく手もある。
(画像タイプみて変換する処理は省略。TPNGImageだと別の方法があるらしい)

実行結果。

4.Drawメソッドの第4引数を使う(お手軽)

Canvas->Drawメソッドで、第4引数の不透明度(Opacity)を指定する方法。
これはかなり手軽。TImageの画像がどんな形式でも、変換する必要はない。

C++Builder10.2.3 / Windows10(21H2)で確認。

TImageに表示されている画像の種類を知る[VCL]

C++Builder(Delphi)のTImageにはJpegやPng、ビットマップなど多様な画像が読み込めるが、あとから画像種類を知りたい場合
 Image->Picture->Graphic->ClassName()
で画像のVCLクラス名が取れる。今さら知ったのでメモ。

C++Builder10.2.3 / Windows10(21H2)で確認。

TStringListからvector / TStringDynArrayからvector[C++Builder]

C++Builder(およびDelphi)でよく使われるTStringListとかDynamicArrayを、std::vectorに変換したい場合がよくある。
この場合、いちいちループを回さなくても、STLライクなイテレータ操作が使える、ということを今さら公式ヘルプで知ったのでメモ。

TStringListからvector

TStringListからstd::vectorは以下のように一発で変換可能。

既存のvectorにはassignで代入。

スマートポインタでも問題ない。

文字列リストの基底クラスであるTStringsも同様。
おなじみのフォント一覧を取得するケース。

ソートされた結果を最初から得たい場合は、std::setをいきなり初期化してもいい。

 

DynamicArrayからvector

Delphi動的配列型のDynamicArrayも似たような代入が可能。

IOUtilsのTDirectory::GetFilesはディレクトリ内の全ファイルを一発で取れる便利な関数だが、この返値TStringDynArrayDynamicArray<String>のtypedefなので、同様にvector等に変換できる。

 

以上、C++Builder10.2.3 / Windows10(21H2)で確認。

[Vcl]AcrobatDCを利用したPDF印刷が失敗する対策

Adobe Acrobatはクラウド版(Acrobat DC)になってから使っていなかったのだが、アプリケーションの印刷ダイアログで選択して使用すると、PDF作成に失敗するようになっていた。

以下は、四角形を1つ描くだけの単純なテストプログラム。

PrintDialogでAdobe PDFを選択すると、下記のダイアログが出たまま止まる。

Microsoft Print to PDF および JUST PDF4 だと問題なくPDFが作成された。

Acrobat DCは最近のアップデートで、どうやら「文書名」が空のままだとダメになったらしい。
というわけで、以下のようにTitleを適当に付加すると正常にPDFが作成された。

C++Builder10.2.3/Windows10(64bit/バージョン21H2)で確認。

[C++Builder]画像ファイルをテキストで保存、読込

C++Builderのフォーム情報(*.dfm)ではアイコン等の画像を16進文字列で保存しているようで、似たようなことをやってみる。(使いどころはパッとは思い浮かばないが)。

とりあえずVclの例。

サンプルプログラム

TFormにTButtonとTImageを2つずつと、TOpenPictureDialogを設置。TImageは2つともProportionalプロパティをtrueにしておく。

画像を16進文字列で保存

手順は以下の通り
①画像ファイルをTMemoryStreamに読み込む
②サイズの同じunsigned char配列を用意し、MemoryStreamからデータを書き込む
③1バイトずつ16進文字列に変換
④文字列をファイル保存

なお、C++Builderは、デフォルトではcharがsignedなので、明示的にunsigned charを指定している。

C:\Windows\Web\Wallpaper\Theme2\img7.jpgを読み込んだ結果:

保存されたテキストデータをエディタで見てみる。

テキストファイルから画像を読込、表示

復元手順は以下の通り。
①文字列のテキストファイルを読み込む
②2文字ずつ数値変換しunsigned char配列に格納
③MemoryStreamにデータを書き込む
④TImageにMemoryStreamから読み込む

復元して右側のImageに表示した結果:

FireMonkeyの場合

FireMonkeyでも、ほぼ同様なコードで可能。
違いは
・OpenPictureDialogがないので代わりにOpenDialogを使う
・Image関連のコードの修正(Pictureの代わりにBitmapを使う)
・jpeg/png/gif用のインクルード不要
だけ。
Image関連の違いは以下の通り。

対象ファイル

TImageに読込・表示できる画像ならおおおむね可能な模様。JPEGのほかPNG/BMP/GIF/TIF、さらにアイコンファイル(ico)でOKだった。メタファイル(emf)はVclでないと読み込めない模様。

ただし、C:\Windows\Web\Wallpaper\Theme1配下にあるWindows10壁紙用のjpeg画像は、VCLの場合だけ失敗した。テキストとして保存はできても、復元でTImageに読み込む時点で「サポートされていないストリーム形式」のエラーが出て、復元不能だった。理由は謎。

C++Builder10.2.3/Windows10(64bit/バージョン20H2)で確認。

[FireMonkey] ImageとBitmapの大きさを揃える

簡単そうなことなのに、ちょっとハマったのでメモ。

FireMonkeyでTImageに文字や図形などを初めて描く場合は、先にImage->Bitmapのサイズを指定する必要がある。
ImageとBitmapのサイズが異なってもよいが(WrapModeで描画結果が変わる)、ここはシンプルに同じ大きさを指定したい場合の話。たとえば以下のように書けば何の問題もない。

だがちょっとまだるっこしいので1行で書きたい。こうも書ける。

Imageの名前(この例では”Image”だが)を3回も書くのも面白くない。TImageにもTBitmapにもSizeプロパティがある。これを使おうと、以下のように書いてみると、エラーになる。

TSizeとTSizeF

FireMonkeyの場合、TImageに限らず、TMemoやTButtonなどTControl派生のコンポーネントは、位置やサイズ情報を実数値(float)でも持つ。VclのTSizeでなくTSizeF、TPointでなくTPointF、そしてTRectの代わりにTRectF
ところがTControl派生でないTBitmapの場合、Sizeプロパティは整数型のTSize。このため単純な代入は出来ない。
たとえば、四角形の領域を、指定されたビットマップから現在のビットマップへコピーするCopyFromBitmapメソッドの宣言。RectもPoint(X,Y)も整数になっている。

で、こうした実数型のクラスには整数型に変換するメンバ関数Round/Trunk/Ceilingが用意されている。それぞれ四捨五入、切り捨て、切り上げ。
 ・
というわけで、以下のように書き換えてみる。

やはりエラー。

TSizeFとTControlSize

実はTControl派生クラスのSizeプロパティはTSizeFでなくTControlSizeだった。
このTControlSizeはPixel値(整数)のメンバHeight / Widthと、TSizeF型のメンバSizeを持つ。ややこしい。

というわけで、ImageのサイズにImage->Bitmapのサイズを合わせるシンプルなコードは以下の通り。

微妙にシンプルでもない気もする。

AndoroidのToastをC++Builderから呼び出す

ラムダ式を使う。

FireMonkeyでPCのフォント一覧を取得する(手抜き版)

Vclでは、システムのフォント一覧は以下のように簡単に取れた。

Firemonkeyでは、同様の手段は提供されてない模様。
が、PCに限れば、プリンタから取得するという手段があるようだ。

MacOSでも同様に可能という情報もあるが(海外の掲示板)私は未確認。

なお、FireMonkeyでもシステムのデフォルトフォントを取得する手段は提供されている。

Windowsの場合、Vclと同様、フォント一覧に縦書き用(先頭に@)も取得してしまう。
縦書き用を排除したフォント一覧をFormのComboBoxに設定し、さらにデフォルトフォントを選択した状態にするのは以下のような感じだろうか。

C++Builder10.2.3 / Windows10(64bit)で確認。