印刷機能の概要

2007/01/29

実際にプリンタに出力する部分はおいておくものとする。とりあえずは、PostScriptを生成するところについて考えてみる。

まず、どういう機能が必要になるのだろうか。余り難しいことはやるつもりはない。だから、お手本として、Windowsのメモ帳を参考にしてみる。

ざっと機能を書き上げてみると、Windowsのメモ帳では、下記のような機能を提供しているようだ。
・上下左右の余白を指定する。
・紙の大きさを指定する。
・紙の向きを指定する。
・ヘッダとフッタを指定する。
・ヘッダとフッタを出力する位置を指定する。
・ヘッダとフッタには、通常のテキストの他にファイル名・ページ数・日付・時刻などを出力する事ができる。

また、PostScriptによる印刷の実行方式を考えると、フォントと文字の大きさを指定する機能も必要になるようだ。

(PostScriptで利用可能なフォントは、プリンタがプリンタ内に持っているフォントだけだ。だから、画面の描画に使用しているフォントを使って文字列を出力することはできないようだ。だから、印刷時用に別途フォントを指定する機能が必要になる。だが、あらかじめアプリケーション側で印刷後のイメージを生成しておいて、そのイメージを出力するPostScriptを生成してあげれば、任意のフォントを生成することもできるようだ。しかしそれは、俺にとっては余りにも負担の大きい手法だと思う。できればPoscScriptの持つ機能の範囲内で片付けたいものだ。)

では次に、実装の方法について考えてみる。

単純に考えれば、「主筆」本体にPostScriptを生成するロジックを組み込むことになる。しかし、そうすると「主筆」がますます肥大化することになる。だから、ここはプロセスを分割することを考える。ではどこで切断するか。

まず、PostScriptを生成する処理そのものは別プロセスの方に含まれるだろう。それと、できれば印刷時の設定を行うUI部分も切り離したい気もするが、それは難しいような気がする。つまり、モーダルとかモードレスとか、あるいは「主筆」が内部で保持しているデータをいつ別プロセスに渡すのか等と言ったことを考えると、切り離した別プロセスでUIを表示するのは利口とは言えないような気がする。

そういうことで、単純にPostScript生成処理だけを、独立したプロセスにすることにする。

では、そのプロセスのインターフェースはどうするのが良いか。用紙サイズや余白などの各種設定は引数で与えればいいだろう。印刷する文字列自体はどうするのがいいか。テキスト部分は量が増えることが予想されるから、引数というわけにもいかない。おそらく、別のファイルを読み込ませるか、あるいは標準入力から読み込ませることになるのだろう。

後は、エンコードの問題や、色を指定する機能に関する問題が残ってはいるが、当面はいいだろう。これだけ決まれば、何とか作ることができそうだ。

このところ、作業が一向にはかどっていないが、できるだけ時間を見つけて進めることにしよう。

印刷機能

2007/01/22

主筆」の第18版の機能として行番号を表示する機能を付けたり、あるいは第17版のバグの対策を行ったりしたところで、やる気が失せた。

行折り返しや、マネージャ機能・エンコードの自動認識など、実装しようと思ってはいるがやる気のでないものが沢山ある。だが、そういったものの中で、是非とも実装する必要があり、なおかつ今まで忘れ去られていたものが一つだけあった。

印刷機能だ。

今、俺が使用しているSolarisのマシンには、プリンタが接続されていない。何分、PostScriptに対応したプリンタは値段が高いのだ。だから、今まで印刷方面については、完全に放置していた。だが、今後はそうも行かないだろう。少しでも実用的なエディタを目指すのであれば、印刷する機能は必須であるといえる。

無論、Solarisで動作するエディタであるという必然から、印刷関係のデータは全てPostScriptで出力しなければならない。だから俺は、PostScriptの入門本のようなものを調達してきた。

それを読んで、とりあえずPostScriptの文法自体は理解したし、簡単な図形やテキストであれば出力できるようになった。だが、どうやって紙の大きさや紙の向きなどを指定すればいいのだろうか。その辺の具体的なロジックがいまいちよく分からない。

PostScriptの本によれば、PostScriptはデバイスには一切依存しないと書いてあるぐらいなのだから、おそらく紙のサイズや用紙の向きなどはSolarisとプリンタの間で決定されることなのだろう。だが、PostScriptを出力するアプリケーション側でも、ある程度は意識しなければならないはずだ。n-UPぐらいなやらデバイス側の処理でどうとでもなるのだろうが、横向きに印刷する場合や、不定型な紙に印刷する場合などは、デバイス側の小手先の処理ではどうにもならないはずだ。

