サンプルのダウンロード

1.高速描画

直接、デバイスコンテキストに描画することもできるが、一手間かけて、メモリビットマップに描画した後、デバイスコンテキストに描画したほうが高速に描画できる
#include <windows.h>
#include <GdiPlus.h>

#pragma comment(lib,"GdiPlus.lib")

using namespace     Gdiplus;
GdiplusStartupInput gdiSI;
ULONG_PTR           gdiToken;
Bitmap              *Bmp_R = NULL;

LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
void Draw_Image(HDC hDC, int width, int height);
void Read_Image(char *fname);

LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    static int  width,height;
    
    switch (uMsg)
    {
    case WM_CREATE:
        GdiplusStartup(&gdiToken,&gdiSI,NULL);
        Read_Image("sample.jpg");
        return 0;
    case WM_SIZE:
        width   = LOWORD(lParam);
        height  = HIWORD(lParam);
        return 0;
    case WM_PAINT:
        HDC         hDC;
        PAINTSTRUCT ps;
        // 描画準備
        hDC = BeginPaint(hWnd,&ps);
        Draw_Image(hDC, width, height);
        // 描画終了
        EndPaint(hWnd,&ps);
        return 0;
    case WM_CLOSE:
        delete Bmp_R;
        DestroyWindow(hWnd);
        GdiplusShutdown(gdiToken);
        PostQuitMessage(0);
        return 0;
    }
    return DefWindowProc(hWnd, uMsg, wParam, lParam);
}// WndProc

// 画像の描画
void Draw_Image(HDC hDC, int width, int height)
{
    // クライアント領域サイズの作業用ビットマップを作成
    Bitmap bmpCp(width, height, PixelFormat32bppARGB);
    // 作業用ビットマップのグラフィックオブジェクトを作成
    Graphics gBmp(&bmpCp);
    // 作業用ビットマップのクリッピング
    Rect rcBmp(0, 0, width, height);
    gBmp.SetClip(rcBmp);
    // 画像を作業用ビットマップへ描画
    if(Bmp_R){
        gBmp.DrawImage(Bmp_R, 0, 0, Bmp_R->GetWidth(), Bmp_R->GetHeight());
    }
    // クリッピング解除
    gBmp.ResetClip();
    // デバイスコンテキストのグラフィックオブジェクトを作成
    Graphics    g(hDC);
    // 作業用ビットマップをデバイスコンテキストへ描画
    g.DrawImage(&bmpCp, 0, 0, bmpCp.GetWidth(), bmpCp.GetHeight());
}// Draw_Image

// ファイルから画像を読み込む
void Read_Image(char *fname)
{
    WCHAR   wfname[MAX_PATH];
    
    MultiByteToWideChar(CP_ACP,0,fname,-1,wfname,sizeof(wfname));
    delete Bmp_R;
    Bmp_R = Bitmap::FromFile(wfname,FALSE);
}// Read_Image

2.画像をJPEG形式で保存

JPEG形式で保存するときは、画像品質を設定できる
#define FREE(p)         {free(p);p=NULL;}

Bitmap              *Bmp_R1 = NULL,*Bmp_R2 = NULL,*Bmp_S = NULL;

LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
void Draw_Image(HDC hDC);
void Read_Image(Bitmap **Bmp, char *fname);
void Save_Jpeg(char *sfname);
int GetEncoderClsid(const WCHAR* format, CLSID* pClsid);

LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg)
    {
    case WM_CREATE:
        GdiplusStartup(&gdiToken,&gdiSI,NULL);
        Read_Image(&Bmp_R1, "sample.jpg");
        Read_Image(&Bmp_R2, "sample2.png");
        Bmp_S = new Bitmap(240, 320, PixelFormat32bppARGB);
        return 0;
    case WM_PAINT:
        HDC         hDC;
        PAINTSTRUCT ps;
        // 描画準備
        hDC = BeginPaint(hWnd,&ps);
        Draw_Image(hDC);
        // 描画終了
        EndPaint(hWnd,&ps);
        return 0;
    case WM_CLOSE:
        Save_Jpeg("save.jpg");
        delete Bmp_R1;
        delete Bmp_R2;
        delete Bmp_S;
        DestroyWindow(hWnd);
        GdiplusShutdown(gdiToken);
        PostQuitMessage(0);
        return 0;
    }
    return DefWindowProc(hWnd, uMsg, wParam, lParam);
}// WndProc

