0xAA55 发表于 2015-3-9 21:53:37

【C】真·抖动算法(GPU加速)Dithering with GPU

我真是醉了,一直以为抖动算法都是要靠将颜色当成向量然后在空间中进行几何运算,曾经写的抖动算法都是这样的,效果却很不好。
这次从维基百科找到了算法= =果然是被自己坑死了
http://en.wikipedia.org/wiki/Ordered_dithering
其实算法很简单。看下面的伪指令:For Y = 1 To 高度
    For X = 1 To 宽度
      最近距离 = 取得距离(找到最接近的调色板颜色(原始颜色(x, y)), 原始颜色(x, y)) / 最大距离
      旧像素 = 原始颜色(x, y) + 抖动矩阵(x Mod 16, Y Mod 16) * 最近距离
      新像素 = 找到最接近的调色板颜色(旧像素)
    Next
Next这样的处理是完全可以进行并行处理的,所以不能浪费GPU资源。当然如果没有找到OpenGL 2.0支持的时候,我写的这个库,也能自动进行软件抖动。



测试用图:
图像来源:http://www.pixiv.net/member_illust.php?mode=medium&illust_id=45035418
画师:木葵ひなた
槚本贵音 (Ene)
电视动画:目隐都市的演绎者

经过抖动算法之后,图像在被降级至256色的时候,仍然能表现出不错的画质。这就是抖动算法的作用了。当然刚才给的示例中我只是用的平均抖动算法,所以效果不太好。要配合八叉树算法才能将图像的损失降到最低。
相关资料:八叉树算法http://www.0xaa55.com/thread-1214-1-1.html//=============================================================================
//作者:0xAA55
//网站:http://www.0xaa55.com/
//请保留原作者信息,否则视为侵权。
//-----------------------------------------------------------------------------
//必须在包含gl.h之前包含glew.h
#include<gl\glew.h>
#include<stdio.h>
#include<stdint.h>
#include<malloc.h>
#include<math.h>

#define        D_Source
#include"Dither.h"

#define MaxWaitTime 1000
//#define Software_Only

//着色器代码
extern char                g_szDitherVSCode[];
extern char                g_szDitherFSCode[];

#ifdef _DEBUG
static void DbgInfo(char*szFormat,...)
{
        char szBuf;//4K缓冲区
        va_list ap;
        va_start(ap,szFormat);
        vsprintf(szBuf,szFormat,ap);
        MessageBoxA(NULL,szBuf,"Dither",MB_ICONINFORMATION);
        va_end(ap);
}
static void ShowShaderOutput(GLuint uProgram)
{
        GLsizei Length;
        glGetProgramiv(uProgram,GL_INFO_LOG_LENGTH,&Length);
        if(Length)
        {
                GLsizei BytesRet;
                char*pBuffer=(char*)malloc(Length);
                if(!pBuffer)
                        return;
                glGetProgramInfoLog(uProgram,Length,&BytesRet,pBuffer);
                if(strlen(pBuffer))
                        MessageBoxA(NULL,pBuffer,NULL,MB_OK);
                free(pBuffer);
        }
}
#else
#define DbgInfo(fmt,...)
#define ShowShaderOutput(u)
#endif // _DEBUG

const uint8_t g_DitherMatrix[]=
{
        0x00,0xEB,0x3B,0xDB,0x0F,0xE7,0x37,0xD7,0x02,0xE8,0x38,0xD9,0x0C,0xE5,0x34,0xD5,
        0x80,0x40,0xBB,0x7B,0x8F,0x4F,0xB7,0x77,0x82,0x42,0xB8,0x78,0x8C,0x4C,0xB4,0x74,
        0x21,0xC0,0x10,0xFB,0x2F,0xCF,0x1F,0xF7,0x22,0xC2,0x12,0xF8,0x2C,0xCC,0x1C,0xF4,
        0xA1,0x61,0x90,0x50,0xAF,0x6F,0x9F,0x5F,0xA2,0x62,0x92,0x52,0xAC,0x6C,0x9C,0x5C,
        0x08,0xE1,0x30,0xD0,0x05,0xEF,0x3F,0xDF,0x0A,0xE2,0x32,0xD2,0x06,0xEC,0x3C,0xDC,
        0x88,0x48,0xB0,0x70,0x85,0x45,0xBF,0x7F,0x8A,0x4A,0xB2,0x72,0x86,0x46,0xBC,0x7C,
        0x29,0xC8,0x18,0xF0,0x24,0xC5,0x14,0xFF,0x2A,0xCA,0x1A,0xF2,0x26,0xC6,0x16,0xFC,
        0xA9,0x69,0x98,0x58,0xA4,0x64,0x94,0x54,0xAA,0x6A,0x9A,0x5A,0xA6,0x66,0x96,0x56,
        0x03,0xE9,0x39,0xD8,0x0D,0xE4,0x35,0xD4,0x01,0xEA,0x3A,0xDA,0x0E,0xE6,0x36,0xD6,
        0x83,0x43,0xB9,0x79,0x8D,0x4D,0xB5,0x75,0x81,0x41,0xBA,0x7A,0x8E,0x4E,0xB6,0x76,
        0x23,0xC3,0x13,0xF9,0x2D,0xCD,0x1D,0xF5,0x20,0xC1,0x11,0xFA,0x2E,0xCE,0x1E,0xF6,
        0xA3,0x63,0x93,0x53,0xAD,0x6D,0x9D,0x5D,0xA0,0x60,0x91,0x51,0xAE,0x6E,0x9E,0x5E,
        0x0B,0xE3,0x33,0xD3,0x07,0xED,0x3D,0xDD,0x09,0xE0,0x31,0xD1,0x04,0xEE,0x3E,0xDE,
        0x8B,0x4B,0xB3,0x73,0x87,0x47,0xBD,0x7D,0x89,0x49,0xB1,0x71,0x84,0x44,0xBE,0x7E,
        0x2B,0xCB,0x1B,0xF3,0x27,0xC7,0x17,0xFD,0x28,0xC9,0x19,0xF1,0x25,0xC4,0x15,0xFE,
        0xAB,0x6B,0x9B,0x5B,0xA7,0x67,0x97,0x57,0xA8,0x68,0x99,0x59,0xA5,0x65,0x95,0x55,
};

