GDI+の処理に失敗するときには、初期化しているか確認します。
GDI+の利用には、標準的に次のヘッダーとライブラリが必要です。
#include <windows.h> #include <Gdiplus.h> #pragma comment (lib,"Gdiplus.lib")
GDI+オブジェクトを生成する前にはGdiplusStartup()でGDI+を初期化し、それらが不要になったら破棄した後にGdiplusShutdown()を呼び出さねばなりません。
ULONG_PTR gdiplusToken; GdiplusStartupInput gdiplusStartupInput; Gdiplus::Status status = GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL); // GDI+ オブジェクトの生成 // … // GDI+ オブジェクトの破棄 GdiplusShutdown(gdiplusToken);
Windows GDI+を初期化できます。
Status GdiplusStartup( ULONG_PTR *token, // トークン。GDI+を終了するときに、これをGdiplusShutdown()へ渡す const GdiplusStartupInput *input, // GDI+バージョンなどの情報 GdiplusStartupOutput *output // フックやアンフックの通知を受け取る構造体。GdiplusStartupInput.SuppressBackgroundThreadをTRUEとしていなければ、NULLで良い );GdiplusStartup function (gdiplusinit.h) - Win32 apps | Microsoft Learn
実行の誤りは、戻り値のStatus列挙型で確認できます。この関数を呼び出さずにGDI+オブジェクトを生成しようとすると、nullやStatus::GdiplusNotInitializedが返されます。Status::Win32Errorが返されたときは、GetLastError()で具体的なエラーコードを取得できます。
この関数の呼び出しごとに、GdiplusShutdown()の呼び出しが必要です。
列挙子 | 値 | 内容 |
---|---|---|
Ok | 0 | the method call was successful. |
GenericError | 1 | there was an error on the method call, which is identified as something other than those defined by the other elements of this enumeration. |
InvalidParameter | 2 | one of the arguments passed to the method was not valid. |
OutOfMemory | 3 | the operating system is out of memory and could not allocate memory to process the method call. For an explanation of how constructors use the OutOfMemory status, see the Remarks section at the end of this topic. |
ObjectBusy | 4 | one of the arguments specified in the API call is already in use in another thread. |
InsufficientBuffer | 5 | a buffer specified as an argument in the API call is not large enough to hold the data to be received. |
NotImplemented | 6 | the method is not implemented. |
Win32Error | 7 | the method generated a Win32 error. |
WrongState | 8 | the object is in an invalid state to satisfy the API call. For example, calling Pen::GetColor from a pen that is not a single, solid color results in a WrongState status. |
Aborted | 9 | the method was aborted. |
FileNotFound | 10 | the specified image file or metafile cannot be found. |
ValueOverflow | 11 | the method performed an arithmetic operation that produced a numeric overflow. |
AccessDenied | 12 | a write operation is not allowed on the specified file. |
UnknownImageFormat | 13 | the specified image file format is not known. |
FontFamilyNotFound | 14 | the specified font family cannot be found. Either the font family name is incorrect or the font family is not installed. |
FontStyleNotFound | 15 | the specified style is not available for the specified font family. |
NotTrueTypeFont | 16 | the font retrieved from an HDC or LOGFONT is not a TrueType font and cannot be used with GDI+. |
UnsupportedGdiplusVersion | 17 | the version of GDI+ that is installed on the system is incompatible with the version with which the application was compiled. |
GdiplusNotInitialized | 18 | the GDI+API is not in an initialized state. To function, all GDI+ objects require that GDI+ be in an initialized state. Initialize GDI+ by calling GdiplusStartup. |
PropertyNotFound | 19 | the specified property does not exist in the image. |
PropertyNotSupported | 20 | the specified property is not supported by the format of the image and, therefore, cannot be set. |
ProfileNotFound | 21 | the color profile required to save an image in CMYK format was not found. |
この関数の呼び出し前に、生成したすべてのGDI+オブジェクトを破棄します。
この関数の呼び出し後にGDI+オブジェクトを破棄しようとすると、アクセス違反が発生します。
int main()
{
ULONG_PTR gdiplusToken;
GdiplusStartupInput gdiplusInput;
GdiplusStartup(&gdiplusToken, &gdiplusInput, NULL);
Bitmap bitmap(L"C:\\sample.bmp");
GdiplusShutdown(gdiplusToken);
return 0; // ブロックを抜けるときにbitmapが破棄されるため、アクセス違反となる
}
画像をファイルに保存できます。
Status Save( [in] const WCHAR *filename, [in] const CLSID *clsidEncoder, // 画像の保存に使用するエンコーダーを指定するCLSID [in] const EncoderParameters *encoderParams // エンコーダーで使用されるパラメータ );Image::Save - Win32 apps | Microsoft Learn
GDI+を初期化していないと、Status::InvalidParameterが返されます。
IStreamを引数に取るオーバーロードでは、ストリームへ出力できます。
Status Save( [in] IStream *stream, [in] const CLSID *clsidEncoder, [in] const EncoderParameters *encoderParams );Image::Save(IN IStream,IN const CLSID,IN const EncoderParameters) (gdiplusheaders.h) - Win32 apps | Microsoft Learn
HRESULT WriteToStream(Gdiplus::Image *image) { IStream *stream = NULL; HRESULT hr = CreateStreamOnHGlobal(NULL, TRUE, &stream); if (FAILED(hr)) goto done; CLSID clsidEncoder; hr = CLSIDFromString(L"{557CF401-1A04-11D3-9A73-0000F81EF32E}", &clsidEncoder); // image/jpeg if (FAILED(hr)) goto done; Gdiplus::Status status = image->Save(stream, &clsidEncoder); if (status != Gdiplus::Status::Ok) throw status; // ストリームからバッファへ読み込み HGLOBAL memoryHandle = NULL; hr = GetHGlobalFromStream(stream, &memoryHandle); if (FAILED(hr)) goto done; size_t bufferSize = GlobalSize(memoryHandle); char *buffer = new char[bufferSize]; LPVOID pMemoryHandle = GlobalLock(memoryHandle); if (pMemoryHandle == NULL) throw GetLastError(); memcpy(buffer, pMemoryHandle, bufferSize); if (GlobalUnlock(memoryHandle) == TRUE) throw GetLastError(); // バッファから出力ストリームへ書き込み _setmode(_fileno(stdout), _O_BINARY); std::cout.write(buffer, bufferSize); done: stream->Release(); return hr; }C++ gdi::Bitmap to PNG Image in memory - Stack Overflow
ストリームからは、istream.read()でも読み込めます。
LARGE_INTEGER potision; potision.QuadPart = 0; hr = stream->Seek(potision, STREAM_SEEK_SET, NULL); if (FAILED(hr)) goto done; STATSTG streamStat; hr = stream->Stat(&streamStat, 0); if (FAILED(hr)) goto done; ULONG bufferSize = (ULONG)streamStat.cbSize.QuadPart; char *buffer = new char[bufferSize]; ULONG read; hr = stream->Read(buffer, bufferSize, &read); if (FAILED(hr)) goto done;
CLSIDを表す文字列は、Retrieving the Class Identifier for an Encoderにあるサンプルコードで下表のように得られます。
MimeType | CLSIDを表す文字列 |
---|---|
image/bmp | {557CF400-1A04-11D3-9A73-0000F81EF32E} |
image/jpeg | {557CF401-1A04-11D3-9A73-0000F81EF32E} |
image/gif | {557CF402-1A04-11D3-9A73-0000F81EF32E} |
image/tiff | {557CF405-1A04-11D3-9A73-0000F81EF32E} |
image/png | {557CF406-1A04-11D3-9A73-0000F81EF32E} |
そしてCLSIDFromString()で、文字列からCLSIDを得られます。
HRESULT CLSIDFromString( [in] LPCOLESTR lpsz, [out] LPCLSID pclsid );CLSIDFromString function (combaseapi.h) - Win32 apps | Microsoft Learn
SetPropertyItem()で書き込めます。
Status SetPropertyItem( [in] const PropertyItem *item );Image::SetPropertyItem (gdiplusheaders.h) - Win32 apps | Microsoft Learn
char *str = "sample";
PropertyItem item;
item.id = PropertyTagImageDescription;
item.type = PropertyTagTypeASCII;
item.length = strlen(str) + 1; // null終端文字を含む
item.value = str;
Image image(L"C:\\sample.jpg");
Status status = image.SetPropertyItem(&item);
画像ファイルからBitmapを作成できます。
void Bitmap( IN const WCHAR *filename, // 画像ファイルのパス IN BOOL useEmbeddedColorManagement // TRUEならば、画像ファイルに埋め込まれた色補正を適用する。省略した場合はFALSE );Bitmap::Bitmap(IN const WCHAR,IN BOOL) | Microsoft Learn
Bitmap bitmap(L"C:\\sample.bmp"); CLSID clsidEncoder; CLSIDFromString(L"{557CF401-1A04-11D3-9A73-0000F81EF32E}", &clsidEncoder); Status status = bitmap.Save(L"C:\\sample.jpg", &clsidEncoder, NULL);
new演算子でオブジェクトを動的に割り当てるとき、
Bitmap *bitmap = new Bitmap(L"C:\\sample.bmp");
GDI+を初期化していなかったり、すでに終了していた場合はNULLが返されます。
バイト配列からBitmapを作成できます。
void Bitmap( INT width, // ピクセル単位の幅 INT height, // ピクセル単位の高さ INT stride, // 1行あたりのバイト数。1ピクセルあたり16ビットのフォーマットならば、2バイト (16ビット) x ピクセル単位の幅の値 PixelFormat format, // Image Pixel Format Constantsで定義されているピクセルフォーマット BYTE *scan0 // ピクセル データを含むバイト配列 );Bitmap::Bitmap(INT,INT,INT,PixelFormat,BYTE) | Microsoft Learn
scan0で示されるメモリ ブロックの割り当てと解放は、呼び出し元 (caller) が担当します。そして解放は、このBitmapを破棄した後とします。
画像データを自前で用意すれば、新規に作成することもできます。
const int width = 16; const int height = 16; BYTE scan0[width*height]; for (int i = 0; i < width*height; i++) { scan0[i] = (i%width == i / width) ? 0 : 0xff; } Bitmap bitmap(width, height, width * 1, PixelFormat8bppIndexed, scan0); CLSID clsidEncoder; CLSIDFromString(L"{557CF400-1A04-11D3-9A73-0000F81EF32E}", &clsidEncoder); Status status = bitmap.Save(L"C:\\sample.bmp", &clsidEncoder, NULL);
これは「」のような画像を生成します。このときLockBits()で取得したBitmapData.Scan0へ、バイト配列のデータをコピーする方法もあります。
BYTE *buffer = new BYTE[width*height]; const int BYTES_PER_PIXEL = 1; int bufferSize = width * height * BYTES_PER_PIXEL; ... Bitmap bitmap(width, height, PixelFormat8bppIndexed); Rect rect(0, 0, width, height); BitmapData bitmapData; Status status = bitmap.LockBits( &rect, ImageLockModeWrite, bitmap.GetPixelFormat(), &bitmapData); CopyMemory(bitmapData.Scan0, buffer, bufferSize); status = bitmap.UnlockBits(&bitmapData);
Bitmap bitmap(L"C:\\sample.bmp");
Rect rect(0, 0, bitmap.GetWidth(), bitmap.GetHeight());
BitmapData bitmapData;
Status status = bitmap.LockBits(
&rect,
ImageLockModeRead,
PixelFormat24bppRGB,
&bitmapData);
BYTE *rawData = (BYTE*)bitmapData.Scan0;
int stride = bitmapData.Stride;
// バイト配列を使用した処理
bitmap.UnlockBits(&bitmapData);
c++ - Gdiplus::Bitmap to BYTE array? - Stack Overflow
Using LockBits in GDI+ | The Supercomputing Blog
Gdiplus::Bitmap *bitmap1 = new Gdiplus::Bitmap(L"C:\\sample.bmp"); // ビットマップへのハンドルの取得 HBITMAP hbm; Gdiplus::Status status = bitmap1->GetHBITMAP(Gdiplus::Color::Black, &hbm); // ハンドルからビットマップを作成 HPALETTE hpal = NULL; Gdiplus::Bitmap *bitmap2 = new Gdiplus::Bitmap(hbm, hpal);Bitmap::GetHBITMAP (gdiplusheaders.h) - Win32 apps | Microsoft Learn Bitmap::Bitmap(IN HBITMAP,IN HPALETTE) (gdiplusheaders.h) - Win32 apps | Microsoft Learn
ハンドルから作成したビットマックを削除するまで、元のビットマップを削除してはなりません。Remarks - Bitmap::Bitmap(IN HBITMAP,IN HPALETTE) (gdiplusheaders.h) - Win32 apps | Microsoft Learn