内蔵ファイラーからの PNG/Jpeg のプレビューを実装した。PNG の読み書きには GLDPNG を用いている。このクラスは TJpegImage と同等の仕組みで、Delphian World で手にいれた。インクルードするだけで TImage に読み込めるようになっている。現行では実に小さなウィンドウであるが、HTML を書くと言う環境だから割りとこんなモンでも問題なかろう。
さらに、Susie のブラグインにも対応して、実質的には読み込めるデータの種類には事欠かないぞ(笑) ただ流石にアレなんで GIF だけは読み込まないようにするけど(汗)
Susie プラグインで読み込むファイルはダブルクリックしても、そのままリンクを貼らない。GLDPNG のお力によって PNG フォーマットにコンバートされ、それにリンクを貼る事になる……。今はそれだけ。
最終的にはコンバートした元のファイルを自動で削除できたりするだろう。
ちなみに BMP には直接リンクを貼ってしまう(^^;) IE でも NN でも表示できるから良いのだ(笑)
Susie プラグインの制御はコンポーネントを自作した。中々てこずらされたが、この程度のことで既存のコンポーネントを使ってソースコードの配布に手間を増やしたくないからだ。とは言えソースコードは色々と参考にされてもらったが。特に、宣言部分の型なんかの扱いね。人によって解釈が違ってて困った(^^;)
*.SPI を検索して LoadLiblary する、所謂ところの動的インポートって奴を実装した。これで対象となる画像フォーマットは内部で制限されていない形式以外はなんでも OK だ。
内部での制限とは、拡張子が GIF になるものと(まだ未実装)、プラグインの形式が '00IN' でないものを読み込まないことだ。後者は良くわかってないが、アーカイブの中を覗くタイプのプラグイン類は無視するはずだ。要は単体で1枚の画像を保持するフォーマットに限定しているつもりなのだが、どうだか(^^;)
DLL の動的インポートだが、俺も今回が初めての実装なので色々と苦労した。LoadLiblary でモジュールのハンドルを取得するのは良いとして、そのあとのエクスポートされている関数の取得とか実行とかで。
基本的な流れとしては、
1.DLL モジュールのハンドルを取得する → LoadLiblary() API
2.使いたいメソッドのポインタを取得する → GetProcAddress() API
まずは1つめ。
対象のフォルダの *.SPI を検索してハンドルを取得し、条件に見合うものをリスト FAccounts に登録する。
procedure TSusiePluginControl.LoadPlugins; const faTarget = faReadOnly or faHidden or faSysFile or faArchive; var Found: TSearchRec; Rslt : Integer; SPI : THandle; Wrk : String; Fmt : String; I : Integer; begin (* 初期化 *) if (FAccounts.Count <> 0) then FreePlugins; I := 0; FMasks := NIL; (* SPI ファイルの検索ループ *) Rslt := FindFirst( Optimum.fnSusiePlugins+'*.SPI', faTarget, Found ); while (Rslt = 0) xor Application.Terminated do begin SPI := LoadLibrary( PChar(Optimum.fnSusiePlugins+Found.Name) ); if (SPI = 0) then raise EFileNotFound.CreateFmt( EMSG_FileNotFound, [ExtractFilePath(Found.Name)] ); if CompareApiVersion( SPI ) then begin (* SPI の API バージョンが '00IN' の物だけを読みこむ *) Wrk := GetPluginInfo( SPI, piGetExtentions ); Delete( Wrk, 1, 2 ); Fmt := GetPluginInfo( SPI, piGetFormatName ); SetLength( FMasks, I+1 ); FMasks[I] := Wrk; FAccounts.AddObject( Wrk+'='+Fmt, TObject(SPI) ); Inc( I ); end; Rslt := FindNext( Found ); end; FindClose( Found ); end; |
FMasks 変数は array of TFileName で宣言してあって、内蔵ファイラーが画像ファイルを表示するのに必要なマスクのリストを格納している。
FAccounts は TStringList だ。文字列には読み込む対象の拡張子、関連付けられる Objects にDLL のハンドルを保持しているので、新たに継承して特化したリストを作成しても良いかもしれない。
次に二つ目の、API のメソッドポインタを取得する部分。
Susie プラグインの GetPluginInfo API が引数によって返す値が違うので、そのようなコードを含んでややこしいかもしれないが、肝心なのは変数宣言で API をメソッド型を指定してしまうこと。プロパティでイベントを定義するのと考えは同じで、型宣言を行わずに直接指定しているだけである。
なお、外部にあるモジュールの関数なので stdcall を指定するのを忘れないように。
function TSusiePluginControl.GetPluginInfo( hSPI:THandle; iMode:TSusieGetPluginInfoCommand ): String; var GetPluginInfo: function( InfoNo:Integer; Buf:LPSTR; BufLen:Integer ):integer; stdcall; ApiCmd: Integer; Buff : PAnsiChar; begin @GetPluginInfo := GetProcAddress( hSPI, 'GetPluginInfo' ); if (@GetPluginInfo)=NIL then raise EFileNotFound.CreateFmt( EMSG_EntryNotFount, ['Susie プラグイン' ,'GetPluginInfo'] ); case iMode of piGetApiVersion: ApiCmd := 0; piGetAboutFile : ApiCmd := 1; piGetExtentions: ApiCmd := 2; piGetFormatName: ApiCmd := 3; else ApiCmd := 0; end; Buff := AllocMem( 100 ); GetPluginInfo( ApiCmd, Buff, 100 ); Result := Buff; FreeMem( Buff ); end; |
ついでなので Susie プラグインで画像を読み込ませるルーチンも載せておこう。解説は無しだ(笑)
function TSusiePluginControl.GetPicture( ImageName:TFileName; BMP:TBitmap ): Integer; var GetPicture : function( FileName:PChar; DataLength:LongInt; Flag:LongInt; pBmpInfo:PHandle; pBmpData:PHandle; lpPrgressCallback:Pointer; lData:LongInt ):integer; stdcall; hSPI: THandle; hBmpInfo: THandle; hBitmap : THandle; pBmpInfo: Pointer; pBitmap : Pointer; BmpInfo : ^TBitmapInfo; begin Result := -1; if Assigned(BMP) then begin try hSPI := GetPluginHandle( ImageName ); if (hSPI = 0) then raise EInvalidPicture.CreateFmt( EMSG_InvalidPicture, [ExtractFileExt(ImageName)] ); @GetPicture := GetProcAddress( hSPI, 'GetPicture' ); GetPicture( PChar(ImageName), 0, 0, @hBmpInfo, @hBitmap, NIL, 0 ); pBmpInfo := GlobalLock( hBmpInfo ); pBitmap := GlobalLock( hBitmap ); BmpInfo := pBmpInfo; BMP.Width := BmpInfo^.bmiHeader.biWidth; BMP.Height := BmpInfo^.bmiHeader.biHeight; SetDiBits( BMP.Canvas.Handle, BMP.Handle, 0, BmpInfo^.bmiHeader.biHeight, pBitmap, BmpInfo^, DIB_RGB_COLORS ); GlobalUnlock( hBmpInfo ); GlobalUnlock( hBitmap ); GlobalFree( hBmpInfo ); GlobalFree( hBitmap ); Result := 0; except Result := 1; end; end; end; |
GLDPNG はいいぞ! 将来、アニメーションに対応してくれるかは不明だが。
で、HotMilk での実装は、読み込みに関しては何も考えずに TImage 任せである。
function TfrmHM_ImageViewer.LoadFromFile( ImageName: TFileName ): Boolean; const BMP : TBitmap = NIL; Flag = SWP_NOACTIVATE or SWP_SHOWWINDOW; var Aspect: Integer; Base : Integer; X : Integer; Y : Integer; CX : Integer; CY : Integer; begin (* 転写用 TBitmap が存在しなければ作成する *) if not Assigned(BMP) then BMP := TBitmap.Create; (* ファイル名無しの時は各オブジェクトを破棄する(意味不明) *) if (ImageName = '') then begin FreeAndNil( BMP ); Result := False; Exit; end; (* イメージの読みこみ *) try Image.Picture.LoadFromFile( ImageName ); BMP.Assign( Image.Picture.Graphic ); except (* ここに来ると言う事は TImage で読み込めないってことだ… *) on EInvalidGraphic do begin (* ここで Susie を使い、各画像フォーマットを ビットマップとして読みこむ *) { DONE -c実装 : Susie プラグインで読みこみ、GLDPNG で保存する処理 } SusiePlugins.GetPicture( ImageName, BMP ); Image.Picture.Bitmap.Assign( BMP ); end; else raise; end; (* 適当なサイズでファイラーの右下(左下)に表示 *) if (BMP.Width < BMP.Height) then Base := BMP.Width else Base := BMP.Height; Aspect:= (96 * 100) div Base; Caption := ExtractFileName( ImageName ); Image.Hint := ImageName; pumIC_x1.Checked := True; CX:= (BMP.Width * Aspect) div 100; CY:= (BMP.Height* Aspect) div 100; X := frmFiler.Left + frmFiler.Width; Y := frmFiler.Top + frmFiler.Height - CY; if (X+CX > Screen.Width) then X := frmFiler.Left - CX; Result := SetWindowPos( Handle, HWND_NOTOPMOST, X, Y, CX, CY, Flag ); end; |
中段のセンテンスが読み込みの核であろう。TImage で読み込めないフォーマットは例外を生むので、それを拾ってプラグインに処理を移す。プラグイン側で発生してしまった例外は特に処理せずにおいた。特にハンドルする必要もないだろうと思って……
単に未対応で読み込めなかったときの処理がないが、それはそのうちに(笑)
最後のセンテンスでプレビューのサイズを決定している。縦か横のどちらか小さい方を 96 ピクセルで固定し、その比率をもう一方のサイズに適用する。表示位置はファイラーの隣にする。まぁ、実行して見ればわかるだろう。右クリックで色々とメニューを実装したし……
ソートなどの不具合が回収された内蔵ファイラー。
ファイラーで画像を選択すると、プレビューが表示される。
しかし、ウィンドウの優先順位の処理が上手くなく、必要なときに限って最善面に表示されてない(泪)
色々と細かい設定項目が増えた気がする、環境設定ダイアログ。
0.0.8 のアップロードは中止……(泪) なんか、起動時と終了時に例外が出てしまうのだ。なんでだろう?