TIFF形式:その2



 落ち着いてImageWriterクラスを読んでいたら、

canInsertImage(int imageIndex)
指定されたインデックスでの新規イメージの挿入を書き込み側がサポートする場合、true を返します。

というメソッドを見つけた。imageIndexという引数の存在が、マルチページファイルを扱える可能性を感じさせるではないか!
 という訳でImageReaderクラスを読んでみる。

read(int imageIndex)
imageIndex によってインデックスを付けられたイメージを読み込み、デフォルトの ImageReadParam を使用して、完全な BufferedImage として返します。

 あるじゃん!
 他にもimageIndexを引数にするメソッドがいっぱいある!
 さっそく試してみよう・・・と思ったらImageReaderクラスって抽象クラスなんですね。

このクラスは、Java Image I/O フレームワークのコンテキストで、イメージに読み取られるクラスによってサブクラス化します。

 相変わらずドヘタな日本語だなァ。「イメージの種類ごとにサブクラスを作らなければなりません。」って意味なのか?”イメージに読み取られるクラス”ってのが意味不明なのだが。

Documentation

The API documentation can be browsed from the following links:
Final releases:

を見てもTIFFを読むためのImageReaderのサブクラスが見当たらない。つまり自分で作れって事?いや〜それは無いよねぇ、ここまで作っておいて各規格に対しては自作しろなんて。


という訳できわどいが今日も怒らずに終わる。

TIFF形式:その1



 レポートやら何やらで忙しかった期間も終わり、一息つけるこの時期。何をするかと言えば、この前やってた画像表示プログラムの開発を再開しようか。



 ・・・と、思ったのですが、壁にぶち当たりました。
 原因はTIFF形式ですよ。
 先方が求めているスペックがハッキリしたのは良いんですが、なんと対象がマルチページTIFF形式のデータなんですね。TIFFなんてMacでしか見たこと無いですよ。・・・と思ったら、昔のPC世界ではかなり普及した画像形式だったらしく、それなりにバージョンが多岐・複雑化してますね。
 どうも、基本的に無圧縮な形式だったのが災いして、ナローバンド時代のWebでは嫌われていたらしいです。だからその時代に生まれた(というか成熟した)OSをそのまま引き継いでいる今のPC・Web世界ではほとんど絶滅してるんですね。WindowsXPでもマルチページ形式TIFFは閲覧できませんでした。
 で、調べてみると、実はJavaでもTIFF形式を扱えるようです。
 じゃまず、「tiff Java」でググります。

JAIを利用するとTIFFファイルでも表示されました

 ほう。しかしこの文脈からするとTIFFTIFFでもマルチページじゃないTIFFのようだな・・・。

 しかし色々な方法が存在するんだなぁ。
 で、インストール法と使い方は・・・?
「JAI ImageIO Tools インストール」でググると・・・

JAI 自体は 2 次元処理を行うためのライブラリだ。これを Image I/O から使うためには Java Advanced Imaging-Image I/O Tools を使用する。今のところ 1.1 は α なので、1.0_01 か 1.0 をダウンロードしよう。Tools には JAI が含まれているので JAI 本体を別途ダウンロードする必要はない。
* http://java.sun.com/products/java-media/jai/current.html

1.0_01 を選択した場合は 4 種類のインストーラーから選ぶことが出来る。
【以下略】

 なるほど。
 という訳で、複雑なことは後々の面倒の元なのでインストーラーさんに任せよう。はい、インストール!


 で、NetBeansでフレームクラスを新規作成し、GUI経由でLabelを貼り付け、そのプロパティのiconの項目から「外部イメージ」選択し、マルチページTIFFを選択。そしてこのクラスを実行すると・・・

うーむ、真っ黒だなぁ。

こんな風に表示して欲しかったんだが。(この画像はマルチページTIFFファイルを生成する(先方の持っている)プログラムから一枚目の画像を単独でbmp形式に変換・保存したものを持ってきた。)
 まあ、想定の範囲内だがな。だってそうだろう?マルチページなんだからその内の何ページ目を表示するのか指定する方法が必要なはずなのに

Java Media APIsのページの

Java Image I/O

The Java Image I/O API provides a pluggable architecture for working with images stored in files and accessed across the network. The JAI Image I/O Tools classes provide additional plugins for other stream types and for advanced formats such as JPEG-LS, JPEG2000, and TIFF.

から

Documentation

The API documentation can be browsed from the following links:

Final releases:

- 1.0_01
- 1.1

と辿ると、プラグインの全てのクラスを見ることが出来るのだが、
public class TIFFImageReadParamを見ると

  • default,
  • BaselineTIFFTagSet,
  • FaxTIFFTagSet,
  • EXIFParentTIFFTagSet,
  • GeoTIFFTagSet

には対応しているようだが、マルチページTIFFの記述が見当たらない。(対応している形式がどんなものかよく分からないのが気になるが。)
また、TIFF関連のクラスを見ても、マルチページファイルの内の何ページ目を表示するか指定するメソッドが見当たらない。或いは自動で、もしくは一ページ目くらいは表示してくれるかと思ったが、駄目だった・・・。
なお、Windowsのペイントで、jpgからTiffに変換した単独画像はちゃんとJavaで表示できた。(もっともこれはもともとwindowsでも表示できるのだが。)Tiff画像を表示するプラグインはちゃんと機能しているようだ。


う〜ん。と言うことは、マルチページTIFFのデータ構造を解析してTIFF用ブラウザを自前で作らないといけないのか?と思ったら、
クラス ImageWriter
public void prepareWriteSequence(IIOMetadata streamMetadata)
において

次に続く一連の writeToSequence 呼び出しを受け付けるために、提供されるストリームメタデータオブジェクトを使用して、ストリームを準備します。メタデータがイメージデータに先行する必要がある場合、このメタデータはストリームに書き込まれます。引数が null の場合、デフォルトストリームメタデータが使用されます。

出力が ImageOutputStream の場合、現在のシーク位置以前の既存の出力内容はフラッシュされ、読み込み可能または書き込み可能である必要はありません。その形式において、1 つの TIFF ファイル内の一連のイメージのように、ヘッダー情報を補修するために endWriteSequence が巻き戻しできることを要求される場合、このメソッドで書き込まれるメタデータは、ストリームの書き込み可能部分に存在する必要があります。他の形式は、このメソッドと各イメージのあとで、ストリームをフラッシュできます。

と、書かれているのを発見した。相変わらず何を言ってるのかよく分からないが、マルチページTIFFを扱う方法がありそうな雰囲気だ。次はこのあたりをキーワードに検索すれば解決するかも知れない、というところで、本日は終わる。

何も無いのにerrorがある



 これはJavaではなくC++を入力していたときのことであること、NetBeansではなくeclipseを使用していた時の事であることを先に断っておく。


 関数の末尾に新たに条件分岐を加えるべく、先に日本語混じり構文で概形を作っておく。ダブルスラッシュで保護しながら進めようとしたが、時々ダブルスラッシュ無しで打ってしまいeclipseの自動error検出が警告を表示する。そこまではいい。
 しかしコンパイルしてエラーが出た部分を丸ごと削除して、以前の状態に戻したのになぜかエラーは残ったまま。改行しようと、エラー行を移動させようと消えない。
 具体的には、


コンパイルエラーの無い状態から

if(true){
    return//←わざと";"無しとする
}

と書いて挿入。もちろんSyntax errorその他をeclipseは検出。
・この三行を削除してもなぜかソースエディタの左の列に赤丸にX字が出てエラー発生を表示する。
赤丸にX字に出るポップアップコメントは

expected ';' before '}' token

「知らない!俺は知らない!」と言っても聞いてくれない。
エラー行の上に出るポップアップコメントの内容は

Multiple markers at this line
−expect '!' before '}' token
−return - statement with a value infunction returning 'void'
−expected primary-expression before '}' token

である。これも全く身に覚えの無い完全な冤罪である(昔の罪は完全に清算したハズだから)。しかも振り払っても消えないと来た。何とかしてくれ!
おのれeclipse!貴様は
長野県警以上に見込み捜査が得意
なのかよぉぉぉぉぉ!!
このフリーソフトめがッ!!

その後の進展:粗筋



書き掛けです。


 jarファイル内部にアクセスする方法は実は必要なく(単に好奇心で調査してた)、jarの外部画像を読み込むことが目的です。必要なのは

  • フォルダを選択できる
  • 連番の名前の画像を縦横に表示
  • 画像をダブルクリックして別窓で表示
  • 別窓で画像に対して計算処理

 まずはフォルダ選択を実装する。
 メニューバーを設置。サブメニュー設置。ファイルダイアログ実装(サンプルコードを分解するのに手間取った)。フォルダとjpgのみ表示する。ファイル選択と同時にテーブルを再描画(コレが面倒)。フォルダだけでなくjpgを選択しても一連の動作を実行するように調整。表示する画像を縮小画像に変更。連番画像名をfor文で自動生成。

