1.直線

2013/5/25 修正
// Bresenham の線分アルゴリズム
// コンピュータ・グラフィックス 日本コンピュータ協会 P.443 〜 p.446
#include <windows.h>
#include <stdio.h>
#include <GdiPlus.h>

#define MYWNDTITLE  "Line"
#define MYWNDCLASS  "Line_CLASS"

using namespace     Gdiplus;
GdiplusStartupInput gdiSI;
ULONG_PTR           gdiToken;
HDC                 g_hDC;
HWND                g_hWnd;

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
void Bre_Line(int x1, int y1, int x2, int y2, Color clr);
void Bre_Line_Sub(int dx, int dy, int x1, int y1, int x2, int y2, Color clr, bool bSlantGently);
void Bre_Write_Pixel(int x, int y, Color clr, bool bSlantGently);

LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch(uMsg)
    {
    case WM_CREATE:
        g_hWnd = hWnd;
        GdiplusStartup(&gdiToken,&gdiSI,NULL);  // GDI+オブジェクト初期化
        break;
    case WM_PAINT:
        {
            PAINTSTRUCT     ps;
            g_hDC = BeginPaint(hWnd,&ps);
            Bre_Line(100, 100, 50, 200, Color(255,255,0,0));
            Bre_Line(0, 0, 200, 200, Color(255,0,0,255));
            EndPaint(hWnd,&ps);
        }
        break;
    case WM_CLOSE:
        DestroyWindow(hWnd);
        GdiplusShutdown(gdiToken);
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, uMsg, wParam, lParam);
    }
    return 0;
}

// x1,y1:始点の座標、x2,y2:終点の座標、clr:色
void Bre_Line(int x1, int y1, int x2, int y2, Color clr)
{
    int dx,dy;
    
    dx = abs(x2 - x1);
    dy = abs(y2 - y1);
    
    if(dx >= dy){
        Bre_Line_Sub(dx, dy, x1, y1, x2, y2, clr, true);
    }
    else{
        Bre_Line_Sub(dy, dx, y1, x1, y2, x2, clr, false);
    }
}// Bre_Line

void Bre_Line_Sub(int dx, int dy, int x1, int y1, int x2, int y2, Color clr, bool bSlantGently)
{
    int d,x,y,xend,i1,i2,iy;
    
    i1 = 2 * dy;
    i2 = 2 * (dy - dx);
    d = i1 - dx;
    if((x1 <= x2 && y1 <= y2) || (x1 >= x2 && y1 >= y2)){
        iy = 1;
    }
    else{
        iy = -1;
    }
    if(x1 > x2){
        x = x2;
        y = y2;
        xend = x1;
    }
    else{
        x = x1;
        y = y1;
        xend = x2;
    }
    Bre_Write_Pixel(x, y, clr, bSlantGently);
    while(x < xend){
        x ++;
        if(d < 0){
            d += i1;
        }
        else{
            y += iy;
            d += i2;
        }
        Bre_Write_Pixel(x, y, clr, bSlantGently);
    }
}// Bre_Line_Sub

void Bre_Write_Pixel(int x, int y, Color clr, bool bSlantGently)
{
    int x2,y2;
    
    if(bSlantGently){
        x2 = x;
        y2 = y;
    }
    else{
        x2 = y;
        y2 = x;
    }
    Graphics g(g_hDC);
    SolidBrush brush(clr);
    Rect rect(x2, y2, 1, 1);
    g.FillRectangle(&brush, rect);
}// Bre_Write_Pixel

2.円

// Michener, Bresenham の円アルゴリズム
// コンピュータ・グラフィックス 日本コンピュータ協会 P.452 〜 p.455
#include <windows.h>
#include <stdio.h>
#include <GdiPlus.h>

#define MYWNDCLASS  "Circle_CLASS"
#define MYWNDTITLE  "Circle"

using namespace     Gdiplus;
GdiplusStartupInput gdiSI;
ULONG_PTR           gdiToken;
HDC                 g_hDC;

LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
void Mich_Circle(POINT center, int radius, Color clr);
void Circle_Points(POINT center, int radius, int x, int y, Color clr);
void Write_Pixel(POINT center, int x, int y, Color clr);

LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch(uMsg)
    {
    case WM_CREATE:
        GdiplusStartup(&gdiToken,&gdiSI,NULL);  // GDI+オブジェクト初期化
        break;
    case WM_PAINT:
        PAINTSTRUCT     ps;
        g_hDC = BeginPaint(hWnd,&ps);
        {
            int     radius = 100;
            POINT   center = {radius, radius};
            Mich_Circle(center, radius, Color(255,255,0,0));
        }
        EndPaint(hWnd,&ps);
        break;
    case WM_CLOSE:
        GdiplusShutdown(gdiToken);
        DestroyWindow(hWnd);
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, uMsg, wParam, lParam);
    }
    return 0;
}// WndProc

// center:中心点、radius:半径、clr:色
void Mich_Circle(POINT center, int radius, Color clr)
{
    center.x -= radius;
    center.y -= radius;
    int x,y,d;
    x = 0;
    y = radius;
    d = 3 - 2 * radius;
    while(x < y){
        Circle_Points(center, radius, x, y, clr);
        if(d < 0){
            d += 4 * x + 6;
        }
        else{
            d += 4 * (x - y) + 10;
            y --;
        }
        x ++;
    }
    if(x == y){
        Circle_Points(center, radius, x, y, clr);
    }
}// Mich_Circle

void Circle_Points(POINT center, int radius, int x, int y, Color clr)
{
    x += radius;
    y += radius;
    int diameter = radius * 2;
    Write_Pixel(center, x, y, clr);
    Write_Pixel(center, y, x, clr);
    Write_Pixel(center, y, diameter-x, clr);
    Write_Pixel(center, x, diameter-y, clr);
    Write_Pixel(center, diameter-x, diameter-y, clr);
    Write_Pixel(center, diameter-y, diameter-x, clr);
    Write_Pixel(center, diameter-y, x, clr);
    Write_Pixel(center, diameter-x, y, clr);
}// Circle_Points

void Write_Pixel(POINT center, int x, int y, Color clr)
{
    Graphics g(g_hDC);
    SolidBrush brush(clr);
    Rect rect(center.x+x, center.y+y, 1, 1);
    g.FillRectangle(&brush, rect);
}// Write_Pixel

3.中間階調化(Halftoning)

 コンピュータ・グラフィックス 日本コンピュータ協会 P.607 〜 p.611

 中間階調化は、ディザ処理の1手法である。
 ディザリングは他に、誤差拡散法などがある。

 2×2 のパターンを使用する場合、2値画像では以下のように5階調を使える。

 0 0   1 0   1 0   1 1   1 1
 0 0   0 0   0 1   0 1   1 1


[size×size パターンの場合]
     
  1. 画像を 24bit色に変換する  
  2. 各画素値を 256 階調から size×size 階調に変換する  
  3. パターン pattern[y%size][x%size]+1 が、座標(x,y)の画素値より大きければ画素値を 0 にする
 パターンは他に、3×3, 4×4, 8×8 がある。
[2×2]          [3×3]             [4×4]                [8×8]
 0  2            6  8  4            0  8  2 10            0 32  8 40  2 34 10 42
 3  1            1  0  3           12  4 14  6           48 16 56 24 50 18 58 26
                 5  2  7            3 11  1  9           12 44  4 36 14 46  6 38
                                   15  7 13  5           60 28 52 20 62 30 54 22
                                                          3 35 11 43  1 33  9 41 
                                                         51 19 59 27 49 17 57 25
                                                         15 47  7 39 13 45  5 37 
                                                         63 31 55 23 61 29 53 21
// 8色、2×2行列のハーフトーニング
void Halftone()
{
    Color   clr;
    BYTE    r,g,b,c;
    float   p,av;
    int     size = 2;
    int pattern[4] = { 0, 2,
                       3, 1 };
    
    for(UINT y=0;y<Bmp->GetHeight();y++){
        for(UINT x=0;x<Bmp->GetWidth();x++){
            Bmp->GetPixel(x, y , &clr);
            p = (float) (pattern[y%size*size+x%size]+1)*255/(size*size);
            r = clr.GetR();
            g = clr.GetG();
            b = clr.GetB();
            
            if(p > r)   r = 0;
            else        r = 255;
            if(p > g)   g = 0;
            else        g = 255;
            if(p > b)   b = 0;
            else        b = 255;
            Bmp->SetPixel(x, y , Color(r,g,b));
        }
    }
}

