CloverPaint雑記

CloverPaintの解説・開発状況・内部仕様・利用法などについてつらつらと

Clover SCと描画遅延に関する考察

 この一ヶ月ほど開発リソースのすべてを傾けていたClover SC (スタイラス・チェッカー) がようやく完成に近づきました。

 

Clover SCは端末の、特にペイントアプリ利用シーンでの描き心地に影響する性能について分析し、客観的なデータを出力するためのツールです。

これまではぼんやりと、なんとなくこの端末は描き心地がいい/悪い、という感覚でしか語れなかった部分を、Clover SCは客観的な数値として表示できるようにすることで、簡単にペイントアプリで利用した時の端末の優劣を評価できるようになります。

 

具体的には以下のような機能を備えています

 

・描画遅延の測定用ガイドアニメーション

・タッチパネル/スタイラス座標イベント時間分解能(pps/スキャンレート)の実測値の表示

・タッチパネル/スタイラス座標イベント時間分解能の安定度(標準偏差)の実測値の表示

・タッチパネル/スタイラスが対応している機能をタッチイベントから解析

(同時押し点数・ボタン・ホバー・筆圧段階・サイズ・傾き・方向・距離等)

・Clover Paintのストローク描画を使った、ペイントアプリ利用シーンに特化したベンチマーク

・タッチパネル/スタイラスから送られてくる座標イベントのリアルタイム表示

・遅延の少ない(Clover Paint比)ペンストローク描画のサンプル

・ポイント予測付きストローク描画(超低遅延ストロークのサンプル)

 

 ベンチマーク等は他に類似ソフトが多くあるので意味がわかりやすいのですが、特にClover SCが問題にしている「遅延」に関してはあまり馴染みのない人が多いと思われます。

ネットで「遅延」を話題にしている人でも、CPUやアプリを単純に高速化すれば遅延が少なくなると思っている事がかなり多いようです。しかし、それは誤解です。遅延には様々な要因があり、CPUの速度やアプリはその要因のごく一部に過ぎません。

 

そのような誤解を解き、どうして遅延を測定するこんなアプリが必要なのか、遅延の少ない端末が登場すると、なぜ描き味が向上するのかを理解してもらうために、いまこの記事を書いています。

多くの人が問題意識を共有し、Clover SCを利用して遅延が大きく、不安定な端末はダメ端末遅延が少なく安定した端末は素晴らしい端末だということをネットで広めれば、必ずメーカーに対するプレッシャーになり、Androidタブレットでも低遅延・安定性を重視した端末が多く発売されるようになると信じています。

 

Clover SC開発の経緯

ツイッターのTLを調べてみると、私がClover SCを最初に作りたいと思ったのは2月上旬のようです。2月10日には時間分解能という言葉を使っていて、いまのClover SCの開発意図を完全にイメージしていた事がわかります。実際に作り始めたのは4月末からですが。

ただ、2月上旬というと、Clover Paintの「ナビゲーション・ビュー」作成の真っ最中、その後も「PSD入出力」や「変形ツール」など、要望の多い機能の実装が控えていて、作る暇がなかったようです。

その後、スタイラスベンチマークの事を忘れかけ、キー設定が自分の中で盛り上がって作っている最中に、突如あらわれたのがenchantMOONという国産のデジタイザ付き端末でした。詳しくはネットで調べてもらえばわかりますが、私のツイッターのTLはデジタル絵描きの人が多いため、必然的にデジタイザーを搭載した端末の発表に関しては盛り上がる傾向があるのですが、この端末は紙のような描き心地を目指した、低遅延を実現するためにはAndroid OSが邪魔だったので独自にカスタマイズしたOSを作ったなどと、ペンで直接画面に描き込む液晶タブレットでは非常に気にする人の多い部分について、革命を起こすような端末である・・・と感じた人が多かったようです。私も前述のように時間分解能に関するベンチマークを作りたいなどと考えていたので、同じようなことを考えている人がいる!と、俄然盛り上がりました。 

 

enchantMOONタッチ&トライイベント当時のTLです。

http://twilog.org/Clover_Paint/date-130423

 

見ての通り、私が触った感触では、期待していたようなものには程遠い内容だったわけですが、それでも、そのコンセプトに共感した人がネット上で非常に多く、予想以上の反響があったということは事実です。それを目の当たりにして、「やはり2月上旬頃に考えていたようなベンチマークを作ることには意味がある、ついでにClover Paintでは無視していたペンストロークの遅延について、出来る限り小さくする実験アプリにしてみよう!」となったわけです。

http://twilog.org/Clover_Paint/date-130424

この日はTwitter上でペイントアプリの描画遅延に関する考察を行なっています。このブログ記事では、ここで話した内容をさらに詳しく突っ込んで説明していますが、Twitter上でしか話していない部分もあるので、ツイッターにも目を通しておいても無駄ということはないと思われます。

 

ところでClover SCを作るにあたり、遅延を極限まで短縮したペンストロークを作成することを一応思いついたものの(時間分解能を高めることで、より正確な筆致を再現できるようになるという部分には確信がありましたが)本当に遅延をなくす事にそれほどの意味があるのか、この時点では私自身確信が持てませんでした。自分自身が欲しいと思わないものは、モチベーションが保てないため、作らないというのが、私の開発スタンスです。

 

遅延のない描画を試したいだけなら紙にペンで描けばいいのですが、いまいちタブレット上でどういう感覚になるかピンと来なかったので買ってみたのがこれです。

 

 

これはブギーボード4という、一種のホワイトボードのようなものです。

これは普通の液晶等とは違い、バックライトや自己発光をしません。初期状態では真っ黒ですが、ペン等で触れると圧力により表面の液晶が化学変化して反射色が変わり、文字等が描けるというしくみになっています。ボタンを押すと電気が流れ、画面全体が初期の真っ黒状態に戻ります。

まあ昔あったレバーを引くと書いたものが消えるおもちゃのようなものですが、これが実に描き味がいい。これを使うことで、タブレット上で遅延の少ないペンストロークの実現には価値があると、自分自身納得することができました。

 

しかしBB-4には色々と欠点があります。

・黒背景に薄緑色の文字しか描けない

・部分的な消去ができない

・Undoができない

・反転もできない

・セーブができない

 

一方で長所は以下のとおりです

・軽い(100g)

・バッテリーが長持ち(電池一個で5万回消去可能)

・描画遅延が皆無

・書き味のいいペン特性

 

Clover SCはブギーボードの欠点を無くし、それでいて出来る限り遅延を無くし、ブギーボードのような書き味のいいペン特性を目指すことに決めました。(軽さやバッテリーに関する問題はハードウェアの進歩を待つしかありませんが・・・)

 

Clover SCのストローク描画サンプルの主な仕様は以下のとおりです

・基本は白背景に黒文字(白黒反転可能)

・左右反転

・全体消去

・Undo/Redo

・消しゴム

・遅延を極小にする補間前描画(後に詳述します)

・ハード特性による遅延を擬似的に無くすストローク予測(後に詳述します)

・ブギーボードに比べ少し硬めのペンストローク

(筆圧と速度により主に太さが変化するタイプのブラシ)

 

Clover Paintと比べると主に以下の機能を省いています

・ブラシの太さやその他パラメータの変更

・レイヤー機能

・キャンバスの移動・スケール・回転

 

ブラシの変更やレイヤーがないのは、 インターフェイスを単純化し、アイコン等を排除するためです。(むろん処理落ちが起きるような、重く複雑なブラシ演算処理になるのを未然に防ぐ効果もあります)

また、スケール・回転等がないのは、Clover Paintのようにリアルタイムで高速なスケール・回転を実現するためにはOpenGLを利用した描画処理が必要なのですが、機種依存の激しいAndroidの場合、どうしてもダブルバッファにしなければOpenGLを利用することができないと判断しました。しかし、ダブルバッファを使うとどうしてもその分の遅延が発生します(後に詳述します)。ダブルバッファによる遅延が他の方法で回避できるという保証もないのですが、遅延を極小にするということが至上命題である以上、この時点で諦めるという選択肢はありえないため、この機能も排除しました。

要するにClover SCのペンストローク描画は、やり直しと反転ができ、スクリーンショットの撮れる(セーブは未実装)ブギーボードを目指す、という今の形に落ち着いたわけです。

Clover SCの仕様策定に関しては、安倍吉俊様のこちらのツイートも示唆に富んでいて一部参考にしています。エゴサーチする人と知らなかったとはいえ、少々失礼な物言いをした私の質問に丁寧に答えて下さったことに感謝致します。

 

Androidタブレットの遅延について

描画遅延には様々な要因が考えられますが、大まかに分けると以下の4つに分類できます。

  1. ペン入力から、アプリにそのイベントが到達するまでの遅延
  2. ストローク演算処理による遅延
  3. 画面演算による遅延
  4. 画面(モニター)表示の遅延

このうち、2だけがアプリケーションに起因する遅延となります。

結論から言うと、私の試したAndroid端末(GALAXY Note 10.1等)では2の処理は(処理落ちが起きることがないような細いブラシの場合)Clover Paintで50ms~70ms程度ですが、これがClover SCでは10ms前後程度になりました。ただし、依然として見た目の遅延はClover Paintで140ms前後、Clover SCで90ms前後はあります。そもそもシステムが内部処理で表示しているホバーカーソル(GALAXY Note 10.1ではそういう機能があります)でさえ、70ms以上遅延しているのです。アプリ側がどう頑張ってもそれ以下の遅延になるはずがありません。70msの詳細な内訳は測定のしようがないため推測でしか語れませんが、1、3、4の合計が70ms程度の遅延の原因になっているのは間違いありません。

 

  • ペン入力の遅延

