找回密码
 立即注册→加入我们

QQ登录

只需一步,快速开始

搜索
热搜: 下载 VB C 实现 编写
查看: 10668|回复: 0

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

[复制链接]
发表于 2015-1-29 06:00:45 | 显示全部楼层 |阅读模式

欢迎访问技术宅的结界,请注册或者登录吧。

您需要 登录 才可以下载或查看,没有账号?立即注册→加入我们

×
这次的算法是我的上一个帖子中的程序的完全重写版。之前的程序参考了微软的相关代码,那代码可读性不高,而且用起来麻烦。因此我重新写了这个代码。
八叉树算法用于产生最适合一副图像的256色调色板。这个算法使用一种八叉树的结构,通过建立八叉树,将颜色筛选出来,接近的颜色会被合并,而在图像中出现次数极少的颜色和出现次数很多的颜色都会被照顾到。
这次我写了个窗口,我用它来显示整个八叉树的产生过程。大家可以自己下载了然后修改使其能发展出一个完整的八叉树。因为我的代码是为了达到取得最适应调色板的目的(而不单纯只是为了建立八叉树),所以在树的产生过程中就有“砍树”的行为,一个是减少内存占用,一个是加速程序运行。
Bin在运行的时候,大家可以直接拖拽一个24位BMP文件到窗口内,然后它就会分析这个文件,取得它的像素,再产生八叉树、调色板。
技术结界.png
20150129052319.png
201307020106_002.png
20150129052744.png
颜色越多,最终产生的八叉树的深度就越小。
同时八叉树的展开程度决定了图像颜色的明暗度分布情况。
现在我写的这个库是这么用的:
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[8];//八叉树子节点(Index=红绿蓝三个颜色值各贡献一位)
    struct _OctNode*pNext;//下一个可删减节点
}OctreeNode,*OctreeNodeP;//八叉树节点
typedef struct
{
    OctreeNodeP pRoot;
    OctreeNodeP pReducibleNodes[ColorBits];//可删减节点
    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[Level];
        pGen->pReducibleNodes[Level]=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[i])
                GetTreePal(pNode->pChild[i],pfnOnGetColor,pUserData);
        }
    }
}

//=============================================================================
//函数:DestroyTree
//描述:遍历八叉树,从最深级开始删除整个八叉树。
//-----------------------------------------------------------------------------
static void DestroyTree(OctreeNodeP pTree)
{
    unsigned i;
    for(i=0;i<8;i++)
    {
        if(pTree->pChild[i])
            DestroyTree(pTree->pChild[i]);
    }
    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[Index]))
        {
            pNode->pChild[Index]=CreateNode(i,pGen);
            if(!(pNode->pChild[Index]))
            {
                ErrNumber=OPErr_NoMemory;
                goto ErrorReturn;
            }
        }
        pNode=pNode->pChild[Index];
    }

    //树叶统计颜色
    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[i])
            break;
    }
    if(!pGen->pReducibleNodes[i])
    {
        ErrNumber=OPErr_NoReducibleNodes;
        goto ErrorReturn;
    }

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

    //删减节点
    for(i=0;i<8;i++)
    {
        if(pNode->pChild[i])
        {
            pNode->RSum+=pNode->pChild[i]->RSum;
            pNode->GSum+=pNode->pChild[i]->GSum;
            pNode->BSum+=pNode->pChild[i]->BSum;
            pNode->NbColors+=pNode->pChild[i]->NbColors;
            free(pNode->pChild[i]);
            pNode->pChild[i]=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[0x1000];

    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[256];
void OPCallBack OnGetColor
(
    ColorVal R,
    ColorVal G,
    ColorVal B,
    void*pUserData
)
{
    size_t Index=*(size_t*)pUserData;
    if(Index>=256)
        return;
    g_Palette[Index].rgbRed=R;
    g_Palette[Index].rgbGreen=G;
    g_Palette[Index].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[i])
            {
                //计算下一个节点的位置
                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[i],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[256];

            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[Index].rgbRed,
                        g_Palette[Index].rgbGreen,
                        g_Palette[Index].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: OctreePalette_exe.7z (44.1 KB, 下载次数: 58)
SRC: OctreePalette.7z (90.99 KB, 下载次数: 87)

本帖被以下淘专辑推荐:

回复

使用道具 举报

本版积分规则

QQ|Archiver|小黑屋|技术宅的结界 ( 滇ICP备16008837号 )|网站地图

GMT+8, 2026-1-30 12:31 , Processed in 0.039737 second(s), 30 queries , Gzip On.

Powered by Discuz! X3.5

© 2001-2026 Discuz! Team.

快速回复 返回顶部 返回列表