RenderTargetBitmap.RenderAsyncとBitmapEncoder.SetPixelDataの連携2015年12月03日 10:41

Gridに表示した漢字をRenderTargetBitmapのRenderAsyncでイメージデータとして取り込み、これをバイト列に変換した上、BitmapEncoderのSetPixelDataでBMPやPNGファイルとして出力する処理を書いていた。

崩れた漢字イメージ

PCでは問題なかったが(左)、mobileのエミュレータだとイメージが崩れる。mobileの実機を入手したので確認するとやはり崩れる(右)。

Endianの違いかと確認すると、内部ではBigEndianに揃えて処理している様子。

悪戦苦闘の跡

調べてみると、RenderTargetBitmapのRenderAsyncの引数でイメージサイズをscaledWidth, scaledHeightで指定しているが、生成されたイメージのPixelWidthとPixelHeightは指定した値とは異なるのが原因。BitmapEncoderのSetPixelDataでもイメージサイズを指定するが、scaledWidthとscaledHeightで設定したのと同じ値では、イメージが崩れる。

値が変わる理由は、dpiの相違。PCでは、96dpi。エミュレータや手元のmobile端末では、144dpi。RenderAsyncは、96dpiの場合を1として、機種のdpiとの比に応じたサイズでイメージを生成する。144dpiでは、縦横1.5倍のサイズのイメージになる。これを理解して、生成されたイメージのサイズとdpiを用いて、SetPixelDataを呼び出せば、正しいイメージのファイルが生成される。

プログラム中で使わないまでも、イメージのサイズや、バイト配列のサイズを、確認しながら、進めるべきだったか。mobileでのバイト配列のサイズが異様に大きいことを見過ごしていた。

それにしても、MSのリファレンスでは、ここまでは読み取れない。

※RenderTargetBitmapの説明

結局、ファイルを比較するために、PNGをBMPに変え、PictureLibraryに出力する処理を用意し、CygwinでBMPを"od -x"でテキストに落としてdiffを掛ける羽目に。デバッグ中は圧縮フォーマットは、色々気づきにくい。UWPとはいえ、PCとmobileの隠れた相違には、これからも悩まされそう。