#ifdef _WIN32

//=============================================================================
//函数:WndProcForHiddenWindow
//描述:隐藏窗口的消息处理程序
//-----------------------------------------------------------------------------
static LRESULT CALLBACK WndProcForHiddenWindow
(
        HWND hWnd,
        UINT Msg,
        WPARAM wp,
        LPARAM lp
)
{
        switch(Msg)
        {
        default:
                return DefWindowProc(hWnd,Msg,wp,lp);
        case WM_DESTROY:
                PostQuitMessage(0);
                return 0;
        }
}

//=============================================================================
//函数:HiddenWindowThreadProc
//描述:创建一个线程用于处理窗口消息循环。
//平台相关。参数为堆上的D_GLContext结构体
//-----------------------------------------------------------------------------
static DWORD WINAPI HiddenWindowThreadProc(D_GLContextP pContext)
{
        MSG msg;
        WNDCLASSEX WCEx=
        {
                sizeof(WNDCLASSEX),
                0,
                (WNDPROC)WndProcForHiddenWindow,
                0,
                0,
                GetModuleHandle(NULL),
                NULL,
                NULL,
                (HBRUSH)(COLOR_WINDOW+1),
                NULL,
                TEXT("Dither_HiddenWindow"),
                NULL
        };
        pContext->aHiddenWindow=RegisterClassEx(&WCEx);
        if(pContext->aHiddenWindow && !pContext->hWnd)
                pContext->hWnd=CreateWindowEx(0,MAKEINTATOM(pContext->aHiddenWindow),
                TEXT(""),WS_POPUP|WS_SYSMENU,0,0,1,1,NULL,NULL,WCEx.hInstance,NULL);
       
        while(GetMessage(&msg,pContext->hWnd,0,0))
        {
                if(msg.message==WM_CLOSE)
                        break;
                TranslateMessage(&msg);
                DispatchMessage(&msg);
        }
        return msg.wParam;
}

//=============================================================================
//函数:CreateHiddenWindow
//描述:创建一个隐藏的窗口,用于初始化OpenGL
//平台相关。
//-----------------------------------------------------------------------------
static void CreateHiddenWindow(D_GLContextP pContext)
{
        DWORD dwTimer;
        pContext->hThread=CreateThread(NULL,0,
                (LPTHREAD_START_ROUTINE)HiddenWindowThreadProc,pContext,0,
                &(pContext->dwThreadID));
        if(!pContext->hThread)
        {
                //创建线程失败。
                return;
        }
        dwTimer=GetTickCount();
        while(!pContext->hWnd)
        {
                DWORD dwExitCode;
                GetExitCodeThread(pContext->hThread,&dwExitCode);
                if(dwExitCode!=STILL_ACTIVE)//如果线程已经退出
                {
                        CloseHandle(pContext->hThread);
                        pContext->hThread=NULL;
                        //线程失败
                        return;
                }
                else//线程仍在运行
                {
                        //超出能等待的时间
                        if(GetTickCount()-dwTimer>MaxWaitTime)
                        {
                                TerminateThread(pContext->hThread,1);
                                CloseHandle(pContext->hThread);
                                pContext->hThread=NULL;
                                //线程失败
                                return;
                        }
                        //切换到线程
#                        if(_WIN32_WINNT >= 0x0600)
                        SwitchToThread();
#                        else
                        Sleep(1);
#                        endif
                }
        }
}