4.領域塗り

 クリックした位置と同色で隣接している画素を塗り潰す。
 Win32API には、ExtFloodFill() がある。
 参考文献:高速塗りつぶし法(http://lee.phys.titech.ac.jp/~yasutake/PaintArea.html) 領域塗りとマジックワンドで使用するサンプル画像
// 領域塗り
// コンピュータ・グラフィックス 日本コンピュータ協会 P.459 〜 p.461
#include <windows.h>
#include <malloc.h>
#include <GdiPlus.h>

#define MYWNDCLASS      "Class_GDI_Bucket"
#define MYWNDTITLE      "GDI_Bucket"
#define SAFE_DELETE(p)  if (p){delete p;p=NULL;}

using namespace         Gdiplus;
GdiplusStartupInput     gdiSI;
ULONG_PTR               gdiToken;
Bitmap                  *Bmp;

// 分岐情報を保持するスタック
POINT   *g_pt_stack = NULL;
int     g_num_stack = 0;

LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
Status  Bucket(Bitmap *Bmp, Point pos, Color clr_aft);
bool    Bucket_Sub(Bitmap *Bmp, int wdh, int hgt, ARGB rgb, Color clr_aft);
POINT   Bucket_Sub_x(Bitmap *Bmp, int wdh, int hgt, int xPos, int yPos, ARGB rgb, Color clr_aft);
bool    Check_StackSize();
void    Read_Image(HWND hWnd, char *fname);

int APIENTRY WinMain(HINSTANCE hInst, HINSTANCE hInstPrev, LPSTR lpCmdLine, int nCmdShow)
{
    MSG        msg;
    WNDCLASSEX wc;
    HWND       hWnd;
    
    // 親ウィンドウ
    ZeroMemory(&wc, sizeof(wc));
    wc.cbSize           = sizeof(WNDCLASSEX);
    wc.lpfnWndProc      = (WNDPROC)WndProc;
    wc.hInstance        = hInst;
    wc.hIcon            = LoadIcon(hInst, IDI_APPLICATION);
    wc.hCursor          = LoadCursor(NULL, MAKEINTRESOURCE(IDC_ARROW));
    wc.hbrBackground    = (HBRUSH)(COLOR_BTNFACE + 1);
    wc.lpszMenuName     = NULL;
    wc.lpszClassName    = MYWNDCLASS;
    RegisterClassEx(&wc);
    
    hWnd = CreateWindowEx(WS_EX_CLIENTEDGE | WS_EX_ACCEPTFILES,
                MYWNDCLASS,
                MYWNDTITLE,
                WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_VISIBLE,
                CW_USEDEFAULT, CW_USEDEFAULT, 480, 320,
                NULL,
                NULL,
                hInst,
                NULL);

    while (GetMessage(&msg, NULL, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return msg.wParam;
}// WinMain()

// 親ウィンドウ プロシージャ
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg)
    {
    case WM_CREATE:
        GdiplusStartup(&gdiToken,&gdiSI,NULL);  // GDI+オブジェクト初期化
        char s[256];
        strcpy(s,MYWNDTITLE);
        strcat(s," - 画像ファイルをDrag&Dropしてください");
        SendMessage(hWnd,WM_SETTEXT,0,(LPARAM)s);
        return 0;
    case WM_SHOWWINDOW:
        if(__argc > 1){
            char    fname[MAX_PATH];
            strcpy(fname,__argv[1]);
            Read_Image(hWnd,fname);
            __argc = 0;
        }
        return 0;
    case WM_PAINT:
        HDC             hDC;
        PAINTSTRUCT     ps;
        hDC = BeginPaint(hWnd,&ps);
        if(Bmp){
            Graphics g(hDC);
            g.DrawImage(Bmp, 0, 0, Bmp->GetWidth(), Bmp->GetHeight());
        }
        EndPaint(hWnd,&ps);
        return 0;
    case WM_LBUTTONDOWN:
        if(Bmp){
            Point pos;
            pos.X = LOWORD(lParam);
            pos.Y = HIWORD(lParam);
            // 座標posの色を黄色で塗りつぶす
            Bucket(Bmp, pos, Color(255,255,255,0));
            InvalidateRect(hWnd,NULL,TRUE);
        }
        return 0;
    case WM_DROPFILES:
        HDROP   hDrop;
        hDrop = (HDROP) wParam;
        char    fname[MAX_PATH];
        DragQueryFile(hDrop, 0, fname, sizeof(fname));
        Read_Image(hWnd,fname);
        DragFinish(hDrop);
        InvalidateRect(hWnd,NULL,TRUE);
        return 0;
    case WM_CLOSE:
        SAFE_DELETE(Bmp);
        DestroyWindow(hWnd);
        GdiplusShutdown(gdiToken);
        PostQuitMessage(0);
        return 0;
    }
    return DefWindowProc(hWnd, uMsg, wParam, lParam);
}// WndProc

// 塗りつぶし処理のサブルーチン
// Bmp:元画像, pos:クリック座標, clr_aft:置き換える色
Status Bucket(Bitmap *Bmp, Point pos, Color clr_aft)
{
    Status      st = GenericError;
    int         wdh  = Bmp->GetWidth();
    int         hgt  = Bmp->GetHeight();
    Color       clr_bfr;        // 置き換えられる色
    
    st = Bmp->GetPixel(pos.X, pos.Y, &clr_bfr);
    if(st != Ok){
        MessageBox(NULL,"色を取得できません","",0);
        return st;
    }
    
    if(clr_bfr.GetValue() == clr_aft.GetValue()){
        MessageBox(NULL,"同色です","",0);
        return st;
    }
    
    g_pt_stack = (LPPOINT) malloc(1024*sizeof(POINT));
    
    g_num_stack = 0;
    g_pt_stack[g_num_stack].x = pos.X;
    g_pt_stack[g_num_stack].y = pos.Y;
    
    if(!Bucket_Sub(Bmp, wdh, hgt, clr_bfr.GetValue(), clr_aft)){
        free(g_pt_stack);
        MessageBox (NULL,"メモリが不足しています","",0);
        return OutOfMemory;
    }
    
    free(g_pt_stack);
    
    return st;
}// Bucket

// 塗りつぶし範囲を求める
bool Bucket_Sub(Bitmap *Bmp, int wdh, int hgt, ARGB rgb, Color clr_aft)
{
    // flag1,flag2 は、1つ左のピクセルが塗り込み対象ではない場合で
    // カレントピクセルが塗り込み対象の場合には、スタックにカレント座標を積むためのフラグ
    // flag3 は、そのY座標でスタックに分岐座標を積んだかどうかのフラグ
    bool    flag1,flag2,flag3;
    int     x;
    int     xPos,yPos;
    POINT   pt;
    Color   clr;
    
    while(g_num_stack >= 0){    // スタックが空になったら終了
        flag3 = 1;
        while (flag3){
            xPos = g_pt_stack[g_num_stack].x;
            yPos = g_pt_stack[g_num_stack].y;
            pt = Bucket_Sub_x(Bmp, wdh, hgt, xPos, yPos, rgb, clr_aft);
            flag1 = 0;
            flag2 = 0;
            flag3 = 0;
            for(x=pt.x; x<=pt.y; x++){
                // 上向き
                if(yPos > 0){
                    Bmp->GetPixel(x, yPos-1, &clr);
                    if(clr.GetValue() != rgb){
                        flag1 = 0;
                    }
                    else if(!flag1 && clr.GetValue() == rgb){
                        g_pt_stack[g_num_stack].x = x;
                        g_pt_stack[g_num_stack].y = yPos-1;
                        g_num_stack ++;
                        if(!Check_StackSize())
                            return 0;
                        flag1 = 1;
                        flag3 = 1;
                    }
                }
                // 下向き
                if(yPos < hgt - 1){
                    Bmp->GetPixel(x, yPos+1, &clr);
                    if(clr.GetValue() != rgb){
                        flag2 = 0;
                    }
                    else if(!flag2 && clr.GetValue() == rgb){
                        g_pt_stack[g_num_stack].x = x;
                        g_pt_stack[g_num_stack].y = yPos+1;
                        g_num_stack ++;
                        if(!Check_StackSize())
                            return 0;
                        flag2 = 1;
                        flag3 = 1;
                    }
                }
            }
            g_num_stack --;
            if(g_num_stack < 0) break;
        }
    }
    return 1;
}// Bucket_Sub

// 点(xPos, yPos)の横方向のRGB(r,g,b)色で連続したピクセル値をclr_aftで塗り替える
POINT Bucket_Sub_x(Bitmap *Bmp, int wdh, int hgt, int xPos, int yPos, ARGB rgb, Color clr_aft)
{
    int     x;
    POINT   pt;
    Color   clr;
    
    // 点(xPos, yPos)の左側を調べる
    x = 0;
    while (xPos >= x){
        Bmp->GetPixel(xPos-x, yPos, &clr);
        if(clr.GetValue() == rgb)
            Bmp->SetPixel(xPos-x, yPos, clr_aft);
        else
            break;
        x ++;
    }
    pt.x = xPos - (x - 1);
    
    // 点(xPos, yPos)の右側を調べる
    x = 1;
    while (x < (wdh - xPos)){
        Bmp->GetPixel(xPos+x, yPos, &clr);
        if(clr.GetValue() == rgb)
            Bmp->SetPixel(xPos+x, yPos, clr_aft);
        else
            break;
        x ++;
    }
    pt.y = xPos + (x - 1);
    
    return pt;
}// Bucket_Sub_x

// スタックを使い切ったら、スタックサイズを増やす
bool Check_StackSize()
{
    size_t size = _msize(g_pt_stack);
    if(g_num_stack * sizeof(POINT) >= size){
        g_pt_stack = (POINT*)realloc(g_pt_stack, size+512*sizeof(POINT));
        if(!g_pt_stack){
            return 0;
        }
    }
    return 1;
}// Check_StackSize

// ファイルから画像を読み込む
void Read_Image(HWND hWnd, char *fname)
{
    WCHAR   wfname[MAX_PATH];
    MultiByteToWideChar(CP_ACP,0,fname,-1,wfname,sizeof(wfname));
    SAFE_DELETE(Bmp);
    Bmp = Bitmap::FromFile(wfname,FALSE);
    
    char s[256];
    strcpy(s,MYWNDTITLE);
    strcat(s," - 画像ファイルをクリックしてください");
    SendMessage(hWnd,WM_SETTEXT,0,(LPARAM)s);
}// Read_Image

5.多角形塗り

 頂点座標が指定されている多角形を塗り潰す。
 本来は、バケットと呼ばれるポインタとバケットソートで行うらしいが、代わりに、データ番号(配列の添え字)とクイックソートを使った。

多角形塗り説明図

 多角形塗りは、スキャンラインアルゴリズムを使う。
 これは、多角形の頂点リストのY座標の最小値(上図では1)から最大値(上図では10)までスキャンラインを動かして、スキャンラインと各辺の交点を順に直線で結ぶことで色を塗る。
 上図の場合は、E2−E3間、E4−E5間において、それぞれのスキャンラインとの交点を結ぶ。
 スキャンラインと各辺との交点は、
 交点のX座標 = [辺の最小Y座標のX座標] + [辺の傾きの逆数の和]
 で求まる。
 先ず、辺データ リストを作成する。
 スキャンラインが下から上へ動くため、各辺データは総て、Y座標が低い方から高い方へのベクトルにする。
 傾きが0の辺データはリストに加えない。
 例えば、y == 3 のスキャンラインの場合、交点は、E1とE2の交点であるP1が2つと、E6との交点になるが、これでは適切に線を結ぶ事はできない。
 スキャンラインが y == 4 の場合も同様である。
 このような問題は、ある頂点において、隣接する2つの頂点のy座標が、その頂点のy座標よりも一方が低く、もう一方が高い場合に発生する。
 この問題を解決するには、P1やP5の点を1つ無くせばいい。
 そのためには、スキャンラインに最初に乗せるE2とE5のY座標を1つ上げればいい。
 例えば、E2の場合は、点(2,4)と点P2を端点とする辺となる。
 E5の場合は、点(10+(7-10)/(10-4),5)と点p4を端点とする辺となる。
 [最小Y座標のX座標]と[スキャンラインと辺との交点のX座標]は、この例外を除き、同じ数値にし、[次のデータ番号]は空(から)を意味する-1を入れる。

--- 辺データ リスト ---
E1E2E3E4E5E6
データ番号012345
最小Y座標136641
最大Y座標39910104
最小Y座標のX座標6255106
スキャンラインと辺との交点のX座標同上同上同上同上10+(7-10)/(10-4)同上
傾きの逆数(2-6)/(3-1)(2-2)/(9-3)(2-5)/(9-6)(7-5)/(10-6)(7-10)/(10-4)(10-6)/(4-1)
傾きの逆数の和同上同上同上同上上×2同上
次のデータ番号-1-1-1-1-1-1

 次に、この辺データ リストを元に各辺のスキャンラインのY座標ごとに集計した辺テーブルを作成する。

--- 辺テーブル ---
スキャンラインY座標データ番号
62(E3)→3(E4)
54(E5)
41(E2)
3-1
2-1
10(E1)→5(E6)

 スキャンラインのY座標を頂点リストのY座標の最小値(上図では1)から最大値(上図では10)までインクリメントして、以下の1〜5を繰り返す。
  1. アクティブ辺テーブル(AET)と呼ばれるスキャンライン情報に、現在のスキャンラインY座標と同じY座標の辺テーブルのデータを総て追加する
  2. AETの各データをX座標についてソートする
  3. AETのX座標対を直線で結び、色塗りをする
  4. スキャンラインのY座標が、辺の最大Y座標と等しくなったデータをAETから削除する
  5. AETの各データのX座標を次のスキャンラインでの値に更新する
// 多角形塗り
// コンピュータ・グラフィックス J.D.FOLEY/A.VAN DAM著 日本コンピュータ協会 p.466 〜 p.470
#include <windows.h>

#define MYWNDCLASS  "PolygonPaintingCLASS"
#define MYWNDTITLE  "PolygonPainting"
#define WIDTH       640     // ウィンドウの幅
#define HEIGHT      480     // ウィンドウの高さ
#define WIDTHBYTES(bits)    ((((bits) + 31)>>5)<<2)

typedef struct{
    int     y_min;      // 辺の最小Y座標
    int     y_max;      // 辺の最大Y座標
    int     x_min;      // 辺の最小Y座標のX座標
    int     x;          // スキャンラインと辺の交点のX座標
    double  slant;      // 辺の傾きの逆数
    double  slant_sum;  // 辺の傾きの逆数の和
    int     next;       // 次のデータ番号
}EDGE,*LPEDGE;

typedef struct{
    int     index;      // 最初のデータ番号
    int     last;       // 最後のデータ番号
}ET,*LPET;

HBITMAP ghBmpWork = NULL;
BYTE    *gpBitsWork = NULL;

LRESULT CALLBACK ProcWnd(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
void    Create_Work_Dib();
int     AETCmp(const EDGE *a, const EDGE *b);
void    Paint_Polygon(HDC hDC, POINT *pt, int pt_num);
void    Set_Data1(int idx_min, int idx_max, int y_min, int &data_ct, LPEDGE data, LPPOINT pos, LPET et);
void    Set_Data2(int idx_min, int idx_max, int y_min, int &data_ct, LPEDGE data, LPPOINT pos, LPET et);

LRESULT CALLBACK ProcWnd(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg)
    {
    case WM_PAINT:
        {
            HDC         hDC;
            PAINTSTRUCT ps;
            hDC = BeginPaint(hWnd,&ps);
            POINT   pt[] = {{2,3},{2,9},{5,6},{7,10},{10,4},{6,1}}; // 多角形の頂点座標
            int     pt_num = sizeof(pt) / sizeof(POINT);    // 頂点数
            
            // 多角形のサイズを40倍にする
            for(int i=0;i<pt_num;i++){
                pt[i].x *= 40;
                pt[i].y *= 40;
            }
            // 多角形塗り
            Create_Work_Dib();
            if(ghBmpWork){
                HDC     hDCcp;
                HBITMAP hBmpPre;
                hDCcp = CreateCompatibleDC(hDC);
                hBmpPre = (HBITMAP) SelectObject(hDCcp, ghBmpWork);
                Paint_Polygon(hDCcp, pt, pt_num);
                BitBlt(hDC, 0, 0, WIDTH, HEIGHT, hDCcp, 0, 0, SRCCOPY);
                SelectObject(hDCcp, hBmpPre);
                DeleteDC(hDCcp);
            }
            DeleteObject(ghBmpWork);
            EndPaint(hWnd,&ps);
        }
        return 0;
    case WM_CLOSE:
        DestroyWindow(hWnd);
        PostQuitMessage(0);
        return 0;
    default:
        break;
    }
    return DefWindowProc(hWnd, uMsg, wParam, lParam);
}// ProcWnd

// 作業用DIBの作成
void Create_Work_Dib()
{
    BITMAPINFO  bmi;
    
    ZeroMemory(&bmi, sizeof(bmi));
    bmi.bmiHeader.biSize            = sizeof(BITMAPINFOHEADER);
    bmi.bmiHeader.biWidth           = WIDTH;
    bmi.bmiHeader.biHeight          = HEIGHT;
    bmi.bmiHeader.biPlanes          = 1;
    bmi.bmiHeader.biBitCount        = 24;
    bmi.bmiHeader.biCompression     = BI_RGB;
    
    ghBmpWork = (HBITMAP) CreateDIBSection(NULL, &bmi, DIB_RGB_COLORS, (VOID **)&gpBitsWork, NULL, 0);
    // 作業用DIBの横幅を求める
    int stride = WIDTHBYTES(bmi.bmiHeader.biWidth * bmi.bmiHeader.biPlanes * bmi.bmiHeader.biBitCount);
    // 各行の余りバイト
    int rest = stride - bmi.bmiHeader.biWidth * (bmi.bmiHeader.biBitCount / 8);
    // 作業用DIBを白色で塗りつぶす
    int x,y;
    for(y=0; y<bmi.bmiHeader.biHeight; y++){
        for(x=0; x<bmi.bmiHeader.biWidth; x++){
            *(gpBitsWork ++) = 255;
            *(gpBitsWork ++) = 255;
            *(gpBitsWork ++) = 255;
        }
        for(x=0; x<rest; x++){
            gpBitsWork += rest;
        }
    }
}// Create_Work_Dib

// クイックソートの比較関数
int AETCmp(const EDGE *a, const EDGE *b)
{
    return (a->x - b->x);
}// AETCmp

// 多角形塗り
// pt       : 多角形の頂点座標
// pt_num   : 配列 pt の要素数
void Paint_Polygon(HDC hDC, POINT *pt, int pt_num)
{
    LPEDGE  data = NULL;    // 頂点リスト
    LPEDGE  aet = NULL;     // アクティブ辺テーブル(AET)
    LPET    et = NULL;      // 辺テーブル(ET)
    int i,j,y_min,y_max,y_num,prev,next,data_ct;
    
    // Y座標の最大値と最小値を求める
    y_min = pt[0].y;
    y_max = pt[0].y;
    for(i=1;i<pt_num;i++){
        if(pt[i].y < y_min)
            y_min = pt[i].y;
        else if(pt[i].y > y_max)
            y_max = pt[i].y;
    }
    // 頂点リストのメモリ確保
    data = (LPEDGE) malloc(sizeof(EDGE) * pt_num);
    if(!data){
        MessageBox(GetFocus(), "メモリ確保エラー", MYWNDTITLE, MB_ICONWARNING);
        return;
    }
    ZeroMemory(data, sizeof(EDGE) * pt_num);
    for(i=0;i<pt_num;i++){
        data[i].next = -1;
    }
    // AETのメモリ確保
    aet = (LPEDGE) malloc(sizeof(EDGE) * pt_num);
    if(!aet){
        MessageBox(GetFocus(), "メモリ確保エラー", MYWNDTITLE, MB_ICONWARNING);
        free(data);
        return;
    }
    ZeroMemory(aet, sizeof(EDGE) * pt_num);
    // ETの作成
    y_num = y_max - y_min + 1;
    et = (LPET) malloc(sizeof(ET) * y_num);
    if(!et){
        MessageBox(GetFocus(), "メモリ確保エラー", MYWNDTITLE, MB_ICONWARNING);
        free(data);
        free(aet);
        return;
    }
    for(i=0;i<y_num;i++){
        et[i].index = -1;
        et[i].last = -1;
    }
    data_ct = 0;    // data の要素数
    for(i=0;i<pt_num;i++){
        prev = i - 1;
        if(prev < 0){
            prev = pt_num - 1;
        }
        next = i + 1;
        if(next >= pt_num){
            next = 0;
        }
        if(pt[i].y < pt[prev].y && pt[i].y < pt[next].y){
            Set_Data1(i, prev, y_min, data_ct, data, pt, et);
            Set_Data1(i, next, y_min, data_ct, data, pt, et);
        }
        else if(pt[i].y > pt[prev].y && pt[i].y < pt[next].y){
            Set_Data2(i, next, y_min, data_ct, data, pt, et);
        }
        else if(pt[i].y > pt[next].y && pt[i].y < pt[prev].y){
            Set_Data2(i, prev, y_min, data_ct, data, pt, et);
        }
        else if(pt[i].y == pt[prev].y && pt[i].y < pt[next].y){
            Set_Data1(i, next, y_min, data_ct, data, pt, et);
        }
        else if(pt[i].y == pt[next].y && pt[i].y < pt[prev].y){
            Set_Data1(i, prev, y_min, data_ct, data, pt, et);
        }
        else if(pt[i].y == pt[next].y && pt[i].y == pt[prev].y){
            Set_Data1(i, i, y_min, data_ct, data, pt, et);
        }
    }
    // スキャン・ライン・アルゴリズムによる塗り潰し
    int aet_ct = 0; // AETの要素数
    for(i=y_min;i<=y_max;i++){
        int et_idx = i - y_min;
        if(et[et_idx].index != -1){
            aet[aet_ct ++] = data[et[et_idx].index];
            int next = data[et[et_idx].index].next;
            while(next != -1){
                aet[aet_ct ++] = data[next];
                next = data[next].next;
            }
        }
        // AET を x について昇順にソート
        qsort(aet, aet_ct, sizeof(EDGE), (int (*)(const void*, const void*))AETCmp);
        // スキャンラインの描画
        int ct = 0;
        while(ct < aet_ct - 1){
            COLORREF color = RGB(0, 0, 255);    // 青色
            for(j=aet[ct].x;j<=aet[ct+1].x;j++){
                SetPixel(hDC, j ,i, color);
            }
            ct += 2;
        }
        // i == y_max の要素を AET から削除
        for(j=aet_ct-1;j>=0;j--){
            if(i == aet[j].y_max){
                if(j < aet_ct-1){
                    memmove(aet+j, aet+(j+1), (aet_ct-(j+1))*sizeof(EDGE));
                }
                aet_ct --;
            }
        }
        // AET の各 x を次のスキャンラインでの値に更新
        for(j=0;j<aet_ct;j++){
            aet[j].x = (int)(aet[j].x_min + aet[j].slant_sum);
            aet[j].slant_sum += aet[j].slant;
        }
    }
    free(data);
    data = NULL;
    free(aet);
    aet = NULL;
    free(et);
    et = NULL;
}// Paint_Polygon

// 頂点リスト設定1
void Set_Data1(int idx_min, int idx_max, int y_min, int &data_ct, LPEDGE data, LPPOINT pos, LPET et)
{
    int et_idx = pos[idx_min].y - y_min;
    if(et[et_idx].last == -1)
        et[et_idx].index = data_ct;
    else
        data[et[et_idx].last].next = data_ct;
    et[et_idx].last = data_ct;
    
    data[data_ct].y_min = pos[idx_min].y;
    data[data_ct].y_max = pos[idx_max].y;
    data[data_ct].x_min = pos[idx_min].x;
    data[data_ct].x     = data[data_ct].x_min;
    data[data_ct].slant = (double)(pos[idx_max].x - pos[idx_min].x) / (pos[idx_max].y - pos[idx_min].y);
    data[data_ct].slant_sum = data[data_ct].slant;
    data_ct ++;
}// Set_Data1

// 頂点リスト設定2
void Set_Data2(int idx_min, int idx_max, int y_min, int &data_ct, LPEDGE data, LPPOINT pos, LPET et)
{
    int et_idx = pos[idx_min].y - y_min + 1;
    if(et[et_idx].last == -1)
        et[et_idx].index = data_ct;
    else
        data[et[et_idx].last].next = data_ct;
    et[et_idx].last = data_ct;
    
    double slant = (double)(pos[idx_max].x - pos[idx_min].x) / (pos[idx_max].y - pos[idx_min].y);
    data[data_ct].y_min = pos[idx_min].y;
    data[data_ct].y_max = pos[idx_max].y;
    data[data_ct].x_min = pos[idx_min].x;
    data[data_ct].x     = (int)(pos[idx_min].x + slant);
    data[data_ct].slant = slant;
    data[data_ct].slant_sum = data[data_ct].slant + slant;
    data_ct ++;
}// Set_Data2

6.アンチエイリアジング多角形

多角形塗りで、線分のピクセルに占める領域の比率を透明度とすると、アンチエイリアスをかけることが出来る。 以下のコードでは、多角形塗りと被る部分は省略。
アンチエイリアスをかける前(左)と後(右)
// アンチエイリアジング多角形
// コンピュータ・グラフィックス J.D.FOLEY/A.VAN DAM著 日本コンピュータ協会
// 多角形塗り(p.466 〜 p.470)
// アンチエイリアス(P.470 〜 p.471)
#include <windows.h>

#define MYWNDCLASS  "GDI_AntialiasCLASS"
#define MYWNDTITLE  "GDI_Antialias"

typedef struct{
    BYTE    red;        // 赤
    BYTE    green;      // 緑
    BYTE    blue;       // 蒼
}COLOR;

typedef struct{
    int     y_min;      // 辺の最小Y座標
    int     y_max;      // 辺の最大Y座標
    int     x_min;      // 辺の最小Y座標のX座標
    int     x;          // スキャンラインと辺の交点のX座標
    double  d;          // 辺のそのピクセルに占める領域の比率
    double  slant;      // 辺の傾きの逆数
    double  slant_sum;  // 辺の傾きの逆数の和
    int     next;       // 次のデータ番号
}EDGE,*LPEDGE;

// 多角形塗り
// pt       : 多角形の頂点座標
// pt_num   : 配列 pt の要素数
void Paint_Polygon(HDC hDC, POINT *pt, int pt_num)
{
    LPEDGE  data = NULL;    // 頂点リスト
    LPEDGE  aet = NULL;     // アクティブ辺テーブル(AET)
    LPET    et = NULL;      // 辺テーブル(ET)
    int     i,j,y_min,y_max,y_num,prev,next,data_ct;
    COLOR   color_brush = {  0,   0, 255};  // 塗りつぶし色(青)
    COLOR   color_back  = {255, 255, 255};  // 背景色(白)
    COLOR   color_dst;                      // アンチエイリアス色
    
    // Y座標の最大値と最小値を求める
    y_min = pt[0].y;
    y_max = pt[0].y;
    for(i=1;i<pt_num;i++){
        if(pt[i].y < y_min)
            y_min = pt[i].y;
        else if(pt[i].y > y_max)
            y_max = pt[i].y;
    }
    // 頂点リストのメモリ確保
    data = (LPEDGE) malloc(sizeof(EDGE) * pt_num);
    if(!data){
        MessageBox(GetFocus(), "メモリ確保エラー", MYWNDTITLE, MB_ICONWARNING);
        return;
    }
    ZeroMemory(data, sizeof(EDGE) * pt_num);
    for(i=0;i<pt_num;i++){
        data[i].next = -1;
    }
    // AETのメモリ確保
    aet = (LPEDGE) malloc(sizeof(EDGE) * pt_num);
    if(!aet){
        MessageBox(GetFocus(), "メモリ確保エラー", MYWNDTITLE, MB_ICONWARNING);
        free(data);
        return;
    }
    ZeroMemory(aet, sizeof(EDGE) * pt_num);
    // ETの作成
    y_num = y_max - y_min + 1;
    et = (LPET) malloc(sizeof(ET) * y_num);
    if(!et){
        MessageBox(GetFocus(), "メモリ確保エラー", MYWNDTITLE, MB_ICONWARNING);
        free(data);
        free(aet);
        return;
    }
    for(i=0;i<y_num;i++){
        et[i].index = -1;
        et[i].last = -1;
    }
    data_ct = 0;    // data の要素数
    for(i=0;i<pt_num;i++){
        prev = i - 1;
        if(prev < 0){
            prev = pt_num - 1;
        }
        next = i + 1;
        if(next >= pt_num){
            next = 0;
        }
        if(pt[i].y < pt[prev].y && pt[i].y < pt[next].y){
            Set_Data1(i, prev, y_min, data_ct, data, pt, et);
            Set_Data1(i, next, y_min, data_ct, data, pt, et);
        }
        else if(pt[i].y > pt[prev].y && pt[i].y < pt[next].y){
            Set_Data2(i, next, y_min, data_ct, data, pt, et);
        }
        else if(pt[i].y > pt[next].y && pt[i].y < pt[prev].y){
            Set_Data2(i, prev, y_min, data_ct, data, pt, et);
        }
        else if(pt[i].y == pt[prev].y && pt[i].y < pt[next].y){
            Set_Data1(i, next, y_min, data_ct, data, pt, et);
        }
        else if(pt[i].y == pt[next].y && pt[i].y < pt[prev].y){
            Set_Data1(i, prev, y_min, data_ct, data, pt, et);
        }
        else if(pt[i].y == pt[next].y && pt[i].y == pt[prev].y){
            Set_Data1(i, i, y_min, data_ct, data, pt, et);
        }
    }
    // スキャン・ライン・アルゴリズムによる塗り潰し
    int aet_ct = 0; // AETの要素数
    for(i=y_min;i<=y_max;i++){
        int et_idx = i - y_min;
        if(et[et_idx].index != -1){
            aet[aet_ct ++] = data[et[et_idx].index];
            int next = data[et[et_idx].index].next;
            while(next != -1){
                aet[aet_ct ++] = data[next];
                next = data[next].next;
            }
        }
        // AET を x について昇順にソート
        qsort(aet, aet_ct, sizeof(EDGE), (int (*)(const void*, const void*))AETCmp);
        // スキャンラインの描画
        int ct = 0;
        while(ct < aet_ct - 1){
            double d2;
            d2 = 1 - aet[ct].d;
            color_dst.red   = (BYTE)(color_back.red   * aet[ct].d + color_brush.red   * d2);
            color_dst.green = (BYTE)(color_back.green * aet[ct].d + color_brush.green * d2);
            color_dst.blue  = (BYTE)(color_back.blue  * aet[ct].d + color_brush.blue  * d2);
            SetPixel(hDC, aet[ct].x , i, RGB(color_dst.red, color_dst.green, color_dst.blue));
            d2 = 1 - aet[ct+1].d;
            color_dst.red   = (BYTE)(color_back.red   * d2 + color_brush.red   * aet[ct+1].d);
            color_dst.green = (BYTE)(color_back.green * d2 + color_brush.green * aet[ct+1].d);
            color_dst.blue  = (BYTE)(color_back.blue  * d2 + color_brush.blue  * aet[ct+1].d);
            SetPixel(hDC, aet[ct+1].x , i, RGB(color_dst.red, color_dst.green, color_dst.blue));
            for(j=aet[ct].x+1;j<aet[ct+1].x;j++){
                SetPixel(hDC, j ,i, RGB(color_brush.red, color_brush.green, color_brush.blue));
            }
            ct += 2;
        }
        // i == y_max の要素を AET から削除
        for(j=aet_ct-1;j>=0;j--){
            if(i == aet[j].y_max){
                if(j < aet_ct-1){
                    memmove(aet+j, aet+(j+1), (aet_ct-(j+1))*sizeof(EDGE));
                }
                aet_ct --;
            }
        }
        // AET の各 x を次のスキャンラインでの値に更新
        for(j=0;j<aet_ct;j++){
            double sum = aet[j].x_min + aet[j].slant_sum;
            aet[j].x = (int) sum;
            aet[j].d = sum - aet[j].x;
            aet[j].slant_sum += aet[j].slant;
        }
    }
    free(data);
    data = NULL;
    free(aet);
    aet = NULL;
    free(et);
    et = NULL;
}// Paint_Polygon

7.線分のクリッピング

// Cohen-Sutherland クリッピング・アルゴリズム
// コンピュータ・グラフィックス J.D.FOLEY/A.VAN DAM著 日本コンピュータ協会 p.152 〜 p.155
#include <windows.h>

#define MYWNDCLASS  "ClippingLineCLASS"
#define MYWNDTITLE  "ClippingLine"

typedef struct{
    POINT   pt1,pt2;    // 線分の両端点の座標
    WORD    outcode1;   // pt1 の外コード フラグ
    WORD    outcode2;   // pt2 の外コード フラグ
}LINE,*LPLINE;

LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
void Cohen_CilpLine(HDC hDC, LINE *line, RECT rc);
void Set_OutCodes(POINT pt, RECT rc, WORD *outcode);
bool Reject_Check(WORD outcode1, WORD outcode2);
bool Accept_Check(WORD outcode1, WORD outcode2);
void Swap_Point(LINE *line);

LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg)
    {
    case WM_PAINT:
        HDC         hDC;
        PAINTSTRUCT ps;
        hDC = BeginPaint(hWnd,&ps);
        {
            LINE        line;
            static RECT rc = {200,100,500,300};
            
            Rectangle(hDC, rc.left, rc.top, rc.right, rc.bottom);
            
            ZeroMemory(&line, sizeof(line));
            line.pt1.x = 100;
            line.pt1.y = 200;
            line.pt2.x = 600;
            line.pt2.y = 0;
            
            Cohen_CilpLine(hDC, &line, rc);
        }
        EndPaint(hWnd,&ps);
        return 0;
    case WM_CLOSE:
        DestroyWindow(hWnd);
        PostQuitMessage(0);
        return 0;
    default:
        break;
    }
    return DefWindowProc(hWnd, uMsg, wParam, lParam);
}// WndProc