それではまず 1のペン入力の遅延から考えていきます。ペンの座標が送られて来る頻度(時間分解能・スキャンレート)は現状ほとんどのハードで最高7ms~17ms間隔になっています。画面リフレッシュ間隔で言い換えると、これは130fps~60fps程度の頻度速度ということになります。スタイラスから取得したデータが一切遅延なくプログラムに伝えられたとしても、最高で座標が送られてくる間隔分、つまり7ms~17msの遅延が発生するわけです。

 f:id:CloverPaint:20130605012532p:plain

私は特にこの辺りに詳しいわけではないので正確なことはわかりませんが、いったん取得された座標データそのものは特に大きな遅延はなく(USBの速度は十分、遅延も大きくはないし、バッファリングする理由も特にない)プログラムに伝えられるのではないかと考えています。ただし、最低でも平均で時間分解能(スキャンレート)の半分の遅延が発生するわけで、時間分解能が高ければ高いほど、遅延が少なくなる第一の理由がこれになります。もし時間分解能が2msになれば、この段階で発生する最大遅延も単純に2ms、平均遅延は1msになるわけです。

 

  • 画面演算による遅延

2のストローク演算処理による遅延は、アプリケーション処理内容に起因するもので端末やOSの特性による回避不能な遅延とは別なので後回しにして、次に3の画面演算による遅延を考えます。

昨今のハードウェアと、ファミコン時代のハードウェア、どちらが遅延が少ないと思われますか?と聞かれれば大体想像がつくでしょうが、実はファミコン時代の方が圧倒的に遅延が少ないのです。いまのCPUやGPUの演算速度はその頃に比べ数百万倍にもなろうというのに、どうしてそんなことになるのでしょうか。

それは画面表示のためのデータの扱いの違いに原因があります。

 ファミコンスーパーファミコン世代のコンピューターは扱えるメモリが少なく、低解像度でも1画面のデータをそのままメモリに入れることはできませんでした。そのためどうしたかというと、8x8等の小さなパターンをいくつか作っておき、8x8パターンのどれを表示するかという番号を並べて背景画像を作っていたのです。(今で言うアスキーアートのようなものと考えれば大体合っています。AAに加え、個々の文字にある程度自由に色・形状を設定できるようにしただけです)ただし、それだけだと8ドット単位でしか移動できないし、背景とキャラクターを重ねることもできません。そこでスプライトという特別な表示機能を用意し、それを使うことでシューティングの自機等、1ドット単位で自由に動くキャラクターを表示していたわけです。ただし、この頃のGPUは貧弱なためスプライトを幾つでも処理できたわけではありません。走査線1ラインごとに表示できる数等が制限されていました。というのも、この頃のGPUはラインバッファといって、1ライン分だけのバッファをその場で構成し、その都度ブラウン管TVに送っていました。この頃のアナログTVは約60fpsで200本の走査線を送り出していましたから、単純に考えて1200fps以上でVRAMバッファ書き換えを行なっていたわけです。すごいもんです。60fps間隔で上から下へ200本の走査線を表示しますが、下から上へ走査線が移動するときに、垂直ブランク期間と言って少しだけ走査線表示が途切れる期間があります。この間に、次の画面用に背景パターンの数値を入れ替えたり、背景のドット単位のズラしレジスタを調整したりすることで、スーパーマリオのようなスクロールをさせたり、キャラクターを移動させたりしたわけです。さらには200本の走査線毎に、右の末端から左へ戻る、ほんの一瞬の表示の間隙(これを水平ブランク期間と言います)をぬってそのタイミングで背景をズラしたりレジスタをいじったりすることで、ラスタースクロールなどという特殊なエフェクトを行うソフトもありました。これには有名なところではドラゴンクエストの「たびのとびら」エフェクトがあります。スケール等も同時にいじって擬似的に奥行きがあるような背景効果を作っていたアーケードゲームも多々有りました。今では完全に忘れ去られてしまった技術ですが、なかなか面白いもんでした。

なんだか懐古趣味に走って話が少々横にそれてしまいましたが、この時代のハードウェアはラインバッファであるため、さっき言った水平ブランク期間等で書き換えを行った場合、ほぼ遅延ゼロでプログラムが書き換えた情報が画面に反映されていました。・・・まあこれを何も考えずに行うと今で言うティアリングが発生するため、大抵の場合は垂直ブランクまで情報をためて、そこで一気に切り替えという方法を採っていたはずですが、それでもほぼ1フレーム程度の遅延で入力された情報が画面に反映されていたわけです。この時代のハードウェアは。

一方で、最近のハードウェアはどうでしょうか。最近のハードウェアはすべてフレームバッファタイプのものになっています。フレームバッファとは、1画面分のバッファをまるまるメモリに貯めこんでおくタイプのハードウェアを指します。ちなみにラインバッファがフレームバッファに切り替わったのは大体、初代プレイステーションの頃です。3DS/PSはフレームバッファ、SSはフレームバッファとラインバッファの間の子みたいなヘンテコなハード構成になっていました。

フレームバッファの利点は、ラインバッファと違って1200fpsで書き換えないと処理が破綻する!などという事がない、ということです。すべてのデータがメモリ(VRAM)内にあるわけですから、たとえ処理が間に合わなくても最低限、前のフレームのデータはTVに表示されるわけです。

ところで、この頃のハードウェアに垂直ブランク期間に全画面分のメモリ内容を書きかえる程のパワーはありません。そうするとTVがVRAMのデータを読み込んでいる最中にVRAMデータを書きかえる事になるわけですが、人間の性というか、リッチな絵を描けるとなると描きたくなるわけで、そうすると60fps(16.7ms)でも全然間に合いません。そもそも必ず間に合うならラインバッファでもいいわけです。それにこの頃にはすでに3Dポリゴンを表示したいという要求があり、一度画面全体を消して、奥から三角形のポリゴンを表示していくことで3Dのシーンを構成していくという方法が採られていました。そうなるとタイミングによっては(どんなに高速に描いても画面の上の方では)描きかけのシーンが表示されてしまうわけです。それはまずいということで考えだされたのがダブルバッファです。

ダブルバッファとは、画面サイズと同じフレームバッファを2つ用意しておき、一方をバックバッファ・一方をフロントバッファとして利用する方法です。バックバッファはプログラムがいま3Dのシーンを構成中のもので、画面には表示されません。フロントバッファには前回構築された3Dシーンのデータが入っていて、TVはこれを表示します。バックバッファの3Dシーンが構築し終わったら、垂直ブランク期間に合わせてバックバッファとフロントバッファを入れ替えます。こうすることで、書き換え最中のデータが見えないようになります。バックバッファはこのように便利なものですが、書き換えに時間がかかってもフロントバッファは一応そのまま表示されていて画面に破綻が起きないため、これまでほぼ絶対遵守されていた60fpsの書き換えを守るというタガが外れてしまい、低フレームレートのゲームが溢れかえりました。人間の目はだいたい50fps前後の時間分解能を持っていて、それ以上の頻度で書き換わっているものは連続してアニメーションしているように見えますが、それ以下になると動いたり止まったりしながらアニメーションしているように感じられます。これが60fpsのゲームと、それ以下のゲームが全く違うように感じる原因です。

・・・また話が横道にそれたようですが、Androidの場合、OpenGLやSurfaceViewなどといったゲーム用に用意された高速な描画処理を行おうとすると、どうしてもこのダブルバッファを使わざるを得なくなります。ダブルバッファにすると全画面を毎回書きかえる必要があり(正確には必須ではありませんがほぼそうなります)垂直ブランク同期タイミングに合わせて切り替えることになるため、どうしても1フレーム(16ms)程度以上の遅延がここで発生します。Clover SCでは遅延を回避するために敢えて低速なViewを使ってストローク描画を行なっています。(しかし、正直な所低遅延化に貢献しているかどうかは判然としません)さらにAndroid4.1以降の場合はトリプルバッファと言って、ダブルバッファよりもう一段バッファを深くしてスクロールを滑らかに見せるという処理が加えられるようになりました。一見滑らかな、ぬるぬるした動作にはなるかもしれませんが、遅延という観点からすれば、さらに1フレーム分遅延するようになっただけです。

(6月10日追記)

私の手元にある端末は現状すべてAndroid 4.0.x以下のため試せなかったのですが、オフ会でGALAXY Note 8.0を触らせてもらってAndroid 4.1から搭載された「時間分解能(スキャンレート)を出来る限り画面の更新速度の60fpsと同じに保つ」という処理の内容がだいたい判明したのでその情報を追記します。

結論から言えば期待していたものとは違いました。内部で高レートの座標スキャンを行い、出力だけは16.7msをできるだけ維持するようにしているのならば(本当はもっと高スキャンレートで送って欲しいのですが、それを通常にしてしまうと、アプリによってはおかしくなるものがあるのも事実なので、高スキャンレートを考慮したアプリを起動する時だけターボモードみたいなものになるよう、ユーザーが指定できるようにするのが理想です。ついでにGALAXY Noteのジェスチャも特定アプリでは無効にするようユーザーが指定出来ればいうことなしなんですが)よかったのですが、8msや24/32msなどといった16msからかけ離れたデータが10.1と同じくらいの頻度であらわれます。多数のデータの平均を取ると確かに16.7msピッタリくらいになるのですが、これでは安定しているとはとても言えません。GALAXY Note 8.0だけの話なので、Android 4.1のすべてがそうなのかはわかりませんが、こんなもののせいで4.1では平均スキャンレートが60ppsで頭打ちになるのであれば、そんな機能はいりません。これでは単なる足枷です。