// 画像の描画
void Draw_Image(HDC hDC)
{
    // 保存用ビットマップのグラフィックオブジェクトを作成
    Graphics gBmp(Bmp_S);
    // 作業用ビットマップのクリッピング
    Rect rcBmp(0, 0, Bmp_S->GetWidth(), Bmp_S->GetHeight());
    gBmp.SetClip(rcBmp);
    // 画像を作業用ビットマップへ描画
    if(Bmp_R1){
        gBmp.DrawImage(Bmp_R1, 0, 0, Bmp_R1->GetWidth(), Bmp_R1->GetHeight());
    }
    if(Bmp_R2){
        gBmp.DrawImage(Bmp_R2, 50, 50, Bmp_R2->GetWidth(), Bmp_R2->GetHeight());
    }
    // クリッピング解除
    gBmp.ResetClip();
    // デバイスコンテキストのグラフィックオブジェクトを作成
    Graphics    g(hDC);
    // 作業用ビットマップをデバイスコンテキストへ描画
    g.DrawImage(Bmp_S, 0, 0, Bmp_S->GetWidth(), Bmp_S->GetHeight());
}// Draw_Image

// ファイルから画像を読み込む
void Read_Image(Bitmap **Bmp, char *fname)
{
    WCHAR   wfname[MAX_PATH];
    
    MultiByteToWideChar(CP_ACP,0,fname,-1,wfname,sizeof(wfname));
    delete *Bmp;
    *Bmp = Bitmap::FromFile(wfname,FALSE);
}// Read_Image

// 画像をJPEGファイルに保存する
void Save_Jpeg(char *sfname)
{
    Status              st;
    CLSID               clsid;
    EncoderParameters   ecps;
    ULONG               quality;
    WCHAR               wsfname[MAX_PATH];
    
    // JPEGのCLSIDを取得
    GetEncoderClsid(L"image/jpeg", &clsid);
    // ファイル名をユニコードに変換
    MultiByteToWideChar(CP_ACP,0,sfname,-1,wsfname,sizeof(wsfname));
    // JPEG品質設定
    ecps.Count = 1;
    quality = 80;   // 画像の品質(0 - 100)
    ecps.Parameter[0].Guid = EncoderQuality;
    ecps.Parameter[0].Type = EncoderParameterValueTypeLong;
    ecps.Parameter[0].NumberOfValues = 1;
    ecps.Parameter[0].Value = &quality;
    // 画像をファイルに保存
    st = Bmp_S->Save(wsfname, &clsid, &ecps);
    if(st == Ok){
        MessageBox(GetFocus(), "画像を保存しました",MYWNDTITLE ,MB_OK);
    }
}// Save_Jpeg

// MIMEからCLSIDを取得する
int GetEncoderClsid(const WCHAR* format, CLSID* pClsid)
{
    UINT num  = 0; // number of image encoders
    UINT size = 0; // size of the image encoder array in bytes
    
    ImageCodecInfo* pImageCodecInfo = NULL;
    
    GetImageEncodersSize(&num, &size);
    if(size == 0)
        return -1; // Failure
    
    pImageCodecInfo = (ImageCodecInfo*)(malloc(size));
    if(pImageCodecInfo == NULL)
        return -1; // Failure
    
    GetImageEncoders(num, size, pImageCodecInfo);
    
    for(UINT j = 0; j < num; ++j)
    {
        if( wcscmp(pImageCodecInfo[j].MimeType, format) == 0 )
        {
            *pClsid = pImageCodecInfo[j].Clsid;
            FREE(pImageCodecInfo);
            return j; // Success
        }
    }
    
    FREE(pImageCodecInfo);
    return -1; // Failure
}// GetEncoderClsid

3.画像をPNG形式で保存

BMP, PNG, TIFF で保存するときは、色深度を設定できるが、画像の色深度が保存するそれと異なっている場合は期待した結果にはならない。GIF の色深度は8ビットのみだ。以下は、24ビットで保存した場合。
#define FREE(p)             {free(p);p=NULL;}
#define SAFE_DELETE_OBJ(p)  if (p){delete  p;p=NULL;}

LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
void Draw_Image(HDC hDC);
Status ChangeFormat_32bppTo24bpp();
void Read_Image(Bitmap **Bmp, char *fname);
void Save_Png(char *sfname);
int GetEncoderClsid(const WCHAR* format, CLSID* pClsid);