// Cohen-Sutherland クリッピング・アルゴリズム
void Cohen_CilpLine(HDC hDC, LINE *line, RECT rc)
{
    bool bAccept,bReject,bDone;
    
    bAccept = false;
    bReject = false;
    bDone = false;
    
    do{
        Set_OutCodes(line->pt1, rc, &line->outcode1);
        Set_OutCodes(line->pt2, rc, &line->outcode2);
        bReject = Reject_Check(line->outcode1, line->outcode2);
        if(bReject){
            bDone = true;
        }
        else{
            bAccept = Accept_Check(line->outcode1, line->outcode2);
            if(bAccept){
                bDone = true;
            }
            else{
                if(!line->outcode1){
                    Swap_Point(line);
                }
                if(line->outcode1 & 0x1000){
                    line->pt1.x += (line->pt2.x - line->pt1.x) * (rc.bottom - line->pt1.y)
                                   / (line->pt2.y - line->pt1.y);
                    line->pt1.y = rc.bottom;
                }
                else if(line->outcode1 & 0x0100){
                    line->pt1.x += (line->pt2.x - line->pt1.x) * (rc.top - line->pt1.y)
                                   / (line->pt2.y - line->pt1.y);
                    line->pt1.y = rc.top;
                }
                else if(line->outcode1 & 0x0010){
                    line->pt1.y += (line->pt2.y - line->pt1.y) * (rc.right - line->pt1.x)
                                   / (line->pt2.x - line->pt1.x);
                    line->pt1.x = rc.right;
                }
                else if(line->outcode1 & 0x0001){
                    line->pt1.y += (line->pt2.y - line->pt1.y) * (rc.left - line->pt1.x)
                                   / (line->pt2.x - line->pt1.x);
                    line->pt1.x = rc.left;
                }
            }
        }
    }while(!bDone);
    
    if(bAccept){
        POINT pt[2] = {{line->pt1.x,line->pt1.y}, {line->pt2.x,line->pt2.y}};
        Polyline(hDC, pt, 2);
    }
}// Cohen_CilpLine