テーブルの下の領域は内部状態を知るためのTextAreaなので将来的には無くなります。

ワザあり!逆引き速引きリファレンスJava (逆引き速引きリファレンスシリーズ)

ワザあり!逆引き速引きリファレンスJava (逆引き速引きリファレンスシリーズ)

今回はこの本にかなりお世話になりました。
 次にダブルクリックで別窓を開く機能を実装。
 まず、ダブルクリックでエディットが出来るのを殺す。


以下、続く・・・。


書き掛けです。

JTableに画像を3





昨日の記事(NetBeansで実行すると画像が表示されるがjarダブルクリックでは表示できない)に対して一昨日と昨日のコメントで、Guernsey氏ときしだ氏が教えて下さったイディオム

new ImageIcon(getClass().getResource("img/brazil.gif"));

を試してみた。*1→jarファイルをダブルクリック・・・


 成功!





 いやーよかった。ご教授ありがとうございました。*2
 これで今夜も安心して配布できる!*3


しかし、いままでと違う反応が返ってきてちょっとビックリ。
わざと画像が存在しないパスを書いたらコンパイルは通すくせに、実行すると

Exception in thread "main" java.lang.NullPointerException
        at javax.swing.ImageIcon.<init>(ImageIcon.java:138)
        at tablegiftest.Main.<init>(Main.java:21)
                                   ↑配列宣言開始行
        at tablegiftest.Main.main(Main.java:41)
                                  ↑Mainクラスコンストラクト行

コマンドラインに返って動作せず。最初ダブルクリックで試した時はエラーメッセージすら無いから何度もダブルクリックしてしまった。三回も無駄なダブルクリックを強いられ精神的苦痛を味わった!謝罪と賠償を【以下略】。
これまでの様に空白を表示してくれればいいのに。しかもヌルポを出しているのはImageIconじゃないか!jarの外部の画像を読むときは、そこに画像が有るか無いか分からないから空白にしてでもとりあえず動作を保証してるのはとても良い。でもそこまで配慮するならjarにまとめる時に画像が存在しないことを検出してコンパイルエラーを出せよ。コンパイル通しておいて「画像が無いからヌルポ」だなんてバカすぎる。*4
 それが面倒ならこれまで通り空白を出力しておけ


 この半端仕事めがぁぁぁぁ!


 まったく、なにが”Write once Run Anywhere”だか。

別のコードを書く羽目になってしまいましたよ(笑)


もっとも、環境ごとに修正を入れなければならないのは現在のJavaでは珍しくないらしいですが。

*1:上記のリンク先では「getClass().getClassLoader().getResource("./example/test.png");だとjar内にパッケージした時うまくいかない。」とある。一昨日のきしだ氏の「./」への指摘はこの事と思われる。

*2:なお、間に「.getClassLoader()」を入れると動作しない。下記と全く同じヌルポエラーが発生する。しかし上記のリンク先サイトではこれを入れたソースも書いている。Javaのバージョンアップ時に仕様変更でもあったのだろうか?

*3:・・・ウソ。どうせ他にも色々落とし穴があるに決まっている。徹底的な動作テストをしない限りとてもじゃないが安心して配布など出来ない。

*4:しかもこのエラーメッセージだと、どの行のImageIconでエラー出てるのかすら判らねー(怒)!

今日は引用を多用してみました。読みづらいですか?

JTableに画像を2





昨日の記事(NetBeansでは画像が表示されないがコマンドラインで表示できた)で、きしだ氏のコメントと同じ事を、隣に座っている友人に指摘された。
いわく、「./を付けると、コマンドプロンプトで実行したらその時のカレントディレクトリが相対パスの起点になる」というのだ。実行されたファイルがどこにあるか、ではなく、どこでファイルを実行したか、によるのだと言う。


耳を疑ったね。


だってそうだろう?HTMLでそんなルール聞いたことある?


ネーよ!


HTMLはどこで誰が呼び出そうとも、相対パスの起点は呼び出されたHTMLファイルのあるディレクトリだ。例えばフレーム(画面分割)を指示するHTMLで別のHTMLを呼び出して、更にそのHTMLが画像やHTMLを呼び出しても、二つ目のHTMLに書かれた相対パスの起点となるのは二つ目のHTMLの有る場所になる。(CGIとかはまた別だが。)