(追記 ここまで)

(7月17日追記)

その後調べていてわかったのですが、Androidは座標データを送ってくるイベント間隔こそ60ppsなものの、そのイベントの中に以前のイベント以降に検出した座標データをすべて取得する特殊なインターフェイスを備えていて、それを使えば全スキャンデータを取りこぼしなく取得することができることが判明しました。(AT703で200pps、それ以外は133pps)遅延を考えるとハードウェアからデータが来た瞬間に送ってくれた方がいいのですが、単純にそれをやると前述のようにそれを考慮していないアプリは動作がおかしくなるかもしれないし、画面更新タイミングも60fps間隔なのでそれ以上高速に書き換えても無駄なため、この構造はある程度合理的です。問題があるとすれば、ハードからデータが来た瞬間に送ってくれていれば、その瞬間から計算を始められるので、計算に関わる遅延が多少削減できるかもしれない・・・といった程度でしょうか。

(追記 ここまで)

さらにAndroidではアプリケーションのレンダリング内容をOS側が合成したりするための処理段階があるように感じられます。恐らくこの部分でも1フレームか2フレーム程度の遅延が起きています。勘弁して欲しいものです。ただしこれはAndroidだけの話ではなく、最近ではWindowsでも、Aeroでアプリケーションの表示内容をタスクバー等に縮小表示する機能やウインドウの半透明重ね合わせを実現するなどといった非常につまらない事のために同じような遅延が発生しているようです。Windows7まではクラシック表示にすることで回避できたようですが、Windows8からはそのようなオプションもなくなり遅延は回避不可能になっています。遅延など誰も気にしないと思っているんでしょうか。

ユーザーが声を上げなければ、この部分の遅延はどんどん増える一方です・・・

 

  • 画面(モニター)表示の遅延

これは比較的有名な話で、誰でも感じたことがあるだろう、液晶の遅延です。残像として見えることが多いものです。

昔のブラウン管モニターは蛍光面に電子ビームを当てて発光させるタイプのものなので、光っているのは1フレーム以内、遅延も1フレーム以内になります。

それに比べ現在主流の液晶は、バックライトは常時発光していて、バックライトの光をどの程度通すかを液晶に電気信号を送ることで変化させて制御しているわけですが、液晶の種類によっては変化完了までに数十秒以上かかるものがあります。

最近ではひどい残像を感じるような液晶は少なくなって来ましたが、液晶を素早く変化させるために、前のフレームとの差分を取って電気信号を適切な値に調整する回路が入ったりして、残像こそ感じないものの遅延は増えている、などというものも存在します。モニター側で色補正・動画補正・その他フィルタ機能などが入っている場合も、その処理のために遅延が発生していることが多いです。Androidタブレットの場合、搭載されている液晶のこういった性能が表記されていることはまずないので、これもユーザーからはなかなかわからない部分ですが。

 

  • 端末・OSに起因する遅延まとめ

以上、端末・OSに起因する3つの遅延「ペン入力」「画面演算」「モニター」について比較的突っ込んで見て来ました。私はソフトウェアが専門ですがゲームプログラマなので、ハードに関する知識が多少必要だったため、遅延の原因に関しても多少は目星がつくわけですが、逆に言えばゲームプログラムに必要な程度の知識しかありません。もし現状に即しておらず、間違っている部分や、さらに深くご存知の方があれば、コメント等でご教授頂ければ幸いです。

現在のデジタイザ付きAndroidタブレットでは(GALAXY Note10.1/ThinkPad Tablet/HTC Flyerで確認)端末・OSに起因する遅延が、70ms~100ms程度発生しています。

私の推測では「ペン入力」部分で1フレーム程度(平均で8ms)、「画面演算」部分で2フレーム程度(32ms)、「モニター」部分で2フレーム程度(32ms)発生しているものと考えています。合計すると70ms前後です。端末メーカーやOSのチューニングを低遅延に向けて頑張れば、すぐに30ms程度は削減できるはずです。これはおよそストローク予測2フレーム分(予測については後述)にあたります。

 

ペイントアプリの遅延について

ここでこれまで説明したような大きな遅延がAndroid端末では現状不可避についてまわること、私が絵の訓練をまともにしていないため、スムーズな画面の回転機能がどうしても欲しかったこと、またCloverメモ開発当初は本格的なペイントアプリを作ることは予定しておらず、むしろゲームエンジンを作るためのハードの下調べのために、ちょっとした軽いアプリを作った方が目標になっていいという程度の認識であり、ペイント版もその流れで作られていること、などから、Clover Paintは遅延に関してかなり無頓着な作りになっています。具体的にはClover Paintではアプリケーション起因のペンストローク遅延が70ms程度発生しています。これをClover SCでは10ms前後程度まで短縮しているのですが、その変更点の詳細をここで説明します。4月24日の以下のツイートで遅延にこだわるならばやるべきだと言っている事を自分自身やったにすぎませんが。

 (ただしここで言っている予測とは多少不正確な線を描くという意味で、後で説明するストローク予測とは内容が異なります。ややこしいですが)

 この前後のツイートでも話していますが、8ms~16ms程度の間隔で座標が送られてくるということは、例えば1秒で10センチペンを移動させると、10cm*16ms/1000msで、0.16cmとなり、0.8mm~1.6mm程度の間隔で座標が来るということになります。この間隔ならば補間がなくても、まあそれほど気にはならないのですが、長い髪の毛などを綺麗に描くため素早くペンを動かすと、これが1cm程度になります。ここまでくると少しのカーブでも補間がないとガクガクになってしまいます。

f:id:CloverPaint:20130605121227p:plain

これはClover Paintで補間のON/OFFスイッチを切り替えて同じように描いたものです。違いは歴然です。Clover Paintの補間は現状、この種の補間で、もっとも扱いが簡単だと思われる二次Bスプラインで行なっています。これがどういうものは以下の図でイメージしてください。f:id:CloverPaint:20130605123158p:plain

意味がないので数式等は省きますが、これでだいたいのイメージは掴めると思います。「1」「2」などとなっているのがスタイラスから送られてきた座標です。4つ目の座標が来るまで、描画を開始できないのがわかります。

一見すると、3つ目のポイントが来た時点で、曲線を構成するための情報は揃っているので描画できるじゃないかとおもわれるかもしれませんが、3つめのポイントでストロークが終わり、4つめのポイントが来ないことがあり得ます。その場合、1-2-3を使った、3の終点までのカーブを描く必要があるため、4で描いた曲線とは異なるものを描かなければならないのです。というわけで、4が来るまでは描画できません。その後はポイントが来るたびに各座標の中点を結んだ曲線を描くことが出来るようになります。ポイントの来る間隔が1フレーム(16ms)だとすると、1のポイントが来た時間を起点として、4のポイントが来るまでの時間は3*16ms=48msになります。5以降は2.5ポイント前まで描画されているので2.5*16ms=40msと考えていいでしょう。ただし、実際のClover Paintではストロークの抜き処理を実現するため、実は図よりもさらに1フレーム待っています。つまりポイントの来る間隔が1フレーム(16ms)だとすると、56ms~64ms程度の遅延が発生します。実際には私の使っている端末は平均13ms程度のなので、この部分の遅延はもう少し小さくなります。さらにClover PaintではOpenGL描画を採用しているため、ポイントが来てからストロークを描画し、それを元にテクスチャを構成、そのテクスチャをバックバッファに描画、さらにそれをフリップしてフロントバッファで表示という手順を踏む必要があるため、ここで最低1フレーム以上の遅延が発生します。

以上の合計でだいたい4~5フレーム程度ということになり、ポイントの来る間隔によりかなり変動はあるものの、Clover Paintのアプリに起因する遅延は60ms~80m程度ということになります。

以上の遅延要因を見れば分かる通り、実はCPUの速度により削減できる遅延は、上記の「ポイントが来てからストロークを描画」の部分だけであることがわかります。非常に太いブラシや複雑な混合処理を持ったブラシの場合、ここにものすごい時間がかかることもあるのですが、Clover SCで使っているような数ピクセル幅の単純なブラシでは、この辺りの処理は2ms以下程度で終わります。そしてそれは大抵、垂直同期でのフリップ待ちなどといったディレイ要因で吸収されてしまいます。つまりCPUがいくら速くても全体の描画遅延には特に影響しないと言えます。

 また、現状の端末のポイントの来る時間間隔では非常に大きな遅延になっているこの部分の処理ですが、「ポイント間隔(ms)x4+16ms」という式で表せることに注意してください。この式に当てはめれば、ポイントの来る時間間隔が16ms→2ms等にさえなれば、それだけで80msあった遅延が、24msまで短縮されるのです。私がClover SCのようなツールを作り、ポイントの送られてくる時間間隔(時間分解能・スキャンレート)の計測にこだわる理由の一部がこれで分かって頂けたでしょうか。

 

  • Clover SCのペンストローク

Clover SCでは上記のような遅延を少なくするため、ストローク途中で以前のキャンバス状態を(Undo用とは別に)保存しておき、必要に応じて以前のストローク描画を書き換えながらペンストローク描画を進行させます。以下の図を前のClover Paintの図と比べて下さい。

f:id:CloverPaint:20130605141523p:plain