// 外コードの設定
void Set_OutCodes(POINT pt, RECT rc, WORD *outcode)
{
    *outcode = 0;
    if(pt.x < rc.left){
        *outcode |= 0x0001;
    }
    if(pt.x > rc.right){
        *outcode |= 0x0010;
    }
    if(pt.y < rc.top){
        *outcode |= 0x0100;
    }
    if(pt.y > rc.bottom){
        *outcode |= 0x1000;
    }
}// Set_OutCodes

// 明らかな拒否テスト
// 戻り値   : true→拒否、false→その他
bool Reject_Check(WORD outcode1, WORD outcode2)
{
    if(outcode1 & outcode2){
        return true;
    }
    return false;
}// Reject_Check

// 明らかな受け入れテスト
// 戻り値   : true→受け入れ、false→その他
bool Accept_Check(WORD outcode1, WORD outcode2)
{
    if(!outcode1 && !outcode2){
        return true;
    }
    return false;
}// Accept_Check

// 端点の交換
void Swap_Point(LINE *line)
{
    POINT   pt;
    WORD    outcode;
    pt = line->pt2;
    outcode = line->outcode2;
    line->pt2 = line->pt1;
    line->outcode2 = line->outcode1;
    line->pt1 = pt;
    line->outcode1 = outcode;
}// Swap_Point

8.多角形のクリッピング

// Sutherland-Hodgman クリッピング・アルゴリズム
// 〔ただし、(外→外)の線分がクリップ領域を横切る場合には対応していない〕
// コンピュータ・グラフィックス J.D.FOLEY/A.VAN DAM著 日本コンピュータ協会 p.462 〜 p.464
#include <windows.h>
#include <malloc.h>

#define MYWNDCLASS  "ClippingPolygonCLASS"
#define MYWNDTITLE  "ClippingPolygon"

// 浮動小数点数を四捨五入して整数にする
inline int Round(float val)
{
    return (int)(val + 0.5f);
}// Round

typedef struct{
    POINT   pt1,pt2;    // 線分の両端点の座標
}EDGE,*LPEDGE;

typedef struct{
    POINT   pt1,pt2;    // 線分の両端点の座標
    POINT   ptMin,ptMax;// 線分の座標の最小値と最大値
    float   slant;      // 線分の傾き
    float   height;     // 直線とY軸との交点のY座標
}EDGE2,*LPEDGE2;

typedef struct{
    POINT   pt;     // 座標
    short   flagIS; // 交点フラグ(0:交点以外, 1:外→内, 2:内→外)
    long    idxEdge;// 交点が属するクリップ領域の辺の番号
}POINTIS,*LPPOINTIS;

LRESULT CALLBACK ProcWnd(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
bool    Is_Inside(POINT ptSrc, EDGE2 *eBoundary, int eBoundary_num);
void    Set_DestPoint(POINT ptSrc, POINTIS *ptDst, int *ptDst_num);
void    Set_PtMinMax(LPPOINT ptMin, LPPOINT ptMax, POINT pt1, POINT pt2);
void    Set_DestPoint(POINTIS ptisSrc, POINTIS *ptDst, int *ptDst_num, short flagIS);
POINTIS Get_IntersectPoint(POINT s, POINT p, EDGE2 *eBoundary, int eBoundary_num);
int     Get_LengthSegment(EDGE2 *eBoundary, int b_idx, POINTIS *ptDst, int d_idx,
                          bool bClockwiseBoundary);
void    S_H_Clipping(HDC hDC, POINT *ptSrc, int ptSrc_num,
                     EDGE2 *eBoundary, int eBoundary_num, bool bClockwiseBoundary);
bool    Is_Clockwise(POINT *pVtx, int vtx_num);

LRESULT CALLBACK ProcWnd(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg)
    {
    case WM_PAINT:
        HDC         hDC;
        PAINTSTRUCT ps;
        hDC = BeginPaint(hWnd,&ps);
        {
            // 多角形の頂点座標
            // (右回り)
            static POINT    ptSrc[] = {{600,200},{450,200},{450,350},{250,350},
                                       {250,200},{100,200},{350,25}};
//          static POINT    ptSrc[] = {{100,50},{100,200},{300,200},{300,350},
//                                     {50,350},{50,25},{450,25},{450,200}};
            // (左回り)
//          static POINT    ptSrc[] = {{600,200},{350,25},{100,200},{250,200},
//                                     {250,350},{450,350},{450,200}};
//          static POINT    ptSrc[] = {{100,50},{450,200},{450,25},{50,25},
//                                     {50,350},{300,350},{300,200},{100,200}};
            static RECT     rcBoundary = {200,100,500,300};             // クリップ領域
            int             ptSrc_num = sizeof(ptSrc) / sizeof(POINT);  // 多角形の頂点数
            int             i;
            
            // 多角形の描画
            for(i=0;i<ptSrc_num;i++){
                int next = i + 1;
                if(next >= ptSrc_num){
                    next = 0;
                }
                POINT pt[2] = {{ptSrc[i].x, ptSrc[i].y}, {ptSrc[next].x, ptSrc[next].y}};
                Polyline(hDC, pt, 2);
            }
            // クリップ領域の頂点座標
            // (右回り)
            POINT ptBoundary[] = {{rcBoundary.left, rcBoundary.top},
                                {rcBoundary.right, rcBoundary.top},
                                {rcBoundary.right, rcBoundary.bottom},
                                {rcBoundary.left, rcBoundary.bottom}};
/*          // (左回り)
            POINT ptBoundary[] = {{rcBoundary.left, rcBoundary.top},
                                {rcBoundary.left, rcBoundary.bottom},
                                {rcBoundary.right, rcBoundary.bottom},
                                {rcBoundary.right, rcBoundary.top}};
*/          int ptBoundary_num = sizeof(ptBoundary) / sizeof(POINT);
            // クリップ領域は右回りか?
            bool bClockwiseBoundary = Is_Clockwise(ptBoundary, ptBoundary_num);
            // クリップ領域を構成する線分
            LPEDGE2 eBoundary = (LPEDGE2) malloc(ptBoundary_num * sizeof(EDGE2));
            POINT ptBd1,ptBd2;
            for(i=0;i<ptBoundary_num;i++){
                int next = i + 1;
                if(next >= ptBoundary_num){
                    next = 0;
                }
                if(bClockwiseBoundary){
                    ptBd1 = ptBoundary[i];
                    ptBd2 = ptBoundary[next];
                }
                else{
                    ptBd1 = ptBoundary[next];
                    ptBd2 = ptBoundary[i];
                }
                eBoundary[i].pt1 = ptBd1;
                eBoundary[i].pt2 = ptBd2;
                if(eBoundary[i].pt2.x != eBoundary[i].pt1.x){
                    eBoundary[i].slant = (float)(eBoundary[i].pt2.y - eBoundary[i].pt1.y) / 
                                                (eBoundary[i].pt2.x - eBoundary[i].pt1.x);
                    eBoundary[i].height = (float)eBoundary[i].pt1.y - eBoundary[i].slant *
                                                 eBoundary[i].pt1.x;
                }
                else{
                    eBoundary[i].slant = 0;
                    eBoundary[i].height = 0;
                }
                Set_PtMinMax(&eBoundary[i].ptMin, &eBoundary[i].ptMax,
                             eBoundary[i].pt1, eBoundary[i].pt2);
            }
            int eBoundary_num = ptBoundary_num;
            
            // クリップ領域の描画
            for(i=0;i<eBoundary_num;i++){
                POINT pt[2] = {{eBoundary[i].pt1.x, eBoundary[i].pt1.y},
                               {eBoundary[i].pt2.x, eBoundary[i].pt2.y}};
                Polyline(hDC, pt, 2);
            }
            
            // 多角形のクリッピング
            S_H_Clipping(hDC, ptSrc, ptSrc_num, eBoundary, eBoundary_num, bClockwiseBoundary);
            
            free(eBoundary);
        }
        EndPaint(hWnd,&ps);
        return 0;
    case WM_CLOSE:
        DestroyWindow(hWnd);
        PostQuitMessage(0);
        return 0;
    default:
        break;
    }
    return DefWindowProc(hWnd, uMsg, wParam, lParam);
}// ProcWnd

// 線分ptが辺eBoundaryの内側にあればtrueを返す
bool Is_Inside(POINT ptSrc, EDGE2 *eBoundary, int eBoundary_num)
{
    bool    bIn = true;
    for(int i=0;i<eBoundary_num;i++){
        POINT   vecSrc = {{ptSrc.x - eBoundary[i].pt1.x}, {ptSrc.y - eBoundary[i].pt1.y}};
        POINT   vecBoundary = {{eBoundary[i].pt2.x - eBoundary[i].pt1.x},
                               {eBoundary[i].pt2.y - eBoundary[i].pt1.y}};
        int     cross_z = vecSrc.x * vecBoundary.y - vecSrc.y * vecBoundary.x;
        if(cross_z >= 0){   // 外積のZ成分が負ではない場合
            bIn = false;
        }
    }
    return bIn;
}// Is_Inside

