VCLによるJpeg読み込みDLL

今回は、DelphiのVCLコンポーネントを利用してJpegファイルを読み込むライブラリを作成してみます。Jpeg関連では、IJG(Indipendent JPEG Group)がフリーのライブラリを公開しているのですが、このライブラリはちょっと使いにくいんですよね。
そこで、今回はDelphiTJpegImageTBitmapを使ってC/C++でも利用できるDLLを作ってみる事にしました。

DLLの作成

 今回作るDLLでは、Jpegファイルを読み込んでその大きさとピクセル列を返す関数load()をエクスポートします。この関数には、PCharLPCTSTR)型でファイル名を渡し、PIntegerLPDWORD)型でビットマップの大きさと32ビットDIB形式のピクセル列をまとめたバッファの先頭アドレスを返すようにしましょう。関数宣言は、

  function load(fn:PChar):PInteger;export;stdcall;

 とします。Jpeg読み込み処理は、

  1. TJpegImageTBitmapオブジェクト作成
  2. TBitmapオブジェクトの形式を32ビットDIBに設定
  3. TJpegImageオブジェクトにJpegファイルを読み込む
  4. 読み込んだイメージの大きさを取得し、TBitmapオブジェクトに設定
  5. TBitmapオブジェクトにJpegイメージを描画
  6. バッファを確保し、先頭にイメージの幅と高さを記録
  7. バッファにTBitmapオブジェクトのピクセル列をコピー

 という流れで処理してみました。バッファには、先頭2ダブルワード(8バイト)にイメージの幅と高さを記録し、その後にイメージのピクセル列を32ビットDIBピクセル列形式で記録します。バッファの大きさは、イメージの幅×高さ×4+8バイトなので、バッファの確保は

  p:=PInteger(GlobalAlloc(GPTR,bmp.width*bmp.height*4+8));

 となります。後はこのバッファの先頭にイメージの大きさを書き込み、続いてTBitmapオブジェクトbmpのピクセル列をscanLineメソッドで取得して書き込んでいくだけですね。

  pp:=p;
  pp^:=jpg.width;
  inc(pp,1);
  pp^:=jpg.Height;
  inc(pp,1);

  for i:=0 to bmp.height-1 do
  begin
    CopyMemory(pp,bmp.scanLine[(bmp.height-1)-i],bmp.width*4);
    inc(pp,bmp.width);
  end;

 DLLのソース全体は、以下のようになります。

  library jload;

  uses
    Windows,Classes,Graphics,jpeg;

  function load(fn:PChar):PInteger;export;stdcall;
  var
    jpg:TJpegImage;
    bmp:TBitmap;
    p,pp:PInteger;
    i:Integer;
  begin

    jpg:=TJpegImage.Create;

    jpg.PixelFormat:=jf24bit;
    jpg.LoadFromFile(fn);

    bmp:=TBitmap.Create;

    bmp.HandleType:=bmDIB;
    bmp.PixelFormat:=pf32bit;
    bmp.Width:=jpg.Width;
    bmp.Height:=jpg.Height;
    bmp.Canvas.Draw(0,0,jpg);

    p:=PInteger(GlobalAlloc(GPTR,bmp.width*bmp.height*4+8));

    pp:=p;
    pp^:=jpg.width;
    inc(pp,1);
    pp^:=jpg.Height;
    inc(pp,1);

    for i:=0 to bmp.height-1 do
    begin
      CopyMemory(pp,bmp.scanLine[(bmp.height-1)-i],bmp.width*4);
      inc(pp,bmp.width);
    end;

    jpg.free;
    bmp.free;

    load:=p;

  end;

  exports
    load name 'load';

  begin
  end.

 DLLの作成は、DLLの新規プロジェクトを作成してこのプログラムソースを作成するだけです。

Jpegの読み込み

 では、このDLL内のload()VC++のプログラムから呼び出してみましょう。今回は、Jpegファイルをドロップするとそのファイルをloadに渡して32ビットDIBを作成し、そのDIBを表示するプログラムを作ってみました。

 プログラムでは、まずload()用のポインタを宣言します。

  typedef LPDWORD (CALLBACK* pLoad)(LPCTSTR);
  pLoad pload;

 次に、プログラムの最初でDLLjload.dll)を読み込んで関数のポインタを取得。

  hDll=LoadLibrary("jload");

  pload=(pLoad)GetProcAddress(hDll,"load");

 これでjload.dll内のload()を関数ポインタploadを介して呼び出せるようになったので、この関数を使ってJpegファイルから32ビットDIBを作成する関数readJpeg()を定義します。

  BOOL readJpeg(LPCTSTR fn) { /* Jpegファイル読み込み */

      LPDWORD lpP;

      /* DLL内のload関数呼び出し */
      lpP=pload(fn);

      /* ビットマップの大きさ保存 */
      dwWidth=*lpP;
      dwHeight=*(lpP+1);

      /* DIB用メモリ確保・ポインタ設定 */
      lpDIB=(LPBYTE)GlobalAlloc(GPTR,sizeof(BITMAPINFO)+dwWidth*dwHeight*4);
      lpInfo=(LPBITMAPINFO)lpDIB;
      lpPixels=lpDIB+sizeof(BITMAPINFO);

      /* DIB用BITMAPINFOHEADER設定 */
      lpInfo->bmiHeader.biSize=sizeof(BITMAPINFOHEADER);
      lpInfo->bmiHeader.biWidth=dwWidth;
      lpInfo->bmiHeader.biHeight=dwHeight;
      lpInfo->bmiHeader.biPlanes=1;
      lpInfo->bmiHeader.biBitCount=32;
      lpInfo->bmiHeader.biCompression=BI_RGB;

      /* DLL内で読み込んだピクセル列をコピー */
      CopyMemory(lpPixels,lpP+2,dwWidth*dwHeight*4);

      /* DLLから取得したピクセル列バッファを解放 */
      GlobalFree(lpP);

      return TRUE;

  }

 これで、JpegファイルからDIBを作れるようになったので、後はファイルがドロップされたらこの関数を呼び出すだけですね。

プログラム

 実行ファイル(またはプロジェクト)と同じディレクトリにjload.dllを置いて実行したら、ウインドウにJpegファイルをドロップしてみてください。ただし、エラーチェックはしていないのでJpegファイル以外はドロップしないでください。

 今回は読み込みだけでしたが、同じようにしてビットマップの大きさとピクセル列のバッファを渡すとそのビットマップをJpegファイルに保存する関数も作れるでしょう。

プログラムダウンロード(jdll.lzh/105KB)

戻る