GDI+ --- 画像ファイル読み書き
C++で画像ファイルを読み書きする際は、libjpegやlibpng等を利用します。
しかしWindowsでは標準ライブラリでないため、ライブラリを準備する必要があります。
ちょっと動作確認の時など、ライブラリの準備が面倒な時はGDI+を利用することができます。
コードはエラー処理を省いてあります。(返り値がGdiplus::Statusの関数は、Gdiplus::OKかどうか確認してください。)
画像読み込み
Gdiplus::Bitmapを利用して画像を読み込みます。
画素データは4Byte単位のStrideで保存されており、今回は余白を切り詰めて取得するよう実装してあります。
LockBits時に渡すフォーマットは、元データのフォーマットである必要はなく、可能であれば自動で変換してくれます。
void GdiplusLoadImage(std::wstring path, int& width, int&height, std::vector<byte>& pixels) { auto image = Gdiplus::Bitmap::FromFile(path.c_str()); width = image->GetWidth(); height = image->GetHeight(); auto stride = 3 * width; pixels.resize(stride * height); Gdiplus::Rect rect(0, 0, width, height); Gdiplus::BitmapData bmpData; image->LockBits(&rect, Gdiplus::ImageLockModeRead, PixelFormat24bppRGB, &bmpData); for (int y = 0; y < height; ++y) memcpy(pixels.data() + y * stride, (byte*)bmpData.Scan0 + y * bmpData.Stride, stride); image->UnlockBits(&bmpData); }
画像保存(jpeg)
読み込み時と同様に、Gdiplus::Bitmapを用いて画像を保存します。
画素データは4Byte単位である必要があるので、余白を追加したものを作成しています。
エンコーダのIDはGdiplus::GetImageEncodersで探すのが普通ですが、IDが変わる可能性はほぼ無いと考えられるため直指定しています。(その他例えばpngの場合は0x557cf406...となります。)
void GdiplusSaveJpeg(std::wstring path, int width, int height, const std::vector<byte>& pixels) { auto stride = 3 * width; auto gdiStride = stride % 4 == 0 ? stride : stride + 4 - stride % 4; std::vector<byte> gdiPixels(gdiStride * height); for (int y = 0; y < height; ++y) memcpy(gdiPixels.data() + y * gdiStride, pixels.data() + y * stride, stride); Gdiplus::Bitmap image(width, height, gdiStride, PixelFormat24bppRGB, gdiPixels.data()); CLSID id = { 0x557cf401, 0x1a04, 0x11d3,{ 0x9a, 0x73, 0x00, 0x00, 0xf8, 0x1e, 0xf3, 0x2e } }; image.Save(path.c_str(), &id); }
アプリケーションコード
GDI+を利用するには、GdiplusStartupとGdiplusShutdownを呼ぶ必要があります。
今回は入力画像を赤青反転して出力するコードを作成してみました。
#include <Windows.h> #include <gdiplus.h> #include <gdipluspixelformats.h> // PixelFormat24bppRGB #pragma comment(lib, "gdiplus.lib") int main() { Gdiplus::GdiplusStartupInput input; ULONG_PTR token; Gdiplus::GdiplusStartup(&token, &input, NULL); int width, height; std::vector<byte> pixels; GdiplusLoadImage(L"Parrots.bmp", width, height, pixels); for (int i = 0; i < width * height; ++i) std::swap(pixels[3 * i], pixels[3 * i + 2]); GdiplusSaveJpeg(L"output.jpg", width, height, pixels); Gdiplus::GdiplusShutdown(token); return 0; }