// 出力データの設定
void Set_DestPoint(POINT ptSrc, POINTIS *ptDst, int *ptDst_num)
{
    ptDst[*ptDst_num].pt        = ptSrc;
    ptDst[*ptDst_num].idxEdge   = 0;
    ptDst[*ptDst_num].flagIS    = 0;
    (*ptDst_num) ++;
}// Set_DestPoint

// 出力データの設定(オーバーロード)
void Set_DestPoint(POINTIS ptisSrc, POINTIS *ptDst, int *ptDst_num, short flagIS)
{
    ptDst[*ptDst_num].pt        = ptisSrc.pt;
    ptDst[*ptDst_num].idxEdge   = ptisSrc.idxEdge;
    ptDst[*ptDst_num].flagIS    = flagIS;
    (*ptDst_num) ++;
}// Set_DestPoint

// 座標の最小値と最大値を設定する
void Set_PtMinMax(LPPOINT ptMin, LPPOINT ptMax, POINT pt1, POINT pt2)
{
    if(pt1.x < pt2.x){
        ptMin->x = pt1.x;
        ptMax->x = pt2.x;
    }
    else{
        ptMin->x = pt2.x;
        ptMax->x = pt1.x;
    }
    if(pt1.y < pt2.y){
        ptMin->y = pt1.y;
        ptMax->y = pt2.y;
    }
    else{
        ptMin->y = pt2.y;
        ptMax->y = pt1.y;
    }
}// Set_PtMinMax

// クリップ領域と多角形の辺の交点を求める
POINTIS Get_IntersectPoint(POINT s, POINT p, EDGE2 *eBoundary, int eBoundary_num)
{
    POINTIS ptIS = {0, 0, 0, 0};
    POINT   ptMin,ptMax;
    float   slant[2],height[2];
    int     x,y;
    for(int i=0;i<eBoundary_num;i++){
        Set_PtMinMax(&ptMin, &ptMax, s, p);
        // ptMin と ptMax で形成される矩形は、2つの線分でそれぞれ形成される矩形の重なる部分とする
        if(ptMin.x < eBoundary[i].ptMin.x){
            ptMin.x = eBoundary[i].ptMin.x;
        }
        if(ptMin.y < eBoundary[i].ptMin.y){
            ptMin.y = eBoundary[i].ptMin.y;
        }
        if(ptMax.x > eBoundary[i].ptMax.x){
            ptMax.x = eBoundary[i].ptMax.x;
        }
        if(ptMax.y > eBoundary[i].ptMax.y){
            ptMax.y = eBoundary[i].ptMax.y;
        }
        if(p.x != s.x && eBoundary[i].pt2.x != eBoundary[i].pt1.x){
            slant[0] = (float)(p.y - s.y) / (p.x - s.x);
            slant[1] = eBoundary[i].slant;
            height[0] = (float)s.y - slant[0] * s.x;
            height[1] = eBoundary[i].height;
            if(slant[0] != slant[1]){
                float fx = (height[1] - height[0]) / (slant[0] - slant[1]);
                x = Round(fx);
                y = Round(slant[1] * fx + height[1]);
                if(x >= ptMin.x && x <= ptMax.x && y >= ptMin.y && y <= ptMax.y){
                    ptIS.pt.x = x;
                    ptIS.pt.y = y;
                    ptIS.idxEdge = i;
                    break;
                }
            }
        }
        else if(p.x != s.x && eBoundary[i].pt2.x == eBoundary[i].pt1.x){
            slant[0] = (float)(p.y - s.y) / (p.x - s.x);
            height[0] = (float)s.y - slant[0] * s.x;
            x = eBoundary[i].pt1.x;
            y = Round(slant[0] * eBoundary[i].pt1.x + height[0]);
            if(x >= ptMin.x && x <= ptMax.x && y >= ptMin.y && y <= ptMax.y){
                ptIS.pt.x = x;
                ptIS.pt.y = y;
                ptIS.idxEdge = i;
                break;
            }
        }
        else if(p.x == s.x && eBoundary[i].pt2.x != eBoundary[i].pt1.x){
            slant[1] = eBoundary[i].slant;
            height[1] = eBoundary[i].height;
            x = s.x;
            y = Round(slant[1] * s.x + height[1]);
            if(x >= ptMin.x && x <= ptMax.x && y >= ptMin.y && y <= ptMax.y){
                ptIS.pt.x = x;
                ptIS.pt.y = y;
                ptIS.idxEdge = i;
                break;
            }
        }
    }
    return ptIS;
}// Get_IntersectPoint

// 線分の長さ(ただし、計算速度重視の比較用で正確な長さではない)を返す
int Get_LengthSegment(EDGE2 *eBoundary, int b_idx, POINTIS *ptDst, int d_idx,
                      bool bClockwiseBoundary)
{
    POINT ptBd;
    if(bClockwiseBoundary){
        ptBd = eBoundary[b_idx].pt1;
    }
    else{
        ptBd = eBoundary[b_idx].pt2;
    }
    return (abs(ptBd.x - ptDst[d_idx].pt.x) + abs(ptBd.y - ptDst[d_idx].pt.y));
}// Get_LengthSegment

// Sutherland-Hodgman クリッピング・アルゴリズム
void S_H_Clipping(HDC hDC, POINT *ptSrc, int ptSrc_num,
                  EDGE2 *eBoundary, int eBoundary_num, bool bClockwiseBoundary)
{
    POINT       p,s;
    POINTIS     is;
    int         i;
    int         ptDst_num;  // ptDstの要素数
    LPPOINTIS   ptDst = (LPPOINTIS) malloc(0 * sizeof(POINTIS));// 出力する頂点座標
    
    ptDst_num = 0;
    s = ptSrc[ptSrc_num-1];
    
    for(i=0;i<ptSrc_num;i++){
        // バッファサイズが足りなくなったら追加
        size_t size = _msize(ptDst);
        if((UINT)(ptDst_num + 2) > size / sizeof(POINTIS)){
            ptDst = (LPPOINTIS) realloc(ptDst, (ptDst_num + 2) * sizeof(POINTIS));
        }
        // 多角形の各辺の両端点が、クリップ領域の内か外かの4つの場合分け
        // (外→内、内→内、内→外、外→外)で出力頂点を求める
        p = ptSrc[i];
        if(Is_Inside(p, eBoundary, eBoundary_num)){
            if(Is_Inside(s, eBoundary, eBoundary_num)){ // 内→内
                Set_DestPoint(p, ptDst, &ptDst_num);
            }
            else{   // 外→内
                is = Get_IntersectPoint(s, p, eBoundary, eBoundary_num);
                Set_DestPoint(is, ptDst, &ptDst_num, 1);
                Set_DestPoint(p, ptDst, &ptDst_num);
            }
        }
        else{
            if(Is_Inside(s, eBoundary, eBoundary_num)){ // 内→外
                is = Get_IntersectPoint(s, p, eBoundary, eBoundary_num);
                Set_DestPoint(is, ptDst, &ptDst_num, 2);
            }
        }
        s = p;
    }
    
    // 交点のアウトとインをつなぐ線分を追加
    LPEDGE  eDst = (LPEDGE) malloc(ptDst_num * sizeof(EDGE));   // 出力する線分
    ZeroMemory(eDst, ptDst_num * sizeof(EDGE));
    int     eDst_num = ptDst_num;   // eDstの要素数
    int     out_in_index;           // 現在の(外→内)交点の ptDst の要素番号
    int     idx_b_out;              // 現在の(内→外)交点の eBoundary の要素番号
    int     idx_b_in;               // 現在の(外→内)交点の eBoundary の要素番号
    int     k,ct;
    int     sz = 1;                 // ct の増分
    bool    bClockwiseSrc = Is_Clockwise(ptSrc, ptSrc_num);
    
    if((!bClockwiseSrc && bClockwiseBoundary) || (bClockwiseSrc && !bClockwiseBoundary)){
        sz = -1;
    }
    
    int eDst_idx = 0;
    for(i=0;i<ptDst_num;i++){
        int next = i + 1;
        if(next >= ptDst_num){
            next = 0;
        }
        if(ptDst[i].flagIS == 2){   // (内→外)の時
            eDst_num --;        // 後で余分に加算するため、予め引いておく
            // 最も近い(外→内)交点を求める
            out_in_index = -1;
            int j2,dst_i,dst_k,dst_k_min;
            int j_start = ptDst[i].idxEdge;
            int j_end;
            if(sz == 1){
                j_end = j_start + eBoundary_num;
            }
            else{
                j_end = j_start - eBoundary_num;
            }
            dst_i = Get_LengthSegment(eBoundary, j_start, ptDst, i, bClockwiseBoundary);
            ct = j_start;
            do{
                j2 = ct;
                if(j2 >= eBoundary_num){
                    j2 -= eBoundary_num;
                }
                if(j2 <= -1){
                    j2 += eBoundary_num;
                }
                dst_k_min = 0x7fffffff; // 最上位ビットは符号、Win32 の int は long
                for(k=0;k<ptDst_num;k++){
                    if(ptDst[k].idxEdge == j2 && ptDst[k].flagIS == 1){ // (外→内)の時
                        dst_k = Get_LengthSegment(eBoundary, j2, ptDst, k, bClockwiseBoundary);
                        int dst1,dst2;
                        if(sz == 1){
                            dst1 = dst_i;
                            dst2 = dst_k;
                        }
                        else{
                            dst1 = dst_k;
                            dst2 = dst_i;
                        }
                        if((ct == j_start && dst1 < dst2) || ct != j_start){// ct を j2 にしない
                            if(dst_k < dst_k_min){
                                dst_k_min = dst_k;
                                out_in_index = k;
                            }
                        }
                    }
                }
                if(out_in_index != -1){
                    break;
                }
                ct += sz;
            }while(ct != j_end);    // 最初の辺から一周して最初の辺まで
            if(out_in_index == -1){
                SendMessage(GetFocus(), WM_SETTEXT, 0, (LPARAM)"(外→内)交点がありません");
                break;
            }
            // (内→外)から(外→内)への線分を追加する
            idx_b_out = ptDst[i].idxEdge;
            idx_b_in  = ptDst[out_in_index].idxEdge;
            ct = idx_b_out;
            do{
                if(ct >= eBoundary_num){
                    ct -= eBoundary_num;
                }
                if(ct <= -1){
                    ct += eBoundary_num;
                }
                if(ct == idx_b_in){ // 最後の場合
                    eDst_num ++;
                    eDst = (LPEDGE) realloc(eDst, eDst_num * sizeof(EDGE));
                    if(ct == idx_b_out){    // 最初と最後が同じ場合
                        eDst[eDst_idx].pt1 = ptDst[i].pt;
                    }
                    else{
                        eDst[eDst_idx].pt1 = eDst[eDst_idx-1].pt2;
                    }
                    eDst[eDst_idx].pt2 = ptDst[out_in_index].pt;
                    eDst_idx ++;
                    break;
                }
                // 最後以外は、クリップ領域の辺を(外→内)交点まで回る
                eDst_num ++;
                eDst = (LPEDGE) realloc(eDst, eDst_num * sizeof(EDGE));
                int pre_eDst_idx = eDst_idx - 1;
                if(pre_eDst_idx < 0){   // 最初の場合
                    eDst[eDst_idx].pt1 = ptDst[i].pt;
                }
                else{
                    eDst[eDst_idx].pt1 = eDst[pre_eDst_idx].pt2;
                }
                if(bClockwiseSrc){
                    eDst[eDst_idx].pt2 = eBoundary[ct].pt2;
                }
                else{
                    eDst[eDst_idx].pt2 = eBoundary[ct].pt1;
                }
                eDst_idx ++;
                ct += sz;
            }while((ct + sz) != idx_b_in);
        }
        else{       // (内→外)以外の時
            eDst[eDst_idx].pt1 = ptDst[i].pt;
            eDst[eDst_idx].pt2 = ptDst[next].pt;
            eDst_idx ++;
        }
    }
    
    // クリップされた多角形の描画
    HPEN hPen,hPenOld;
    hPen = CreatePen(PS_SOLID, 1, RGB(255,0,0));
    hPenOld = (HPEN) SelectObject(hDC, hPen);
    for(i=0;i<eDst_num;i++){
        POINT pt[2] = {{eDst[i].pt1.x, eDst[i].pt1.y}, {eDst[i].pt2.x, eDst[i].pt2.y}};
        Polyline(hDC, pt, 2);
    }
    SelectObject(hDC, hPenOld);
    DeleteObject(hPen);
    
    // メモリの解放
    free(ptDst);
    free(eDst);
}// S_H_Clipping

// 多角形が右回りの場合は、true を返す
// pVtx     : 頂点座標
// vtx_num  : 頂点の数(pVtxの要素数)
bool Is_Clockwise(POINT *pVtx, int vtx_num)
{
    // 凸(Convex)多角形の外積のZ成分(つまり法線ベクトルの向き)が負の場合は、
    // その多角形は左回り、正の場合は右回り。
    // 凹多角形にも対応させる時は下のような工夫が必要。辺が交差する多角形には対応できない。
    int i,y_max_idx = 0;
    int y_max = pVtx[0].y;
    for(i=1;i<vtx_num;i++){
        if(pVtx[i].y > y_max){
            y_max = pVtx[i].y;
            y_max_idx = i;
        }
    }
    int idx[3];
    idx[0] = y_max_idx - 1;
    if(idx[0] < 0){
        idx[0] = vtx_num - 1;
    }
    idx[1] = y_max_idx;
    idx[2] = y_max_idx + 1;
    if(idx[2] >= vtx_num){
        idx[2] = 0;
    }
    POINT vec1 = {pVtx[idx[1]].x - pVtx[idx[0]].x, pVtx[idx[1]].y - pVtx[idx[0]].y};
    POINT vec2 = {pVtx[idx[2]].x - pVtx[idx[1]].x, pVtx[idx[2]].y - pVtx[idx[1]].y};
    int cross_z = vec1.x * vec2.y - vec1.y * vec2.x;    // 外積のZ成分
    if(cross_z > 0){
        return true;
    }
    return false;
}// Is_Clockwise