青が、とりあえず確定はしていないけど、やり直し可能な状態を維持して描画している部分です。赤は描画内容が確定した部分、黄は前ステージで青く描画されていたものを、消した部分です。赤と青がそこで実際に表示されているものになります。見て分かる通り、本来ならば4が来るまで表示開始されていなかったものが、2の時点で表示されています。これで2フレーム分の遅延短縮になります。実際にはCloverPaintではさらに1フレーム待っていますので、3フレームの短縮になります。4から5の黄→青の部分の変化を見ると、かなり変な表示になっているように思いますが、実際にはほとんど気にならないようです。

ただし、以前の描画を消す処理と、新しく描画する内容が増えているため、CPUのストローク処理内容は数倍に増えています。細いブラシならばこの程度の差は誤差範囲に収まりますが、太いブラシではかえって遅延を大きくする要因になる可能性があります。

また、Clover SCではOpenGLによる描画をやめることで、ダブルバッファのフリップ待ちの遅延を回避しています。実測するとスタイラスの座標イベントが来てから5~10ms程度でシステムの描画処理へデータが渡っています。Clover SCの理想的な動作環境でのアプリ起因による遅延は「ポイント間隔x0.5+5~10ms」で、だいたい10ms~18ms程度だと考えられます。

 

  • Clover SCのペンストローク予測

Clover SCではこれにとどまらず、現在までのストローク情報から、この先到達するであろうストローク位置を予測し、あらかじめそれを描画しておくことで、本来アプリ側では解消しようのない遅延を見た目少なくする機能を実験的に実装しています。これは私自身、以下のツイートについて、確かに気持ち悪くなるだけでしょうねと言ったものの、一応確認のために実装してみたものです。

Clover SCを動かして、実際に試してみてもらえばよくわかりますが、予想通り折り返しなどで変な表示になって気持ち悪いのは確かなんですが、絵描き用途だとそこまで複雑に折り返すということは滅多にないため、遅延の少ない描画に感じられ、気持ちいいことも結構あります。文字とかを素早く書くと全然ダメですが。

f:id:CloverPaint:20130605144203p:plain

上の図で緑になっている部分が予測によるストロークの描画です。

予測は少々ややこしい事をしているので詳細は省きますが、Clover SCでは過去のポイント間隔を1フレームとして「1~3フレーム先」までの予測を選んで表示することができます。HTC Flyerでは16ms間隔で安定しているため、かなり綺麗な予測になっていて、3フレーム先予測にするとペン先と描画がほぼぴったりと合って非常に気持のいいストローク描画になります。

 

まとめ

Android端末等の遅延に関する考察と、Clover SCの低遅延ストローク描画に関する説明は以上です。デジタイザー付きAndroidタブレット端末をお持ちの方は、是非Clover SCで予測ありストロークを試していただきたいのです。そして、それを見て、そのストロークの描き味が変な予測ではなく、普通に得られるようになればなぁという、私の願望に同調してくださる方は、手持ちの端末をClover SCで試し、ペイントアプリの描き味に関わる描画性能をブログやSNS等ネットでどんどん流してください。

 

もし、Clover SCのペンストロークが気に入り、これを軽いペイントアプリとして使いたいという方があれば、寄付版を用意してありますので、そちらをご購入ください。寄付版を購入し、ペイントアプリとして使いたいという要望が多ければ、機能を追加していくつもりです。ただし、寄付版はCloverSCをAndroidアプリとして考えた場合、かなり高めの値段設定をしています。普通に考えればこんな低機能なアプリを好き好んで買う人は限られているだろうし、アプリ化はこういった活動に意義を感じ、支援してくれる人に対するオマケだと考えているからです。 

Clover SCに関しては私自身が好きで作っているわけで、こんな考え方をするのもどうかとは思いますが、大部分はClover Paintの流用とはいえ、開発にフルで一ヶ月強の時間と労力をかけています。経済学の機会費用の考え方でいけば、百万円くらいの費用がかかっていることになります。Clover SCの趣旨からして、有料にすると意味が無いため、その経費はすべて私が一人で負担しています。もし私のやっているようなことに賛同し、自分も時間とプログラム技能さえあればClover SCのようなアプリを開発していたのになぁ・・・という方があれば、経費の分担という意味合いで寄付して頂ければと考えています。いま一部で流行っている、開発費を利益目当ての投資家や銀行から集めるのではなく、そのプロジェクトに意義があると考える、一般消費者や賛同者から集めるというクラウドファンディング的な考え方です。というわけで、前述のペイントアプリ化云々は、賛同してくださった方へのささやかなお礼程度のアプリや機能になる予定です。

キー設定(ショートカット)機能を大幅に拡張

Clover Paint v1.18を公開しました。

おもに操作系に関するアップデートのため、メモ版でも実装されています。

 

このバージョンではショートカットに登録できる機能が大幅に増え、また、外部のフルキーボードやゲームパッド等を、いわゆる左手デバイスとして利用できるようになりました。

 

 基本的に、以前設定していた機能はそのまま引き継がれますが、「ギャラリー→オプション」内で設定していたハードキーをトリガとしたショートカットコマンド呼び出しや、「ギャラリー→スタイラス」内で設定していたスタイラスボタンをトリガとしたコマンド呼び出しは、すべて「ギャラリー→キー設定」に移動し、その中で新しい形式で設定されています。

 

 以下、「キー設定」内部のコマンドについて、特殊なコマンドや、カテゴリに特徴的な設定・操作方法等について説明していきます。

 

おそらく、少なくともスマホタブレット系のアプリとしては他に類を見ないほど高度で柔軟な設定が可能になっていますが、その分、使いこなすのはかなり難しくなっていると思われます。以下の解説をひと通り読んで、十分に理解してからカスタマイズされることをお勧めします。

 

リピート

キーを押し続けた時の、リピートの開始までの時間と、リピート間隔をここで設定できます。レンジバー(ペン太さ設定などでよく使われているもの)の横にあるボタンを押し続けた時のリピートも、ここで設定した値と同じになります。

また、コマンドトリガで普通のクリックではなく、長押しと判定される時間も、ここの開始までの時間で設定された時間になります。

 

モードシフトキー

他のキーと組み合わせるために利用するキーを指定します。通常のタイピングではSHIFTやCTRL、ALTなどといったキーが他のキーとの組み合わせでキーの動作を変えるモードシフトに利用されますが、Clover Paintではモードシフトに利用するキーをユーザーが任意に選択できます。

AやBなどといったキーをこのモードシフトキーに登録しておけば、A+1~9、B+1~9、A+B+1~9などといった組み合わせを、全く別々のコマンドを呼び出すトリガにすることができるようになります。また、ゲームパッド等を利用している場合は、パッドの4方向をシフトキーにすることで、斜めも合わせて8方向を他のキーのモード変更に利用できます。

ただし、モードシフトキーに登録されたキーは後述の「長押し系のトリガ(タッチを除く)」のキートリガとしては利用できなくなり、クリック押しながらタッチのトリガとしての利用に制限されます。

また、キーボードの種類によっては、通常使われるSHIFTやCTRL、ALT以外のボタンの組み合わせが制限されたり、同時押しが認識されるキーの数が非常に少ないものもあります。それらはハードウェアの仕様ですので、そちらにあわせた設定を工夫してください。

 

 コマンドキャンセルキー

動作中に、ユーザーの操作が継続するタイプのコマンド(例えばペンストローク描画や、カメラの移動、横スライドによるペン太さ変更など)で、操作中にいまの変更をキャンセルして元に戻したい時に利用するキーをここで指定します。コマンドトリガだけでなく、レンジバーのつまみ等でも有効です。

  

スタイラスDown処理の遅延(ms)

スタイラスや指がタッチパネルに触れてから、実際に触れた時の処理を開始するまでの間に、ここで指定された時間分だけ処理を遅延させます。遅延している間もタッチイベントはバッファリングしているため、計算結果としての描画内容に変化はありません。

 

なぜこのような遅延設定を用意しているのかというと、例えば、トリガキーを押しながら画面にタッチすると画面移動になるコマンドがあります。その時、トリガキーと画面に触れるのを感覚的にほぼ同時にした時に、そのイベントを受取るプログラム側では、トリガキーが微妙に遅れてくる場合が半分くらいの確率で起こります。その場合、画面移動にならず普通にブラシ描画してしまうわけです。これがかなりストレスなため、トリガキーを待つための時間としてここで遅延を入れられるようにしました。

ただし、どのくらいの遅延が必要かはそれぞれ感覚や利用している機器により違うし、遅延が大きすぎるとストロークの最初の追従が非常に遅れて気持ちが悪いという人も出てくるわけで、遅延させるくらいなら意識してトリガーを早めに押すようにするという人も多いと思われるため、ここで自由に設定できるようになっています。

 

 

戻る/キャンセル

決定

ダイアログが開いている時にのみ有効なショートカットキーです。確認用のポップアップタイプのダイアログの他に、ブラシの設定やオプション画面等も内部ではダイアログとして扱っているので、このキーが有効です。ただし、戻る/キャンセルは明確にダイアログの中に戻るボタンキャンセルボタンが含まれる時にのみ有効です。それ以外の場合は単純にダイアログを閉じる操作になり、決定と同じ意味になります。

 

 

元に戻す

やり直し

 ごく普通のコマンドですが、リピートが可能なタイプです。

 

←左↑上→右↓下

ファイル出力モードでの画像位置調整、移動モード、変形モードでの位置調整に利用されます。

 

キャンバス移動

キャンバス回転

キャンバス倍率