// 画像フォーマット変換 32bpp→24bpp
Status ChangeFormat_32bppTo24bpp()
{
    Status      st = GenericError;
    PixelFormat pfmt_r = PixelFormat32bppARGB;
    PixelFormat pfmt_s = PixelFormat24bppRGB;

    if(!Bmp_S){
        return st;
    }
    
    if(Bmp_S->GetPixelFormat() == pfmt_r){
        BitmapData  bitmapData;
        int         wdh = Bmp_S->GetWidth();
        int         hgt = Bmp_S->GetHeight();
        Rect        rect(0, 0, wdh, hgt);
        BYTE        *pxl_buf = NULL;
        // 画素値を読み込む
        st = Bmp_S->LockBits(&rect, ImageLockModeRead, pfmt_r, &bitmapData);
        if (st == Ok)
        {
            BYTE    *pxl1 = NULL;   // 画素値を読み込むポインタ
            // 画素値の先頭アドレスを pxl1 に代入 
            if (bitmapData.Stride > 0)
                pxl1 = (BYTE*) bitmapData.Scan0;
            else
                pxl1 = (BYTE*) bitmapData.Scan0
                        + (bitmapData.Stride)*(bitmapData.Height-1);
            // pxl_buf のメモリを確保
            pxl_buf = (LPBYTE) malloc (wdh*hgt*4);
            // 画素値を pxl1 から pxl_buf にコピー
            memcpy(pxl_buf, pxl1, wdh*hgt*4);
            // bitmapData.Scan0 のメモリを解放
            st = Bmp_S->UnlockBits(&bitmapData);
        }
        SAFE_DELETE_OBJ(Bmp_S);
        Bmp_S = new Bitmap(wdh, hgt, pfmt_s);
        // 画素値を書き込む
        st = Bmp_S->LockBits(&rect, ImageLockModeWrite, pfmt_s, &bitmapData);
        if (st == Ok)
        {
            BYTE    *pxl1 = NULL;
            UINT    stride1;
            int     x,y,rest,ct1 = 0,ct2 = 0;
            if (bitmapData.Stride > 0)
                pxl1 = (BYTE*) bitmapData.Scan0;
            else
                pxl1 = (BYTE*) bitmapData.Scan0
                        + (bitmapData.Stride)*(bitmapData.Height-1);
            // 4byte境界幅
            stride1 = abs(bitmapData.Stride);
            // 4byte境界幅で余った画素数
            rest = stride1 - wdh * (24 / 8);
            // Alpha値以外の画素値を順に pxl1 へコピーする
            if(pxl_buf){
                for(y=0;y<hgt;y++){
                    for(x=0;x<wdh;x++){
                        pxl1[ct1++] = pxl_buf[ct2++];   // Blue
                        pxl1[ct1++] = pxl_buf[ct2++];   // Green
                        pxl1[ct1++] = pxl_buf[ct2++];   // Red
                        pxl_buf[ct2++];                 // Alpha
                    }
                    // 4byte境界幅で余った画素を0で埋める
                    for(x=0;x<rest;x++){
                        pxl1[ct1++] = 0;
                    }
                }
            }
            st = Bmp_S->UnlockBits(&bitmapData);
        }
        FREE(pxl_buf);
    }
    return st;
}// ChangeFormat_32bppTo24bpp

// 画像をPNGファイルに保存する
void Save_Png(char *sfname)
{
    Status              st;
    CLSID               clsid;
    EncoderParameters   ecps;
    ULONG               bpp = 24;   // 色深度(1,4,8,24,32)
    WCHAR               wsfname[MAX_PATH];
    
    if(!Bmp_S){
        return;
    }
    // 画像フォーマット変換 32bpp→24bpp
    st = ChangeFormat_32bppTo24bpp();
    // PNGのCLSIDを取得
    GetEncoderClsid(L"image/png", &clsid);
    // ファイル名をユニコードに変換
    MultiByteToWideChar(CP_ACP, 0, sfname, -1, wsfname, sizeof(wsfname));
    // PNG色深度設定
    ecps.Count = 1;
    ecps.Parameter[0].Guid = EncoderColorDepth;
    ecps.Parameter[0].Type = EncoderParameterValueTypeLong;
    ecps.Parameter[0].NumberOfValues = 1;
    ecps.Parameter[0].Value = &bpp;
    // 画像をファイルに保存
    st = Bmp_S->Save(wsfname, &clsid, &ecps);
    if(st == Ok){
        MessageBox(GetFocus(), "画像を保存しました", MYWNDTITLE, MB_OK);
    }
}// Save_Png

4.画像のプロパティを取得

