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;
}
f:id:any-programming:20170319005947j:plain f:id:any-programming:20170319010003j:plain