//=============================================================================
//函数:DestroyHiddenWindow
//描述:销毁隐藏的窗口
//-----------------------------------------------------------------------------
static void DestroyHiddenWindow(D_GLContextP pContext)
{
        if(pContext->hWnd)
        {
                //发送关闭窗口的命令
                PostMessage(pContext->hWnd,WM_CLOSE,0,0);
        }
        if(pContext->hThread)
        {
                DWORD dwExitCode=0;
                //判断线程是否仍在运行
                GetExitCodeThread(pContext->hThread,&dwExitCode);
                if(dwExitCode==STILL_ACTIVE)
                        WaitForSingleObject(pContext->hThread,MaxWaitTime);
                //判断线程是否仍在运行
                GetExitCodeThread(pContext->hThread,&dwExitCode);
                if(dwExitCode==STILL_ACTIVE)
                        TerminateThread(pContext->hThread,0);
                CloseHandle(pContext->hThread);
        }
}

//=============================================================================
//函数:InitGL
//描述:定义初始化OpenGL的操作
//平台相关。
//-----------------------------------------------------------------------------
static HGLRC InitGL(HDC hDC)
{
        HGLRC hRC;
        PIXELFORMATDESCRIPTOR PFD={0};
        int nPixelFormat;

        PFD.nSize                =sizeof(PFD);
        PFD.nVersion        =1;
        PFD.dwFlags                =PFD_DRAW_TO_WINDOW|PFD_SUPPORT_OPENGL|PFD_DOUBLEBUFFER;
        PFD.iPixelType        =PFD_TYPE_RGBA;
        PFD.cDepthBits        =0;
        PFD.cBlueBits        =8;        PFD.cBlueShift        =0;
        PFD.iLayerType        =PFD_MAIN_PLANE;

        nPixelFormat=ChoosePixelFormat(hDC,&PFD);
        SetPixelFormat(hDC,nPixelFormat,&PFD);

        hRC=wglCreateContext(hDC);
        if(hRC)
        {
                wglMakeCurrent(hDC,hRC);
                glewInit();
                wglMakeCurrent(NULL,NULL);
                return hRC;
        }
        else
                return NULL;
}

//=============================================================================
//函数:SetDitherGLContext
//描述:设置OpenGL上下文,进行OpenGL渲染前必须调用此函数。
//-----------------------------------------------------------------------------
static void SetDitherGLContext
(
        D_GLContextP pContext
)
{
        if(pContext)
                wglMakeCurrent(pContext->hDC,pContext->hRC);
        else
                wglMakeCurrent(NULL,NULL);
}

//=============================================================================
//函数:UnsetDitherGLContextt
//描述:取消设置OpenGL上下文,完成OpenGL渲染后必须调用此函数。
//和SetDitherGLContext匹配使用
//-----------------------------------------------------------------------------
static void UnsetDitherGLContext()
{
        wglMakeCurrent(NULL,NULL);
}