そういったのは、どうやって処理したらいいのだろうか。

というよりも、実際に印刷できるプリンタがない状況で、アプリケーションに印刷機能を実装しようという時点で間違っているような気がする。

とりあえずは、プリンタなしでPostScriptを出力する機能を作り込んで、ある程度機能が実装できたら、中古でもいいのでプリンタを買ってみることにしよう。金はかかるが。

減色アルゴリズムの並列化

2007/01/21

減色するアルゴリズムのうち、各ピクセルにインデックス値を設定する処理を並列化することを考えてみる。

下記のようにピクセルがあるものとする。




ABC
BCD
CDE


誤差拡散法によって、各ピクセルに設定するインデックス値を決定する場合、上記の表におけるAのピクセルの値が決定しなければ、Bのピクセルを処理する事ができない。同様に、Bのピクセルの値が決定しなければCのピクセルの値は決定できない。

だが、逆に言えば、上記の表での同じアルファベットのピクセルは、並列に処理することができると言うことを意味している。つまり、Bのピクセルは右上にある奴を先に処理しても、左下にある奴を先に処理してもいいのだ。

だが、この知見をもとに、単純に並列処理を行うプログラムを実装したところで、処理速度は向上しない。なぜならば、並列化の粒度が小さすぎるからだ。1ピクセル単位の処理を並列化して、しかも必要に応じてロックや待ち合わせを行うようにしたら、シングルスレッドで処理したときよりも、大幅に処理速度が劣化することは目に見えている。

ではどうするのか。

粒度を大きくすればいいのだから、処理をブロック単位に分割することを考える。つまり、上記の表を、たとえば64×64のブロックとして考えるのだ。そして、一つのブロック内の処理は、シングルスレッドの場合と同じ方法で処理を行い、各部ロック間でのみ並列化を実施するようにする。そうすれば、同期や待ち合わせの負荷を相対的に小さくすることができるようになる。

だが、それにはいろいろといやな問題がつきまとう。一番難しいのは、ブロック間での誤差の伝播だ。一つのブロックを処理すると、そのブロックの右側と下側に、1列分の誤差が発生する。それを隣のブロックの左側と上側にピクセルを処理する際に、考慮に入れてあげなければならないのだ。

だがその難解さも、SMP構成のマシンで実行すれば、それに見合うだけの恩恵が得られる。ブロックの大きさにもよるのだろうが、うまくすれば処理時間を大幅に縮めることが可能となる。

俺はとりあえず、8192×4096・24bitカラーの画像を、16bitに減色する処理を、逐次と並列で実装してみた。そうしたら、逐次で約20秒程度かかっていた処理を、2CPU使って11秒程度に落とすことに成功した。

だがしかし、何が悲しいかって、8192×4096の画像は、ファイルサイズが96MBもあるため、それを読み込んで結果を出力するのに相当時間がかかると言うことだ。実は、減色処理自体よりも、そのファイル入出力にかかっている時間の方が長かったりもするので、並列化によるありがたみも半減してしまう。

ファイル入出力中のCPU利用率などを見ていると、結構CPUバウンドだったりもするから、ここの部分の並列化してあげれば、もっと早くなるかもしれない。

背景画像用に画像を変換する

2007/01/14

おもむろに、sigmarionIIIに背景画像を設定することを考えてみる。

こいつの画面の解像度は800×480、16bitカラーである。画像を800×480のサイズにすること自体は何の問題もない。何か適当なソフトで、画像のサイズを調節してやればいい。

問題は色の解像度である。単純にフルカラーの画像を背景に指定すると、色がなだらかに変化している部分に、かなり醜いしましまが発生することになる。まぁ、当然といえば当然だ。何分、16bitカラーなのだから。

通常、16bitカラーでは、赤に5bit、緑に6bit、青に5bitを割り当て、32段階・64段階・32段階の色の強さを表現するようにしている。緑が1bit多いのは、人間の目の特質によるものだそうだ。

そして、フルカラーの画像、例えば24bitカラーの画像が与えられた場合には、単純に下位のビットを無視することで画像表示を行うらしい。まぁ、余り金のかかっていないハードウェアで、高速に画像を表示しなければならないのだから、当然といえば当然の挙動だろう。