ここで設定したキーを押しながらタッチするか、ホバーに対応したスタイラスを備える端末ではホバー/長押し中でカメラの移動、回転(左右スライド)、倍率(左右スライド)を行うことができます。

 

キャンバス操作(移動/回転/倍率)

1つのトリガキーで3つのカメラ操作をまとめて出来るコマンドです。トリガキーを押した時に表示される円の内側をタッチしてスライドすると移動、外側を円周に沿って回転させると回転、放射状にスライドすると倍率変更になります。続く下2つの欄で、円の大きさと、ホバーに対応した端末では、ホバーカーソルに円の中心を合わせるかどうかを設定できます。

 

これらコマンドのみでカメラを操作する場合、ピンチによるカメラ操作はむしろ邪魔になるかもしれません。特に大きなタブレットでは手のひらが予期せずマルチタッチのような状態になることがあり、誤操作の原因になるため、これらコマンドを利用した方がやりやすいかもしれません。v1.18からは回転や倍率に加えて、ピンチによる移動操作も禁止するオプションが追加されていますので、ご利用ください。

 

中央表示(?)

キャンバス反転

倍率?%

回転?度

これらは表記された状態にカメラを遷移させます。

トリガとして単純なクリックなどを指定した場合は、その状態に遷移するだけですが、長押し中を指定した場合は少し特殊で、トリガとなるキーを押している間だけその状態になり、離すと元に戻ります。

ただし、遷移している間にペンストロークを行ったり、ピンチ操作を行うと元に戻らず遷移した状態を維持するようになります。

また、クリック長押し中を同じトリガキーで同時に有効にすると、通常クリックだけでは離した時に状態遷移するのが、押した瞬間に遷移して戻らなくなります。マッピングできるキー数に余裕があり、反応がいい方が好きだという場合に利用してください。

 

倍率上げる/下げる

回転反時計廻り/時計廻り

これらは現在のカメラ状態に対して相対的にカメラを遷移させるものです。

 回転にはディフォルトで三本指ジェスチャが設定されています。これまで三本指の同時タップは画面反転でしたが、タップダウン瞬間のコマンド発動をやめ、離した段階での発動に仕様を変更しました。これにより指を上下左右にスワイプしてから指を離すジェスチャ操作が可能になり、三本指ジェスチャに5つのコマンドを登録できるようになっています。

 

アプリケーション終了

アプリケーション終了ダイアログを表示します。

この設定はギャラリー内でも有効です。

 

ペイントへ戻る

 ペイントから遷移したサブモード(移動・変形・範囲選択モード)からペイントモードに戻ります。

 

ナビ表示エリア移動

キャンバス移動コマンドと同じく、カメラを移動しますが、こちらはいま開いているナビゲーションウインドウの表示枠をドラッグしているものとして移動量や方向が計算されます。(ナビウインドウがなければ無視されます)

このコマンドを通して操作する場合は、特に表示枠内を押す必要はなく、またナビのキーイベント透過設定等も無視して操作できます。これと新しくナビに追加されたカーソル表示を利用して、ナビ画面上でペンタブを使った時のような操作が可能になります。これは試験的な実装で、現状実用的ではありませんが、Android4.2から標準追加された外部モニター表示機能により、将来的には別モニターにナビの内容を表示できるようにする計画があります。

 

Clover選択01~30

カラー選択01~30

Cloverツールバーのリストやカラーパレットツールバーのリストにあらかじめ登録されているブラシやコマンド、色を選択します。トリガとして押しながらタッチを選択すると、押している間だけ一時的にそのブラシや色を利用し、トリガキーを離すと元のブラシや色に戻るようになります。

また、「表示設定→回転→+回転状態」「表示設定→倍率→+倍率状態」「表示設定→初期状態→+全状態」で、それぞれ現在の回転状態のみ、倍率状態のみ、移動・回転・倍率・反転すべての状態を保存したCloverアイコンを作成できます。そのアイコンをCloverリストに登録し、ここでトリガを設定すれば、任意の回転、倍率、カメラ状態をショートカットに登録することができるようになります。

 

消しゴム

このバージョンから、消しゴムはいま選択しているブラシの状態を出来る限り保ったまま、透明にするブラシになるように調整されました。消しゴムを特定の状態にしたい場合は、Cloverリスト内のeraserにその消しゴムを登録することで可能です。

 

また、ブラシのCloverはこのバージョンからブラシパラメータのどの部分を保存・変更し、どの部分を現状維持するかを、以前より細かく設定出来るようになりました。これはその他パラメータにまとめて分類されていたパラメータから、ブラシを消しゴムにするために必要なパラメータを分離し、他のパラメータに影響を与えずその部分だけ変更する必要があったためです。つまり、ユーザーが自由に一時的に消しゴムのようになるブラシも作成できるようになっています。

 

太さ調整

ぼかし調整

流量調整

ペン合成不透明度調整

カラー調整 - 色相

カラー調整 - 彩度

カラー調整 - 輝度

カラー調整 - 不透明度

いま利用しているブラシに関するパラメータをバーを利用して調整します。

ホバー/長押し中押しながらタッチをトリガとして使用できます。

バーが表示されたら→で値を大きく、←で小さくします。また、バーを上に移動すると、バー全体が横に縮小され、設定できる範囲全体が表示されるようになります。逆に下に移動すると、現在値を中心とした範囲が拡大され、細かく値を調整できます。

 

スポイト

ホバー/クリックホバー/長押し中押しながらタッチをトリガとして利用できます。ホバー/クリックホバー/長押し中を同時に設定すると押した瞬間から離すまでずっと色を取得し続けます。また、すぐ下の項目でトリガキーを押していない状態でも常に取得する色や現在の色を表示し続けるかどうか設定できます。

 

レイヤー表示ボタン

レイヤーリスト・レイヤーツールバーの表示・非表示を切り替えるボタンをキャンバスから消すことができます。レイヤーリスト開閉のトリガキーを登録していれば必要ないからです。

 

新規レイヤー作成

(その他レイヤー関連)

レイヤーに関する操作です。Clover Paintのみの機能になります。

 

自由選択ツールを選択

(その他ツール選択)

単純なツール選択や、標準であるコマンドのショートカットです。

 

 

個別コマンドに関する説明は以上です。

 

コマンドのライン

コマンドはお互いに干渉するものとしないものがあり、それはラインという概念で管理されています。例えばダイアログでのみ有効なコマンドに登録されたキーは、ペイントモードでも同じキーを使って別コマンドに登録することができます。ただし、キャンバスで使われるコマンドはペイントモードでも選択モードでも同様に利用されるため、キャンバスのコマンドで使ったキーをペイントモードで別のコマンドに登録しようとすると重複しているとして、古いキー設定は削除されます。

基本的には矛盾が生じないようキー設定処理がうまく重複を検出し調整します。なぜ同じキーを登録しても消される場合と消されない場合があるのかという事に関しては、上で説明したような事情があると理解してください。コマンド毎の細かいライン設定に関しては説明が長くなり、あまり意味もないので割愛します。

 

トリガ種類

コマンドのトリガとなるキーを押した時、どのタイミングで、どのようにコマンドが実行されるのかという、トリガ種類は以下のとおりです。

 

クリック

ワンショットのトリガで、座標など他に必要な情報がない、基本的なコマンドを実行するのに利用されます。設定ウインドウの開閉や、中央表示・選択モードへの遷移などです。

クリックのイベント発生タイミングは通常離した瞬間ですが、同じキーでクリックとリピートを同時に有効にした場合、押した瞬間にイベントが発生するようになります。また、ホバー/長押し中と同時に有効にした場合、押した瞬間にイベントが発生するようになるコマンドもあります。

 

ホバー/クリック

(ホバー検知可能なスタイラスのある端末でのみ有効です)

クリックにキャンバス上の座標情報が必要なコマンドで利用されます。

ホバー中のカーソル座標とクリックがコマンドに送られます。

現状はスポイトでのみ設定可能です。

 

ホバー/長押し中

(ホバー検知可能なスタイラスのある端末でのみ有効です)

ホバー中にトリガを押している間だけ有効なコマンドです。

ホバー中のカーソル移動量が画面の移動や、調整バーの設定に利用されます。

長押し中なので、リピート開始時間分コマンド処理状態になるのにタイムラグがある場合があります。(調整バーではカーソルを移動することで即時受け付け状態になります。)

 

長押しトリガ

長押しした場合に発生させるコマンドを登録します。基本的にワンショットなので、設定できるコマンドの種類はクリックと同等です。

 

長押し中

カメラ状態の遷移系コマンド等で、押している間だけコマンドの状態にし、離すと元に戻るという特殊な操作を可能にするトリガです。クリックを同時にトリガに設定すると、押した瞬間に遷移し、そのまま状態が戻らないトリガになります。

2014/02/07 修正 

これまで長押し中クリックを、同じキーで同時に設定すると、押した瞬間に遷移してそのまま戻らないトリガにしていましたが、v1.22.20から独立したコマンドとして動作するよう仕様が変更されます。素早く押すと状態遷移、長押しすると押している間だけ遷移し、元に戻るようになります。また、これは押しながらタッチクリックを組み合わせた時にも同様の動作になります。

 

リピート

カーソル移動やUndoなど、キー長押し状態の時にコマンドが繰り返し送られる事に意味があるコマンドで設定可能なトリガです。通常は同じキーにクリックとペアで設定しておく事をお勧めします。

 

押しながらタッチ

Cloverリスト上に設定されたブラシのストローク等に使われます。