ここでのプロパティは、EXIF情報の事で、画像情報の事ではない。
両者に相関関係はない。
例えば、画像サイズや解像度などは、両方で設定する必要がある。
以下のサンプルコードは、PropertyTagTypeASCII と PropertyTagTypeRational だけであり、その他はやってないが、他も似たような感じである。

マイクロソフトは、プロパティ表示のサンプルをほとんど公表していない。
それを公表すると、この会社は、GDI+を使用できなくしてしまうから、書けない。
つまり、次のOSのバージョンでGDI+がサポートされなくなる。
だからと言って、代わりのGDI機能をリリースする事もない。
ウィンドウズはOSとして既に潮時であり、他のOSへの乗り換えを検討すべきだと、マイクロソフト自身も考えている証拠だが、使い勝手のいいOSは出ないかな。
クロームOSは、シンクライアント専用だから駄目だ。
現在でもPC−9801やオフコンが現役で動いている企業は多いらしいが、ウィンドウズもそうなりそうだ。
98は、工作機械を動かすのに使っている。
ソフトもPCもサポートが切れているから、修理できないのが町工場の悩みの種だ。
察するに彼らの望みは、工作機械はそのままにPCとソフトだけをウィンドウズに切り替える事だ。
彼らは「東京の企業はサポートに来ない」とぼやいている。
それだけの技術力を持った企業に地方に拠点を持ってもらうのが彼らのベストだろう。
ウィンドウズがなくなると開発に携わる人の数も減るから、スマホやタブレットPCも消滅する運命が待っているわけで、今後、オタクのリナックスが主流となる可能性がある。
最先端はリナックス、現場は化石のウィンドウズという形だ。
#define _CRT_SECURE_NO_WARNINGS
#include <windows.h>
#include <stdio.h>
#include <GdiPlus.h>

#pragma comment(lib,"GdiPlus.lib")

using namespace     Gdiplus;
GdiplusStartupInput gdiSI;
ULONG_PTR           gdiToken;
Bitmap              *Bmp_R = NULL;          // ビットマップオブジェクト
PropertyItem        *gpPropBuffer = NULL;   // ビットマップ情報
UINT                gnumProperties = 0;     // ビットマップ情報の項目数

// 関数のプロトタイプ宣言
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
Status Get_Property();
void Read_Image(char *fname);

LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    static int  width,height;
    
    switch (uMsg)
    {
    case WM_CREATE: // 初期化
        GdiplusStartup(&gdiToken, &gdiSI, NULL);
        Read_Image("sample.jpg");
        return 0;
    case WM_PAINT:  // 描画
        HDC         hDC;
        PAINTSTRUCT ps;
        hDC = BeginPaint(hWnd, &ps);
        if(gpPropBuffer){
            UINT    i;
            char    strValue[256],strBuf[512];
            // プロパティ数の表示
            _itoa(gnumProperties ,strValue, 10);
            _snprintf(strBuf, sizeof(strBuf)-1, "項目数 %s", strValue);
            TextOut(hDC, 0, 0, strBuf, strlen(strBuf));
            _snprintf(strBuf, sizeof(strBuf)-1, "ID    TYPE 長さ 値", strValue);
            TextOut(hDC, 0, 18, strBuf, strlen(strBuf));
            // プロパティ値の表示
            for(i=0; i<gnumProperties; i++){
                switch (gpPropBuffer[i].type)
                {
                case PropertyTagTypeASCII:
                    strncpy(strValue, (char*) gpPropBuffer[i].value, sizeof(strValue)-1);
                    break;
                case PropertyTagTypeRational:
                    {
                        DWORD* value = (DWORD*)(gpPropBuffer[i].value);
                        if(gpPropBuffer[i].length == 24){
                            _snprintf(strValue, sizeof(strValue)-1, "%d/%d/%d/%d", value[0],
                                      value[1], value[2], value[3]);
                        }
                        else{
                            _snprintf(strValue, sizeof(strValue)-1, "%d/%d", value[0], value[1]);
                        }
                    }
                    break;
                default:
                    {
                        BYTE* value = (BYTE*) gpPropBuffer[i].value;
                        _snprintf(strValue, sizeof(strValue)-1, "%d", value[0]);
                    }
                    break;
                }
                _snprintf(strBuf, sizeof(strBuf)-1, "0x%04x %d % 6u %s", gpPropBuffer[i].id,
                          gpPropBuffer[i].type, gpPropBuffer[i].length, strValue);
                TextOut(hDC, 0, (i+2)*18, strBuf, strlen(strBuf));
            }
        }
        // 描画終了
        EndPaint(hWnd, &ps);
        return 0;
    case WM_CLOSE:  // 終了処理
        free(gpPropBuffer);
        delete Bmp_R;
        DestroyWindow(hWnd);
        GdiplusShutdown(gdiToken);
        PostQuitMessage(0);
        return 0;
    }
    return DefWindowProc(hWnd, uMsg, wParam, lParam);
}// WndProc

