見出し画像

ツバメビューア v1.4~v1.5 制作メモ(UWPの画像表示の高速化ほか)

ツバメビューア v1.5 をリリースしました! 画像ビューアを改修したことで

・これまで中途半端だった先行読み込みを改善して真の先行読み込みを実現
・大きな画像をウィンドウサイズまでダウンスケールして省メモリ、表示速度改善
・見開き表示時に画像が一部欠けてしまっていた問題を解消
・画像ビューア上での拡大縮小に対応

といったことをやりました。記事本文ではそれぞれざっくりと解説していきます。

(当初v1.4リリースとして記事制作していましたが、ストアの審査時間が長引いたその間にv1.5が出来てしまったので2つのバージョン分をまとめてお届けしています。)


UWPにおけるImageの先行読み込み

画像表示自体はとても楽ちんです。

・StorageFileやネットワークからByteStreamを取得して
・BitmapImage.SetSourceでストリームをセットし
・BitmapImageをImage.Sourceに渡す

これだけです。UWPであればWIC(Windows Imaging Component)が裏側でよしなに画像を処理してくれるため、アプリコードとして画像のデコード処理を書く必要はありません。超助かる…。

ただ、ここで表示を高速化したいと思った時に、前提として勘違いしていた点がありました。それはImage.SourceにBitmapImage等を設定するだけでは実際にGPUに画像データが乗り、ディスプレイに表示する準備が完了するわけではないことです。

Imageの画像データをGPUに転送+デコードを実行させるには、XamlのレイアウトシステムにとってImageが可視状態である必要があります。

つまり
「Image.Source に有効なデータがセットされている」&&「Opacity > 0.0」&&「Visibility == Visible」
といった条件を満たさないと表示準備が完了しない、ということでした。表示に必要となるまで描画処理がスキップされていた、とも言えます。
(クリッピング、表示範囲の限定による影響については未調査)

そこで ツバメビューア v1.4 からは表示予定の画像を可視状態のImageに読み込ませてGPU転送+デコードを済ませた状態で表示を待機させるように変更しました。表示予定の画像は10000px彼方で配置され、表示されるその時を待っています。

この先行読み込みのためにCPUメモリ、GPUメモリともに6枚分を画像ビューアページで保持するようにしています。6枚なのは 見開き表示機能 のために左右の2枚、それが前・現在・後の3つで合計6枚必要となってます。

ツバメビューアをご利用の場合、先行読み込みはOFFにすることも出来ます。画像ビューアの設定ボタン(歯車アイコン)をご確認ください。

大きい画像の扱いを改善

v1.3 までは大きいサイズの画像を原寸そのままで表示していました。7000px*4800px でもそのまま表示。ザ・男気実装。

当然そんな画像を表示しようとするともちろん重いし、メモリの使用量もすごいことになります。数枚表示切り替えてるだけで500MBや1GBとか使ってました。ローエンドのノートPCやタブレッドだとかなり厳しい。

そこでv1.4 からはBitmapImage.DecodePixelWidthとBitmapImage.DecodePixelHeightを適宜設定することで、ダウンサイズしながら画像をデコードしてもらうように変更しました。それでも大きい画像だと一枚で50MBとか使いますが仕方ない。必要経費ということで。

前述の先行読み込みも含めて、画像表示と画像の切り替えの快適性はv1.4 で大幅に改善出来たかなと思います。これでやっと画像ビューアとして最低限レベルのスピード感が出せるようになってきました。

見開きの2枚画像を中央詰め+左右の余白が均等になるよう表示

地味なバグですが、見開き表示した際の画像が特定のウィンドウサイズと画像サイズの組み合わせにおいてうまく表示出来ない状態になっていました(色々と調整しても画面の左右に表示欠損が出ていた)(ItemsControl + StackPanelによる実装)

そこで v1.4 からは ItemsRepeater + FlowLayout を利用して表示してます。異なるサイズの画像2枚を見開きで表示した場合でもウィンドウサイズの横幅目一杯に並べて表示できるようになりました。

