!DOCUMENT HTML PUBLIC "-//W3C//DTD HTML 4.0 Frameset//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">

GDI+の導入

contents

  1. 導入
  2. 開始・終了処理
  3. イメージの読み込み
  4. イメージの保存

GDI+の導入

GDI+は、C++ベースの、GDIに置き換わるクラスライブラリです。 2次元同次変換可能なグラフィクス、様々なフォーマットの画像の読み書き、αブレンドなどが使用できます。 XPではネイティブですが、NT4.0sp6, 2000, 98/Me ではDLLの追加が必要です。

導入

VC++.NETではデフォルトで使用できます。それ以外の場合は、PlatformSDKをダウンロードします。

VC6付属のSDKは古すぎるので、なるべくならPlatformSDKを入れたほうが良いのですが、 ちょっとGDI+を試したいという場合のために、こっそりGDI+のライブラリ(.lib)とヘッダを置いておきます。 Windows XP 以外の場合は、さらにDLLが必要なので、ここから再配布可能ランタイムをダウンロードしてください。 マニュアルも必要でしょうけど、圧縮しても3MB以上あったので、こっそりは無理。 オンラインのMSDNを参照してください。

gdiplus.dllの配布について

再配布可能GDI+パッケージに、以下のような記述があります。

(1) You may distribute gdiplus.dll solely for use with Windows 2000, Windows Millennium Edition, Windows NT 4.0 and Windows 98.

アプリケーションを配布する場合は、気にせずにDLLを添付してしまってよいようです。 また、XPはデフォルトでGDI+を持っていますが、再配布可能GDI+を使っても問題はないようです。 ただ、gdiplus.dll のサイズは 1667KB もあり、Cランタイム (msvcr71.dll=340KB, msvcp71.dll=488KB) と併せると、 アプリケーション本体よりも容量を使ってしまいそうですね……。

参考資料

『Professional C# - Graphics with GDI+』 By Wrox [html] [pdf]
C#用ですが、印刷して眺めるにはちょうどよいかと。

開始・終了処理

まず、GDI+を使用する前にGdiplusStartup()を呼びます。

Status GdiplusStartup(
  ULONG_PTR* token,
  const GdiplusStartupInput* input,
  GdiplusStartupOutput* output
);

tokenはGdiplusShutdown()に渡すために保存しておきます。 inputでは、デバッグ用のコールバック、外部コーデックの使用などの指定ができます。 GDI+は処理用に独自のスレッドを作成しますが、それを抑制することもできます。 特別なことを行わないならば、デフォルトコンストラクタでかまいません。 outputは、GDI+にスレッドを作成させない場合の通知コールバック関数です。 デフォルトならばNULLでかまいません。

終了前に、GdiplusShutdown()を呼びます。 引数としてGdiplusStartup()から返されたtokenを渡します。

VOID GdiplusShutdown(
  ULONG_PTR token
);

GdiplusStartup()の第3引数はNULLでよいため、最も単純な初期化コードは以下のようになります。 Gdiplusの全てのクラス・関数は"Gdiplus"名前空間の中にあることに注意してください。

using namespace Gdiplus;
int main()
{
  GdiplusStartupInput gdiplusStartupInput;
  ULONG_PTR gdiplusToken;
  GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);

  ...

  GdiplusShutdown(gdiplusToken);
  return 0;
}

イメージの読み込み

Gdiplus::Imageには、引数にファイル名をとるコンストラクタと、IStreamをとるコンストラクタがあります。 どちらも画像フォーマットを自動的に判別して読み込んでくれるので便利です。 しかし、リソースから直接作成する機能がありません。 GDI::HBITMAPの場合には、LoadImage, LoadBitmapで簡単に読み込めるんですけどね。 そこで、リソースに対するIStreamを作成するライブラリを作成しました。

追記:Bitmap(hInstance, bitmapName)なるコンストラクタがありましたw (以前見たときには無かった気がするんだけど……) これを使えば、RT_BITMAPリソースを読み込むことが可能です。 ただし、RT_BITMAP以外のタイプで.bmpでない画像をリソースにしている場合は、以下の方法が依然有効です。

ResourceStream : IStreamの機能補完

ソースコード: Stream.h Stream.cpp

IStream自体が少し使いにくい(LARGE_INTEGERを引数に取るあたり)ので、簡単なラッパークラスも作りました。 Stream::CreateOnResource()でリソースに対するストリームが作成できます。 使い方は以下のような流れです。

IO::Stream stream;
stream.CreateOnResource(hInstance, MAKEINTRESOURCE(ID_IMAGE), "MyImageType");
Image* pImage = new Image(stream);

カスタムストリームをGDI+に渡すときの注意

Gdiplus::Image は、それ自体がdeleteされない限り、渡されたストリームを保持しつづけます(参照カウントを増加させたままにする)。 そのため、Streamが指しているバッファは解放してはいけません。 (DLLをダイナミックロードし、FreeLibrary() ⇒ ‾Image() の順で呼んだときににハマりました。)

イメージの保存

C++版GDI+が、.NET版GDI+と比べて多少面倒かと思うのが、イメージの保存です。 読み込みはImage/Bitmapのコンストラクタにファイル名を渡すだけでよいのですが、 保存する場合はフォーマットの指定のため、エンコーダのCLSIDを渡してやらなければなりません。 このCLSIDは、どうやら実行時に取得するしかないようです。 CLSIDの取得法は、MSDNにGetEncoderClsid()というサンプルがあります。

イメージ保存のための簡単なライブラリ

ソースコード:GdiplusUtil.cpp

ついでにGDI+自体の初期化もするので、グローバル変数としてインスタンスを1つだけ作成してください。 一応売りとしてしては、GetEncoderByExtension()で保存したいファイル名から自動的にCLSIDを取れることがあります。 指定された保存ファイル名の拡張子に合わせた形式で保存できます。