// 画像のプロパティを取得する
Status Get_Property()
{
    Status  st = GenericError;
    UINT    totalBufferSize;
    
    if(!Bmp_R){
        return st;
    }
    
    st = Bmp_R->GetPropertySize(&totalBufferSize, &gnumProperties);
    if(st == Ok){
        gpPropBuffer = (PropertyItem*) malloc(totalBufferSize);
        st = Bmp_R->GetAllPropertyItems(totalBufferSize, gnumProperties, gpPropBuffer);
    }
    return st;
}// Get_Property

// ファイルから画像を読み込む
void Read_Image(char *fname)
{
    WCHAR   wfname[MAX_PATH];
    
    MultiByteToWideChar(CP_ACP,0,fname,-1,wfname,sizeof(wfname));
    delete Bmp_R;
    Bmp_R = Bitmap::FromFile(wfname,FALSE);
    Get_Property();
}// Read_Image

5.線の描画


// 画像の描画
void Draw_Image(HDC hDC, int width, int height)
{
    // クライアント領域サイズの作業用ビットマップを作成
    Bitmap bmpCp(width, height, PixelFormat32bppARGB);
    // 作業用ビットマップのグラフィックオブジェクトを作成
    Graphics gBmp(&bmpCp);
    // 作業用ビットマップのクリッピング
    Rect rcBmp(0, 0, width, height);
    gBmp.SetClip(rcBmp);
    // 作業用ビットマップへ直線を描画
    Pen pen1(Color::Blue, 8);
    gBmp.DrawLine(&pen1, 20, 30, 200, 30);
    // ラインキャップをつけた場合
    pen1.SetStartCap((LineCap)(LineCapNoAnchor + 2));
    pen1.SetEndCap((LineCap)(LineCapNoAnchor + 1));
    gBmp.DrawLine(&pen1, 20, 60, 200, 60);
    // 矢印をつけた場合
    AdjustableArrowCap acap(4.0f, 3.0f);
    acap.SetMiddleInset(1.0f);
    pen1.SetCustomEndCap(&acap);
    gBmp.DrawLine(&pen1, 20, 90, 200, 90);
    // 線種の設定
    pen1.SetDashStyle((DashStyle)(DashStyleSolid + 1));
    gBmp.DrawLine(&pen1, 20, 120, 200, 120);
    // 線種のラインキャップをつけた場合
    pen1.SetDashCap((DashCap)(DashCapFlat + 2));
    gBmp.DrawLine(&pen1, 20, 150, 200, 150);
    // スムージング
    gBmp.SetSmoothingMode(SmoothingModeAntiAlias);
    gBmp.DrawLine(&pen1, 20, 180, 200, 180);
    // 折れ線の場合
    Point pt1[] = {Point(20, 210), Point(200, 210), Point(200, 300)};
    Pen pen2(Color::Coral, 12);
    gBmp.DrawLines(&pen2, pt1, 3);
    // 線の曲がり角の設定
    Point pt2[] = {Point(20, 240), Point(150, 240), Point(150, 300)};
    pen2.SetLineJoin((LineJoin)(LineJoinMiter + 2));
    gBmp.DrawLines(&pen2, pt2, 3);
    // 曲線の場合
    Point pt3[] = {Point(20, 270), Point(100, 270), Point(100, 300)};
    gBmp.DrawCurve(&pen2, pt3, 3);
    // ハッチブラシの場合
    HatchBrush brush1((HatchStyle) 35, Color::Green, Color::LightSkyBlue);
    pen2.SetWidth(20);
    pen2.SetBrush(&brush1);
    gBmp.DrawLine(&pen2, 20, 330, 200, 330);
    // クリッピング解除
    gBmp.ResetClip();
    // デバイスコンテキストのグラフィックオブジェクトを作成
    Graphics    g(hDC);
    // 作業用ビットマップをデバイスコンテキストへ描画
    g.DrawImage(&bmpCp, 0, 0, bmpCp.GetWidth(), bmpCp.GetHeight());
}// Draw_Image

6.パスの作成と描画


