[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)で確認。

[c++]vectorを配列に/配列をvectorに

どっちからどっちに変換するかで方法が違うため、すぐ忘れるのでメモ。

vectorから配列

std::copyを使う方法。

配列からvector

std::copyやvectorのメンバ関数assignを使う方法もあるが、c++11以降なら、以下がたぶん一番シンプル。

配列の大きさとか

以下、おまけ。
既存のvectorに配列を代入するなど、配列のサイズ(要素数)を知りたい場合、c++11以降だと、sizeofを利用する以外の方法も用意されている。

ただし、既存のvectorに、異なるサイズの配列を代入するだけなら、メモリや実行速度にシビアでなければ、いちいちサイズを求めなくとも以下で済むといえば済む。

[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のサイズを合わせるシンプルなコードは以下の通り。

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

[c++11]本日の経過ミリ秒を取得する

現在、本日が始まってから何ミリ秒過ぎたかを求めるため、c++11で追加されたstd::chronoライブラリを勉強がてら使ってみた。もっとスマートな方法もあるかもしれないが。以下、c++11/Windows環境の場合。c++17やLinux環境だと別の方法もあるらしい。

現在時刻に関する情報はnow()関数で取れる。なおこの時刻はUTC(協定世界時)であり日本時間とは異なる。

time_since_epoc関数で「エポックからの経過時間」を求めて、メンバ関数countでミリ秒を取得。そこから現在時刻のミリ秒部分を得る。この「エポック」が何時なのかは未規定だが概ね1970/1/1で、経過ミリ秒は軽く1兆を超える値になり、32bit intでは正常に収容できないので注意が必要。(もちろんautoを使えば問題ない)。

時間・分・秒を取得するため、time_tに変換。さらに現地時間(日本時間)に変換する。

この時点で本日の経過ミリ秒は自前で計算可能だが、せっかくtime_pointは減算が効くので、本日0時0分0秒000のtime_stampを求めてみる。

time_point同士の減算から本日の経過ミリ秒を求める。

以下、まとめてシンプルに。

月初めからや年頭からの経過ミリ秒も求めたいが、std::chrono::monthやstd::chrono::yearはc++20で追加予定らしいので、c++11だとすんなりとはいかないようだ。
c++20では、ローカル時間用のtime_pointとか、 1日内の時間情報を取得するクラスtime_of_dayなども追加されるらしく、time_t変換などせずに、もっと楽に取得できるようになるかも。

c++builder10.2.3/Windows10(64bit/1909)で確認。

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

ラムダ式を使う。

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

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

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

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

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

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

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

続・C++Builderで異体字セレクタを使う サロゲートペアの場合

サロゲートペア文字にも異体字セレクタ対象の文字があることに、「C++Builderで異体字セレクタを使う」の投稿後に気付いた。

1.表示は簡単

基本は変わらない。対象文字のコードポイントの上位・下位バイトが分かっていれば、以下の数行のコードで表示できる。
なおTEditのフォントは、Windows8.1以降に標準バンドルされている游明朝を使っている。(オープンソースの花園明朝Aを使えば、より多くの異体字が表示できる)

表示結果

標準字形と異体字を並べてみる。

表示結果

2.両対応の汎用メソッド

実際にはコードポイントをいちいち探したり求めるのは面倒くさいので、実際の文字と異体字番号(ゼロから指定)から異体字を求める汎用メソッドを作ってみる。サロゲートかどうかも気にしなくてよい。
(以下のサンプルは、Stringが複数の文字からなる場合は、先頭文字の異体字を返す)

テストコード。
LabeledEditからIVS番号を取得して、文字を変換する。
2つのTEditのフォントは游明朝に設定している。

実行結果。

3.TRichEditの挙動が非サロゲートと異なる(?)

サロゲートペアの異体字をTRichEditに表示した場合、非サロゲートペアの場合と異なり、余分な空白が表示されないようだ。
異体字と標準字形を並べて表示してみると…。(左がTMemo、右がTRichEdit)

この挙動は謎。
なお、異体字の後ろにカーソルを置いた状態で、BackSpaceキーを押下すると〝先祖返り〟する挙動は、非サロゲート文字と変わらない。

C++Builder10.2.3とWindows10で確認。

C++Builderで異体字セレクタを使う

0.異体字セレクタとは

非常に大雑把かつ乱暴に言うと、山ほどある異体字を表示させるための仕組みのひとつ。
文字にいちいち固有の番号を割り当てるのではなく、番号に枝番号(符号)を付ける仕掛けである。
対応するフォントが必要だが、Windowsに標準添付の一部フォントでもそれなりに対応している。
詳しくはWikipediaなど参照してもらった方がいい。

Windows7からメモ帳でも利用可能になったので試してみた人も多いかもしれない。

1.表示は簡単

とりあえずよくある「1点しんにょうの辻」をTEditに表示する。
フォームにTEditとTButtonを一つずつ貼り付け、TEditのフォントはMS 明朝、フォントサイズは判別しやすいように30にしておく。
以下の数行のコードで、1点しんにょうの辻が表示される。

このように表示される。

2.仕組みとか

異体字セレクタは、通常の文字番号に、0xE0100から始まる枝番号を付けることで作動する。(最大で240までの枝番号が付けられるが、実際にそんなにたくさんの枝番号が定義された文字は現在ない。)
0xE0100は2byteで収まらないので、当然、wchar_tに格納できない。(C++Builderはwchar_tが2byte)
が、上位2byteは0xDB40で固定。下位2byteに0xDD00~0xDDEFまでのいずれかを格納してやればよい。

なお存在しない枝番を指定した場合、単に標準の文字が表示される。

3.フォントによって異なる枝番号

どの枝番号にどの異体字形が割り振られているかは、実はフォントによって違う。(正確に言うとフォントによって採用している字形コレクションが違う)。
異なるフォントを設定した3つのMemoに、「辻」の異体字の枝番を順に表示してみる。

実行結果

1点しんにょうの辻は、IPAmj明朝の場合は3番目、游明朝およびMS明朝なら1番目に割り振られているのが分かる。

いったん表示した後、フォントを変えると、文字が変化してしまう危険がある。

4.1文字で3文字分

文字数を数えるプログラムなどでは注意が必要。
SystemのLength()でもstd::wstringのlength()でも、3文字になる。
サロゲートペアの判定はIsSurrogateで簡単だが、異体字セレクタの場合はもう一工夫必要になるはず。

5.TMemoとTRichEditで挙動が異なる

異体字セレクタ文字は、TMemoとTRichEditで挙動・表示が違う。
1点しんにょうの辻と通常の辻の2文字をそれぞれ表示してみる。

実行結果。左がTMemo, 右がTRichEdit。

このようにRichEditは枝番分の文字の空きが取られてしまう。

ちなみに、この空きをキーボードで削除すると

枝番情報が消えるので最初の文字が通常の「辻」に戻ってしまう。

また、Memoの方でも、1点しんにょうの辻の後ろにカーソルを置きBackSpaceキーを押すと、文字は消えないがやはり先祖返りする。



以上、基本的にVclとFireMonkey共通。(ただしFireMonkeyにはRichEditはない。)
C++Builder10.2.3 / Windows10 で確認。

vectorの要素をloopしながら削除(添え字版)

久しぶりにやるといつも忘れて、Web検索するハメになるけれど、イテレータの使用例ばかり出てくる(気がする)ので、添え字版をメモ。

std::dequeでも同様。

std::accumulateとtemplate

一度どハマりしたことがあるのでメモ。
std::accumulateは、vectorなどコンテナの要素を加算してくれる便利な関数だが、第三引数にちょっと罠がある。
まず特に問題ないサンプル。

出力結果 : sum : 6

ところが以下の例だと値が丸められてしまう。

出力結果 : sum : 6

この場合は、第三引数がdoubleでなくてはならない。

出力結果 : sum : 6.6

テンプレートクラス内などで使う場合はstatic_castを使う。
例えばメンバをvectorで保持するクラスに合計値を出す関数を実装する例。

出力してみる。

出力結果:
sum : 6
sum : 6.6