//=============================================================================
//函数:GetDitherGLContext
//描述:创建GLContext结构,并且初始化OpenGL
//代码平台相关
//-----------------------------------------------------------------------------
D_Func(D_GLContextP,CreateDitherGLContext)()
{
        D_GLContextP pRet=NULL;
        GLint iUniform;//着色器参数
        GLuint uDitherVS;//顶点着色器
        GLuint uDitherFS;//像素着色器
        GLchar*pszDitherVSCode=g_szDitherVSCode;//顶点着色器代码
        GLchar*pszDitherFSCode=g_szDitherFSCode;//像素着色器代码

        //分配内存
        pRet=(D_GLContextP)malloc(sizeof(D_GLContext));
        if(!pRet)
        {
                //内存不足
                return NULL;
        }
        memset(pRet,0,sizeof(D_GLContext));

        CreateHiddenWindow(pRet);
        if(!pRet->aHiddenWindow||!pRet->hWnd)
        {
                //创建隐藏的窗口失败
                goto ErrorHandler;
        }

        //初始化OpenGL
        pRet->hDC=GetWindowDC(pRet->hWnd);
        pRet->hRC=InitGL(pRet->hDC);
        if(pRet->hRC)
        {
                //如果功能没有支持到OpenGL 2.0
#                ifndef Software_Only
                if(!GLEW_VERSION_2_0)
                {
#                endif
                        wglDeleteContext(pRet->hRC);
                        pRet->hRC=NULL;
                        ReleaseDC(pRet->hWnd,pRet->hDC);
                        pRet->hDC=NULL;
                        DestroyHiddenWindow(pRet);
                        pRet->hWnd=NULL;
#                ifndef Software_Only
                }
#                endif
        }
        else
        {
                //初始化OpenGL失败
                //使用软件加速
                ReleaseDC(pRet->hWnd,pRet->hDC);
                pRet->hDC=NULL;
                DestroyHiddenWindow(pRet);
                pRet->hWnd=NULL;
        }

        if(pRet->hRC)
        {
                SetDitherGLContext(pRet);

                //编译着色器代码
                uDitherVS=glCreateShader(GL_VERTEX_SHADER);
                uDitherFS=glCreateShader(GL_FRAGMENT_SHADER);
                if(!uDitherVS || !uDitherFS)
                {
                        //创建着色器失败
                        goto ErrorHandler;
                }
                glShaderSource(uDitherVS,1,&pszDitherVSCode,NULL);
                glShaderSource(uDitherFS,1,&pszDitherFSCode,NULL);
                glCompileShader(uDitherVS);
                glCompileShader(uDitherFS);
                pRet->uDitherProgram=glCreateProgram();
                if(!pRet->uDitherProgram)
                {
                        //创建着色器失败
                        goto ErrorHandler;
                }
                glAttachShader(pRet->uDitherProgram,uDitherVS);
                glAttachShader(pRet->uDitherProgram,uDitherFS);
                glLinkProgram(pRet->uDitherProgram);
                ShowShaderOutput(pRet->uDitherProgram);
       
                //设置着色器常数参数
                glUseProgram(pRet->uDitherProgram);
                iUniform=glGetUniformLocation(pRet->uDitherProgram,"Pal");
                glUniform1i(iUniform,0);
                iUniform=glGetUniformLocation(pRet->uDitherProgram,"Tex");
                glUniform1i(iUniform,1);
                iUniform=glGetUniformLocation(pRet->uDitherProgram,"DitherMat");
                glUniform1i(iUniform,2);
                glUseProgram(0);

                //创建纹理缓冲区
                glGenTextures(3,pRet->uTextures);

                //抖动矩阵的纹理
                glBindTexture(GL_TEXTURE_2D,pRet->uTextures);
                glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
                glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
                glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_REPEAT);
                glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_REPEAT);
                glTexImage2D(GL_TEXTURE_2D,0,GL_RGB,16,16,0,
                        GL_BLUE,GL_UNSIGNED_BYTE,g_DitherMatrix);       
                glBindTexture(GL_TEXTURE_2D,0);

                UnsetDitherGLContext();
        }

        //返回创建的结构体
        return pRet;
ErrorHandler:
        DestroyDitherGLContext(&pRet);
        return NULL;
}

//=============================================================================
//函数:DestroyDitherGLContext
//描述:销毁创建的GLContext结构,结束OpenGL
//-----------------------------------------------------------------------------
D_Func(void,DestroyDitherGLContext)
(
        D_GLContextP*ppContext
)
{
        if(ppContext && *ppContext)
        {
                if((*ppContext)->hRC)
                        wglDeleteContext((*ppContext)->hRC);
                if((*ppContext)->hDC && (*ppContext)->hWnd)
                {
                        ReleaseDC((*ppContext)->hWnd,(*ppContext)->hDC);
                        DestroyHiddenWindow(*ppContext);
                }
                free(*ppContext);
                *ppContext=NULL;
        }
}

//=============================================================================
//函数:Get24BitBits
//描述:从一个HDC取得24位的Bits。缓冲区需要用户自己分配
//-----------------------------------------------------------------------------
D_Func(BOOL,Get24BitBits)
(
        HDC                hDC,
        LONG        lOrgWidth,
        LONG        lOrgHeight,
        void        *pBits24
)
{
        HDC                hDIBDC=NULL;
        HBITMAP        hDIBitmap=NULL;
        BITMAPINFOHEADER BMIF24={0};
        void        *pBits;

        BMIF24.biSize=sizeof(BMIF24);
        BMIF24.biWidth=lOrgWidth;
        BMIF24.biHeight=lOrgHeight;
        BMIF24.biPlanes=1;
        BMIF24.biBitCount=24;
        BMIF24.biClrImportant=BI_RGB;
        BMIF24.biSizeImage=CalcSize(24,lOrgWidth,lOrgHeight);

        //先创建一个HDC
        hDIBDC=CreateCompatibleDC(hDC);
        if(!hDIBDC)
                goto ErrorHandler;

        //创建位图
        hDIBitmap=CreateDIBSection(hDIBDC,(BITMAPINFO*)&BMIF24,DIB_PAL_COLORS,
                &pBits,NULL,0);
        if(!hDIBitmap)
                goto ErrorHandler;

        //选入位图
        SelectObject(hDIBDC,hDIBitmap);

        //绘制位图到我们创建的24位DIB位图缓冲区
        BitBlt(hDIBDC,0,0,lOrgWidth,lOrgHeight,hDC,0,0,SRCCOPY);

        //将Bits拷贝出去
        memcpy(pBits24,pBits,(size_t)CalcSize(24,lOrgWidth,lOrgHeight));

        //删除已经创建的缓冲区和设备上下文
        DeleteObject(hDIBitmap);
        DeleteDC(hDIBDC);
        return TRUE;
ErrorHandler:
        if(hDIBitmap)
                DeleteObject(hDIBitmap);
        if(hDIBDC)
                DeleteDC(hDIBDC);
        return FALSE;
}
#else
#error TODO:添加其它平台代码
#endif // _WIN32