また、ホバー検知機能のない端末では調整バーや画面移動に使える唯一のトリガでもあります。

 

 

これらコマンド呼び出しのトリガ種類はクリック系(上2つ)と長押し系(その他)に大別され、1つのキーにクリック系の中から1つと長押し系の中から1つ、別のコマンドを登録することができます。例えばスタイラスの1つのボタンにクリック系のUndo(クリック)と、長押し系のスポイト(押しながらタッチ)の2つを登録することができます。ただし、組み合わせによっては誤操作の原因になるので、うまく組み合わせて使って下さい。

 

 スタイラス設定に最低筆圧待機処理を追加

「ギャラリー→スタイラス」の一番下に「最低筆圧の待機時間(ms)」と「スタイラスDown判定の最低筆圧(x0.1)」が増えています。これはペアで使われ、スタイラスがタッチパネルに触れてから、後者の筆圧になるまでスタイラスから送られてくるイベントを無視するための設定になっています。触れてから、待機時間で設定した時間になっても一度も最低筆圧を超えなければ、以降のイベントもすべて無視します。待機時間内に最低筆圧を超えれば、バッファに溜めておいた、触れてから今までに送られてきたイベントを一気に処理します。また、スタイラスがタッチパネルから離れたら、この状態はリセットされます。

なぜこのような処理が必要なのかというと、ネット上ではWacomのfeel系スタイラスを採用した端末(あいていに言えば、GALAXY Noteシリーズ)の利用者の中でスタイラスを改造して、最低反応筆圧荷重をほぼ0に近づける方法が広まっていて、半ば一般化しています。ただし、この改造をした場合、端末や改造度合によっては実際にタッチパネルに触れていないホバー状態でも触れたようなイベントが来ることがあり、これがアプリ側の誤作動として表れます。特に改造を推奨するわけではありませんが、それで使い勝手が良くなるのも事実らしいので、このような処理を入れて、誤作動を補正できるようにしたというわけです。

 

 

 

非常に長く難解な説明になりましたが、頑張って実際にキー設定をいじりながら自分なりに使いやすい設定を見つけてください。また、現在左手デバイスはBluetooth接続で様々なタイプのものが発売されていて、手持ちの端末と組み合わせて使いやすいデバイスを探すのも楽しいものです。ただし、端末・デバイスの組み合わせによっては認識しなかったり色々問題がある場合も多いので、その辺のトラブルも含めて楽しめる人に限られますが。(そもそも私がこのような高度で複雑なキー設定を作る気になったのも、iBOW mobileという左手デバイスを使ってどういう操作ができるか興味が湧いたからだったりします)

 

※ Cloverリスト内のeraser以外の特殊枠、menu/back/vol↑/vol↓などの枠もまだ残っていますが、ショートカットとしての機能はなくなりました。そのうち枠自体も消す予定ですので、いまのうちに退避しておいてください。

移動・変形ツールとUIサイズ変更オプション追加

Clover Paint v1.17を公開しました。

このバージョンでは移動ツールの機能拡張と、新たに変形ツールの追加が行われています。

メイン画面から「ツール選択→変形」で変形ツールの操作モードに移行し、以下のような画面になります。

 

f:id:CloverPaint:20130325160934p:plain

(この画面はすでにある程度操作しています)

 

画面上で赤黒の点線枠は、変形前の画像レイヤーの枠です。

白黒の点線枠は、変形された後の画像レイヤー枠、青黒の点線枠はイメージ全体の編集枠となります。

キャンバス上にボタンがありますが、それぞれの機能は以下の通りです。

  • 中央の十字矢印 移動
  • 横・縦の矢印 横軸・縦軸のスケール
  • 矢印の外側にある台形 回転・傾き・パース(変形選択で変更可能)
  • 斜めの矢印 縦横の比率を保ったスケール

回転等の中心は中央の十字になります。

戻るの上にある初期化ボタンでこの中央の十字のレイヤーイメージ上の位置を変更するモードに一時的に遷移します。

ボタンを移動でピボット位置の変更、×ボタンで通常の変形ツール操作に戻ります。

 この変形ツールはパース等にも対応しているのですが、それらを使うと予期せず非常に大きなレイヤーを作ってしまい、メモリ不足で落ちる可能性が高いため制限をかけています。

具体的には変形後のレイヤーサイズが編集枠サイズ以上になり、さらに画面サイズの大きい方の軸の長さの2倍を超えるとき、編集枠サイズに強制的にクリップします。面倒ですが、大きなレイヤーを作りたい場合はあらかじめ「イメージ→設定→編集枠…→固定」等を使って編集枠を目的のサイズに設定しておいてください。

 

 フィルタは変形時に利用するフィルターを設定します。だいたい一般的なものなのですが、sw.mipボタンが特殊なので一応説明しておくと、画像縮小時に縮小率に合わせて付近のピクセルの平均値を取得し、ジャギが立つのを防ぐフラグとなります。通常はONのままでいいと思われます。むしろジャギが大好きという場合以外は。

 

変形ツールは、今回移動ツールで拡張された90度単位での回転や水平・垂直反転の機能をすべて含んでいます。敢えてツールを完全に分けているのは、移動ツールはピクセル単位で完全な情報が残るのに比べ、変形ツールはピクセルが壊れるような変換を許すためです。

Clover Paintは通常の絵を描くだけでなく、アイコンや2Dゲームのキャラクターパターン等のドット絵を作成する用途も想定しています。これらの場合、ピクセルが絶対に壊れない操作しかできない移動ツールを利用した方が便利でしょう。

 

UIサイズ変更

 

UIの表示サイズをユーザーが自由に変更できるようになりました。

「ギャラリー→オプション→UIサイズ調整」で、0.5~1.33までの範囲で指定できます。1.0でこれまでと同じサイズ、0.5では縦横共に従来の50%のサイズになります。

スマホサイズの端末で、高精細な画面を持つ場合は、v1.17起動直後から0.8倍まで縮小されるような初期設定処理を行なっています。

あまり小さくすると操作不能になりかねないので、注意して設定してください。

 

UIのスケーリングは本来、Clover Paintの海外ユーザーで非常に多いGALAXY Note 10.1など、画面が広く低解像度なタブレット端末で文字が細かくて読みにくいため、拡大する用途で考えたものです。(Clover Paintは、どの端末でもUIの見た目のサイズが同じになるよう自動調整しています)

ただ、作ってみて気づいたのですが、最近は小型のスマートフォンでも高解像度な端末が多いため、タッチパネルの精度さえ許せばUIを小さく表示しても問題ないのではないかと。Clover Paintの基本的設計思想として、ユーザーに開放できる有用なカスタム項目はすべて開放するのが方針なので、それでこれもオプションとしてカスタマイズできるようにしよう、ということになりました。

GALAXY NoteシリーズやIGZO液晶を搭載した端末等では先の細いスタイラスが使えるので、ある程度小さくしても操作可能だと思われます。使いやすいサイズに調整して使ってみてください。これが同じ端末かと目を疑うくらいに世界が変わると思います。

ただし、あまり精度の高くないタッチパネルでは特に画面の隅の方で歪みが激しい場合が多いため誤判定が多くなり、最悪オプションに入れなくなり、一度アンインストールしてパラメータを初期化する必要が出てくるかもしれません。注意して少しずつ小さくしていってください。

 

今回は他にもレイヤーリストの表示レイアウトを調整したり、フォントやアイコンの縮小時にアンチエイリアスがかかるようにしたりして、小さくても見やすくなるよう色々と調整を加えてあります。

バックアップファイルの利用法

ちょっと小ネタとしてバックアップファイルを利用して操作ミスなどで失ってしまったデータを回復する方法を紹介します。

マニュアルの以下のページでも少し触れていますが、Clover Paintではツールの現在状態保存に3つのセーブファイルを利用します。

http://www.cloverpaint.net/CRef_option.html#外部環境データ

このうち本体環境データは普通ユーザーからは見えない部分にあるためバックアップもありませんが、ここはオプションとカラーパレットだけなので操作ミスで失ってしまっても、それほどダメージのない部分でしょう。

 

 問題は主にブラシ情報の保存されている外部環境データと画像一時ファイルです。

Cloverツールバー(リスト)上のディフォルトブラシを消してしまったり、編集中の画像をギャラリーに移動するときに間違って破棄してしまうと、Undoもできないため困ったことになります。

 

そこでバックアップファイルを利用します。バックアップファイルはブラシ情報ならば/mnt/sdcard/CloverPaint/~~saveenv.bin(/mnt/sdcard/は環境により違うことがあります)、画像一時ファイルなら/mnt/sdcard/CloverPaint/~~saveimg.binに入っています。

これらのファイルの「~~」の部分のないファイルが実際に利用されているファイルなので、ファイラーなどで名前を変えて上書きしてください。これで回復します。

 

ただし、問題があることに気づいたのがアプリ終了直後ならこれでいいのですが、前回終了したときにすでに問題があり、再度起動したとき問題があることに気づいた時には注意が必要です。そこでアプリを終了したり、HOMEボタンを押したりするとバックアップファイルに問題がある情報が書き込まれてしまいます。そのような場合はUSBのストレージ接続を利用し、PCからバックアップファイルを直接操作して一度別名で保存するか、PCに退避してください。そのあとアプリを終了して回復作業を行なえば回復できます。

 

今回セーブファイル関連の話題なので、それについて、ついでに幾つか。