そういう固定概念のせいでえらい遠回りをしたぜ。


しかしそういう大事なことはちゃんと書いてくれよ入門書!


と思って手元の入門書を片っ端からめくってみるが、絶対パス相対パスとが確かに書いてある本もあるがファイル同士の位置関係に対する書き方を解説してるだけで、コマンドプロンプトでの実行時ディレクトリへの注意とか、NetBeansではどうするんだ?とかの情報は書いてない。
まあ、そもそもNetBeansを扱ってる本が「創るJava」だけなので書いて無くてもしょうがないのかも知れないが。

絶対パス相対パスについて書いてないという信じられない本もある。「本格学習Java」キミの事だよ。目次にも索引にも無いぞ。まあ、コイツは画像を扱ってないし、NetBeans使ってないからいいのかもしれんが。
しかし「創るJava」にも書いてない。おいおいじゃあ昨日のような問題はNetBeansでどう処理させてるんだ?って思ったら画像ファイルは常に絶対パスでアドレスを指定してました。ぎゃふん。

eclipsではどうなってるんだろう?eclips使ってないし、本も持ってないからわからない。


さて、NetBeansから動かす時の相対パスの書き方はわかった。
なるほど、プロジェクトフォルダを起点にすればいいのね〜。*1
うむ、成功した!

なお、実験してみたところ、NetBeans上で実行したときは「./」が無くてもプロジェクトフォルダ直下から探し始めるようです。そしてjarファイルを一つ上のディレクトリから実行しても「./」無しの相対パスの画像は表示されず、imgフォルダを上の階層に引き上げると表示されました。


つまり、ここまでの実験において、「./」有る無しの差は認められませんでした。

もう面倒だからプロジェクトフォルダにimgフォルダを作ってそこに画像を置いておこう。これならソースを書き換える必要もないし。(HTMLでもそういうディレクトリ構成をしているサイトを良く見かける。HTML用のIDEも似たような仕様なのだろうか?それともそれ以前にこうする慣習がプログラミングの世界に出来上がっているのだろうか?そういう慣習をまとめた本って無いかなァ・・・。その内探しておこう。)


さて。


・・・問題はjar内部の画像にアクセスする方法だ。これがわからん。jarファイルを解凍すると
プロジェクト名\パッケージ名\img\japan.gif
てな感じでフォルダとファイルが出てくる。この相対パスでjapan.gifを表示しようとしてもダメ。jarファイルのあるフォルダに解凍したモノが置いてあると表示されちゃったりするがそれじゃあダメだろう。jarファイル内部のデータを使わなければ意味が無い。
パッケージ名\img\japan.gif
img\japan.gif
japan.gif
としてもダメ。
jarファイルをダブルクリックしてもコマンドプロンプトから起動しても画像が表示されない。ソースをいじってもプロジェクト内のimgフォルダの位置をいじってもダメ。
どうすりゃいいのッ!?
(という訳で→翌日の記事に続く)

*1:しかしこの機能、コマンドライン入力の時には当然だとしても、クラスファイル内で記述されているときは実行時ディレクトリじゃなくて読まれているクラスを起点にしてくれないかなぁ・・・HTMLの相対パスと同じに。

JTableに画像を1





 作ろうとしているゲームとは違う話なのだが、Javaでテープル(表)に画像を縦横に表示するアプリケーションを作る必要が出てきた。
 大量の画像かつ欠損がある可能性があるから、ただ並べただけでは画像の並び方に不具合が出る可能性があるのでやっぱりテープルが望ましい、と思う。
 HTMLの経験から、テーブルのセル(エクセルのセルと似た概念)に画像を表示するのは簡単だろうと思ったのだが、甘かった!




 まず、手元の初心者用入門書を読んでも方法が書いてない。
 googleで”java table 画像”で検索してみる。

イヌでもわかるJavaScript講座