//=============================================================================
//函数:CalcPitch
//描述:根据像素位数和图像宽度计算每行字节数(32位对齐)
//-----------------------------------------------------------------------------
D_Func(GLsizei,CalcPitch)
(
        GLsizei BitCount,
        GLsizei Width
)
{
        return _CalcPitch(BitCount,Width);
}
#define CalcPitch _CalcPitch

//=============================================================================
//函数:CalcSize
//描述:根据像素位数和图像高度计算整个位图的字节数(32位对齐)
//-----------------------------------------------------------------------------
D_Func(GLsizei,CalcSize)
(
        GLsizei BitCount,
        GLsizei Width,
        GLsizei Height
)
{
        return _CalcSize(BitCount,Width,Height);
}
#define CalcSize _CalcSize

//=============================================================================
//函数:ConvToP2Image
//描述:将位图转换为尺寸是2的N次方的位图。返回错误代码。ppDstBits用于接收新创建
//的位图。
//-----------------------------------------------------------------------------
static int        ConvToP2Image
(
        void        *pSrcBits,                //原位图指针
        GLuint        uBitCount,                //原位图位数
        GLsizei        BMPWidth,                //位图宽度
        GLsizei        BMPHeight,                //位图高度
        GLsizei        *NewWidth,                //返回的新位图宽度
        GLsizei        *NewHeight,                //返回的新位图高度
        void        **ppDstBits                //新位图指针
)
{
        GLsizei        TexWidth=1,TexHeight=1;//数值扩展到2的N次方的位图的新尺寸
        void        *pNewBits=NULL;
        size_t        cbSrcPitch,cbDstPitch;
        size_t        cbDstBits;
        void        *pSrcLine;
        void        *pDstLine;
        GLsizei y;
        int                iErrCode=DErr_Unknown;

        //计算新的位图尺寸
        while(TexWidth && TexWidth<BMPWidth)TexWidth<<=1;
        if(!TexWidth)return DErr_BitmapTooLarge;
        while(TexHeight && TexHeight<BMPHeight)TexHeight<<=1;
        if(!TexHeight)return DErr_BitmapTooLarge;

        //计算每行字节数
        cbSrcPitch=CalcPitch(uBitCount,BMPWidth);
        cbDstPitch=CalcPitch(uBitCount,TexWidth);

        //给新的位图分配缓冲区
        pNewBits=malloc(cbDstBits=cbDstPitch*TexHeight);
        if(!pNewBits)
        {
                iErrCode=DErr_NoEnoughMemory;
                goto ErrorHandler;
        }
        memset(pNewBits,0,cbDstBits);

        //一行一行拷贝内容
        pSrcLine=pSrcBits;
        pDstLine=pNewBits;
        for(y=0;y<BMPHeight;y++)
        {
                memcpy(pDstLine,pSrcLine,cbSrcPitch);
                (uint8_t*)pSrcLine+=cbSrcPitch;
                (uint8_t*)pDstLine+=cbDstPitch;
        }

        *NewWidth=TexWidth;
        *NewHeight=TexHeight;
        *ppDstBits=pNewBits;
        return DErr_OK;
ErrorHandler:
        free(pNewBits);
        return iErrCode;
}

//=============================================================================
//函数:GetColorIndex
//描述:从调色板中找到最接近的颜色
//-----------------------------------------------------------------------------
static uint8_t GetColorIndex
(
        uint8_t R,uint8_t G,uint8_t B,        //颜色值
        D_PaletteItemP        pPaletteItems,        //调色板项
        GLsizei                        NbPaletteItems        //调色板项数
)
{
        uint32_t uLastDist=255*255*3;
        GLsizei i,ColorIndex;
        for(i=0;i<NbPaletteItems;i++)
        {
                int32_t RDiff,GDiff,BDiff;
                uint32_t uDist;
                RDiff=(int32_t)R-(int32_t)pPaletteItems.R;
                GDiff=(int32_t)G-(int32_t)pPaletteItems.G;
                BDiff=(int32_t)B-(int32_t)pPaletteItems.B;
                uDist=RDiff*RDiff+GDiff*GDiff+BDiff*BDiff;
                if(uDist<uLastDist)
                {
                        ColorIndex=i;
                        uLastDist=uDist;
                }
        }
        return (uint8_t)ColorIndex;
}