9.マジックワンド

領域塗りを利用すると、マジックワンドのアルゴリズムを作成できる。
以下は、自作で、輪郭幅が1で袋小路がある場合には対応していない。
輪郭点を総てパスに入れているが、同じ傾きが並んでいる場合は、間引く事もできるはずである。
WinMain() は領域塗りと同じ。
Search_NextPt() は、再帰呼び出しで置き換え可能だが、再帰スタックのサイズには制限があるため、置き換えないほうが良い。
再帰スタックサイズは、コンパイルオプションで設定できる。
マジックワンドの手順は、
  1. クリック位置の領域に領域塗りでフラグを設定する
  2. フラグを反転させて、輪郭を取りたい領域を確定する
  3. クリック位置に最も近い輪郭線上の座標を見つけ、その座標を座標リストに加える
  4. その座標の4近傍の座標を検索し、それが輪郭線上の座標であれば、リストに加える
  5. 見つけた座標を次のカレントピクセルとして、Bの座標に戻るまでCを繰り返す
#include <windows.h>
#include <malloc.h>
#include <GdiPlus.h>

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

#define MYWNDCLASS  "Class_GDI_MagicWand"
#define MYWNDTITLE  "GDI_MagicWand"

typedef struct{
    POINT   pos;
    bool    flag;
}RETPOS,*LPRETPOS;

using namespace     Gdiplus;
GdiplusStartupInput gdiSI;
ULONG_PTR           gdiToken;
Bitmap              *Bmp = NULL;
GraphicsPath        *Path = NULL;

const POINT g_pt1[9] = {{0,1},{-1,1},{-1,0},{-1,-1},
                        {0,-1},{1,-1},{1,0},{1,1},{0,1}};   // 8近傍の相対座標

// マジックワンドの座標データ
typedef struct{
    Point   *pt;        // 座標
    int     num;        // ptの数
}MGC,*LPMGC;

MGC g_mgc1;

// 分岐情報を保持するスタック
POINT   *g_pt_stack = NULL;
int     g_num_stack = 0;

LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
Status  Magic_Wand(Bitmap *Bmp, GraphicsPath *path, Point pos);
Point   Search_FirstPt(BYTE *pxl, int wdh, int hgt, int x, int y);
RETPOS  Search_NextPt(BYTE *pxl, int wdh, int hgt, int x, int y);
bool    Bucket_Sub(Bitmap *Bmp, BYTE *pxl1, int wdh, int hgt, ARGB rgb);
POINT   Bucket_Sub_x(Bitmap *Bmp, BYTE *pxl1, int wdh, int hgt, int xPos, int yPos, ARGB rgb);
bool    Compare_Argb(Color clr, ARGB rgb);
bool    Check_StackSize();
bool    Check_MagicMemorySize();
void    Set_SafePos(Point *pt, int wdh, int hgt);
void    Read_Image(HWND hWnd, char *fname);

// 親ウィンドウ プロシージャ
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg)
    {
    case WM_CREATE:
        GdiplusStartup(&gdiToken,&gdiSI,NULL);  // GDI+オブジェクト初期化
        char s[256];
        strcpy(s,MYWNDTITLE);
        strcat(s," - 画像ファイルをDrag&Dropしてください");
        SendMessage(hWnd,WM_SETTEXT,0,(LPARAM)s);
        return 0;
    case WM_SHOWWINDOW:
        if(__argc > 1){
            char    fname[MAX_PATH];
            strcpy(fname,__argv[1]);
            Read_Image(hWnd,fname);
            __argc = 0;
        }
        return 0;
    case WM_PAINT:  // 描画
        HDC             hDC;
        PAINTSTRUCT     ps;
        hDC = BeginPaint(hWnd,&ps);
        if(Bmp){
            Graphics g(hDC);
            g.DrawImage(Bmp, 0, 0, Bmp->GetWidth(), Bmp->GetHeight());
            if(Path){
                g.DrawPath(&Pen(Color(255,0,0),2), Path);   // パスは赤色
            }
        }
        EndPaint(hWnd,&ps);
        return 0;
    case WM_LBUTTONDOWN:    // クリックでパスを作成
        if(Bmp && Path){
            Point pos;
            pos.X = LOWORD(lParam);
            pos.Y = HIWORD(lParam);
            // 座標posの色と違う色をパスに入れる
            Magic_Wand(Bmp, Path, pos);
            InvalidateRect(hWnd,NULL,FALSE);
        }
        return 0;
    case WM_RBUTTONDOWN:    // 右クリックでパスをクリア
        if(Path){
            Path->Reset();
        }
        InvalidateRect(hWnd,NULL,TRUE);
        return 0;
    case WM_DROPFILES:      // ウィンドウ内にファイルをドロップした時
        HDROP   hDrop;
        hDrop = (HDROP) wParam;
        char    fname[MAX_PATH];
        DragQueryFile(hDrop, 0, fname, sizeof(fname));
        Read_Image(hWnd,fname);
        DragFinish(hDrop);
        InvalidateRect(hWnd,NULL,TRUE);
        return 0;
    case WM_CLOSE:
        if(Bmp){
            delete Bmp;
            Bmp = NULL;
        }
        if(Path){
            delete Path;
            Path = NULL;
        }
        if(g_mgc1.pt){
            free(g_mgc1.pt);
            g_mgc1.pt = NULL;
        }
        if(g_pt_stack){
            free(g_pt_stack);
            g_pt_stack = NULL;
        }
        DestroyWindow(hWnd);
        GdiplusShutdown(gdiToken);
        PostQuitMessage(0);
        return 0;
    }
    return DefWindowProc(hWnd, uMsg, wParam, lParam);
}// WndProc

// マジックワンド
Status Magic_Wand(Bitmap *Bmp, GraphicsPath *path, Point pos)
{
    Status      st = GenericError;
    
    if(!Bmp || !path){
        MessageBox(GetFocus(),"画像、またはパスがありません","",MB_OK);
        return st;
    }
    
    int         x,y,ab1;
    int         wdh  = Bmp->GetWidth() + 2;
    int         hgt  = Bmp->GetHeight() + 2;
    BYTE        *pxl1 = NULL;   // 同色と輪郭のピクセルのフラグ
    Color       clr_bfr;        // クリック座標の色
    
    st = Bmp->GetPixel(pos.X, pos.Y, &clr_bfr);
    if(st != Ok){
        MessageBox(GetFocus(),"色を取得できません","",MB_OK);
        return st;
    }
    
    Bitmap Bmp2(wdh, hgt, PixelFormat32bppARGB);
    Bmp2.SetResolution(Bmp->GetHorizontalResolution(), 
                        Bmp->GetVerticalResolution());  // 解像度の設定
    Graphics gBmp(&Bmp2);
    gBmp.DrawRectangle(&Pen(clr_bfr, 1), 0, 0, wdh - 1, hgt - 1);
    gBmp.DrawImage(Bmp, 1, 1);  // 右と下に 1 px ずつずらす
    
    pxl1 = new BYTE [wdh*hgt];
    ZeroMemory(pxl1, wdh*hgt);
    
    if(g_pt_stack){
        free(g_pt_stack);
        g_pt_stack = NULL;
    }
    g_pt_stack = (LPPOINT) malloc(1024*sizeof(POINT));
    
    g_num_stack = 0;
    g_pt_stack[g_num_stack].x = pos.X + 1;  // 最初にずらしたから、+1 する
    g_pt_stack[g_num_stack].y = pos.Y + 1;
    
    // clr_bfr と 同色のピクセルにフラグを設定する
    if(!Bucket_Sub(&Bmp2, pxl1, wdh, hgt, clr_bfr.GetValue())){
        if(pxl1){
            delete [] pxl1;
            pxl1 = NULL;
        }
        if(g_pt_stack){
            free(g_pt_stack);
            g_pt_stack = NULL;
        }
        MessageBox(GetFocus(),"メモリが不足しています","",MB_OK);
        return OutOfMemory;
    }
    
    if(g_pt_stack){
        free(g_pt_stack);
        g_pt_stack = NULL;
    }
    
    // マスクを反転
    for(y=0;y<hgt;y++){
        for(x=0;x<wdh;x++){
            ab1 = wdh*y+x;
            if(pxl1[ab1])
                pxl1[ab1] = 0;
            else
                pxl1[ab1] = 1;
        }
    }
    
    // 領域の輪郭を pxl1 に設定
    int         i,x2,y2;
    const POINT pt[4] = {{-1,0},{0,-1},{1,0},{0,1}};    // 4近傍の相対座標
    
    for(y=1;y<hgt-1;y++){
        for(x=1;x<wdh-1;x++){
            ab1 = wdh*y+x;
            if(pxl1[ab1]){
                for(i=0;i<4;i++){
                    x2 = x + pt[i].x;
                    y2 = y + pt[i].y;
                    if(pxl1[wdh*y2+x2] == 0){
                        pxl1[ab1] = 2;  // 輪郭点は 2 にする
                        break;
                    }
                }
            }
        }
    }
    
    // クリックした座標から8方向に開始座標を探索し、輪郭の座標を並び順に g_mgc1 に設定
    
    Point   pos_first;
    
    pos_first = Search_FirstPt(pxl1, wdh, hgt, pos.X + 1, pos.Y + 1);
    
    if(pos_first.X >= 0 && pos_first.Y >= 0){
        g_mgc1.num = 0;
        if(g_mgc1.pt){
            free(g_mgc1.pt);
            g_mgc1.pt = NULL;
        }
        int mem_size = 1024 * sizeof(Point);
        g_mgc1.pt = (Point*) malloc(mem_size);
        RETPOS rp = {pos_first.X, pos_first.Y, 0};
        do{
            if(Check_MagicMemorySize()){
                rp = Search_NextPt(pxl1, wdh, hgt, rp.pos.x, rp.pos.y);
            }
        }while(rp.flag == true);
        if(g_mgc1.num > 0){
            path->StartFigure();
            path->AddLines(g_mgc1.pt, g_mgc1.num);
            path->CloseFigure();
        }
    }
    
    if(g_mgc1.pt){
        free(g_mgc1.pt);
        g_mgc1.pt = NULL;
    }
    if(pxl1){
        delete [] pxl1;
        pxl1 = NULL;
    }
    
    return st;
}// Magic_Wand

Point Search_FirstPt(BYTE *pxl, int wdh, int hgt, int x, int y)
{
    Point   pt2(-1,-1);
    int     i,x2,y2;
    
    for(i=0;i<8;i++){
        bool    flag_loop_out = false;
        
        x2 = x;
        y2 = y;
        
        do{
            x2 += g_pt1[i].x;
            y2 += g_pt1[i].y;
            if(x2 >= 1 && y2 >= 1 && x2 < wdh -1 && y2 < hgt - 1){
                if(pxl[wdh*y2+x2] == 2){
                    flag_loop_out = true;   // 見つかったら終わり
                    pt2.X = x2;
                    pt2.Y = y2;
                }
            }
            else{
                if( i == 0 && y2 >= hgt - 1 || 
                    i == 1 && x2 < 1 && y2 >= hgt - 1 || 
                    i == 2 && x2 < 1 || 
                    i == 3 && x2 < 1 && y2 < 1 || 
                    i == 4 && y2 < 1 || 
                    i == 5 && x2 >= wdh -1 && y2 < 1 || 
                    i == 6 && x2 >= wdh -1 || 
                    i == 7 && x2 >= wdh -1 && y2 >= hgt - 1)
                {
                    flag_loop_out = true;   // 見つからなかったら終わり
                }
            }
        }while(!flag_loop_out);
    }
    
    return pt2;
}// Search_FirstPt

// 輪郭線の次の点を探す
RETPOS Search_NextPt(BYTE *pxl, int wdh, int hgt, int x, int y)
{
    int     i,x2,y2,x3,y3;
    RETPOS  rp = {0, 0, 0};
    
    for(i=0;i<8;i++){
        x2 = x + g_pt1[i].x;
        y2 = y + g_pt1[i].y;
        x3 = x + g_pt1[i+1].x;
        y3 = y + g_pt1[i+1].y;
        if(pxl[wdh*y2+x2] == 2 &&  pxl[wdh*y3+x3] == 0){
            g_mgc1.pt[g_mgc1.num].X = x - 1;   // 最初にずらしたから、-1 する
            g_mgc1.pt[g_mgc1.num].Y = y - 1;
            Set_SafePos(&g_mgc1.pt[g_mgc1.num], wdh-3, hgt-3);
            g_mgc1.num ++;
            pxl[wdh*y2+x2] = 1;     // 座標を g_mgc1 に入れた輪郭点は消去
            rp.flag  = true;
            rp.pos.x = x2;
            rp.pos.y = y2;
            break;
        }
    }
    return rp;
}// Search_NextPt

