ページ 11

一般的な画像フォーマットの縦横サイズの取得方法は?

Posted: 2006年2月26日(日) 15:15
by 淡幻星
いつもお世話になっております。

一般的な画像フォーマットの縦横サイズを取得するにはどうしたらよいのでしょうか?
DLLを用いて取得してみようとしましたが、
原因不明のエラーが出てしまい困っています。


≪やりたいこと≫
・BMPのみならず、jpeg、Gif、png画像の縦横サイズを得たい。
(BMPはLoadImageで読み込める故)

≪アプローチ方法≫
LoadImageのような使い方でBMP、jpeg、Gif、pngを読み込んで
そのビットマップハンドルを返す[loadimgp.dll]というDLLを使い、
その後にGetObject( hBmp, Len(lpBITMAP), lpBITMAP )を用いて、
画像の縦横サイズを得ようとした。
※loadimgp.dllの場所
http://www.vector.co.jp/soft/win95/prog/se375198.html

≪発生した問題≫
DLLの呼び出し自体は成功するが、それを行った関数を抜けようとすると
アクセス違反と言われる。
グローバルから数えて、2階層以上の関数でエラーが起こる。
(グローバル、1階層めの関数ではエラーしない・・・と言って伝わるでしょうか?^^;) 開発環境はWin2000 + AB-Ver.3.13です。
Ver4.10.02で試したところ、アクセス違反は起きなくなりましたが、
コードの動作がおかしくなりました。
(関数の途中で勝手に抜けたり、値が代入されなかったり。)
そちらの方は、どういうエラーが起きているのか理解できていません。


≪聞きたいこと≫
上記の方法でなくても、Gif、Jpeg、pngの縦横サイズが
読み取れるのであれば構いません。
直にフォーマットのヘッダ?を読み取ることも考えて
Googleで調べたのですが、仕様は見つけられませんでした・・・。


宜しくご教授お願い致します。

Posted: 2006年2月26日(日) 21:24
by イグトランス
それをダウンロードしてサンプルを見てみましたが,CDECL関数のようです。

こうしたらどうでしょうか。
Declare Function LoadImagePlus CDECL Lib "loadimgp.dll" (pImgFilePath As BytePtr) As DWord

Posted: 2006年2月28日(火) 01:47
by 淡幻星
イグトランス様、素早い回答をありがとうございます。
たしかに「CDECL」を指定したところ、アクセス違反は起きなくなりました。
しかし、GetImgSquareSize()内に記述を追加して、
別途ソースにて呼び出したところ、
hBmp = LoadImagePlus( pBuf )
のラインのところから突如として関数を抜ける、というような
ことが起こってしまいました(Debugはその瞬間に無効にされました)。

何が起きているのか今ひとつ分かりません。
ちょっと今夜から数日、バグ解析の時間が取れませんので、
また後日に何が起きているか分かったところで、また質問させていただこうと思います。
その際は、またよろしくお願いいたします。

Posted: 2006年3月01日(水) 22:46
by ノッチ
> イグトランス様、素早い回答をありがとうございます。
> たしかに「CDECL」を指定したところ、アクセス違反は起きなくなりました。
> しかし、GetImgSquareSize()内に記述を追加して、
> 別途ソースにて呼び出したところ、
> hBmp = LoadImagePlus( pBuf )
> のラインのところから突如として関数を抜ける、というような
> ことが起こってしまいました(Debugはその瞬間に無効にされました)。

試しでサンプルを作ってみました。
AB Ver.4.21.00で試したらうまく動作しました。
うまく動かないようであれば以下のソースを単独で実行してみて下さい。
(空のabpファイルにソースをコピペしてF2)