//=============================================================================
//函数:DitherPixel
//描述:进行单个像素的抖动运算
//-----------------------------------------------------------------------------
D_Func(uint8_t,DitherPixel)
(
        GLsizei                        x,
        GLsizei                        y,
        uint8_t R,uint8_t G,uint8_t B,        //颜色值
        D_PaletteItemP        pPaletteItems,        //调色板项
        GLsizei                        NbPaletteItems        //调色板项数
)
{
        float fDitherVal=(float)g_DitherMatrix/255.0f;
        uint8_t uNear=GetColorIndex(R,G,B,pPaletteItems,NbPaletteItems);
        float fOrgR,fOrgG,fOrgB;
        float fNearR,fNearG,fNearB;
        float fDistR,fDistG,fDistB;
        fOrgR=(float)R/255.0f;
        fOrgG=(float)G/255.0f;
        fOrgB=(float)B/255.0f;
        fNearR=(float)pPaletteItems.R/255.0f;
        fNearG=(float)pPaletteItems.G/255.0f;
        fNearB=(float)pPaletteItems.B/255.0f;
        fDistR=fNearR-fOrgR;
        fDistG=fNearG-fOrgG;
        fDistB=fNearB-fOrgB;
        fDitherVal=(fDitherVal*2-1)*(float)(sqrt(fDistR*fDistR
                                                                                        +fDistG*fDistG
                                                                                        +fDistB*fDistB))/1.73205080756887f;
        fNearR=fOrgR+fDitherVal;if(fNearR>1)fNearR=1;if(fNearR<0)fNearR=0;
        fNearG=fOrgG+fDitherVal;if(fNearG>1)fNearG=1;if(fNearG<0)fNearG=0;
        fNearB=fOrgB+fDitherVal;if(fNearB>1)fNearB=1;if(fNearB<0)fNearB=0;
        return GetColorIndex((uint8_t)(fNearR*255.0f),
                                               (uint8_t)(fNearG*255.0f),
                                               (uint8_t)(fNearB*255.0f),
                                               pPaletteItems,NbPaletteItems);
}

