DLLでBmpのサイズとピクセル列を引き渡すには?


初心者  2004-04-27 00:32:05  No: 8656

DLLでJpegからBmpに変換し、引渡しパラメータをBmpピクセル列とイメージサイズ
にしたいと考えております。
jpegとBmpに変換まではできますが、変換したBmpのピクセル列とイメージサイズの
引渡し方法が思うようにいきません。
どなたかご教示願います。


にしの  2004-04-27 00:51:41  No: 8657

DLLがJPEGを変換してBMPを返すんですよね?
Susieというビューアのプラグインが、そういう作りをしています。
インターフェースの参考にしてみてはどうでしょう。

http://www.digitalpad.co.jp/~takechin/


初心者  2004-04-27 01:00:54  No: 8658

質問の前に、インタフェースを参考しましたがうまくいきませんでした。
変換したBmpのWidth、Heightまでは簡単に抽出できますが
問題はイメージのピクセル列のバッファの確保の仕方と画像のサイズの取得方法をが取得できません。たびたび恐縮ですがご教授の程お願いいたします。


にしの  2004-04-27 01:10:17  No: 8659

バッファは、VirtualAllocなどで確保します。
# 使い終わったらVirtualFreeで解放します

画像サイズは、
((幅+パディング)*高さ)*色深度/8
でいいんじゃないですか?
画像サイズがわからないと、BMPに変換できないと思いますが。


初心者  2004-04-27 01:21:20  No: 8660

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)がピクセル列
と幅と高さで引き渡すためにはどうすればよいでしょうか?


にしの  2004-04-27 01:26:45  No: 8661

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


初心者  2004-04-27 01:30:58  No: 8662

>引数1つでやりたいのであれば、BMPの構造そのものをコピーした方が簡単です。
># 受け取り側でもBMPとして読み込めばOK

どのように渡してあげればよいのですか?


jok  2004-04-27 01:47:04  No: 8663

本当は 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;

うまくいくようです。


初心者  2004-04-27 01:57:29  No: 8664

ありがとうございます。
上記のように作成したDLLをVCで呼び出すためにはどのようにすればよいでしょうか?受け取り側の作成で失敗します。
jpegファイルのパスをDLLに入れて、得られた幅・高さをDWORDで取り出すまでは、
分かるのですがbmpのイメージそのものをLPBYTEが何かで取ろうとするとエラーが
出てしまいます。何度も恐縮ですがご教示願います。


jok  2004-04-27 02:02:15  No: 8665

> 上記のように作成したDLLをVCで呼び出すためにはどのようにすればよいでしょうか?

残念ですが、それはできません。受け取る方も Delphi の TBitmap が使えることが
前提だからです。汎用的にするには、やはりにしのさんの提案通り Susie の
プラグインのまねをするのがいいと思います。と、いうかそのまま使えると
思いますけど。


初心者  2004-04-27 02:12:14  No: 8666

ありがとうございます。
DLLで変換したBMPを一旦ファイルに落として、
VCで読み込ませれば問題ありませんか?


にしの  2004-04-27 02:36:16  No: 8667

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;


にしの  2004-04-27 02:37:12  No: 8668

呼び出すときはこんな感じ。

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;


初心者  2004-04-27 03:36:50  No: 8669

メモリの配置からバッファに溜め込んで引き渡すことは可能ですか?
引渡しパラメータをPIntegerで設定して
p:=PInteger(GlobalAlloc(GPTR,bmp.width*bmp.height*2));
のようなバッファを確保してメモリで配置したファイル構造とサイズをバッファに格納することはできますか?


にしの  2004-04-27 04:03:41  No: 8670

もう少しわかりやすくかかれると、回答しやすいです^^;

上に書いた関数は試しましたか?
引数にあるPCHAR型のmemに、Bitmapのファイル構造を入れていますが、これ以外に何か必要ですか?
ファイルサイズも画像の大きさも返しているのですが。
# 画像の大きさは、Bitmapを解析すれば取得できますよね


初心者  2004-04-27 04:10:15  No: 8671

下手な文面ですみませんでした。
上に書いた関数からVCLで受けようとする際、メモリのGet方法で詰まってしまいました。


初心者  2004-04-27 18:41:07  No: 8672

Win32で上記の関数を受けようとする場合、どうしたらよいのでしょうか?


shaki  2004-04-27 18:51:00  No: 8673

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
}


初心者  2004-04-27 19:06:32  No: 8674

お手数おかけしました。DelphiのDLL(load)をVCLから受けようとした場合、
hDll=LoadLibrary("testlibrary");から
GetProcAddress(hDll,"load");までは分かりますが
実引数を2つ持つ場合はどのようにすれば良いのでしょうか?


初心者  2004-04-27 20:16:23  No: 8675

度々すみません。
上記のソースを利用していますが、
int size = load(filename, null);
の部分でハンドルエラーが出ます。
nullの部分が怪しいのですが、回避できません。
ご教示お願いいたします。


にしの  2004-04-27 21:01:53  No: 8676

Delphiでは
TFunc=function(fn:PCHAR;mem:PCHAR):Integer;stdcall;
こんな感じです。

C++で動かないのは、宣言がわからないと何ともいえません。
__stdcall(もしくはWINAPI)の宣言をしていますか?
extern "C"していますか?これがないと、C++の名前空間の影響を受けます。


にしの  2004-04-27 21:03:20  No: 8677

TFuncについて、ちょっと言葉足らずでした。
変数の宣言では、

var 
  load: function(fn:PCHAR;mem:PCHAR):Integer;stdcall;
です。

タイプ宣言する場合は、

Type
  TFunc=function(fn:PCHAR;mem:PCHAR):Integer;stdcall;

です。
こうしておくと、
var
  load: TFunc;
とかけます。


初心者  2004-04-28 02:20:17  No: 8678

何度もすみません。
こんな感じで作ってみました。
#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) 

上記のライブラリの作業を行うと、外部参照は未定義とかきかれました。
何度もすみませんがご教示お願いいたします。


にしの  2004-04-28 02:56:19  No: 8679

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ではなくても動きましたが


初心者  2004-04-28 03:18:15  No: 8680

ありがとうございます。
上記のソースを追加して以下のソースに変更しましたがハンドルエラーが出ます。
今度は、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で間違いがありましたらご教授願います。
本当に何度も申し訳ありません。


初心者  2004-04-28 04:44:02  No: 8681

bmp画像を表示することができました。
ありがとうございました!!
グレースケールのjpeg画像をbmp(このソースプログラムを実行すると256色)に変換し、ペイントで表示させると、白または灰色の部分が緑系に変色してしまました。
DLLの部分で
jpg.Grayscale := True;
を追加しても、変化がありませんでした。
どうすれば、グレースケールのbmpに変換できるでしょうか?


jok  2004-04-28 05:08:01  No: 8682

> グレースケールのjpeg画像をbmp(このソースプログラムを実行すると256色)に変換し、

256色のビットマップのパレットではグレースケールを正確に表せないんじゃないですか。
pf24bit にして、同じサイズのビットマップの Canvas.Draw(0,0.jpg) みたいに
するといいんじゃないでしょうか。


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加