バックアップファイルはファイルを安全にセーブするための副産物として出来たものです。現在あるセーブデータを直接上書きすると、途中で何らかの原因で書き込みが継続できなくなった時ファイルが壊れた状態になってしまいます。そこでセーブは一旦頭に「~」を付けたファイルに行います。その後、ファイルセーブが確実に行われたのを確認してから、いまあるセーブデータを頭に「~~」のついたデータに名前変更し、新しく出来たセーブファイルの「~」を取り除きます。この一連の作業で結果として古いセーブデータに~~がついて残っているわけです。

というわけで滅多にセーブデータが壊れることはないと思われるのですが、それでもたまに壊れるという報告があります。特にディフォルトブラシが完全に消えてしまった状態でセーブしてしまい、バックアップファイルもない状態になると結構悲しいことになります。そんなときはsaveenv.binファイルを消してからアプリを起動して下さい。とりあえず初期状態に戻ったセーブデータが作成されます。

 

恐らくセーブデータが壊れるのはアプリが多重起動状態になっているのが原因だと思われます。(一応ガードはしているつもりなのですが)

Clover Paintは終了したように見えても、実際には裏でしばらくセーブ作業を継続していることが多い(とくに大きな画像データを編集中に終了した場合)ので、その間にもう一度Clover Paintを起動しようとしても新規起動は拒否されます。

PSD画像ファイル形式に対応

近日公開予定のClover Paint v1.16でPSD画像ファイル形式の入出力に対応しました。

(PSD対応はClover Paintのみの機能になります。)

ここでは新バージョンで実装された機能を紹介します。

利用において注意すべき点がいくつかあるので一応目を通しておくことをお勧めします。

 

まずPSD画像入力ですが、ファイルの選択に外部のファイラーアプリを利用します。

そのため有効なファイラーが存在しない場合PSD読み込みができません。現状「アストロファイルマネージャー」と「ESファイルエクスプローラー」で動作することを確認しています。もし「ツール→ファイル」「ファイル→入力→イメージ→PSD」を選択しても有効なアプリが見つからなかったと表示されたり、PSDファイルを選択できない場合は上記ファイラーをインストールして下さい。

 

読み込めるPSDファイルの情報は以下の通りです

・各チャンネル8ビット深度のRGB/RGBA画像(レイヤー)

・マスク付きの画像は不透明度にまとめて読み込み

・不透明度・不透明度保護・下のレイヤーでクリップ等のレイヤーフラグ

・レイヤー表示名

・Clover Paintで対応している合成モード

 

無視される、もしくは読み込めない情報の主なものは以下の通りです

・8ビット深度以外(16/32)のPSDファイル

・内部でZIP圧縮を利用している画像データ

・グループ情報

・レイヤーのマスク(不透明度に自動的に合成されます)

・画像以外のレイヤー(調整レイヤー等)

 

出力に関しては読み込めるデータ種類と同じものに対応しています。

グループ情報(フォルダ)やレイヤーのマスクに関しては、Clover Paint側でそれら機能が実装された際にPSDフォーマット側へも入出力できるようにする予定です。

PSD入出力に関しては以上です。

他にもいくつか変更点があります。

 

ネイティブ画像データの最適化処理を実装

 内部データフォーマットの記事でデータの最適化は現状やってないと書きましたが、このバージョンからPSD画像を読み込んだ時、および内部データ形式の画像を読み込んだ際(つまり、起動時の前回画像の読み込みとギャラリーからの画像読み込み)に最適化を行うようになりました。アニメ風のベタや透明部分の多い線画等ではファイルサイズはもちろん、メモリ内部でのサイズも数分の一になります。(もともとかなり大きいのですが)

 

キャンバス表示更新の遅延処理

何らかの修正があってキャンバス画像表示を更新しなければならない時、これまでは画面全体を更新したり、更新が終わるまで描画スレッドの処理を止めていた部分が多かったのですが、処理を大幅に見直して出来る限り停止しないようにする処理ルーチンを追加しました。

ただし、巨大画像等を読んだ直後でもカメラ移動等操作はできますが、画像全体が正しく表示されるまでに時間がかかるのは変わりません。パラパラと表示されるのが気持ち悪ければ「ギャラリー→オプション→キャンバス表示の遅延を利用」のチェックを外せば以前とほぼ同じ処理になります。

 

「下のレイヤーでクリップ」の仕様拡張

このバージョンまでの「下のレイヤーでクリップ」 は実際には「下のレイヤーまでの統合結果でクリップ」する処理になっていました。PSDファイルに対応するにあたり、Photoshopと同じ処理に合わせるため、これまでの「下のレイヤーでクリップ」は名前を改めて残し、新たにPhotoshop互換機能として「下のレイヤーでクリップ」を追加しました。場合により使い分けて下さい。ただし、Clover Paintの旧仕様のクリップを使った場合、PSDファイルに出力したときそのフラグは消えます。

 

(2013/03/05日追記)

「縮小制限拡張」オプションと「中央表示」コマンドの追加(v1.16.5)

このバージョンから大きな画像の読み込み・編集を考慮していますが、その際に縮小の下限である50%から、かなりストレスを感じるようになりました。そこで編集中の画像が画面に入る程度まで縮小できるようにする「縮小制限拡張」オプションを追加しました。(ディフォルトでONになっています)また、画面にちょうどぴったり収まるように中央表示する「表示設定→初期状態→中央表示」コマンドを追加しました。

 

 

今回のバージョンアップではPSDファイルに対応するということで、PC等の既存環境からのデータ移行が多いだろうと予想しました。そのため、これまで想定していなかったような、比較的大きな画像や、多数のレイヤーを含んだ画像を扱った時の動作で重くてストレスになっていた部分を見直して最適化しています。

自分自身では一枚の巨大画像を落書き程度に描くか、複数レイヤーもアイコンを作る際くらいにしか使用してなかったので、そのへんは無関心になっていました(汗)

今回の最適化で2GBクラスのメモリを持っている端末では結構大きな画像を複数レイヤーに分けて色塗りしている場合でも編集しやすくなったとは思いますが、まだメモリ関連の保護処理などは入れていないのでメモリが足りなくなると何も言わずに落ちます。

メモリ関連の保護処理が入るまでは、メモリ残量等を常にオーバーレイ表示してくれる常駐アプリ等を導入して、メモリが少なくなってきたら一度アプリを再起動する等の自己防衛策で切り抜けて下さい(何)

ナビゲーション・ビュー実装

Clover Paint v1.15をアップしました。

すでに公開されています。

ちなみに今回かなりバージョンが飛んでいますが、管理が面倒なためメモ版と合わせただけです。

 

今回のアップデートの目玉は、なんといってもキャンバス全体の縮小画像を表示し、大きな画像でもレイアウトやバランスを確認しながら描けるナビゲーション・ビューです。

普通に使うだけならば特に説明の必要もないくらい、使い方は簡単です。

[表示設定→+ナビ表示]を選択すると、画面の左上に青色のCloverが作成されます。それをタップするとナビ表示、もう一度押すと消えます。(ナビ表示のUIはダイアログ系を除くすべてのUIより優先して手前に表示されます。)

背景色がそのまま表示されている矩形部分が編集中のイメージ内で、現在端末に表示されている領域です。 灰色になっているのは画面外で、ここをドラッグするとナビ表示を移動できます。(後述のオプションによって動作は変わります)

 