だがそのせいで、グラデーションのある画像の場合は、ありもしない縞模様が発生することになってしまう。そして、俺はこれが気に入らない。

ではどうすればいいのか。

そういう場合は、事前に画像の色数を削減しておいてあげればいいのだ。つまり、16bitカラーになることをあらかじめ判った上で、できる限り綺麗に見えるように変換しておいてあげればいいのだ。

ではどういう変換を行うのか。

それは、8bitカラーに削減するのと同じ処理をやってやればいい。ただ、唯一の違いは、任意のカラーテーブルを自前で生成するか、前もって与えられるかの違いだけである。つまり、16bitカラーでは、カラーテーブルは存在しないのだが、それを、65536色の固定的なカラーテーブルがあると考えればいいだけなのである。

そしてその上で、誤差拡散法で画像を変換してやればいい。そうすれば、16bitカラーに変換してもしましまの発生しない画像が得られる。

そういうことで、実際にやってみた。

本来であれば、ここで変換前の画像と変換後の画像を示して、ディスプレイの設定を24bitカラーと16bitカラーに変えることで、比較できるようにしてやるぐらいの事はしたかったのだが、しかし、ここの糞blogは、アップロードした画像を勝手にJPEGに変換するから、比較できなくなってしまう。だから、画像を示すのはやめる。

しかしいずれにせよ、ある程度は良好な結果が得られた。究極的には16bitカラーの制約を逃れることはできないが、それでも、極端な縞模様が発生することが無くなり、気分的には大分よくなった。

だが、シグマリオンIIIは、内部的な処理は16bitかも知れないが、ディスプレイの性能はそれ以下の様だ。古いから仕方がないといえばそれまでだが、しかし、変換後の画像をPCのディスプレイで見たときには目立たなくなっていた縞模様が、シグマリオンIIIに持ってくると結構目立つ。どうやら、もっと大胆に色数を削減するように変換してやる必要があるようだ。

また、真っ正面から見た場合、ディスプレイの真ん中と端の方とでは色が違って見えるから、それを考慮にいれた変換を行ってやっても良いかも知れない。

つまり、タコい液晶は、左右の方から見ると色が反転して見えてしまうのだが、シグマリオンIIIはその傾向がかなり酷い。使っているディスプレイが安物だからなのか、これが当時の最高だったのか、今となっては判らないが、いずれにせよ、酷い。

だから、その色の変化の仕方を定式化して、あらかじめ変化に備えて画像を変換しておいてあげれば、すこしは綺麗に見えるようになるだろうと考えられる。しかし、各ピクセル上における、画面と目線がなす角度の大きさは、顔の位置によって異なるから、この変換は一筋縄ではいかないと考えられる。

事前にできることと言えば、目玉がある位置をある程度推定した上で、目立たない程度に穏やかな変換を行ってやるぐらいだろう。

まぁ、これについても、今後時間があったら挑戦してみたいと思う。

バグの修正

2007/01/08

昨日気がついたバグを修正しておいた。

まず、カーソルがある行に表示する下線は、右端にちゃんと接するようにしておいた。また、拡張情報領域には引かないことにした。別に一番左側まできっちりと線を引いてもいいのだが、そうすると、拡張情報領域とテキスト描画領域を区切る線や、拡張情報領域に表示されたマークに、下線の跡が残ってしまうため、あえてやめておくことにした。

このバグの原因は、下線をどこからどこまで引くのか、という計算の間違いであった。線は、ウインドウの幅を取得して、一番左側(つまり0)から取得したウインドウ幅まで描画するようにしていた。しかし、取得したウインドウ幅というのが、すでに拡張情報領域の幅を差し引いた値であったため、結果として右側まで下線が届かなくなってしまった。

標準出力に引数が表示される問題は、その出力を行っていたデバッグ用のコードを消すことで対処した。

三つ目の致命的なバグだが、これは多少時間がかかったが、昨日のうちにつぶしておいた。

このバグの原因は、サーバ内で同一のファイルを開いているか否かを判断する処理に存在した。サーバ内で同じファイルが開かれているか否かを判断する処理は、単純にファイル名を比較する事で行っている。つまり、すでに開かれているファイルのファイル名を保持しているリストと、今度開こうとしているファイルのファイル名とを比較して、同じものが無いかどうかを比較しているだけなのだ。そのため、同一のファイルを指すファイル名であったとしても、ファイル名中に「.」や「..」が含まれていると、別のファイルとして判断してしまっていた。