//=============================================================================
//函数:Dither
//描述:进行抖动算法
//-----------------------------------------------------------------------------
D_Func(int,Dither)
(
        D_GLContextP        pContext,                //OpenGL上下文
        GLuint                        BitCount,                //位图颜色位数,必须为24或32
        GLsizei                        BMPWidth,                //位图宽度
        GLsizei                        BMPHeight,                //位图高度
        void                        *PixelBits,                //位图
        D_PaletteItemP        pPaletteItems,        //调色板项
        GLsizei                        NbPaletteItems,        //调色板项数
        void                        *pPixelBitsOut        //输出的位图的缓冲区(8-bit位图)
)
{
        GLint        iUniform;//着色器参数

        GLuint        uTexBits=BitCount;//新位图的位数
        GLsizei        TexWidth=1,TexHeight=1;//数值扩展到2的N次方的位图的新尺寸
        void        *pTexImageBits=NULL;//新位图的存储位置

        uint8_t        SourceNot2P=0;//判断是否创建了新位图
        int                iErrCode=DErr_Unknown;
        float        fTU,fTV;
       
        if//检查参数的正确性
        (
                (BitCount!=24 && BitCount!=32) ||
                (BMPWidth<=0 || BMPHeight<=0) ||
                (!PixelBits) ||
                (!pPaletteItems) ||
                (!NbPaletteItems)
        )
                //参数错误
                return DErr_InvalidParam;

        if(pContext->hRC)
        {
                SetDitherGLContext(pContext);

#                ifdef _WIN32
                MoveWindow(pContext->hWnd,0,0,BMPWidth,BMPHeight,FALSE);
#                endif
                glViewport(0,0,BMPWidth,BMPHeight);
       
                //创建一个宽高为2的N次方的新位图,鉴于显卡的兼容性
                while(TexWidth && TexWidth<BMPWidth)TexWidth<<=1;
                if(!TexWidth)return DErr_BitmapTooLarge;
                while(TexHeight && TexHeight<BMPHeight)TexHeight<<=1;
                if(!TexHeight)return DErr_BitmapTooLarge;

                //如果原始的位图并不是宽高为2的N次方的位图,则创建新的位图并拷贝内容
                if(TexWidth!=BMPWidth || TexHeight!=BMPHeight)
                {
                        iErrCode=ConvToP2Image(PixelBits,BitCount,BMPWidth,BMPHeight,
                                &TexWidth,&TexHeight,&pTexImageBits);
                        if(iErrCode!=DErr_OK)
                                goto ErrorHandler;
                }
                else
                {
                        //如果源数据就是2的N次方尺寸的则不必重新分配内存
                        uTexBits=BitCount;
                        pTexImageBits=PixelBits;
                }

                //创建一个调色板位图
                glBindTexture(GL_TEXTURE_2D,pContext->uTextures);
                glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
                glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
                glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_REPEAT);
                glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_REPEAT);
                glTexImage2D(GL_TEXTURE_2D,0,GL_RGB,NbPaletteItems,1,0,
                        GL_RGBA,GL_UNSIGNED_BYTE,pPaletteItems);       
                glBindTexture(GL_TEXTURE_2D,0);

                //原图的纹理
                glBindTexture(GL_TEXTURE_2D,pContext->uTextures);
                glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
                glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
                glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP_TO_EDGE);
                glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP_TO_EDGE);
                glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA,TexWidth,TexHeight,0,
                        uTexBits==24?GL_RGB:GL_RGBA,GL_UNSIGNED_BYTE,pTexImageBits);       
                glBindTexture(GL_TEXTURE_2D,0);
       
                //清空缓冲区
                glClearColor(0,0,0,0);
                glClear(GL_COLOR_BUFFER_BIT);

                //矩阵变换
                glMatrixMode(GL_PROJECTION);
                glLoadIdentity();
                glMatrixMode(GL_MODELVIEW);
                glLoadIdentity();

                //设置着色器参数
                glUseProgram(pContext->uDitherProgram);
                iUniform=glGetUniformLocation(pContext->uDitherProgram,"TexWidth");
                glUniform1i(iUniform,TexWidth);
                iUniform=glGetUniformLocation(pContext->uDitherProgram,"TexHeight");
                glUniform1i(iUniform,TexHeight);
                iUniform=glGetUniformLocation(pContext->uDitherProgram,"BMPWidth");
                glUniform1i(iUniform,BMPWidth);
                iUniform=glGetUniformLocation(pContext->uDitherProgram,"BMPHeight");
                glUniform1i(iUniform,BMPHeight);
       
                //三层纹理
                glActiveTexture(GL_TEXTURE0);
                glBindTexture(GL_TEXTURE_2D,pContext->uTextures);
                glActiveTexture(GL_TEXTURE1);
                glBindTexture(GL_TEXTURE_2D,pContext->uTextures);
                glActiveTexture(GL_TEXTURE2);
                glBindTexture(GL_TEXTURE_2D,pContext->uTextures);
       
                //计算原始位图相对于新位图的尺寸
                fTU=(float)BMPWidth/(float)TexWidth;
                fTV=(float)BMPHeight/(float)TexHeight;

                //画一个全屏BillBoard,使用着色器程序进行抖动算法处理
                glBegin(GL_TRIANGLE_STRIP);
                glTexCoord2f(0,0);                glVertex2f(-1,-1);
                glTexCoord2f(fTU,0);        glVertex2f(1,-1);
                glTexCoord2f(0,fTV);        glVertex2f(-1,1);
                glTexCoord2f(fTU,fTV);        glVertex2f(1,1);
                glEnd();
                glFlush();

                glActiveTexture(GL_TEXTURE0);
                glBindTexture(GL_TEXTURE_2D,0);
                glActiveTexture(GL_TEXTURE1);
                glBindTexture(GL_TEXTURE_2D,0);
                glActiveTexture(GL_TEXTURE2);
                glBindTexture(GL_TEXTURE_2D,0);

                //如果不提供输出数据的指针的话,直接显示到屏幕
                if(pPixelBitsOut)
                        glReadPixels(0,0,BMPWidth,BMPHeight,GL_BLUE,GL_UNSIGNED_BYTE,pPixelBitsOut);
        }
        else//如果硬件不支持则使用软件抖动
        {
                if(BitCount==24)
                {
                        D_RGB24P        pInLinePtr;//输入位图的行指针
                        uint8_t                *pOutLinePtr;//输出位图的行指针
                        size_t                cbInPitch,cbOutPitch;//每行字节数
                        GLsizei                x,y;

                        pInLinePtr=(D_RGB24P)PixelBits;
                        pOutLinePtr=(uint8_t*)pPixelBitsOut;
                        cbInPitch=CalcPitch(BitCount,BMPWidth);
                        cbOutPitch=CalcPitch(8,BMPWidth);

                        for(y=0;y<BMPHeight;y++)
                        {
                                //处理当前行
                                D_RGB24P pInPtr=pInLinePtr;
                                uint8_t*pOutPtr=pOutLinePtr;
                                for(x=0;x<BMPWidth;x++)
                                {
                                        *pOutPtr++=DitherPixel(x,y,pInPtr->R,pInPtr->G,pInPtr->B,
                                                pPaletteItems,NbPaletteItems);
                                        pInPtr++;
                                }
                                //移至下一行
                                (uint8_t*)pInLinePtr+=cbInPitch;
                                (uint8_t*)pOutLinePtr+=cbOutPitch;
                        }
                }
                else if(BitCount==32)
                {
                        D_RGB32P        pInLinePtr;//输入位图的行指针
                        uint8_t                *pOutLinePtr;//输出位图的行指针
                        size_t                cbInPitch,cbOutPitch;//每行字节数
                        GLsizei                x,y;

                        pInLinePtr=(D_RGB32P)PixelBits;
                        pOutLinePtr=(uint8_t*)pPixelBitsOut;
                        cbInPitch=CalcPitch(BitCount,BMPWidth);
                        cbOutPitch=CalcPitch(8,BMPWidth);

                        for(y=0;y<BMPHeight;y++)
                        {
                                //处理当前行
                                D_RGB32P pInPtr=pInLinePtr;
                                uint8_t*pOutPtr=pOutLinePtr;
                                for(x=0;x<BMPWidth;x++)
                                {
                                        *pOutPtr++=DitherPixel(x,y,pInPtr->R,pInPtr->G,pInPtr->B,
                                                pPaletteItems,NbPaletteItems);
                                        pInPtr++;
                                }
                                //移至下一行
                                (uint8_t*)pInLinePtr+=cbInPitch;
                                (uint8_t*)pOutLinePtr+=cbOutPitch;
                        }
                }
        }

        if(SourceNot2P)
                free(pTexImageBits);
        return DErr_OK;