ナビ表示は主に以下3つの使い方を想定していて、それらを実現できるよう設定画面には多数のオプションスイッチがあります。(ナビ表示Cloverは複数作成できますが、実際のナビ表示は同時にはできません。というか、無意味で操作が面倒、さらに重くなるだけだと感じたので

  1. 通常のナビ(表示領域をつまんでドラッグ移動or領域外ダブルタップでジャンプ移動)
  2. 端末画面全体に縮小画像を表示し全体を確認。縮小画像ダブルタップでジャンプ移動と同時にナビを閉じる。
  3. ナビをオーバーラップ表示し、タッチイベントを透過。スマホでの下描き等に。

 

 
クローバーアイコン移動の禁止 全Clover共通
クローバーアイコンの強制表示 全Clover共通
操作イベント透過 ナビ表示領域へのタッチを無視して下のキャンバスやUIを操作できるようにします。ナビの移動等は設定画面から行えます。
表示矩形ドラッグ ナビ表示内の端末に表示されている領域を表す矩形をドラッグすることで画面移動できるようにするかどうかを指定します。
ナビ表示ウインドウ移動の禁止 通常は編集イメージの表示矩形外をドラッグするとナビ表示が移動しますがそれを禁止します。
禁止状態ではダブルタップでの移動が有効になります。
また、幅と高さに端末の全画面を覆う数値を指定している場合、移動後に自動的にナビ表示をOffにします。
ストローク中消去 ストローク中にナビ表示を消します。メインオプションの「UI非表示」が有効な場合にのみ追従します。
背景色 編集中のイメージ領域の、ここでの(ナビ表示での)背景色を指定します。
背景色流用 「背景色」での指定を無視して、「イメージ→BG▽→色…」での背景色設定を利用するスイッチです。
フレーム色 ナビ表示のウインドウ枠の色として使われます。
表示エリア枠色 ナビ表示内の編集中のキャンバスイメージ全体のうち、端末に表示されている領域を示す枠線の色を指定します。
表示エリア外色 ナビ表示内の編集中のキャンバスイメージ全体のうち、端末に表示されている領域外を覆う色を指定します。
原点表示色 仮想イメージ座標の原点を示すラインの表示色を指定します。
表示枠マージン キャンバスイメージ全体の矩形と、ウインドウ枠の間に最低何ピクセルのマージンを取るか指定します。
ウインドウ幅 ウインドウの表示幅を指定します(ポートレート表示基準)
ウインドウ高さ ウインドウの表示高さを指定します
ウインドウ位置X ウインドウの左上座標のX軸値を指定します
ウインドウ位置Y ウインドウの左上座標のY軸値を指定します
イメージ不透明度 キャンバスイメージの表示不透明度を指定します。
不透明処理はGPUで行なっていますが、一部端末では表示されなかったりうまく半透明化されなかったりする可能性があります。
その場合[オプション→スプライトの合成タイプ]の設定を変えることで改善されるかもしれません。
選択時のCloverアイコン不透明度 ナビ表示中のCloverアイコン不透明度
非選択時のCloverアイコン不透明度 ナビ非表示中のCloverアイコン不透明度
高精度な縮小 ナビ表示に使われる縮小画像用にミップマップテクスチャを作成します。
美しい縮小画像がリアルタイム表示されますが、3割強ほどVRAM利用量が増え、ストローク中の処理が多少重くなります。
(この設定はClover毎ではなく、システム全体で共有されます)

 

1番めの 通常のナビは「ウインドウ幅」「ウインドウ高さ」で好みのサイズに変更し、表示位置も調整してから、「ナビ表示ウインドウ移動の禁止」で移動を禁止すると、ダブルタップでの移動が有効になります。ナビ表示移動の方が重要ならこのスイッチは無効のままで。

 

2番のナビ用の設定は通常設定のナビから、

  • 「表示矩形ドラッグ」をOff
  • 「フレーム色」を完全不透明な黒
  • 「表示エリア枠色」を不透明度0
  • 「表示エリア外色」を不透明度0
  • 「表示枠マージン」を0
  • 「ウインドウ幅」を最大
  • 「ウインドウ高さ」を最大

これらを設定することで得られます。

 

3番目の設定は、「操作イベント透過」を使いますが、他の設定はご自分で使いやすいよう工夫して下さい。

本来ならナビ表示の画像を別の単一色に変換する処理ができればよかったのですが、CPUでやると非常に遅くなり、かといってGPUでやろうにも機種毎に動作があまりにも違いすぎ、不透明度表現すら簡単に共通化できない現状では難しいと判断しました。

 

ナビ表示に関しては以上です。

今回は他にもいくつか便利な修正点・追加機能があります。

 

Undo/RedoショートカットのClover化

メインボタンの「元に戻す→+元に戻す/+やり直し」からUndo/Redoのショートカット機能を持ったCloverをキャンバス上に作成できます。後述のオプションと組み合わせ、通常Menuキーに割り当てられている表示モード切り替えで他のUIが消去されている状態でも、特定のCloverは好きな位置に表示したままにして利用できます。

 

 

全Clover共通オプションに「クローバーアイコンの強制表示」を追加

このスイッチがOnになっているデスクトップ上のCloverショートカットは、表示モード切り替えで全UIの表示Offの状態でも表示されたままになります。

 

オートセーブ機能

メモリ不足や電池切れ、またはアプリのバグ等でイメージ編集中に落ちる可能性への対策としてオートセーブ機能を追加しました。

[ギャラリー→オプション→自動セーブ(編集回数/無操作時間)」でオートセーブ開始トリガ条件を指定できます。両方の条件が満たされた時セーブします。

 

ホバー時カーソル表示

一部スタイラス対応の端末ではホバー中にカーソル位置が検出できますが、その表示は、これまでホバー時のショートカットにスポイトを設定するか、設定画面でのみ有効でした。

最新版ではカーソルの表示スイッチを独立させ、ショートカットに何も機能を割り当てていない場合でもホバーカーソルだけは表示できるようにしました。

 

更新内容は以上です。

今回はUIに関する改善が殆どなので、Paint/メモ版共通のアップデート内容となっています。

Clover周りもより便利になりました。工夫して、ぜひあなたの作業効率を上げて下さい。

内部データフォーマット

日本でも、かなりペイントアプリに造詣が深いと思われるユーザーが増えてきたので、ここらへんで少しディープな話をしようということで今回はTwitterで私がよく愚痴ってる、Clover Paint内部の画像データ形式について話したいと思います。

少々プログラマ向けの話ではありますが、デザイナーが見てもClover Paintがどんな風にメモリを使うのかを知ることで、限られたメモリ内で大きな画像や多数のレイヤーを扱う場合のヒントになるかもしれません。

 

ピクセルデータ

RGBA各チャネル毎に16bit幅のデータで持っています。1ピクセル=16bit*4=64bit=8バイトになります。各チャンネルの階調は14bit=1.0にしているので16384段階です。

あまった2bitは将来的に合成等で計算途中に負数や多少のオーバーフローを許す中間値保存が必要になる可能性を考えてバッファを取っています。

 ユーザーに見せているのは一般的な0~255の256段階=8bitですがブラシ効果による色の変化や合成で作成される内部的な演算結果の精度としては最大14bitということです。

 

Cell/Tileデータ

ここからがかなり特徴的で、描画ツールを作るときにこれに対応させるのが大変だったりするわけですが、Clover Paintは画像データを小さい順にPixel→Cell→Tile→全体画像というように複数段階に分けて管理しています。編集画像サイズを予め決めるタイプのペイントアプリだとPixel→直接全体画像となっているものが多い(むろん、高度なキャッシュ・並列実行機能等を備えるPCの有名市販アプリの場合、その多くはそんな単純なものではないでしょうが)のですが、Clover Paintは自動拡張キャンバスを採用しているため、こういう構造をとらざるを得ません。

 

最も単純な内部データ形式 

 f:id:CloverPaint:20130124193230p:plain

 

 Clover Paintの内部データ形式

f:id:CloverPaint:20130124193321p:plain

 一番上の白枠がキャンバスイメージ全体で、内部の青い枠に(0,0)とか(5,0)などと表記されていますが、これはTile単位の座標値です。(実際には負数もとれます)現在のClover Paintではこの座標値は符号付き16bitなので、x座標、y座標それぞれ-32768~32767までの数値をとります。Tileは内部にCellを横16個、縦16個固定で持っていて、16x16=256個のCellを持つことになります。最後にCellは内部に8x8pixelを持ちます。

16個のセルぞれぞれが8pixel幅なので、Tileは縦横16*8=128pixelサイズということになります。Tileの座標値が-32768~32767でそのそれぞれが128pixelなので、キャンバスのとれる座標はおよそ-400万Pixel~+400万Pixelとなります。まあ浮動小数点を内部で使っているのでその演算精度の問題や、そもそもメモリが足りないのでそんなキャンバスを現実的に使うことは不可能ですが、仕様上はそうなります。

これだけだと、ただデータの持ち方をややこしくしただけの話なので、いろいろと例外があります。

まず、イメージプレーン(プログラム内部ではこう名付けています。要するにビットマップ形式のレイヤー)一枚毎にディフォルト色を1つ持てます。ディフォルト色は何に使うかというと、あるTile座標を調べて、Tileデータが存在しない場合、そのTile座標が本来占める128x128Pixelの矩形領域はディフォルト色で埋め尽くされていると判断する…というルールを設定してあります。つまり、何も画像がないただ一色で塗りつぶされたレイヤーは、どんなに大きな領域を占めているように見えても実際には1ピクセル分のデータしか使われないということです。(正確にはブレンド情報その他もあるため、数百バイトくらいは使ってると思われますが)

次にCellにも実は2種類あって、64ピクセルを全て独立して持つタイプと、1ピクセル分のデータで64ピクセル全てを表現するという、Cellが同じ色で塗りつぶされている時に使えるタイプがあります。データサイズが1/64に圧縮されるわけです。この2種類のCellはいつでも交換可能で、Tileの16x16のCellにはどちらかが入っています。

 

さて、上のルールを守りながら、画面上にペンを走らせるとどうなるのか?ということですが、このようになります。 

f:id:CloverPaint:20130124225235p:plain

 

黄色はCellの存在するTileのうち、先ほど説明した1Pixelだけを含むCellです。面倒なので描いていませんが、実際には小さなCellの集合です。赤い四角はペンが通った部分で、8x8Pixel=64Pixelを持つCellに変化しています。青い部分はペンが通っていないので影響がなく、セルを作る必要がないのでそもそもTileすら存在しない部分です。このように、ペンが通った部分だけ完全なデータを持つようになっています。

 

現状のClover Paintで、各描画ツールやその他操作がどうなっているかというと、現状は時間がないため、かなり手を抜いた処理になっています。例えばアニメ風の絵をロードした時は、背景に透明部分等が広くあれば、それは存在しないTileや1ピクセルだけを含むCellとして表現してデータを節約できるはずですが、何もしません。

太い二値ペンでは最適化しようと思えばストローク後に調べて一部のCellを1ピクセルにまとめることも可能ですが、やっていません。

セーブ・ロード時にも特に何もしていません。

とりあえず将来的には対応したいのですが、目に見えない、特にこのままでも致命的な支障はない部分なので後回しになっています。

ただし、バケツ処理だけは最適化を行うので、塗潰しは太い筆よりもできるだけバケツを使うことを意識すれば多少はデータを節約できます。また、レイヤー全体の塗潰しも、内部データをしっかり消してくれます。 

そのうちこの辺はちゃんと書いてメモリを節約したいところですね。

 

2013/03/03追記

Clover Paint v1.16以降ではPSD画像とネイティブ画像フォーマットの読み込み時に完全な最適化を行うようになりました。