しかも、たちの悪いことに、主筆内でファイルを開いたときには、ファイル名中の「.」や「..」を取り除く処理を行った後のファイル名を保持するようにしている。そして、その整形後のファイル名を主筆サーバに通知するようにしている。そのため、ファイル名中に「.」や「..」が含まれているファイルは、何度でも重複して開くことができてしまうという最悪の事態が発生していた。

そういうことで、主筆本体や主筆クライアントが、主筆サーバにファイル名を通知する際には、確実にファイル名の整形処理を行うようにしておいた。

単純に考えれば、ファイル名の整形処理は主筆サーバ側で行うべきだとおもうのだが、しかしこれはあえて行わなかった。なぜかというと、ファイル名の整形処理には、相対パスを絶対パスに変換するという処理も含まれているためだ。主筆クライアントや主筆本体のカレントディレクトリは、主筆サーバとは異なる可能性があるため、相対パスの解決は主筆本体や主筆クライアントではければできないのだ。そのため、結局呼び出し側で整形処理を行わなければならないのだ。

また、それに関連して、もっと根本的な対応策も導入しておいた。

そもそも、UNIXでは異なるファイル名で同一のファイルを参照することが可能なのである。「.」や「..」は単純なファイル名の操作で正規化を行うことにより、対応することができる。だが、ハードリンクやシンボリックリンクにより、同一のファイルに対して複数の名前が付けられた場合には、ファイル名の操作だけでは対処する事ができない。

そのため、直接的にファイルのI-Node番号とデバイスIDで比較を行う要に変更しておいた。これにより、ファイル名に依存しないでファイルの同一性を判断する事ができるようになった。

ただそうすると、ユーザが本当は同じファイルを参照している二つのファイルを、異なるファイルだと思って開こうとしたとき、主筆サーバのチェックに引っかかって開けなくなると言う問題が発生する。これは単純にユーザの問題ではあるのだが、しかし、だからといって何のアナウンスもなく開くことができないというのは不親切すぎるような気もする。

ここは、やはり何らかのメッセージを表示しておくか、あるいは新しいファイル名のファイルとして再度開き直すか否かユーザに問い合わせた方がいいのだろうか。

処理の実装は面倒だが、その方がいいだろう。

行番号

2007/01/07

前回、「主筆」の起動が遅いのはリソースを読み込んでいるからではないかと書いたが、実際はそうではなかったようだ。リソースの読み込み処理をスキップして起動するようにしてみたが、結局、起動時間はほとんど変わらなかった。

では、何が悪いのか。いろいろと調べてみたが、結局分からなかった。どうも全体的に処理が多いのが問題なようだ。これではどうしようもない。あきらめる他ないようだ。

だから仕方がない。起動処理の高速化はやめて、行番号を表示する機能を実装しておいた。行番号表示は前々から実装しようと思ってはいたが、ずっと後延ばしにしてきていた機能の一つだ。それを今回は思い切ってやってみた。しかし、実際やってみると案外あっさりと実装できてしまった。

現在まで行番号表示機能を実現してこなかったのは、それが表示処理周辺をいじることになるからだ。「主筆」では、テキストの描画はCTaEditDrawクラスで行っている。まぁ、それだけではないのだが、実際にXに対してどこに何という文字を描画しろ、と言っているのはこのクラスだ。

だが、このCTaEditDrawクラスはかなり肥大化してきている。やはり、実際にXlibで描画するとなると、どうしても「余分な詳細」が多くて、見通しが悪くなりがちなのだ。だから、俺的にはこれ以上いじりたくないと思っている。そのため、今までずっと放置してきたのだ。

ならば機能を分割すればいいだろうと思うかもしれないが、それもまた面倒だ。結局、いつかは手を着けることになるのだろうが、しかし、なかなか踏ん切りがつかない。まぁ、動いているものには手を出すなという原則もあるしなぁ。

それはいいとして、行番号表示を実装するときに、いくつかバグがあることに気がついてしまった。

