0xAA55 发表于 2015-1-29 06:00:45

【图形】八叉树算法取得图像最适应调色板

这次的算法是我的上一个帖子中的程序的完全重写版。之前的程序参考了微软的相关代码,那代码可读性不高,而且用起来麻烦。因此我重新写了这个代码。
八叉树算法用于产生最适合一副图像的256色调色板。这个算法使用一种八叉树的结构,通过建立八叉树,将颜色筛选出来,接近的颜色会被合并,而在图像中出现次数极少的颜色和出现次数很多的颜色都会被照顾到。
这次我写了个窗口,我用它来显示整个八叉树的产生过程。大家可以自己下载了然后修改使其能发展出一个完整的八叉树。因为我的代码是为了达到取得最适应调色板的目的(而不单纯只是为了建立八叉树),所以在树的产生过程中就有“砍树”的行为,一个是减少内存占用,一个是加速程序运行。
Bin在运行的时候,大家可以直接拖拽一个24位BMP文件到窗口内,然后它就会分析这个文件,取得它的像素,再产生八叉树、调色板。




颜色越多,最终产生的八叉树的深度就越小。
同时八叉树的展开程度决定了图像颜色的明暗度分布情况。
现在我写的这个库是这么用的:
1、包含我的.h
2、编译我的.c
#include"OctPal.h"
定义一个PalGenP类型的变量:PalGenP pGen;
初始化它。pGen=CreatePalGen(NULL);//其中的NULL可以换成你自己的错误处理程序。
然后不断调用AddColor(pGen,红色值,绿色值,蓝色值);将颜色添加到八叉树中。返回值是当前的八叉树所含颜色数。
可以通过检测pGen->NbLeaf判断颜色数量,然后调用ReduceColor(pGen);减少颜色的数量。ReduceColor的行为就是合并一些八叉树的树叶,将八叉树的级数一点点缩小,以筛选出应该被放入调色板的颜色。
最后调用GenPalette(pGen,接收颜色的回调函数,回调函数的参数);生成调色板。
详细的请看源码的内容。
OPConfig.h:```
//=============================================================================
//作者:0xAA55
//网站:http://www.0xaa55.com
//请保留原作者信息,否则视为侵权。
//-----------------------------------------------------------------------------
#ifndef        _OCTREE_PALETTE_GENERATOR_CONFIG_
#define        _OCTREE_PALETTE_GENERATOR_CONFIG_

#ifndef        OPCall//调用约定
#define        OPCall                _cdecl
#endif

#ifndef        OPCallBack//回调函数调用约定
#define        OPCallBack        _cdecl
#endif

#ifndef        OPExpImp//导出导入的前缀
#define        OPExpImp
#endif

#ifndef        OPFunc//函数
#define        OPFunc(r,f)        OPExpImp r OPCall f
#endif

#endif
```OctPal.h:```
//=============================================================================
//作者:0xAA55
//网站:http://www.0xaa55.com
//请保留原作者信息,否则视为侵权。
//-----------------------------------------------------------------------------
#ifndef        _OCTREE_PALETTE_GENERATOR_
#define        _OCTREE_PALETTE_GENERATOR_

#include"OPConfig.h"
#include<stddef.h>

#define        ColorBits 8

typedef enum
{
        OPErr_NoError,                        //没有出错
        OPErr_NoMemory,                        //内存不足
        OPErr_NoReducibleNodes        //没有可删减节点
}OPError,*OPErrorP;

typedef void(OPCallBack*FnOnError)(OPError);

typedef        unsigned char        ColorVal;//颜色值
typedef        unsigned int        ColorSum;//颜色统计值
typedef        struct _OctNode
{
        ColorSum        NbColors;
        ColorSum        RSum;
        ColorSum        GSum;
        ColorSum        BSum;
        int                        IsLeaf;
        struct _OctNode*pChild;//八叉树子节点(Index=红绿蓝三个颜色值各贡献一位)
        struct _OctNode*pNext;//下一个可删减节点
}OctreeNode,*OctreeNodeP;//八叉树节点
typedef        struct
{
        OctreeNodeP        pRoot;
        OctreeNodeP        pReducibleNodes;//可删减节点
        size_t                NbLeaf;
        ColorSum        NbColors;
        FnOnError        pfnOnError;
}PalGen,*PalGenP;

//=============================================================================
//函数:FnOnGetColor
//描述:取得调色板颜色的时候调用的用户回调函数
//-----------------------------------------------------------------------------
typedef void(OPCallBack*FnOnGetColor)
(
        ColorVal R,
        ColorVal G,
        ColorVal B,
        void*pUserData
);

//=============================================================================
//函数:CreatePalGen
//描述:分配内存,创建一个PalGen
//-----------------------------------------------------------------------------
OPFunc(PalGenP,CreatePalGen)(FnOnError);

//=============================================================================
//函数:DestroyPalGen
//描述:释放内存,彻底删除PalGen
//-----------------------------------------------------------------------------
OPFunc(void,DestroyPalGen)(PalGenP);

//=============================================================================
//函数:AddColor
//描述:将一个颜色添加到PalGen的八叉树里。返回当前颜色数
//-----------------------------------------------------------------------------
OPFunc(size_t,AddColor)
(
        PalGenP                pGen,
        ColorVal        R,
        ColorVal        G,
        ColorVal        B
);

//=============================================================================
//函数:ReduceColor
//描述:删减PalGen的八叉树颜色,返回剩余颜色数。
//-----------------------------------------------------------------------------
OPFunc(size_t,ReduceColor)(PalGenP pGen);

//=============================================================================
//函数:GenPalette
//描述:从PalGen中取得调色板
//-----------------------------------------------------------------------------
OPFunc(void,GenPalette)
(
        PalGenP                        pGen,
        FnOnGetColor        pfnOnGetColor,
        void                        *pUserData
);

#endif
```OctPal.c:```
//=============================================================================
//作者:0xAA55
//网站:http://www.0xaa55.com
//请保留原作者信息,否则视为侵权。
//-----------------------------------------------------------------------------
#include"OctPal.h"
#include<stdio.h>
#include<malloc.h>
#include<memory.h>

//=============================================================================
//函数:DefOnError
//描述:默认的错误处理函数
//-----------------------------------------------------------------------------
static void OPCallBack DefOnError(OPError ErrNumber)
{
        //什么也不做
}

//=============================================================================
//函数:CreateNode
//描述:建立节点,函数会根据级数来判断它是否为叶子节点,如果不是,则“可删减”
//-----------------------------------------------------------------------------
static OctreeNodeP CreateNode
(
        size_t                Level,
        PalGenP                pGen
)
{
        OctreeNodeP pNode;
        pNode=(OctreeNodeP)malloc(sizeof(OctreeNode));
        if(!pNode)//错误汇报机制由调用者完成。
                return NULL;

        memset(pNode,0,sizeof(OctreeNode));
        if(Level==ColorBits)
        {
                //判断是否最深一级
                pNode->IsLeaf=1;
                pGen->NbLeaf++;
        }
        else
        {
                //横向连结所有的节点
                pNode->pNext=pGen->pReducibleNodes;
                pGen->pReducibleNodes=pNode;
        }
        return pNode;
}

//=============================================================================
//函数:GetTreePal
//描述:遍历八叉树,从中取得颜色,建立调色板。
//-----------------------------------------------------------------------------
static void GetTreePal
(
        OctreeNodeP                pNode,
        FnOnGetColor        pfnOnGetColor,
        void*                        pUserData
)
{
        if(pNode->IsLeaf)
                pfnOnGetColor(
                        (ColorVal)(pNode->RSum/pNode->NbColors),
                        (ColorVal)(pNode->GSum/pNode->NbColors),
                        (ColorVal)(pNode->BSum/pNode->NbColors),
                        pUserData);
        else
        {
                unsigned i;
                for(i=0;i<8;i++)
                {
                        if(pNode->pChild)
                                GetTreePal(pNode->pChild,pfnOnGetColor,pUserData);
                }
        }
}

//=============================================================================
//函数:DestroyTree
//描述:遍历八叉树,从最深级开始删除整个八叉树。
//-----------------------------------------------------------------------------
static void DestroyTree(OctreeNodeP pTree)
{
        unsigned i;
        for(i=0;i<8;i++)
        {
                if(pTree->pChild)
                        DestroyTree(pTree->pChild);
        }
        free(pTree);
}

//=============================================================================
//函数:CreatePalGen
//描述:分配内存,创建一个PalGen
//-----------------------------------------------------------------------------
OPFunc(PalGenP,CreatePalGen)(FnOnError pfnOnError)
{
        PalGenP pGen;
       
        pGen=(PalGenP)malloc(sizeof(PalGen));
        if(!pGen)
                return NULL;
        memset(pGen,0,sizeof(PalGen));
       
        if(pfnOnError)
                pGen->pfnOnError=pfnOnError;
        else
                pGen->pfnOnError=DefOnError;

        //建立树根节点
        pGen->pRoot=CreateNode(0,pGen);
        if(!pGen->pRoot)
        {
                free(pGen);
                if(pfnOnError)
                        pfnOnError(OPErr_NoMemory);//汇报错误
                return NULL;
        }

        return pGen;
}

//=============================================================================
//函数:AddColor
//描述:将一个颜色添加到PalGen的八叉树里。返回当前颜色数。
//-----------------------------------------------------------------------------
OPFunc(size_t,AddColor)
(
        PalGenP                pGen,
        ColorVal        R,
        ColorVal        G,
        ColorVal        B
)
{
        unsigned i,Index,Shift;
        OctreeNodeP pNode;
        OPError ErrNumber=OPErr_NoError;

        //树根节点必须存在,否则pGen无法初始化成功。见CreatePalGen函数实现。
        pNode=pGen->pRoot;

        //往下扩展树枝
        for(i=1;i<=ColorBits;i++)
        {
                if(pNode->IsLeaf)//如果已经是树叶则不再扩展树枝。
                        break;
                Shift=ColorBits-i;
                Index=        ((R>>Shift)&1)<<0|
                                ((G>>Shift)&1)<<1|
                                ((B>>Shift)&1)<<2;
                if(!(pNode->pChild))
                {
                        pNode->pChild=CreateNode(i,pGen);
                        if(!(pNode->pChild))
                        {
                                ErrNumber=OPErr_NoMemory;
                                goto ErrorReturn;
                        }
                }
                pNode=pNode->pChild;
        }

        //树叶统计颜色
        pNode->RSum+=(ColorSum)R;
        pNode->GSum+=(ColorSum)G;
        pNode->BSum+=(ColorSum)B;
        pNode->NbColors++;
        pGen->NbColors++;

        return 1;
ErrorReturn:
        pGen->pfnOnError(ErrNumber);
        return 0;
}

//=============================================================================
//函数:ReduceColor
//描述:删减PalGen的八叉树颜色,返回剩余颜色数。
//-----------------------------------------------------------------------------
OPFunc(size_t,ReduceColor)(PalGenP pGen)
{
        unsigned i;
        OctreeNodeP pNode;
        OPError ErrNumber=OPErr_NoError;

        //找到最深一级的可删减节点。
        for(i=ColorBits-1;i;i--)
        {
                if(pGen->pReducibleNodes)
                        break;
        }
        if(!pGen->pReducibleNodes)
        {
                ErrNumber=OPErr_NoReducibleNodes;
                goto ErrorReturn;
        }

        //横向指定同级下一个可删减节点
        pNode=pGen->pReducibleNodes;
        pGen->pReducibleNodes=pNode->pNext;

        //删减节点
        for(i=0;i<8;i++)
        {
                if(pNode->pChild)
                {
                        pNode->RSum+=pNode->pChild->RSum;
                        pNode->GSum+=pNode->pChild->GSum;
                        pNode->BSum+=pNode->pChild->BSum;
                        pNode->NbColors+=pNode->pChild->NbColors;
                        free(pNode->pChild);
                        pNode->pChild=NULL;
                        pGen->NbLeaf--;
                }
        }
        pNode->IsLeaf=1;
        pGen->NbLeaf++;

        return pGen->NbLeaf;
ErrorReturn:
        pGen->pfnOnError(ErrNumber);
        return pGen->NbLeaf;
}

//=============================================================================
//函数:GenPalette
//描述:从PalGen中取得调色板
//-----------------------------------------------------------------------------
OPFunc(void,GenPalette)
(
        PalGenP                        pGen,
        FnOnGetColor        pfnOnGetColor,
        void                        *pUserData
)
{
        if(pGen->pRoot)
                GetTreePal(pGen->pRoot,pfnOnGetColor,pUserData);
}


//=============================================================================
//函数:DestroyPalGen
//描述:释放内存,彻底删除PalGen
//-----------------------------------------------------------------------------
OPFunc(void,DestroyPalGen)(PalGenP pGen)
{
        if(pGen->pRoot)
                DestroyTree(pGen->pRoot);
        free(pGen);
}
```Entry.c:```
//=============================================================================
//作者:0xAA55
//网站:http://www.0xaa55.com
//请保留原作者信息,否则视为侵权。
//-----------------------------------------------------------------------------
#define        _USE_MATH_DEFINES
#include<math.h>

#include<tchar.h>
#include<stdio.h>
#include<stdarg.h>
#include<Windows.h>

#include"OctPal.h"
HWND        g_hWnd=NULL;                        //主窗口
HDC                g_hBackgroundDC=NULL;        //后台绘图句柄
HBITMAP        g_hBackgroundBMP=NULL;        //后台绘图的位图
PalGenP        g_pGen=NULL;                        //调色板生成器的存储数据

//=============================================================================
//函数:DbgPrint
//描述:打印调试信息。
//-----------------------------------------------------------------------------
void DbgPrint(char*szFmt,...)
{
        va_list ap;
        char szBuf;

        va_start(ap,szFmt);

        vsprintf(szBuf,szFmt,ap);
        MessageBoxA(g_hWnd,szBuf,"DbgPrint",MB_ICONINFORMATION);

        va_end(ap);
}

//=============================================================================
//函数:DoEvents
//描述:处理窗口消息
//-----------------------------------------------------------------------------
int DoEvents()
{
        MSG msg;
        if(PeekMessage(&msg,g_hWnd,0,0,PM_REMOVE))
        {
                if(msg.message==WM_QUIT)//遇到WM_QUIT消息返回0
                        return 0;
                TranslateMessage(&msg);
                DispatchMessage(&msg);
        }
        return 1;
}

//=============================================================================
//函数:OnGetColor
//描述:取得颜色的回调函数,用于生成调色板。
//-----------------------------------------------------------------------------
RGBQUAD g_Palette;
void OPCallBack OnGetColor
(
        ColorVal R,
        ColorVal G,
        ColorVal B,
        void*pUserData
)
{
        size_t Index=*(size_t*)pUserData;
        if(Index>=256)
                return;
        g_Palette.rgbRed=R;
        g_Palette.rgbGreen=G;
        g_Palette.rgbBlue=B;
        (*(size_t*)pUserData)++;
}

//=============================================================================
//函数:DrawTree
//描述:绘制产生的八叉树。八叉树足够大的时候绘图会变得很慢。
//-----------------------------------------------------------------------------
void DrawTree
(
        HDC hDC,
        OctreeNodeP pNode,
        float fAngle,
        float fX,
        float fY,
        float fLength,
        float fRadius,
        float fMaxSubAngle,
        int LeafMatch
)
{
        unsigned i;
       
        if(pNode->IsLeaf && LeafMatch)//如果是树叶而且只画树叶
        {
                BYTE R,G,B;
                HBRUSH hClrFill;
                LOGBRUSH ClrFill;
                R=(BYTE)(pNode->RSum/pNode->NbColors);
                G=(BYTE)(pNode->GSum/pNode->NbColors);
                B=(BYTE)(pNode->BSum/pNode->NbColors);
                ClrFill.lbStyle=BS_SOLID;
                ClrFill.lbColor=RGB(R,G,B);
                hClrFill=CreateBrushIndirect(&ClrFill);
                hClrFill=(HBRUSH)SelectObject(hDC,hClrFill);
                Ellipse(hDC,
                        (int)(fX-fRadius),(int)(fY-fRadius),
                        (int)(fX+fRadius),(int)(fY+fRadius));
                hClrFill=(HBRUSH)SelectObject(hDC,hClrFill);
                DeleteObject(hClrFill);
        }
        else if(!pNode->IsLeaf)//如果不是树叶则处理下一级
        {
                for(i=0;i<8;i++)
                {
                        if(pNode->pChild)
                        {
                                //计算下一个节点的位置
                                float fSubAng=fAngle+((float)i-3.5f)*fMaxSubAngle/7.0f;
                                float fSubX=fX+cosf(fAngle+fSubAng)*fLength;
                                float fSubY=fY+sinf(fAngle+fSubAng)*fLength;
                                if(!LeafMatch)//如果画树枝,则进行绘图操作。
                                {
                                        MoveToEx(hDC,(int)fX,(int)fY,NULL);
                                        LineTo(hDC,
                                                (int)fSubX,
                                                (int)fSubY);
                                }
                                DrawTree(hDC,pNode->pChild,fSubAng,//递归下一个节点
                                        fSubX,fSubY,fLength,fRadius,fMaxSubAngle,LeafMatch);
                        }
                }
        }
}

//=============================================================================
//函数:ParseBMPFile
//描述:分析BMP文件,产生八叉树,然后产生调色板
//-----------------------------------------------------------------------------
void ParseBMPFile(LPCTSTR szBMPFile)
{
        BITMAPFILEHEADER BMFH;//BMP文件头
        BITMAPINFOHEADER BMIF;//BMP信息头

        size_t Pitch;//每行字节数
        RGBTRIPLE*LineData=NULL;//行像素数据
        size_t x,y,Index;//用于遍历的变量

        RECT rc;//窗口客户区域的左、上、右、下
        DWORD dwLastTick=0;//用于粗略计时

        FILE*fp=NULL;
        //打开文件进行读取
        fp=_tfopen(szBMPFile,TEXT("rb"));
        if(!fp)
                goto ReadFail;
       
        //读取文件头
        if(fread(&BMFH,1,sizeof(BMFH),fp)!=sizeof(BMFH))goto ReadFail;
        if(fread(&BMIF,1,sizeof(BMIF),fp)!=sizeof(BMIF))goto ReadFail;

        //判断文件格式
        if(BMFH.bfType!='MB')
                goto BadFormat;
        if(BMIF.biBitCount!=24)
                goto Not24Bit;

        fseek(fp,BMFH.bfOffBits,SEEK_SET);

        //清空调色板
        memset(g_Palette,0,sizeof(g_Palette));

        //重置调色板生成器数据
        if(g_pGen)
                DestroyPalGen(g_pGen);
        g_pGen=CreatePalGen(NULL);
        if(!g_pGen)
                goto NoMem;
       
        //分配一行像素的内存用于读取
        Pitch=((BMIF.biWidth*BMIF.biBitCount-1)/32+1)*4;//4字节对齐
        LineData=(RGBTRIPLE*)malloc(Pitch);
        if(!LineData)
                goto NoMem;

        //重绘窗口
        GetClientRect(g_hWnd,&rc);
        InvalidateRect(g_hWnd,&rc,TRUE);

        //遍历每一行
        y=BMIF.biHeight;
        do
        {
                RGBTRIPLE*pPixel;//像素指针
                fread(LineData,1,Pitch,fp);//读取一行像素
                pPixel=LineData;//准备遍历这行的每一个像素

                x=BMIF.biWidth;//遍历每一个像素
                do
                {
                        //添加像素颜色到八叉树
                        AddColor(g_pGen,pPixel->rgbtRed,
                                                        pPixel->rgbtGreen,
                                                        pPixel->rgbtBlue);
                        while(g_pGen->NbLeaf>256)//使眼色数量不超过256色
                                ReduceColor(g_pGen);
                        pPixel++;
                }while(--x);

                //每处理一行像素,判断是否应该重绘屏幕
                //最大重绘频率每秒钟100下。因为绘图需要消耗时间。
                if(GetTickCount()-dwLastTick>=10)
                {
                        dwLastTick=GetTickCount();
                        InvalidateRect(g_hWnd,&rc,FALSE);//重绘屏幕
                        if(!DoEvents())
                                break;
                }
        }while(--y);

        Index=0;
        GenPalette(g_pGen,OnGetColor,&Index);//产生调色板
        InvalidateRect(g_hWnd,&rc,FALSE);//最后重绘屏幕

CleanReturn:
        if(fp)//返回前清理内存
                fclose(fp);
        free(LineData);
        return;
NoMem:
        MessageBox(g_hWnd,TEXT("内存不足。"),NULL,MB_ICONEXCLAMATION);
        goto CleanReturn;
ReadFail:
        MessageBox(g_hWnd,TEXT("读取文件失败。"),NULL,MB_ICONEXCLAMATION);
        goto CleanReturn;
BadFormat:
        MessageBox(g_hWnd,TEXT("不是BMP文件或格式错误。"),NULL,MB_ICONEXCLAMATION);
        goto CleanReturn;
Not24Bit:
        MessageBox(g_hWnd,TEXT("不是24位BMP文件。"),NULL,MB_ICONEXCLAMATION);
        goto CleanReturn;
}

LRESULT CALLBACK WndProc(HWND hWnd,UINT uMsg,WPARAM wp,LPARAM lp);//消息处理器
//=============================================================================
//函数:_tWinMain
//描述:整个程序的入口点
//-----------------------------------------------------------------------------
int APIENTRY _tWinMain
(
        HINSTANCE hInst,
        HINSTANCE hPrevInst,
        LPTSTR szCmdLine,
        int nShowCmd
)
{
        WNDCLASSEX WndClass;
        ATOM ClassAtom=0;
        MSG msg;

        //注册窗口类
        memset(&WndClass,0,sizeof(WndClass));
        WndClass.cbSize=sizeof(WndClass);
    WndClass.lpfnWndProc=WndProc;
    WndClass.hInstance=hInst;
        WndClass.hIcon=LoadIcon(NULL,MAKEINTRESOURCE(IDI_APPLICATION));
    WndClass.hCursor=LoadCursor(NULL,MAKEINTRESOURCE(IDC_ARROW));
    WndClass.hbrBackground=(HBRUSH)(COLOR_BTNFACE+1);
    WndClass.lpszClassName=TEXT("NormalWindow");
    WndClass.hIconSm=LoadIcon(NULL,MAKEINTRESOURCE(IDI_APPLICATION));
        ClassAtom=RegisterClassEx(&WndClass);
        if(!ClassAtom)
                goto Cleanup;

        //创建窗口
        g_hWnd=CreateWindowEx(0,(LPCTSTR)ClassAtom,TEXT("主窗口"),
                WS_OVERLAPPEDWINDOW,
                CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,
                NULL,NULL,hInst,NULL);
        if(!g_hWnd)
                goto Cleanup;

        //显示窗口
        ShowWindow(g_hWnd,nShowCmd);
        UpdateWindow(g_hWnd);

        //设置窗口允许接收拖拽文件。
        DragAcceptFiles(g_hWnd,TRUE);

        //消息循环
        while(GetMessage(&msg,NULL,0,0))
        {
                TranslateMessage(&msg);
                DispatchMessage(&msg);
        }

        if(g_hBackgroundBMP)
                DeleteObject(g_hBackgroundBMP);
        if(g_hBackgroundDC)
                ReleaseDC(g_hWnd,g_hBackgroundDC);

        if(g_pGen)
                DestroyPalGen(g_pGen);
        UnregisterClass((LPCTSTR)ClassAtom,hInst);
        return 0;
Cleanup:
        if(ClassAtom)
                UnregisterClass((LPCTSTR)ClassAtom,hInst);
        MessageBox(NULL,TEXT("启动程序失败。"),
                TEXT("无法启动程序"),MB_ICONEXCLAMATION);
        return 1;
}

//=============================================================================
//函数:WndProc
//描述:窗口消息处理程序
//-----------------------------------------------------------------------------
LRESULT CALLBACK WndProc(HWND hWnd,UINT uMsg,WPARAM wp,LPARAM lp)
{
        switch(uMsg)
        {
        case WM_CREATE:
        case WM_SIZING:
        case WM_SIZE:
                {
                        RECT rc;
                        HDC hDC;

                        hDC=GetDC(hWnd);
                        GetClientRect(hWnd,&rc);

                        //重新生成后台画板
                        if(g_hBackgroundBMP)
                                DeleteObject(g_hBackgroundBMP);
                        if(g_hBackgroundDC)
                                ReleaseDC(hWnd,g_hBackgroundDC);
                        g_hBackgroundDC=CreateCompatibleDC(hDC);
                        g_hBackgroundBMP=CreateCompatibleBitmap
                                (hDC,rc.right-rc.left,rc.bottom-rc.top);
                        SelectObject(g_hBackgroundDC,g_hBackgroundBMP);
                        if(hDC)
                                ReleaseDC(hWnd,hDC);
                       
                        //刷白画板
                        FillRect(g_hBackgroundDC,&rc,(HBRUSH)(COLOR_BTNFACE+1));
                        InvalidateRect(hWnd,&rc,FALSE);
                        break;
                }
        case WM_PAINT:
                {
                        PAINTSTRUCT ps;
                        HDC hDC;
                        HBRUSH hBrush;
                        RECT rc;
                        TCHAR szTitle;

                        size_t x,y,Index;
                       
                        //绘图时,先绘制到画板上,再把画板覆盖到窗口上。

                        GetClientRect(hWnd,&rc);
                        hDC=BeginPaint(hWnd,&ps);

                        FillRect(g_hBackgroundDC,&rc,(HBRUSH)(COLOR_BTNFACE+1));

                        Index=0;
                        //先画调色板
                        for(y=0;y<16;y++)
                        {
                                for(x=0;x<16;x++)
                                {
                                        hBrush=CreateSolidBrush(RGB(
                                                g_Palette.rgbRed,
                                                g_Palette.rgbGreen,
                                                g_Palette.rgbBlue));
                                        hBrush=(HBRUSH)SelectObject(g_hBackgroundDC,hBrush);

                                        //调色板,每个颜色8x8
                                        Rectangle(g_hBackgroundDC,x*8,y*8,x*8+8,y*8+8);

                                        hBrush=(HBRUSH)SelectObject(g_hBackgroundDC,hBrush);
                                        DeleteObject(hBrush);
                                        Index++;
                                }
                        }
                        //然后画八叉树
                        if(g_pGen && g_pGen->pRoot)
                        {
                                float        fRootX=(float)(rc.right)*0.5f,
                                                fRootY=(float)(rc.bottom)*0.75f;
                                //先画树枝
                                DrawTree(g_hBackgroundDC,g_pGen->pRoot,
                                        -(float)M_PI_4,fRootX,fRootY,40,4,(float)(M_PI*0.125),0);
                                //后画树叶
                                DrawTree(g_hBackgroundDC,g_pGen->pRoot,
                                        -(float)M_PI_4,fRootX,fRootY,40,4,(float)(M_PI*0.125),1);
                        }
                       
                        BitBlt(hDC,0,0,rc.right-rc.left,rc.bottom-rc.top,
                                g_hBackgroundDC,0,0,SRCCOPY);
                        EndPaint(hWnd,&ps);

                        _stprintf(szTitle,TEXT("%u"),g_pGen->NbLeaf);
                        SetWindowText(hWnd,szTitle);
                }
                break;
        case WM_DESTROY:
                PostQuitMessage(0);
                return 0;
        case WM_DROPFILES:
                {
                        TCHAR*pszDropFileName;
                        size_t cbDropFileName;
                        //取得第0个拖入文件的文件名长度
                        cbDropFileName=DragQueryFile((HDROP)wp,0,NULL,0)+1;//添加'\0'结尾
                        //分配内存并取得第0个拖入文件名
                        pszDropFileName=(TCHAR*)malloc(cbDropFileName*sizeof(TCHAR));
                        if(pszDropFileName)
                        {
                                DragQueryFile((HDROP)wp,0,pszDropFileName,cbDropFileName);
                                DragFinish((HDROP)wp);
                                ParseBMPFile(pszDropFileName);
                                free(pszDropFileName);
                        }
                }
                return 0;
        }
        return DefWindowProc(hWnd,uMsg,wp,lp);
}
```BIN:
SRC:
页: [1]
查看完整版本: 【图形】八叉树算法取得图像最适应调色板