// 塗りつぶし範囲を求める
bool Bucket_Sub(Bitmap *Bmp, BYTE *pxl1, int wdh, int hgt, ARGB rgb)
{
    // pxl1は、塗りこみ対象の座標にフラグを立てるマスク
    // flagCは、同じ色である場合にTRUEにするフラグ
    // flag1,flag2 は、1つ左のピクセルが塗り込み対象ではない場合で
    // カレントピクセルが塗り込み対象の場合には、スタックにカレント座標を積むためのフラグ
    // flag3 は、スタックに分岐座標を積んだかどうかのフラグ
    bool    flag1,flag2,flag3,flagC;
    int     x;
    int     xPos,yPos;
    POINT   pt;
    Color   clr;
    
    while(g_num_stack >= 0){    // スタックが空になったら終了
        flag3 = 1;
        while (flag3){
            xPos = g_pt_stack[g_num_stack].x;
            yPos = g_pt_stack[g_num_stack].y;
            pt = Bucket_Sub_x(Bmp, pxl1, wdh, hgt, xPos, yPos, rgb);
            flag1 = 0;
            flag2 = 0;
            flag3 = 0;
            for(x=pt.x; x<=pt.y; x++){
                // 上向き
                if(yPos > 0){
                    Bmp->GetPixel(x, yPos-1, &clr);
                    flagC = Compare_Argb(clr, rgb);
                    if(pxl1[wdh*(yPos-1)+x] || !flagC){
                        flag1 = 0;
                    }
                    else if(!pxl1[wdh*(yPos-1)+x] && !flag1 && flagC){
                        g_pt_stack[g_num_stack].x = x;
                        g_pt_stack[g_num_stack].y = yPos-1;
                        g_num_stack ++;
                        if(!Check_StackSize())
                            return 0;
                        flag1 = 1;
                        flag3 = 1;
                    }
                }
                // 下向き
                if(yPos < hgt - 1){
                    Bmp->GetPixel(x, yPos+1, &clr);
                    flagC = Compare_Argb(clr, rgb);
                    if(pxl1[wdh*(yPos+1)+x] || !flagC){
                        flag2 = 0;
                    }
                    else if(!pxl1[wdh*(yPos+1)+x] && !flag2 && flagC){
                        g_pt_stack[g_num_stack].x = x;
                        g_pt_stack[g_num_stack].y = yPos+1;
                        g_num_stack ++;
                        if(!Check_StackSize())
                            return 0;
                        flag2 = 1;
                        flag3 = 1;
                    }
                }
            }
            g_num_stack --;
            if(g_num_stack < 0) break;
        }
    }
    return 1;
}// Bucket_Sub

// 点(xPos, yPos)の横方向のRGB(r,g,b)色で連続したピクセル値をclr_aftで塗り替える
POINT Bucket_Sub_x(Bitmap *Bmp, BYTE *pxl1, int wdh, int hgt, int xPos, int yPos, ARGB rgb)
{
    int     x;
    POINT   pt;
    Color   clr;

    // 点(xPos, yPos)の左側を調べる
    x = 0;
    while (xPos >= x){
        Bmp->GetPixel(xPos-x, yPos, &clr);
        if(Compare_Argb(clr, rgb))
            pxl1[wdh*yPos+xPos-x] = 1;
        else
            break;
        x ++;
    }
    pt.x = xPos - (x - 1);
    
    // 点(xPos, yPos)の右側を調べる
    x = 1;
    while (x < (wdh - xPos)){
        Bmp->GetPixel(xPos+x, yPos, &clr);
        if(Compare_Argb(clr, rgb))
            pxl1[wdh*yPos+xPos+x] = 1;
        else
            break;
        x ++;
    }
    pt.y = xPos + (x - 1);

    return pt;
}// Bucket_Sub_x

// 塗り込み値の判定
bool Compare_Argb(Color clr, ARGB rgb)
{
    bool flag = 0;
    
    if( clr.GetA() == 255 && clr.GetValue() == rgb || 
        clr.GetA() < 255 && clr.GetA() == (rgb >> 24) ){
        flag = 1;
    }
    return flag;
}// Compare_Argb

// スタックを使い切ったら、スタックサイズを増やす
bool Check_StackSize()
{
    size_t size = _msize(g_pt_stack);
    if(g_num_stack * sizeof(POINT) >= size){
        g_pt_stack = (POINT*)realloc(g_pt_stack, size+512*sizeof(POINT));
        if(!g_pt_stack){
            return 0;
        }
    }
    return 1;
}// Check_StackSize

// マジックワンドのメモリーを使い切ったら、サイズを増やす
bool Check_MagicMemorySize()
{
    size_t size = _msize(g_mgc1.pt);
    if(g_mgc1.num * sizeof(POINT) >= size){
        g_mgc1.pt = (Point*)realloc(g_mgc1.pt, size+512*sizeof(Point));
        if(!g_mgc1.pt){
            return 0;
        }
    }
    return 1;
}// Check_MagicMemorySize

// 画像内に収まる座標に設定
void Set_SafePos(Point *pt, int wdh, int hgt)
{
    if(pt->X < 0)   pt->X = 0;
    if(pt->X > wdh) pt->X = wdh;
    if(pt->Y < 0)   pt->Y = 0;
    if(pt->Y > hgt) pt->Y = hgt;
}// Set_SafePos

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

    char s[256];
    strcpy(s,MYWNDTITLE);
    strcat(s," - 画像をクリックしてください");
    SendMessage(hWnd,WM_SETTEXT,0,(LPARAM)s);
}// Read_Image

10.メディアンカット

メディアンカットは、フルカラーをインデックスカラーに変換するアルゴリズムの1つである。
手順(256色の場合)は、
     
  1. 画像内で使われている色種を列挙する(同色は入れない)  
  2. 色種の配列をRGB各色について足し合わせ、その最小値、最大値、最大値と最小値の差を求める  
  3. 最大値と最小値の差が最大の色についてソートし、配列の真ん中で分割し、配列を2つにする  
  4. 双方の配列をそれぞれRGB各色について足し合わせ、その最小値、最大値、最大値と最小値の差を求める  
  5. 最大値と最小値の差が最大となる色を持つ配列について、配列数が256個になるまでBとCを繰り返す  
  6. 求められた256個の各配列について平均値を求め、それをパレットとする。  
  7. 各ピクセル値に近い色をパレットから探して色番号を付ける