まず一つ目は、カーソルがある行に引く下線だ。左側の拡張情報領域(ブックマークを設定すると赤い印が表示される領域のこと)を大きくすると、その分だけ下線が短くなる。拡張情報領域の部分に線が引かれないのならまだ分かる。だが、たちの悪いことに、右側が短くなるのだ。つまり、イメージとしては下線が左寄せで表示されているような状態なのだ。これでは言い訳のしようがない。

また二つ目は、「主筆」の起動時に引数を指定すると、与えた引数の一覧が標準出力に表示されることだ。これは、以前つけたデバッグ用のコードを消し忘れていたものだ。

三つ目は、メニューの「ツール」ー「同じ名前のファイル開く」のバグだ。これは、たとえば「a.c」というファイルを開いてきたときに、「a.h」というファイルを自動的に開くという機能なのだが、この機能を使ってファイルを開くと、ファイルの排他制御が効かなくなるという問題がある。つまり、「a.c」と「a.h」のファイルを両方とも開いている状態で、「同じ名前のファイル開く」の機能を使って「a.h」ファイルを開こうとすると、「a.h」を重複して開くことができてしまうのだ。

これは極めて都合の悪いバグだ。第18版では確実に対処しておかなければならない。

それにしても、なかなか安定しないなぁ。なんだかんだ偉そうなことを言っても、結局は常にテスト不足何だよな。これも、どこかでちゃんとしておかなければならないのは分かってはいるのだが。

起動の高速化

2007/01/05

主筆」第17版を公開してから数日の間放置していたが、昨日また少しいじり始めた。

最近、主筆の起動が遅くなってきたような気がするから、その辺りの高速化を図ろうかと思って、設定情報を読み込む処理をいじってみることにした。そして、とりあえずは、色名からPixel値を取得する処理に手を入れることにした。

Xで描画するには、色名からPixel型の値を取得しなければならない。だが、当然だが、この処理自体は描画する直前までに終わっていれば、それで問題ない。起動直後にやらなければならない必要性はどこにもない。だが、今までは設定情報を読み込む処理のところで、文字列の色名からPixel値に変換するところまでを片付けていたので、それをギリギリ必要になるところまで先延ばしするように変更した。

だがしかし、主筆で使用する色自体それ程たくさんあるわけでもないし、色名からPixel値に変換する処理がそれ程重いもではないため、結局の所、俺の努力はほとんど報われることはなかった。

今のところはまだ、正確に時間を計測したわけではないのだが、起動時における処理で処理時間を要するのは、おそらく主筆サーバの起動と設定情報すなわちリソースの読み込みではないかと考えている。

主筆サーバの起動は、ユーザーがログインした後一度だけ行えばいいことなので、ここではおいておくものとして、とりあえず対処しなければならないのはリソースの読み込み処理ではないだろうか。

しかし、この処理に要する時間の大部分はMotifないしはXt内の処理時間であって、俺がどうこうできるものとも思われない。つまり、対処するとしたら、設定情報を取得する元を、リソースから独自の設定情報ファイルに変更しなければならないと言うことだろう。

しかしそうすると、設定情報の記述形式を変更しなければならないと言うことになる。また、Xのリソースにはデータ型を変換してくれるという機能があるのだが、それが利用できなくなってしまうので、その処理を自前で作り込まなくてはならなくなる。そもそも、自前で作った読み込み処理が必ずしも高速であるとは限らない。よほど機能を落として、簡単な処理にするのであれば可能性はなくもないのだろうが。

ではどうするのが良いか。

変更を最小限に抑えつつ、リソースの読み込み処理を省略するには、一度読み込んだりソースをどこかに蓄積しておいて、二回目以降はそこから読み込むようにするという方法が考えられる。

蓄積する場所としては、どこかのテンポラリファイルでも良いし、あるいは主筆サーバに管理させても良いのだろうか。

だが、この方法にも問題がないわけではない。それはリソースが変更されたときの処理だ。参照先のリソースファイルが変更された場合には、環境変数の変化という形でそれと知ることができる。しかし、リソースファイルの内容が書き換えられた場合にはどうしたらいいのか。あるいは、起動時の引数としてリソースの値を指定されたらどうするのか。

リソースが更新されたら、コマンドなどでユーザが明示的にそれを通知するようにする、また、引数にリソースが指定されていたら、そのときは全ての設定情報をリソースファイルから取得する、という風にするという方式も考えられる。しかし、それを作りこむのが面倒そうだ。それに、ユーザの利便性を損なうのは避けがたいようだ。

どうするのが良いのだろうか。