四季でテーブルの背景画像(計4枚の画像)が変わるjavascriptとHTML内 ...
アクセス毎に画像表示を変えるJavaスクリプトの設置方法。ホームページ ...
雛形倶楽部情報データベース [データ詳細]
JavaScript:画像拡大表示ライブラリ「PopBox」、HTMLのテーブルより ...
TABLEの背景画像をCSSに変更したい。 -OKWave
Child Tree (HP Q&A BBS) [One Thread Res View / javascriptの ...
透けるテーブル table を作る-ホームページ作成の大技と小技
スクロールバーに画像
おしえてBP! 縮小画像をマウスオーバーで画像を切り替えるjava ...

 全部JavaScriptだよ!どうすりゃいいんだ!?




 やっと13件目で

[http://java-house.jp/ml/archive/j-h-b/045696.html:title=[JavaHouse-Brewers:45696] JTable における画像表示が出来ない]
getClass(); } } こんな感じでテーブルに画像を表示させようとしました。 ところが、 実際に画面を起動すると、Javaコンソール画面にjava.security.AccessControlException : access denied (java.io. ...

 が、やっとHIT。しかし意味がよく分からない。
・・・ってググり方が悪かったようだ。”java JTable 画像”で再度検索。(画像もってくるのはめんどくさいので省略)

#
java JTable 画像 のイメージ検索結果
- 画像を報告この中に不快/不適切な画像がある場合: 報告 キャンセルご報告いただきありがとうございます。
画像1画像2画像3画像4

文字列以外のデータをテーブルに表示する - JTableクラス - Swing
ImageIcon icon = new ImageIcon("画像ファイル名");. 国旗のアイコンは『世界の国旗アイコン』を使わせて頂きました。 まずはJTableのコンストラクタに渡すデータに、 画像も含めて試してみます。

 有望そうだ。画像の一つ目をクリック。おう!テーブルのセルに画像が貼り付けられている例がある!ソースファイルはどこだ!?
 ・・・無い。どういうサイトなんだここ?・・・英語だし、メンドクサイ上にソースファイル無いなら時間の無駄だから調査はしないで撤退。
 日本語の検索結果の一つ目を見てみる。・・・おお!これは先日発見したサイトJavadriveではないか!期待大!

{new ImageIcon("./img/japan.gif"), "日本", "3勝", "0敗", "1分"},

 ほほう、こうやってtableデータを定義するのか。簡単そうだな!と思ったらこれはマチガイの例らしい。

この理由なのですが、JTableは各セルを表示する際に、TableModelで定義されている"getColumnClass"メソッドを使って、その列のクラス名を調べるようにしているようで

本来は引数で指定した列に定義されているクラス名を返せばいいのですが、引数がなんであれ"Object"クラスを返すようになっています。

これを修正するにはDefaultTableModelクラスを継承し、"getColumnClass"メソッドをオーバーライドしたクラスを用意して利用するようにします。

 これってつまり不具合なんじゃね?getColumnClassって言うならちゃんとクラス名を返さなきゃだめだろ!?それが常にObjectを返すだなんてあきれてモノも言えねぇ。HTMLの簡単さを見習え!セル内部にというタグを入れるだけで表示出来るんだぜ!?HTMLより何年も後に出て改良が重ねられてるJavaで何でこんな手間を掛けなきゃならないんだよ!これじゃHTMLよりも低級言語だぞ!(あ、モノ言っちゃった。)


 まあそれはともかく、とにかくこれの対処法を丁寧にソース解説してくれてるので、さっきのメーリングリストの回答もすっきり理解できた。
 ちゃんと動くソース全体も記述されているので喜んで丸ごとNetBeansに新しくTestTableプロジェクト作ってコピペしてメインファイルにする。画像は同じディレクトリのimgフォルダにまとめておけばよさそうだ、というわけでソースファイルに直接コピペ。できたー!ラクチンラクチン!
 よーし、とりあえずNetBeans上でメインクラスを実行してみよう。



 ・・・なんでやねーん。


 あれ?コレと似たこと前にも無かったかなァ?
 ああ、アレだ、テクスチャー貼り付け不能事件だ。まさかアレとおんなじ結果にならないだろうな・・・。


 と思ったら全く同じ結果になった。クラスファイルの周辺にどんなにガンバって画像を配置してもNetBeans上で実行する限り画像が現れることは無く、jarファイルと同じフォルダに配置してしかもコマンドプロンプトから実行して初めて画像がアプリ上に現れる。

 今回は更に追加実験で、クラスファイルをコマンドプロンプトから実行してみた。
 こっちも成功。imgファイル内からgifファイルを移動させて実行すると失敗することから両方とも実行ファイルのあるフォルダにあるimgファイルを読むようだ。


 と、思ったらそうではないのだった!


(翌日の記事に続く)