ErrorHandler:
        if(SourceNot2P)
                free(pTexImageBits);
        return iErrCode;
}Fragment Shader着色器代码:varying vec2 vTexCoord;//纹理坐标
varying vec2 vDitherCoord;//抖动矩阵的纹理坐标

uniform sampler2D Pal;//调色板采样器
uniform sampler2D Tex;//纹理采样器
uniform sampler2D DitherMat;//抖动矩阵

#define InitDistSq 999.0
#define        MaxDist 0.25
#define        MaxDistSq (MaxDist*MaxDist)

void main()
{
        int i,iNearestColor=0;
        vec4 vOrgColor;//原颜色
        vec3 vCurPos;//当前位置
        vec3 vCurVec;//当前向量
        float fDitherVal;//抖动亮度值
        float fLastDistSq;

        //初始值
        gl_FragColor=texture2D(Pal,vec2(0,0));
        vCurPos=gl_FragColor.xyz;
        vCurVec=vCurPos-vOrgColor.rgb;
       
        //原始颜色
        vOrgColor=texture2D(Tex,vTexCoord);

        //找出最接近的颜色
        fLastDistSq=InitDistSq;
        for(i=0;i<256;i++)
        {
                vec4 vPalColor=texture2D(Pal,vec2(float(i)/256.0,0));
                vec3 vDiff=vPalColor.rgb-vOrgColor.rgb;
                float fDistanceSq=dot(vDiff,vDiff);
                if(fDistanceSq<fLastDistSq)
                {
                        fLastDistSq=fDistanceSq;
                        gl_FragColor=vPalColor;
                        iNearestColor=i;
                }
        }

        //抖动亮度值
        fDitherVal=(texture2D(DitherMat,vDitherCoord).b*2.0-1.0)*sqrt(fLastDistSq)/1.7320508075688772935274463415059;
       
        //抖动原始颜色
        vOrgColor+=vec4(fDitherVal,fDitherVal,fDitherVal,0);
       
        //找出抖动后的最接近颜色
        fLastDistSq=InitDistSq;
        for(i=0;i<256;i++)
        {
                vec4 vPalColor=texture2D(Pal,vec2(float(i)/256.0,0));
                vec3 vDiff=vPalColor.rgb-vOrgColor.rgb;
                float fDistanceSq=dot(vDiff,vDiff);
                if(fDistanceSq<fLastDistSq)
                {
                        fLastDistSq=fDistanceSq;
                        gl_FragColor=vPalColor;
                        iNearestColor=i;
                }
        }
       
        //输出颜色表索引
        gl_FragColor=vec4(vec3(iNearestColor,iNearestColor,iNearestColor)/255.0,1);
}BIN:
SRC:

元始天尊 发表于 2015-3-9 21:56:43

哇,代码快上千了

CarbonEddy 发表于 2015-3-25 17:02:43

牛的一逼

Ink_Hin_fifteen 发表于 2019-5-10 20:42:50

代码有点长,我先收下,慢慢看。
页: [1]
查看完整版本: 【C】真·抖动算法(GPU加速)Dithering with GPU