Bitmapでピクセルに対して操作しようとしたら。。。

FMX,Programing,TBitmap,TBitmapData

FireMonkeyのTBitmapクラスには、VCLで存在していたScanlineがない。
これだと1ピクセルずつ描画していくような処理をする場合に結構困る。

で、どうするかというと

TBitmapDataレコードを使う

worktoolsmith.com

こちらの記事で紹介されているTBitmapDataレコードを使う方法でいけることはわかった。
TBitmapData.DataフィールドにはBitmapのピクセルデータ配列のポインタが格納されている。
これをTAlphaColorArrayとして考え、各ピクセルに対して色情報を与える。
というもの。

TBitmapDataにはScanlineメソッドなども用意されているが、直接アドレス指定した方が処理は早いようだ。

問題点

上記記事のソースでは

YAddr := Y * Bmp.Height;

としているが、実際には

YAddr := Y * Bmp.Width;

が正しいと思われる。
TAlphaColorArrayにはビットマップの左上から1行ずつ並べたデータが配置されている。
つまり2行目の一番左のピクセルのインデックスは、1行分のピクセルということになる。
ダウンロードしたテストソースをいじって確認したけど、やはりWidthで間違いないと思う。

そして更に問題。
ダウンロードしたソースではWidthであっていたのだが、自作クラスの中にこれを組み込もうとしたときに、イメージと実際の描画がずれていた。

原因を探ってみると、どうやらTBitmapDataレコードのPitchフィールドに代入されている値が違うことがわかった。
TBitmapDataレコードの詳細な解説があまり見つからなかったが、これはどうやら1行あたりのバイト数を表しているようだ。
更に、BytesPerPixelプロパティを参照すれば1ピクセルあたりのバイト数がわかる。
つまり、Pitch = Width ✕ BytesPerPixelとなる。。。

と思っていた。でも違った。。。
ダウンロードしたプロジェクトではそうなっていたのだが、自分ので実行してみるとPitchが想定より十数Byteほど多くなっていた。
これのせいでズレが生じていたわけだが、なぜそうなるのかがさっぱりわからない。

ひとまず上記のソースは

YAddr := Y * (BmpData.Pitch div BmpData.BytesPerPixel);

としておくほうが良さそう。
この修正を入れることでイメージ通りの描画はしてくれるようになった。

それにしてもなぜズレるのか。。。時間がないので深入りして調べることはできなかったので、いずれちゃんと調べたいとは思う。