である。
色種列挙の時は、メモリ節約のため、RGB各色の下位3ビットを除去している。
メディアンカットはディザがないと厳しい。ディザありのコードは、PicTer を参照。
参考文献:
減色方法について(http://www1.kamakuranet.ne.jp/smo/proctalk/talk7.htm)
Study of Color Image Quantization(http://www.ynk.ic.kanagawa-it.ac.jp/~kasuga/gentei.html)
#include <windows.h>
#include <stdio.h>
#include ".\\include\\GdiPlus.h"

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

#define MYWNDCLASS              "MEDIANCUT_CLASS"
#define MYWNDTITLE              "MEDIANCUT"
#define WIDTHBYTES(width, bpp)  ((((width * bpp) + 31) >> 5) << 2)
#define SAFE_DELETE_ARRAY(p)        if (p){delete[]p;p=NULL;}
#define SAFE_DELETE_ARRAY_BYTE(p)   if (p){delete[](LPBYTE)p;p=NULL;}
#define FREE(p)                     {free(p);p=NULL;}

using namespace     Gdiplus;
GdiplusStartupInput gdiSI;
ULONG_PTR           gdiToken;
char                fname[MAX_PATH] = ""; // 読み込みファイル名

typedef struct{
    BYTE R;
    BYTE G;
    BYTE B;
}BRGB,*LPBRGB;

typedef struct{
    WORD    Val;
    WORD    Num;
}DATA,*LPDATA;

typedef struct{
    BYTE    min[3],max[3],len[3];
    int     data_len;
    DATA    *pData;
}MEDIAN,*LPMEDIAN;

MEDIAN  g_mdn[256];

// 3つの整数の内、最大の整数を返す
inline int max3(int a, int b, int c)
{
    int max = a;
    if(b > max){
        max = b;
    }
    if(c > max){
        max = c;
    }
    return max;
}// max3

// 3つの整数の内、最大の整数となる番号を返す
inline int max3n(int a, int b, int c)
{
    int max = a;
    int index = 0;
    if(b > max){
        max = b;
        index = 1;
    }
    if(c > max){
        max = c;
        index = 2;
    }
    return index;
}// max3n

LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
int     GetEncoderClsid(const WCHAR* format, CLSID* pClsid);
void    Load_Image(HWND hWnd);
Status  Median_Cut(HWND hWnd, Bitmap *Bmp_R, UINT wdh, UINT hgt);
void    Set_MdnInfo(int index);
int     DATAcmpR(const DATA *a, const DATA *b);
int     DATAcmpG(const DATA *a, const DATA *b);
int     DATAcmpB(const DATA *a, const DATA *b);
DWORD   Get_Pixel_All(BYTE *pixels1, DATA *pData, UINT wdh, UINT hgt, UINT stride1);
void    Set_IdxClrPx(int bpp, BYTE *pixels, UINT stride, int x, int y, int val);

int APIENTRY WinMain(HINSTANCE hInst, HINSTANCE hInstPrev, LPSTR lpCmdLine, int nCmdShow)
{
    MSG        msg;
    WNDCLASSEX wc;
    HWND       hWnd;
    
    // 親ウィンドウ
    ZeroMemory(&wc, sizeof(wc));
    wc.cbSize           = sizeof(WNDCLASSEX);
    wc.lpfnWndProc      = (WNDPROC)WndProc;
    wc.hInstance        = hInst;
    wc.hIcon            = LoadIcon(hInst, IDI_APPLICATION);
    wc.hCursor          = LoadCursor(NULL, MAKEINTRESOURCE(IDC_ARROW));
    wc.hbrBackground    = (HBRUSH)(COLOR_BTNFACE + 1);
    wc.lpszMenuName     = NULL;
    wc.lpszClassName    = MYWNDCLASS;
    RegisterClassEx(&wc);
    
    hWnd = CreateWindowEx(WS_EX_CLIENTEDGE | WS_EX_ACCEPTFILES,
                MYWNDCLASS,
                MYWNDTITLE,
                WS_OVERLAPPEDWINDOW | WS_VISIBLE,
                CW_USEDEFAULT, CW_USEDEFAULT, 480, 320,
                NULL,
                NULL,
                hInst,
                NULL);

    while (GetMessage(&msg, NULL, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return msg.wParam;
}// WinMain

// 親ウィンドウ プロシージャ
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg)
    {
    case WM_CREATE:
        GdiplusStartup(&gdiToken,&gdiSI,NULL);  // GDI+オブジェクト初期化
        break;
    case WM_SIZE:
        if(__argc > 1){                 // コマンドライン引数があるとき
            if(!strcmp(fname, "")){
                strcpy(fname,__argv[1]);
                Load_Image(hWnd);       // 画像の読み込み
            }
        }
        break;
    case WM_DROPFILES:      // ドロップファイルを受け取る
        HDROP   hDrop;
        hDrop = (HDROP) wParam;
        DragQueryFile(hDrop, 0, fname, sizeof(fname));
        Load_Image(hWnd);
        DragFinish(hDrop);
        break;
    case WM_CLOSE:
        DestroyWindow(hWnd);
        GdiplusShutdown(gdiToken);
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, uMsg, wParam, lParam);
    }
    return 0;
}// CALLBACK WndProc

// ファイルから画像を読み込む
void Load_Image(HWND hWnd)
{
    WCHAR   wfname[MAX_PATH];
    Bitmap  *Bmp_R = NULL;
    UINT    wdh,hgt;
    
    MultiByteToWideChar(CP_ACP,0,fname,-1,wfname,sizeof(wfname));
    Bmp_R = Bitmap::FromFile(wfname,FALSE);
    wdh = Bmp_R->GetWidth();
    hgt = Bmp_R->GetHeight();
    Median_Cut(hWnd, Bmp_R, wdh, hgt);
    if(Bmp_R){
        delete Bmp_R;
        Bmp_R = NULL;
    }
}

// フルカラーから256色インデックスを作成する
Status Median_Cut(HWND hWnd, Bitmap *Bmp_R, UINT wdh, UINT hgt)
{
    Status  st;
    BitmapData  bitmapData;
    Rect    rect(0, 0, wdh, hgt);
    int     i,j;
    int     nColors;                // パレットの色の総数(最終インデックス数)
    BYTE    *pixels2 = NULL;        // *pixels1とpal_256の対応表(最終画素情報)
    DWORD   pal_256[256];           // パレットの色データ(最終インデックス情報)
    UINT    bpp = 8;                // カラーデプス(1 or 4 or 8)
    UINT    index = 1 << bpp;       // 色深度から色数を求める
    
    // Bmp_Rを32bitフォーマットの読み込み専用ビットマップ領域としてロックする
    st = Bmp_R->LockBits(&rect, ImageLockModeRead, PixelFormat32bppARGB, &bitmapData);
    if (st == Ok)
    {
        BYTE    *pixels1 = NULL;        // 32bpp色画素情報
        int     pos;
        UINT    x,y;
        UINT    stride1,stride2;        // 読み込みデータと保存データの4byte境界幅
        
        if (bitmapData.Stride > 0)
            pixels1 = (BYTE*) bitmapData.Scan0;
        else
            pixels1 = (BYTE*) bitmapData.Scan0
                    + (bitmapData.Stride)*(bitmapData.Height-1);
        
        stride1 = abs(bitmapData.Stride);
        
        // 保存用4byte境界幅の計算
        stride2 = WIDTHBYTES(wdh, bpp);
        
        pixels2 = new BYTE [stride2 * hgt];
        
        BRGB    bRGB[256];              // pal_256 の元となるデータ
        
        ZeroMemory(g_mdn, sizeof(g_mdn));
        g_mdn[0].pData = (DATA*) malloc(wdh * hgt * sizeof(DATA));
        ZeroMemory(g_mdn[0].pData, wdh * hgt * sizeof(DATA));
        // 画像で使用されている色の種類を総てg_mdn[0].pDataに入れる
        g_mdn[0].data_len = Get_Pixel_All(pixels1, g_mdn[0].pData, wdh, hgt, stride1);
        // データ情報初期化
        Set_MdnInfo(0);
        // メディアン カット
        UINT now = 0;   // 半分に分割するデータの g_mdn 番号
        UINT next = 1;  // 分割したデータを入れる g_mdn 番号
        while(next < index){
            int     v_max = 0;
            for(i=0; i<(int)next; i++){
                int v_max2 = max3(g_mdn[i].len[0], g_mdn[i].len[1], g_mdn[i].len[2]);   // 最大の色範囲を求める
                if(v_max2 > v_max){
                    v_max = v_max2;
                    now = i;
                }
            }
            int     n_max = max3n(g_mdn[now].len[0], g_mdn[now].len[1], g_mdn[now].len[2]); // 範囲が最大の色番号を求める
            int     len_mid1 = g_mdn[now].data_len / 2; // 分割した時の一方のデータの最初の要素番号
            int     len_mid2 = len_mid1 - 1;            // 分割した時のもう一方のデータの最後の要素番号
            if(len_mid1 > 0){
                switch(n_max)
                {
                case 0:
                    qsort(g_mdn[now].pData, g_mdn[now].data_len, sizeof(DATA),
                      (int (*)(const void*, const void*))DATAcmpB);
                    break;
                case 1:
                    qsort(g_mdn[now].pData, g_mdn[now].data_len, sizeof(DATA),
                      (int (*)(const void*, const void*))DATAcmpG);
                    break;
                case 2:
                    qsort(g_mdn[now].pData, g_mdn[now].data_len, sizeof(DATA),
                      (int (*)(const void*, const void*))DATAcmpR);
                    break;
                }
                g_mdn[next].data_len = g_mdn[now].data_len - len_mid1;
                g_mdn[next].pData = (DATA*) malloc(g_mdn[next].data_len * sizeof(DATA));
                memcpy(g_mdn[next].pData, g_mdn[now].pData + len_mid1, g_mdn[next].data_len * sizeof(DATA));
                g_mdn[now].data_len = len_mid2 + 1;
                g_mdn[now].pData = (DATA*) realloc(g_mdn[now].pData, g_mdn[now].data_len * sizeof(DATA));
                Set_MdnInfo(now);
                Set_MdnInfo(next);
                next ++;
            }
            else{
                break;
            }
        }
        nColors = next; // パレットの色数
        // パレット色作成(透明度は0xffにする。色は配列内の色の平均値とする)
        int sum_r,sum_g,sum_b,sum_num;
        for(i=0; i<(int)nColors; i++){
            for(j=0; j<3; j++){
                g_mdn[i].min[j] <<= 3;
                g_mdn[i].max[j] <<= 3;
            }
            sum_b = 0;
            sum_g = 0;
            sum_r = 0;
            sum_num = 0;
            for(j=0; j<g_mdn[i].data_len; j++){
                sum_b += (((g_mdn[i].pData[j].Val >> 10) & 0x1f) * g_mdn[i].pData[j].Num);
                sum_g += (((g_mdn[i].pData[j].Val >> 5)  & 0x1f) * g_mdn[i].pData[j].Num);
                sum_r += (( g_mdn[i].pData[j].Val        & 0x1f) * g_mdn[i].pData[j].Num);
                sum_num += g_mdn[i].pData[j].Num;
            }
            bRGB[i].B = (BYTE)((sum_b << 3) / sum_num);
            bRGB[i].G = (BYTE)((sum_g << 3) / sum_num);
            bRGB[i].R = (BYTE)((sum_r << 3) / sum_num);
            pal_256[i] = 0xff000000 | bRGB[i].R << 16 | bRGB[i].G << 8 | bRGB[i].B;
        }
        // メモリ解放
        for(i=0; i<256; i++){
            FREE(g_mdn[i].pData);
        }
        // 各ピクセルの近似色をパレットから探し、色番号を入れる
        int     p_num;
        BYTE    *pixels3 = NULL;
        BYTE    pxl[3];
        pixels3 = new BYTE  [stride1 * hgt];
        memcpy(pixels3, pixels1, stride1*hgt*sizeof(BYTE));
        for(y=0; y<hgt; y++){
            for(x=0; x<wdh; x++){
                pos = stride1*y+x*4;
                for(i=0; i<3; i++){
                    pxl[i] = pixels3[pos+i] & 0xf8;
                }
                p_num = 0;
                for(i=0; i<(int)nColors; i++){
                    if( pxl[0] >= g_mdn[i].min[0] && pxl[0] <= g_mdn[i].max[0] &&
                        pxl[1] >= g_mdn[i].min[1] && pxl[1] <= g_mdn[i].max[1] &&
                        pxl[2] >= g_mdn[i].min[2] && pxl[2] <= g_mdn[i].max[2])
                    {
                        p_num = i;
                        break;
                    }
                }
                Set_IdxClrPx(bpp, pixels2, stride2, x, y, p_num);
            }
        }
        SAFE_DELETE_ARRAY(pixels3);
        
        st = Bmp_R->UnlockBits(&bitmapData);
        if(st != Ok){
            MessageBox (GetFocus(),"Bmp_Rをロック解除できません","",MB_OK);
            SAFE_DELETE_ARRAY(pixels2);
            return st;
        }
    }
    else{
        MessageBox (GetFocus(),"Bmp_Rをロックできません","",MB_OK);
        return st;
    }
    
    if(st == Ok){
        PixelFormat format;
        if(bpp == 1)
            format = PixelFormat1bppIndexed;
        else if(bpp == 4)
            format = PixelFormat4bppIndexed;
        else
            format = PixelFormat8bppIndexed;
        
        Bitmap  Bmp_W(wdh, hgt, format);
        
        DWORD   dwSizeColorPalette;
        dwSizeColorPalette = sizeof(ColorPalette);
        dwSizeColorPalette += sizeof(ARGB)*(nColors-1);
        
        ColorPalette *ppal = (ColorPalette *)new BYTE[dwSizeColorPalette];
        if (ppal == NULL){
            MessageBox (GetFocus(), "メモリが不足しています", "", MB_OK);
            SAFE_DELETE_ARRAY(pixels2);
            return OutOfMemory;
        }
        
        ZeroMemory(ppal, dwSizeColorPalette);
        ppal->Flags = 0;
        ppal->Count = nColors;
        for (i = 0; i < nColors; i++)
        {
            Color clr(pal_256[i]);
            ppal->Entries[i] = clr.GetValue();
        }
        
        st = Bmp_W.SetPalette(ppal);
        if (st != Ok)
        {
            MessageBox (GetFocus(),"パレットを作成できません","",MB_OK);
            SAFE_DELETE_ARRAY_BYTE(ppal);
            SAFE_DELETE_ARRAY(pixels2);
            return st;
        }
        
        st = Bmp_W.LockBits(&rect,
                            ImageLockModeWrite,
                            Bmp_W.GetPixelFormat(),
                            &bitmapData );
        if (st == Ok)
        {
            BYTE *pixels = NULL;
            if (bitmapData.Stride > 0)
                pixels = (BYTE*) bitmapData.Scan0;
            else
                pixels = (BYTE*) bitmapData.Scan0 + bitmapData.Stride*(hgt-1);
            
            UINT stride = abs(bitmapData.Stride);
            memcpy(pixels, pixels2, stride*hgt*sizeof(BYTE));
            
            st = Bmp_W.UnlockBits(&bitmapData);
        }
        if (st != Ok){
            MessageBox (GetFocus(),"Bmp_Wをロック又はロック解除できません","",MB_OK);
        }
        
        HDC hDC = GetDC(hWnd);
        Graphics MyGraphics(hDC);
        MyGraphics.DrawImage(&Bmp_W, 0, 0, wdh, hgt);
        ReleaseDC(hWnd, hDC);
        
        SAFE_DELETE_ARRAY_BYTE(ppal);
    }
    SAFE_DELETE_ARRAY(pixels2);
    
    return st;
}// Median_Cut

// 配列をRGBそれぞれについて、最小値、最大値、長さを求める
void Set_MdnInfo(int index)
{
    if(g_mdn[index].pData){
        int     i,r,g,b,min_r,min_g,min_b,max_r,max_g,max_b,len_r,len_g,len_b;
        min_r = 255;
        min_g = 255;
        min_b = 255;
        max_r = 0;
        max_g = 0;
        max_b = 0;
        for(i=0; i<g_mdn[index].data_len; i++){
            r = g_mdn[index].pData[i].Val & 0x1f;
            if(min_r > r)
                min_r = r;
            if(max_r < r)
                max_r = r;
            g = (g_mdn[index].pData[i].Val >> 5) & 0x1f;
            if(min_g > g)
                min_g = g;
            if(max_g < g)
                max_g = g;
            b = (g_mdn[index].pData[i].Val >> 10) & 0x1f;
            if(min_b > b)
                min_b = b;
            if(max_b < b)
                max_b = b;
        }
        len_r = max_r - min_r;
        len_g = max_g - min_g;
        len_b = max_b - min_b;
        
        g_mdn[index].min[0] = min_b;
        g_mdn[index].min[1] = min_g;
        g_mdn[index].min[2] = min_r;
        g_mdn[index].max[0] = max_b;
        g_mdn[index].max[1] = max_g;
        g_mdn[index].max[2] = max_r;
        g_mdn[index].len[0] = len_b;
        g_mdn[index].len[1] = len_g;
        g_mdn[index].len[2] = len_r;
    }
}// Set_MdnInfo

// クイックソートの比較関数(昇順・赤)
int DATAcmpR(const DATA *a, const DATA *b)
{
    return ((a->Val & 0x1f) - (b->Val & 0x1f));
}

// クイックソートの比較関数(昇順・緑)
int DATAcmpG(const DATA *a, const DATA *b)
{
    return (((a->Val >> 5) & 0x1f) - ((b->Val >> 5) & 0x1f));
}

// クイックソートの比較関数(昇順・青)
int DATAcmpB(const DATA *a, const DATA *b)
{
    return (((a->Val >> 10) & 0x1f) - ((b->Val >> 10) & 0x1f));
}

// 画像で使用される全ての色種を取り出す
// pixel1  : 画像のピクセル値
// pData    : 取り出した色種データを格納する構造体
// stride1 : 4byte境界の横幅
// 戻り値  : 画素数
DWORD Get_Pixel_All(BYTE *pixels1, DATA *pData, UINT wdh, UINT hgt, UINT stride1)
{
    if(!pixels1 || !pData){
        return 0;
    }
    
    UINT    i,x,y,flag,pos;
    DWORD   k,ct;
    BYTE    pxl[3];
    
    int     n_max = (1 << (sizeof(WORD) * 8)) - 1;
    
    ct = 0;
    for(y=0; y<hgt; y++){
        for(x=0; x<wdh; x++){
            pos = stride1*y+x*4;
            for(i=0;i<3;i++){
                pxl[i] = pixels1[pos+i] >> 3;
            }
            flag = 0;
            for(k=0; k<ct; k++){
                if(((pData[k].Val >> 10) & 0x1f) == pxl[0] &&
                   ((pData[k].Val >>  5) & 0x1f) == pxl[1] &&
                   ( pData[k].Val        & 0x1f) == pxl[2])
                {
                    flag = 1;
                    if(pData[k].Num < n_max){
                        pData[k].Num ++;
                    }
                    break;
                }
            }
            if(flag == 0){
                pData[ct].Val = (pxl[0] << 10) | (pxl[1] << 5) | pxl[2];
                pData[ct].Num ++;
                ct ++;
            }
        }
    }
    return ct;
}// Get_Pixel_All

// 1,4,8bitのカラーデプス別にピクセル値を設定する
void Set_IdxClrPx(int bpp, BYTE *pixels, UINT stride, int x, int y, int val)
{
    if(bpp == 1){
        if(x % 8 == 1)
            pixels[stride*y+x/8] |= ((val & 1) << 6);
        else if(x % 8 == 2)
            pixels[stride*y+x/8] |= ((val & 1) << 5);
        else if(x % 8 == 3)
            pixels[stride*y+x/8] |= ((val & 1) << 4);
        else if(x % 8 == 4)
            pixels[stride*y+x/8] |= ((val & 1) << 3);
        else if(x % 8 == 5)
            pixels[stride*y+x/8] |= ((val & 1) << 2);
        else if(x % 8 == 6)
            pixels[stride*y+x/8] |= ((val & 1) << 1);
        else if(x % 8 == 7)
            pixels[stride*y+x/8] |= val & 1;
        else
            pixels[stride*y+x/8] = val << 7;
    }
    else if(bpp == 4){
        if(x % 2)
            pixels[stride*y+x/2] |= val & 0xf;
        else
            pixels[stride*y+x/2] =  val << 4;
    }
    else{
        pixels[stride*y+x] = (BYTE) val;
    }
}// Set_IdxClrPx

11.