パスは不可視の図形で、描画する事で可視化できる。
図形の回転や拡大・縮小もできる。
ドラッグ&ドロップなどで図形の移動にも使える。
パスでマスクを作ることでコピー領域の設定にも使える。
using namespace     Gdiplus;
GdiplusStartupInput gdiSI;
ULONG_PTR           gdiToken;
GraphicsPath        *Path1 = NULL;
GraphicsPath        *Path2 = NULL;

// 関数のプロトタイプ宣言
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
void Draw_Image(HDC hDC, int width, int height);

LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    static int  width,height;
    
    switch (uMsg)
    {
    case WM_CREATE:
        GdiplusStartup(&gdiToken,&gdiSI,NULL);
        // パスオブジェクトの作成
        Path1 = new GraphicsPath();
        Path2 = new GraphicsPath();
        return 0;
    case WM_SIZE:
        width   = LOWORD(lParam);
        height  = HIWORD(lParam);
        return 0;
    case WM_PAINT:
        HDC         hDC;
        PAINTSTRUCT ps;
        // 描画準備
        hDC = BeginPaint(hWnd,&ps);
        // 画像の描画
        Draw_Image(hDC, width, height);
        // 描画終了
        EndPaint(hWnd,&ps);
        return 0;
    case WM_CLOSE:
        delete Path1;
        delete Path2;
        DestroyWindow(hWnd);
        GdiplusShutdown(gdiToken);
        PostQuitMessage(0);
        return 0;
    }
    return DefWindowProc(hWnd, uMsg, wParam, lParam);
}// WndProc

// 画像の描画
void Draw_Image(HDC hDC, int width, int height)
{
    // クライアント領域サイズの作業用ビットマップを作成
    Bitmap bmpCp(width, height, PixelFormat32bppARGB);
    // 作業用ビットマップのグラフィックオブジェクトを作成
    Graphics gBmp(&bmpCp);
    // 作業用ビットマップのクリッピング
    Rect rcBmp(0, 0, width, height);
    gBmp.SetClip(rcBmp);
    // パス1の作成と描画
    if(Path1){
        // パス1の作成を開始
        Path1->StartFigure();
        // パス1にポイントを追加
        Point pt1[] = {Point(20, 10), Point(200, 10), Point(200, 150)};
        Path1->AddPolygon(pt1, 3);
        // パス1に楕円を追加
        Path1->AddEllipse(30, 60, 150, 100);
        // パス1を閉じる
        Path1->CloseFigure();
        // 塗りつぶしモードの設定
        Path1->SetFillMode((FillMode) 1);   // 0 or 1
        // パス1を黄色で塗りつぶす
        SolidBrush brush1(Color(255,255,255,0));
        gBmp.FillPath(&brush1, Path1);
        // パス1の輪郭を青色で描画
        Pen pen1(Color::Blue, 1);
        gBmp.DrawPath(&pen1, Path1);
        // パス1のバウンディングボックスを取得
        Rect bounds;
        Path1->GetBounds(&bounds);
        // バウンディングボックスを赤色で描画
        Pen pen2(Color::Red, 1);
        gBmp.DrawRectangle(&pen2, bounds);
    }
    // パス2の作成と変更と描画
    if(Path2){
        // パス2の作成を開始
        Path2->StartFigure();
        // パス2にポイントを追加
        Point pt1[] = {Point(220, 10), Point(400, 10), Point(400, 150)};
        Path2->AddPolygon(pt1, 3);
        // パス2を閉じる
        Path2->CloseFigure();
        // パスデータの取得
        PathData path_data;
        Path2->GetPathData(&path_data);
        // パスの最初のポイントを変更
        path_data.Points[0] = PointF(220, 150);
        // パス2を新しいパスデータで作り直す
        delete Path2;
        Path2 = new GraphicsPath(path_data.Points, path_data.Types, path_data.Count);
        // パス2を水色で塗りつぶす
        SolidBrush brush1(Color(255,0,255,255));
        gBmp.FillPath(&brush1, Path2);
        // パス2の輪郭を青色で描画
        Pen pen1(Color::Blue, 1);
        gBmp.DrawPath(&pen1, Path2);
    }
    // クリッピング解除
    gBmp.ResetClip();
    // デバイスコンテキストのグラフィックオブジェクトを作成
    Graphics    g(hDC);
    // 作業用ビットマップをデバイスコンテキストへ描画
    g.DrawImage(&bmpCp, 0, 0, bmpCp.GetWidth(), bmpCp.GetHeight());
}// Draw_Image

7.パスの行列変換