ItemsRepeater も FlowLayout も どちらも WinUI 2.x(3.x)から利用できるようになった(2021年現在において)新しいUI要素です。ItemsRepeater.LayoutにFlowLayoutを配置して使用します。

ItemsRepeaterは大量の表示項目を仮想化して表示するためのコントロールです。(UWPにおいて仮想化とは、表示項目のコンテナUIをリサイクルする仕組みを言います)

従来のListViewやGridViewでは項目表示の仮想化に加えて項目の選択操作やヘッダー・フッターの表示も同時に提供していましたが、ItemsRepeaterはよりシンプルに大量の表示項目を仮想化することにのみ特化しています。ですので選択機能をつけたかったら別途カスタマイズが必要になります。

そして、FlowLayoutはアイテムを一列に並べて表示し、余分がなくなったら次の行(列)にまた一列を表示し…、と表示していきます。WindowsCommunityToolkitやWinRTToolkitにある WrapPanel が近いでしょうか。

今回、見開き表示をしたいツバメビューアにとって要なのはFlowLayoutのLineAlignmentプロパティです。

LineAlignmentプロパティは一列にアイテム配置する時の寄せを指定できるプロパティです。FlowLayoutLineAlignment.Startがデフォルトで、基本的に左寄せで表示されるはずです。(WrapPanelのデフォルトと同じ挙動だと思います。)(デフォルトで左寄せかどうかはシステムの言語指定?によって変わる? ため Startと表現されているようです)

ツバメビューアの見開き表示機能は画像2枚を中央詰め+中央寄せで表示するためにFlowLayoutLineAlignment.Centerを指定しています。(左右中央の3種のほかに均等間隔)

拡大縮小に対応

v1.5から、画像ビューアとして必須感があるのに今まで対応できていなかった拡大縮小機能に対応しました。

最近のUWPは アニメーションがサッと書けるMicrosoft.Toolkit.Uwp.UI.Animationsが整備されているので、ツバメビューアにおける拡大縮小の処理もこの機能に乗っかっています。

コードとしては

AnimationBuilder.Create().Scale(zoom, duration: ZoomDuration).Start(ImagesContainer);

これだけで拡大縮小ができます。正直めっちゃ簡単でした。非常にありがたい……。(実際には拡縮の中心を決めるCenterPointも指定します)

一方で、ツバメビューアはマウス・タッチ・コントローラーの3種類の操作方法に対応しているため、それらの異なる操作全てをサポートしながら「ユーザーに拡縮をどう操作してもらうのか」といった手触り部分に注力したかったところで、拡縮の内部処理をほぼAnimationBuilder(あるいはその土台にあるComposition.UIないしWin2D)に任せられるのは非常に助かりました。

操作方法は素直に組んでいます。マウスはCtrl+スクロールで拡大縮小、マウスポインターの位置を拡縮のセンターとして指定してます。タッチでは指でつまむ操作(ピンチイン・アウト)そのままです。コントローラーだけはちょっと勝手がわからなかったので、LRトリガーで拡縮、拡縮中のみ右スティックで移動という操作にしてみました。

ただ理想としては、そうしたデバイスごとの専用操作を覚えずとも、拡大ボタンや拡大リセットボタンを用意しておいてそれをポチポチするだけもOK、という入力補助があるべきとも思っています。指とか上手く動かせないって人でも簡易に操作できるようになってるといいよねって思います。

v1.5 現在においては拡大縮小に限らずそもそも画像ビューア自体含めての使い方を案内する表示ができていないので、v1.6で改善していきます。

おわり


ツバメビューアのダウンロードは以下のリンクをどうぞ(Windows10 1809以降の端末で動作します。タブレットやXboxも対応してます)


アプリに関する不具合報告、提案などはマシュマロで受け付けています
とらきちにマシュマロを投げる | マシュマロ (marshmallow-qa.com)


この記事が気に入ったらサポートをしてみませんか?