Posted: 2006年3月02日(木) 20:19
by hira
以前に私も似たようなのを作っていたことを思い出しまして。
http://dbp.cool.ne.jp/patio/patio.cgi?mode=view&no=248
相違点は、GDI+を使わないということです。
ということで、PNGは使えないという問題がありますが、とりあえずコードを提示しておきます。
ビットマップハンドルが必要な場合は、lpPicture->get_Handleを使って下さい。
※PNGの場合はサイズだけならバイナリを直接読み込めば取れますが…邪道ですかね?(^^; ハンドルは取れませんが(爆

Posted: 2006年3月06日(月) 12:39
by 淡幻星
>ノッチ様
ありがとうございます。
もうしばらくの間、プログラミングをする時間が取れませんが、
後で試してみます。


>hira様
そのコードは知ってましたが、COMインターフェイスを使っていらっしゃるようなので
手を出さないでいました(今のところ主力開発はVer.3.xですし^^;)。
そうですね。この機会に手を出してみようと思います。
GDI+を使わなくて済むのであれば、それに越したことはありません。
上記の理由で、テスト実行はしばらく後になってしまいますが、
もしよろしければ、PNG縦横サイズのバイナリからの直接読み取り方法も
ご教授いただけないでしょうか?
バイナリから直読み込みはむしろ望むところなのですが、
フォーマットが分からないのです。
(一つずつ読み出して調べれば良いじゃないか、と言われるとその通りなのですが/苦笑)

Posted: 2006年3月06日(月) 22:45
by hira
いちいちPNGのヘッダを全部読もうとすると面倒なので、画像サイズの部分だけ読むようにしてみました。

コード: 全て選択

#prompt
Const BigEndianToLittleEndian(Num)=(((Num) And &HFF)<<24 Or ((Num) And &HFF00)<<8 Or ((Num) And &HFF0000)>>8 Or ((Num) And &HFF000000)>>24)

Dim hFile As HANDLE,dwWidth As DWord,dwHeight As DWord,dwReadSize As DWord
hFile=CreateFile("test.png",GENERIC_READ,FILE_SHARE_READ,ByVal NULL,OPEN_EXISTING,0,0)
SetFilePointer(hFile,16,NULL,FILE_BEGIN)
ReadFile(hFile,VarPtr(dwWidth),4,VarPtr(dwReadSize),ByVal NULL)
ReadFile(hFile,VarPtr(dwHeight),4,VarPtr(dwReadSize),ByVal NULL)
CloseHandle(hFile)
dwWidth=BigEndianToLittleEndian(dwWidth)
dwHeight=BigEndianToLittleEndian(dwHeight)
Print dwWidth;"x";dwHeight
ちなみに、(拡張子だけではなく)ファイルの内容からPNGかどうかを判断するには、最初の8バイトが
89 50 4E 47 0D 0A 1A 0A
であるかをチェックすればいいでしょう。

Posted: 2006年3月19日(日) 14:37
by 淡幻星
レスが大変遅れてしまい申し訳ありません。
やっとデバグする時間が取れました。


≫ノッチ様
動作確認ありがとうございます。
当方でも、1階層目の関数呼び出しでは正常動作しました。
しかし、関数から関数を呼び出し・・・というように2階層以上の
関数呼び出しにおいて、エラーにこそならないものの、
値が代入されなかったり、関数の途中で勝手に抜けたりと、
やはりコードの動作がおかしいようです。
(Ver.3.13, Ver.4.10.02, Ver.4.23.00 にて確認。)

もっとも、DLLを使わずに済むのであればそちらのほうが都合よいので、
hira様のコードで対処しようかと思っています。



≫hira様
PNGヘッダからの画像サイズの読み取りコードも示していただき、
ありがとうございます。
それを踏まえて、画像(Bmp,Gif,Jpeg,Png)の縦横サイズ読み取り&
ビットマップハンドル(Pngを除く)取得をクラスにまとめてみたのですが、
get_Handle() が失敗するようです(返り値がFALSE)。
IPictureインターフェースに関してはさっぱり分からないので、
お手上げ状態なのですが、こちらエラーの原因などわかりますでしょうか?
上記クラスの動作テストが下記

コード: 全て選択

#N88BASIC


Dim lp As ReadPics

Dim s As String
Dim hPic As HBITMAP

s = "test.jpg"

lp.CreatePic( StrPtr(s) )

Print lp.GetHeight()
Print lp.GetWidth()
hPic = lp.GetBmpHandle()
Debug '←hPicが「利用できない」になっている。

Input s
End
Ver.4.23.00にて確認。
よろしくお願いいたします。

Posted: 2006年3月19日(日) 16:06
by hira
IsPNG関数を呼び出しているために、ファイルポインタが8バイト分進んでしまっているようです。
IsPNG関数を呼び出した後、ReadFile関数を呼び出すまでのどこかに

コード: 全て選択

SetFilePointer( hFile, 0, NULL, FILE_BEGIN )
の1行を加えればOKです。
※IsPNG関数の最後にこの処理を追加してもいいでしょう

Posted: 2006年3月19日(日) 17:25
by 淡幻星
> ファイルポインタが8バイト分進んでしまっているようです。
うっかりしてました。 get_Handle() が失敗する問題は、IsPng()を付け加える前から発生しており、
修正版に変更してみましたが、はやりハンドル取得に失敗するようです。
なお、get_Width(), get_Height() は成功しています。

検索してみたところ、get_Handle()で得たハンドルをBITMAP型にキャストする
流れは合っているようなのですが・・・
それとも、IPictureで得た画像はビットマップハンドルを通さずに、
Render()を用いて描画する必要があるのでしょうか?

Posted: 2006年3月19日(日) 18:43
by hira
get_Handle()の戻り値は、成功するとS_OK(0)です。
hOleにハンドルが取れていないなら話は別ですが、ハンドルが取れていて、戻り値が0であるなら正常な動作と考えていいでしょう。

Posted: 2006年3月19日(日) 20:31
by 淡幻星
> get_Handle()の戻り値は、成功するとS_OK(0)です。
そうだったのですか。
FALSE(0)と早とちりしてました。
なお、描画失敗の原因は、描画の前にRelease()メンバを実行してしてしまっていたことでした。
ハンドルは取れていました(=アドレスがNULLじゃないということですよね?)が、
Debugで「->unused 利用できません」と出たのを見て、FALSE(0)とあわせて
「get_Handle()が失敗していることが原因」と思い込んでしました。


解決しました。
hira様、素早い返信をありがとうございました。



以下、まとめ。