パスの移動、回転、拡大・縮小のサンプル。
各行列を追加する順番によっては結果が変わる事がある。
// 画像の描画
void Draw_Image(HDC hDC, int width, int height)
{
    // クライアント領域サイズの作業用ビットマップを作成
    Bitmap bmpCp(width, height, PixelFormat32bppARGB);
    // 作業用ビットマップのグラフィックオブジェクトを作成
    Graphics gBmp(&bmpCp);
    // 作業用ビットマップのクリッピング
    Rect rcBmp(0, 0, width, height);
    gBmp.SetClip(rcBmp);
    // パス1の作成と描画
    if(Path1){
        // パス1の作成を開始
        Path1->StartFigure();
        // パス1に長方形を追加
        Rect rc1 = Rect(50, 50, 150, 50);
        Path1->AddRectangle(rc1);
        // パス1を閉じる
        Path1->CloseFigure();
        // パス1を緑色で塗りつぶす
        SolidBrush brush1(Color(255,0,255,0));
        gBmp.FillPath(&brush1, Path1);
        // 行列オブジェクトの作成
        Matrix mtrx;
        // 行列に(200, 0)移動を追加
        mtrx.Translate(200, 0);
        // 行列に(125, 75)を中心に45度回転を追加
        mtrx.RotateAt(45, PointF(25, 75));
        // 行列に縦横0.5倍を追加
        mtrx.Scale(0.5f, 0.5f);
        // パス1の行列変換
        Path1->Transform(&mtrx);
        // パス1を桃色で塗りつぶす
        SolidBrush brush2(Color(255,255,0,255));
        gBmp.FillPath(&brush2, Path1);
    }
    // クリッピング解除
    gBmp.ResetClip();
    // デバイスコンテキストのグラフィックオブジェクトを作成
    Graphics    g(hDC);
    // 作業用ビットマップをデバイスコンテキストへ描画
    g.DrawImage(&bmpCp, 0, 0, bmpCp.GetWidth(), bmpCp.GetHeight());
}// Draw_Image

8.パスのドラッグ移動


using namespace     Gdiplus;
GdiplusStartupInput gdiSI;
ULONG_PTR           gdiToken;
GraphicsPath        *Path1 = NULL;

LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
void Draw_Image(HDC hDC, int width, int height);
void Drag_Image(HWND hWnd, LPARAM lParam, bool bDown, Point &posStart);

LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    static int      width, height;
    static bool     bDown = false;
    static Point    posStart = Point(0, 0);
    
    switch (uMsg)
    {
    case WM_CREATE:
        GdiplusStartup(&gdiToken,&gdiSI,NULL);
        // パスオブジェクトの作成
        Path1 = new GraphicsPath();
        // パス1の作成
        if(Path1){
            // パス1の作成を開始
            Path1->StartFigure();
            // パス1に長方形を追加
            Rect rc1 = Rect(50, 50, 150, 50);
            Path1->AddRectangle(rc1);
            // パス1を閉じる
            Path1->CloseFigure();
        }
        return 0;
    case WM_SIZE:
        width   = LOWORD(lParam);
        height  = HIWORD(lParam);
        return 0;
    case WM_PAINT:
        HDC         hDC;
        PAINTSTRUCT ps;
        // 描画準備
        hDC = BeginPaint(hWnd, &ps);
        // 画像の描画
        Draw_Image(hDC, width, height);
        // 描画終了
        EndPaint(hWnd, &ps);
        return 0;
    case WM_LBUTTONDOWN:    // 左ボタン押す
        // マウス座標がパス内にあるか?
        if(Path1->IsVisible(LOWORD(lParam), HIWORD(lParam))){
            bDown = true;
            posStart.X = LOWORD(lParam);
            posStart.Y = HIWORD(lParam);
        }
        return 0;
    case WM_LBUTTONUP:      // 左ボタン離す
        bDown = false;
        return 0;
    case WM_MOUSEMOVE:      // マウスカーソル動く
        Drag_Image(hWnd, lParam, bDown, posStart);
        return 0;
    case WM_CLOSE:
        delete Path1;
        DestroyWindow(hWnd);
        GdiplusShutdown(gdiToken);
        PostQuitMessage(0);
        return 0;
    }
    return DefWindowProc(hWnd, uMsg, wParam, lParam);
}// WndProc

