DLLでJpegからBmpに変換し、引渡しパラメータをBmpピクセル列とイメージサイズ
にしたいと考えております。
jpegとBmpに変換まではできますが、変換したBmpのピクセル列とイメージサイズの
引渡し方法が思うようにいきません。
どなたかご教示願います。
DLLがJPEGを変換してBMPを返すんですよね?
Susieというビューアのプラグインが、そういう作りをしています。
インターフェースの参考にしてみてはどうでしょう。
http://www.digitalpad.co.jp/~takechin/
質問の前に、インタフェースを参考しましたがうまくいきませんでした。
変換したBmpのWidth、Heightまでは簡単に抽出できますが
問題はイメージのピクセル列のバッファの確保の仕方と画像のサイズの取得方法をが取得できません。たびたび恐縮ですがご教授の程お願いいたします。
バッファは、VirtualAllocなどで確保します。
# 使い終わったらVirtualFreeで解放します
画像サイズは、
((幅+パディング)*高さ)*色深度/8
でいいんじゃないですか?
画像サイズがわからないと、BMPに変換できないと思いますが。
DLLの中身を以下に示します。
library test1;
uses
Windows,Classes,Graphics,jpeg;
function load(fn:PChar):PInteger;export;stdcall;
var
jpg:TJpegImage;
bmp:TBitmap;
wbmp:TBitmap;
sn:PChar;
p:PInteger;
i:Integer;
begin
jpg:=TJpegImage.Create;
bmp:=TBitmap.Create;
jpg.LoadFromFile(fn);
bmp.Assign(jpg);
//bmp.SaveToFile('C:\jpg2bmp.bmp');
wbmp:=TBitmap.Create; // 作業用ビットマップ作成
wbmp.Width:=bmp.Width;
wbmp.Height:=bmp.Height;
wbmp.Canvas.Draw(0,0,bmp);
//16bitのBMP画像を表示させたい。
p:=PInteger(GlobalAlloc(GPTR,wbmp.width*bmp.height*2));
for i:=0 to wbmp.height-1 do
begin
CopyMemory(p,wbmp.scanLine[(bmp.height-1)-i],bmp.width*2);
inc(p,wbmp.width);
end;
jpg.free;
bmp.free;
load:=p;
end;
exports
load name 'load';
begin
end.
こんな感じで作りました。問題は引渡しパラメータ(上記でいうp)がピクセル列
と幅と高さで引き渡すためにはどうすればよいでしょうか?
BMPでなく、RGBベタデータのみわたしているんですね。
引き渡しパラメータを1つでなく複数にすればできますよ。
function load(fn:PChar):PInteger;export;stdcall;
を、
function load(fn:PChar; var w,h: integer):PInteger;export;stdcall;
として、w,hに対してwbmp.Width, wbmp.Heightを渡してやればよろしいかと。
引数1つでやりたいのであれば、BMPの構造そのものをコピーした方が簡単です。
# 受け取り側でもBMPとして読み込めばOK
>引数1つでやりたいのであれば、BMPの構造そのものをコピーした方が簡単です。
># 受け取り側でもBMPとして読み込めばOK
どのように渡してあげればよいのですか?
本当は TBitmap のインスタンスを DLL と EXE の間でやりとりできれば
いいんですけど、それができませんね。しかし、TBitmap には
SaveToStream と LoadFromStream メソッドがありますので、TMemoryStream
のインスタンスを仲立ちにして、メモリーイメージそのものをやりとり
できます。DLL にするのは面倒なので、ユニットファイルでテストしました。
unit Unit2;
interface
uses
SysUtils, Classes, Graphics, Jpeg;
function JpgToBmp(FileName:PChar;var pImage:pointer):integer;
implementation
var
ms:TMemoryStream;
function JpgToBmp(FileName:PChar;var pImage:pointer):integer;
var
jpg:TJpegImage;
bmp:TBitmap;
begin
jpg := TJpegImage.Create;
bmp := TBitmap.Create;
try
jpg.LoadFromFile(FileName);
bmp.Assign(jpg);
ms.Position := 0;
bmp.SaveToStream(ms);
result := ms.Size;
pImage := ms.Memory;
finally
jpg.Free;
bmp.Free;
end;
end;
initialization
ms := TMemoryStream.Create;
finalization
ms.Free;
end.
これを DLL にみたてて、呼び出すのは以下のようにします。
uses
Unit2;
procedure TForm1.Button1Click(Sender: TObject);
var
Size:integer;
bmp:TBitmap;
mst:TMemoryStream;
pImage:pointer;
begin
bmp := TBitmap.Create;
mst := TMemoryStream.Create;
try
Size := JpgToBmp('C:\Test.jpg',pImage);
mst.SetSize(Size);
mst.Position := 0;
mst.Write(pImage^,Size);
mst.Position := 0;
bmp.LoadFromStream(mst);
Canvas.Draw(10,10,bmp);
finally
bmp.Free;
mst.Free;
end;
end;
うまくいくようです。
ありがとうございます。
上記のように作成したDLLをVCで呼び出すためにはどのようにすればよいでしょうか?受け取り側の作成で失敗します。
jpegファイルのパスをDLLに入れて、得られた幅・高さをDWORDで取り出すまでは、
分かるのですがbmpのイメージそのものをLPBYTEが何かで取ろうとするとエラーが
出てしまいます。何度も恐縮ですがご教示願います。
> 上記のように作成したDLLをVCで呼び出すためにはどのようにすればよいでしょうか?
残念ですが、それはできません。受け取る方も Delphi の TBitmap が使えることが
前提だからです。汎用的にするには、やはりにしのさんの提案通り Susie の
プラグインのまねをするのがいいと思います。と、いうかそのまま使えると
思いますけど。
ありがとうございます。
DLLで変換したBMPを一旦ファイルに落として、
VCで読み込ませれば問題ありませんか?
SaveToStreamした結果は、Bitmapのファイル構造そのままをメモリに配置しただけですので、VCでも使えるかもしれません。
ここに示すソースは、VCLであることを期待していません。
適切なメモリを渡せば、memにBitmapが配置されます。
nilで呼ぶとサイズを返すので、そのサイズ分確保してから再度呼べば取り込めます。
二度呼ぶので少し遅いのがネックですが。
function load(fn:PCHAR; mem: PCHAR): Integer;export;stdcall;
var
jpg:TJpegImage;
bmp:TBitmap;
ms: TMemoryStream;
begin
jpg:=TJpegImage.Create;
bmp:=TBitmap.Create;
ms := TMemoryStream.Create;
Result := 0;
try
jpg.LoadFromFile(fn);
bmp.Assign(jpg);
bmp.SaveToStream(ms);
ms.Seek(0, soFromBeginning);
if Assigned(mem) then
begin
// nilならコピーしない
CopyMemory(mem, ms.Memory, ms.Size);
end;
Result := ms.Size;
finally
ms.free;
jpg.free;
bmp.free;
end;
end;
呼び出すときはこんな感じ。
procedure TForm1.Button1Click(Sender: TObject);
var
buf: PCHAR;
size: Integer;
bmp: TBitmap;
ms: TMemoryStream;
begin
size := load(PCHAR(Edit1.Text), nil);
if size > 0 then
begin
buf := SysGetMem(size);
load(PCHAR(Edit1.Text), buf);
bmp := TBitmap.Create;
ms := TMemoryStream.Create;
ms.Write(buf^, size);
ms.Seek(0, soFromBeginning);
bmp.LoadFromStream(ms);
Image1.Picture.Assign(bmp);
ms.Free;
SysFreeMem(buf)
end;
end;
メモリの配置からバッファに溜め込んで引き渡すことは可能ですか?
引渡しパラメータをPIntegerで設定して
p:=PInteger(GlobalAlloc(GPTR,bmp.width*bmp.height*2));
のようなバッファを確保してメモリで配置したファイル構造とサイズをバッファに格納することはできますか?
もう少しわかりやすくかかれると、回答しやすいです^^;
上に書いた関数は試しましたか?
引数にあるPCHAR型のmemに、Bitmapのファイル構造を入れていますが、これ以外に何か必要ですか?
ファイルサイズも画像の大きさも返しているのですが。
# 画像の大きさは、Bitmapを解析すれば取得できますよね
下手な文面ですみませんでした。
上に書いた関数からVCLで受けようとする際、メモリのGet方法で詰まってしまいました。
Win32で上記の関数を受けようとする場合、どうしたらよいのでしょうか?
int size = load(filename, null);
if( size != 0){
char* buf := (char*)malloc(size); //or new char[size]
load(filename, buf);
//....
free(buf); //or delete[] buf
}
お手数おかけしました。DelphiのDLL(load)をVCLから受けようとした場合、
hDll=LoadLibrary("testlibrary");から
GetProcAddress(hDll,"load");までは分かりますが
実引数を2つ持つ場合はどのようにすれば良いのでしょうか?
度々すみません。
上記のソースを利用していますが、
int size = load(filename, null);
の部分でハンドルエラーが出ます。
nullの部分が怪しいのですが、回避できません。
ご教示お願いいたします。
Delphiでは
TFunc=function(fn:PCHAR;mem:PCHAR):Integer;stdcall;
こんな感じです。
C++で動かないのは、宣言がわからないと何ともいえません。
__stdcall(もしくはWINAPI)の宣言をしていますか?
extern "C"していますか?これがないと、C++の名前空間の影響を受けます。
TFuncについて、ちょっと言葉足らずでした。
変数の宣言では、
var
load: function(fn:PCHAR;mem:PCHAR):Integer;stdcall;
です。
タイプ宣言する場合は、
Type
TFunc=function(fn:PCHAR;mem:PCHAR):Integer;stdcall;
です。
こうしておくと、
var
load: TFunc;
とかけます。
何度もすみません。
こんな感じで作ってみました。
#include<windows.h>
#include <stdio.h>
#include <malloc.h>
#include <string.h>
// 関数のプロトタイプをtypedefを使って定義します。
typedef int (*pload)(LPCSTR filename, char* buf);
int jpeg2bmp(const char *OpenName,const char *SaveName);
int main(int argc,char *argv[])
{
jpeg2bmp("C:\\tfijpeg1.jpg", "C:\tifbmp1.bmp");
return 0;
}
int jpeg2bmp(const char *OpenName,const char *SaveName)
{
FILE *AStream;
LPCSTR filename;
// 関数ポインタを定義
pload pl;
HINSTANCE hInst = LoadLibrary("jload.dll");
if (hInst == NULL){
return false;
}
//関数アドレスを設定
pl=(pload)::GetProcAddress(hInst,"load");
if (pl == NULL) {
FreeLibrary(hInst);
return false;
}
int size = pl(filename, "");
if( size != 0)
{
char* buf = (char*)malloc(size);
pl(filename, buf);
free(buf);
}
// 最後にライブラリをアンロード
FreeLibrary(hInst);
return (0);
}
上記のソースでは、int size = pl(filename, "");
でハンドルエラーが出ます。
>__stdcall(もしくはWINAPI)の宣言をしていますか?
>extern "C"していますか?これがないと、C++の名前空間の影響を受けます。
extern "C"
__declspec(dllimport)
上記のライブラリの作業を行うと、外部参照は未定義とかきかれました。
何度もすみませんがご教示お願いいたします。
NULLは0であって、""ではありません。
ちなみに、Cの場合、nullとNULLも違いますよ。
手元に参考書があれば調べてみてください。googleで調べても出ていると思います。確実にNULLが何なのかを調べたいのであれば、inculdeファイルの中に定義されていると思いますので、探してみてください。
宣言ですが、
typedef int (WINAPI *pload)(char*, char*);
というようにします。
typedef int (__stdcall *pload)(char*, char*);
でもよろしいかと。
もしかしたら、
#ifdef __cplusplus
extern "C" {
#endif
typedef int (WINAPI *pload)(char*, char*);
#ifdef __cplusplus
}
#endif
としておく必要があるかもしれません。
# BCCではなくても動きましたが
ありがとうございます。
上記のソースを追加して以下のソースに変更しましたがハンドルエラーが出ます。
今度は、DLLの中に問題があるようです。
<変更したソース>
#include<windows.h>
#include <stdio.h>
#include <malloc.h>
#include <string.h>
// 関数のプロトタイプをtypedefを使って定義します。
#ifdef __cplusplus
extern "C" {
#endif
typedef int (WINAPI *pload)(char*, char*);
#ifdef __cplusplus
}
#endif
char* filename;
int jpeg2bmp(char *OpenName,char *SaveName);
int main(int argc,char *argv[])
{
jpeg2bmp("C:\\tfijpeg1.jpg", "C:\tifbmp1.bmp");
return 0;
}
int jpeg2bmp(char *OpenName,char *SaveName)
{
filename = OpenName;
// 関数ポインタを定義
pload pl;
HINSTANCE hInst = LoadLibrary("jload.dll");
if (hInst == NULL){
return false;
}
//関数アドレスを設定
pl=(pload)::GetProcAddress(hInst,"load");
if (pl == NULL) {
FreeLibrary(hInst);
return false;
}
int size = pl(filename, NULL);
if( size != 0)
{
char* buf = (char*)malloc(size);
pl(filename, buf);
free(buf);
}
// 最後にライブラリをアンロード
FreeLibrary(hInst);
return (0);
}
<DLLの中身>
library test;
uses
Windows,Classes,Graphics,jpeg;
function load(fn:PCHAR; mem: PCHAR): Integer;export;stdcall;
var
jpg:TJpegImage;
bmp:TBitmap;
ms: TMemoryStream;
begin
jpg:=TJpegImage.Create;
bmp:=TBitmap.Create;
ms := TMemoryStream.Create;
// Result := 0;
jpg.LoadFromFile(fn);
bmp.Assign(jpg);
bmp.SaveToStream(ms);
ms.Seek(0, soFromBeginning);
if Assigned(mem) then
begin
CopyMemory(mem, ms.Memory, ms.Size);
end;
Result := ms.Size;
ms.free;
jpg.free;
bmp.free;
end;
exports
load name 'load';
begin
end.
上記DLLで間違いがありましたらご教授願います。
本当に何度も申し訳ありません。
bmp画像を表示することができました。
ありがとうございました!!
グレースケールのjpeg画像をbmp(このソースプログラムを実行すると256色)に変換し、ペイントで表示させると、白または灰色の部分が緑系に変色してしまました。
DLLの部分で
jpg.Grayscale := True;
を追加しても、変化がありませんでした。
どうすれば、グレースケールのbmpに変換できるでしょうか?
> グレースケールのjpeg画像をbmp(このソースプログラムを実行すると256色)に変換し、
256色のビットマップのパレットではグレースケールを正確に表せないんじゃないですか。
pf24bit にして、同じサイズのビットマップの Canvas.Draw(0,0.jpg) みたいに
するといいんじゃないでしょうか。
ツイート | ![]() |