// 画像の描画
void Draw_Image(HDC hDC, int width, int height)
{
    // クライアント領域サイズの作業用ビットマップを作成
    Bitmap bmpCp(width, height, PixelFormat32bppARGB);
    // 作業用ビットマップのグラフィックオブジェクトを作成
    Graphics gBmp(&bmpCp);
    // 作業用ビットマップのクリッピング
    Rect rcBmp(0, 0, width, height);
    gBmp.SetClip(rcBmp);
    // 背景を白色でクリア
    gBmp.Clear(Color(255,255,255,255));
    // パス1の描画
    if(Path1){
        // パス1を緑色で塗りつぶす
        SolidBrush brush1(Color(255,0,255,0));
        gBmp.FillPath(&brush1, Path1);
    }
    // 文字列表示
    Font fnt(L"Times New Roman", 12);
    SolidBrush brushMes(Color(255,0,0,0));
    WCHAR strMes[] = L"図形はドラッグ移動可能";
    gBmp.DrawString(strMes, wcslen(strMes), &fnt, PointF(0, 0), &brushMes);
    // クリッピング解除
    gBmp.ResetClip();
    // デバイスコンテキストのグラフィックオブジェクトを作成
    Graphics    g(hDC);
    // 作業用ビットマップをデバイスコンテキストへ描画
    g.DrawImage(&bmpCp, 0, 0, bmpCp.GetWidth(), bmpCp.GetHeight());
}// Draw_Image

// 画像をドラッグ
void Drag_Image(HWND hWnd, LPARAM lParam, bool bDown, Point &posStart)
{
    Point pt = Point(LOWORD(lParam), HIWORD(lParam));
    if(bDown){
        static Point pt_pre = Point(pt.X, pt.Y);
        Point pt_mv = Point(pt.X - posStart.X, pt.Y - posStart.Y);
        // 行列オブジェクトの作成
        Matrix mtrx;
        // 行列に移動を追加
        mtrx.Translate((REAL) pt_mv.X, (REAL) pt_mv.Y);
        // パス1の行列変換
        Path1->Transform(&mtrx);
        // 再描画
        InvalidateRect(hWnd, NULL, FALSE);
        // 変数の初期化
        pt_pre = Point(pt_mv.X, pt_mv.Y);
        posStart.X = pt.X;
        posStart.Y = pt.Y;
    }
}// Drag_Image

9.画像の拡大表示


画像を拡大表示するときのスムージングモード設定は、SetInterpolationMode で行う。
InterpolationModeNearestNeighbor に設定した場合は、PixelOffsetModeHalf に設定すると上手く表示される。
g.SetInterpolationMode(InterpolationModeNearestNeighbor);
g.SetPixelOffsetMode(PixelOffsetModeHalf);
以下は画像を縦横5倍表示にしたサンプル。
// 画像の描画
void Draw_Image(HDC hDC, int width, int height)
{
    // クライアント領域サイズの作業用ビットマップを作成
    Bitmap bmpCp(width, height, PixelFormat32bppARGB);
    // 作業用ビットマップのグラフィックオブジェクトを作成
    Graphics gBmp(&bmpCp);
    // 作業用ビットマップのクリッピング
    Rect rcBmp(0, 0, width, height);
    gBmp.SetClip(rcBmp);
    // 背景を白色でクリア
    gBmp.Clear(Color(255,255,255,255));
    // 描画モード設定
    gBmp.SetPixelOffsetMode(PixelOffsetModeHalf);
    gBmp.SetInterpolationMode(InterpolationModeNearestNeighbor);
    // 文字描画用ビットマップを作成
    Bitmap bmpText(300, 300, PixelFormat32bppARGB);
    // 文字描画用ビットマップのグラフィックオブジェクトを作成
    Graphics gBmpText(&bmpText);
    // 文字列表示
    Font fnt(L"Times New Roman", 12);
    SolidBrush brushMes(Color(255,0,0,0));
    WCHAR strMes[] = L"▲◎";
    gBmpText.DrawString(strMes, wcslen(strMes), &fnt, PointF(0, 0), &brushMes);
    // 文字描画用ビットマップを作業用ビットマップへ描画(拡大率:縦横5倍)
    gBmp.DrawImage(&bmpText, 0, 0, bmpText.GetWidth() * 5, bmpText.GetHeight() * 5);
    // クリッピング解除
    gBmp.ResetClip();
    // デバイスコンテキストのグラフィックオブジェクトを作成
    Graphics    g(hDC);
    // 作業用ビットマップをデバイスコンテキストへ描画
    g.DrawImage(&bmpCp, 0, 0, bmpCp.GetWidth(), bmpCp.GetHeight());
}// Draw_Image

10.